From 10a2229a215fdf43b16af5f2bdc9bd66040c3552 Mon Sep 17 00:00:00 2001 From: robin Date: Sat, 3 Jun 2023 01:37:38 +0800 Subject: [PATCH 1/5] feat(bing): enable BIC for cli mode add some options related to it. --- bin/cli.js | 13 +++++-- bin/server.js | 12 ++++++ settings.example.js | 30 +++++++++++++-- src/BingAIClient.js | 89 ++++++++++++++++++++++++++++++++------------- 4 files changed, 111 insertions(+), 33 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 3b50360..fd1bd87 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -40,9 +40,16 @@ if (settings.storageFilePath && !settings.cacheOptions.store) { settings.cacheOptions.store = new KeyvFile({ filename: settings.storageFilePath }); } -// Disable the image generation in cli mode always. -settings.bingAiClient.features = settings.bingAiClient.features || {}; -settings.bingAiClient.features.genImage = false; +// Kick bing's the image generation options for cli. +const enableBicImage = { + enable: settings.bingAiClient?.features?.genImage?.cli?.enable + || (settings.bingAiClient?.features ? false : (settings.bingAiClient.features = {}, false)), + type: settings.bingAiClient?.features?.genImage?.cli?.type || 'markdown_list', +}; +if (enableBicImage.type === 'iframe' || !BingAIClient.isValidBicType(enableBicImage.type)) { + enableBicImage.type = 'markdown_list'; +} +settings.bingAiClient.features.genImage = enableBicImage; let conversationData = {}; diff --git a/bin/server.js b/bin/server.js index b449494..e6f3a84 100755 --- a/bin/server.js +++ b/bin/server.js @@ -39,6 +39,18 @@ if (settings.storageFilePath && !settings.cacheOptions.store) { settings.cacheOptions.store = new KeyvFile({ filename: settings.storageFilePath }); } +// Kick bing's the image generation options for server. +const enableBicImage = { + enable: settings.bingAiClient?.features?.genImage?.server?.enable + || (settings.bingAiClient?.features ? false : (settings.bingAiClient.features = {}, false)) + || (typeof settings.bingAiClient.features?.genImage === 'boolean' ? settings.bingAiClient.features.genImage : false), + type: settings.bingAiClient?.features?.genImage?.server?.type || 'iframe', +}; +if (!BingAIClient.isValidBicType(enableBicImage.type)) { + enableBicImage.type = 'markdown_list'; +} +settings.bingAiClient.features.genImage = enableBicImage; + const clientToUse = settings.apiOptions?.clientToUse || settings.clientToUse || 'chatgpt'; const perMessageClientOptionsWhitelist = settings.apiOptions?.perMessageClientOptionsWhitelist || null; diff --git a/settings.example.js b/settings.example.js index 15f1035..1f45202 100644 --- a/settings.example.js +++ b/settings.example.js @@ -49,10 +49,32 @@ export default { // (Optional) Set 'x-forwarded-for' for the request. You can use a fixed IPv4 address or specify a range using CIDR notation, // and the program will randomly select an address within that range. The 'x-forwarded-for' is not used by default now. // xForwardedFor: '13.104.0.0/14', - // (Optional) Set 'genImage' to true to enable bing to create images for you. It's disabled by default. - // features: { - // genImage: true, - // }, + // (Optional) Set the tone style, possible options: "creative", "precise", "fast", "balanced"(default) + // Note: To enable BIC function, it must be set to "creative" + // toneStyle: 'creative', + // (Optional) The optional features options, these features are all disabled by default + features: { + // (Optional | Deprecated) Set 'genImage' to true to enable BIC. (Default: "false") + // Note: It's deprecated by the new option style. It is only retained for compatibility purposes. + // genImage: true, + // (Optional) Options for genImage + genImage: { + // (Optional) Options for server mode + server: { + // (Optional) Set 'enable' to true to enable BIC + // enable: true, + // (Optional) Possible options: "iframe"(default), "url_list", "markdown_list" + type: 'iframe', + }, + // (Optional) Options for cli mode + cli: { + // (Optional) Set 'enable' to true to enable BIC + // enable: true, + // (Optional) Possible options: "url_list", "markdown_list"(default). + type: 'markdown_list', + }, + }, + }, // (Optional) Set to true to enable `console.debug()` logging debug: false, }, diff --git a/src/BingAIClient.js b/src/BingAIClient.js index f34df38..0bd1c06 100644 --- a/src/BingAIClient.js +++ b/src/BingAIClient.js @@ -40,6 +40,7 @@ export default class BingAIClient { this.options = { ...options, host: options.host || 'https://www.bing.com', + toneStyle: options.toneStyle || 'balanced', // or creative, precise, fast, xForwardedFor: this.constructor.getValidIPv4(options.xForwardedFor), features: { genImage: options?.features?.genImage || false, @@ -47,8 +48,13 @@ export default class BingAIClient { }; } this.debug = this.options.debug; - if (this.options.features.genImage) { - this.bic = new BingImageCreator(this.options); + if (this.options.features?.genImage?.enable) { + this.bic = this.bic ?? { + api: new BingImageCreator(this.options), + type: this.options?.features?.genImage?.type || 'iframe', + }; + } else { + this.bic = undefined; } } @@ -76,6 +82,10 @@ export default class BingAIClient { return undefined; } + static isValidBicType(bicType) { + return (bicType === 'iframe' || bicType === 'url_list' || bicType === 'markdown_list'); + } + async createNewConversation() { const fetchOptions = { headers: { @@ -95,7 +105,7 @@ export default class BingAIClient { 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'sec-ms-gec': genRanHex(64).toUpperCase(), - 'sec-ms-gec-version': '1-115.0.1866.1', + 'sec-ms-gec-version': '1-113.0.1774.57', 'x-ms-client-request-id': crypto.randomUUID(), 'x-ms-useragent': 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50', @@ -103,7 +113,6 @@ export default class BingAIClient { Referer: 'https://www.bing.com/search?q=Bing+AI&showconv=1', 'Referrer-Policy': 'origin-when-cross-origin', // Workaround for request being blocked due to geolocation - // 'x-forwarded-for': '1.1.1.1', // 1.1.1.1 seems to no longer work. ...(this.options.xForwardedFor ? { 'x-forwarded-for': this.options.xForwardedFor } : {}), }, }; @@ -113,7 +122,7 @@ export default class BingAIClient { const response = await fetch(`${this.options.host}/turing/conversation/create`, fetchOptions); const { status, headers } = response; - if (status === 200 && +headers.get('content-length') < 5) { + if (status === 200 && headers.get('content-length') < 5) { throw new Error('/turing/conversation/create: Your IP is blocked by BingAI.'); } @@ -204,7 +213,7 @@ export default class BingAIClient { } = opts; const { - toneStyle = 'balanced', // or creative, precise, fast + toneStyle = this.options.toneStyle, invocationId = 0, systemMessage, context, @@ -345,7 +354,7 @@ export default class BingAIClient { 'cricinfov2', 'dv3sugg', 'nojbfedge', - ...((toneStyle === 'creative' && this.options.features.genImage) ? ['gencontentv3'] : []), + ...((toneStyle === 'creative' && this?.bic) ? ['gencontentv3'] : []), ], sliceIds: [ '222dtappid', @@ -414,7 +423,7 @@ export default class BingAIClient { reject(new Error('Request aborted')); }); - let bicIframe; + let bicContent; ws.on('message', async (data) => { const objects = data.toString().split(''); const events = objects.map((object) => { @@ -439,15 +448,32 @@ export default class BingAIClient { } if (messages[0]?.contentType === 'IMAGE') { // You will never get a message of this type without 'gencontentv3' being on. - bicIframe = this.bic.genImageIframeSsr( - messages[0].text, - messages[0].messageId, - progress => (progress?.contentIframe ? onProgress(progress?.contentIframe) : null), - ).catch((error) => { - onProgress(error.message); - bicIframe.isError = true; - return error.message; - }); + if (this.bic.type === 'iframe') { + bicContent = this.bic.api.genImageIframeSsr( + messages[0].text, + messages[0].messageId, + progress => (progress?.contentIframe ? onProgress(progress?.contentIframe) : null), + ).catch((error) => { + onProgress(error.message); + bicContent.isError = true; + return error.message; + }); + } else { + bicContent = this.bic.api.genImageList( + messages[0].text, + messages[0].messageId, + false, + () => onProgress('.'), + ).catch((error) => { + onProgress(error.message); + bicContent.isError = true; + delete bicContent.isList; + return error.message; + }); + bicContent.isList = true; + bicContent.useMarkdown = this.bic.type === 'markdown_list'; + } + bicContent.prompt = messages[0].text; return; } const updatedText = messages[0].text; @@ -519,22 +545,33 @@ export default class BingAIClient { // delete useless suggestions from moderation filter delete eventMessage.suggestedResponses; } - if (bicIframe) { - // the last messages will be a image creation event if bicIframe is present. + if (bicContent) { + // the last messages will be a image creation event if bicContent is present. let i = messages.length - 1; while (eventMessage?.contentType === 'IMAGE' && i > 0) { eventMessage = messages[i -= 1]; } - // wait for bicIframe to be completed. + // wait for bicContent to be completed. // since we added a catch, we do not need to wrap this with a try catch block. - const imgIframe = await bicIframe; - if (!imgIframe?.isError) { - eventMessage.adaptiveCards[0].body[0].text += imgIframe; - } else { - eventMessage.text += `
${imgIframe}`; - eventMessage.adaptiveCards[0].body[0].text = eventMessage.text; + let bicResult = await bicContent; + let images; + if (bicContent?.isList) { + images = bicResult; + if (bicContent.useMarkdown) { + bicResult = `${bicResult.map((s, idx) => `![${idx + 1}.${bicContent.prompt}](${s})`).join('\n')}`; + } else { + bicResult = `${bicResult.map((s, idx) => `${idx + 1}.${s}`).join('\n')}`; + } + } else if (bicResult?.isError) { + eventMessage.text += `\n${bicResult}`; } + eventMessage.adaptiveCards[0].body[0].text += `\n${bicResult}`; + eventMessage.bic = { + type: this.bic.type, + prompt: bicContent.prompt, + ...(images ? { images } : {}), + }; } resolve({ message: eventMessage, From 8eb412ffe1950689864e22c9f1a84985b7f80355 Mon Sep 17 00:00:00 2001 From: robin Date: Sat, 3 Jun 2023 02:20:54 +0800 Subject: [PATCH 2/5] build(deps): bump @timefox/bic-sydney from 1.1.2 to 1.1.4 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index a5a11db..db9b5d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@dqbd/tiktoken": "^1.0.2", "@fastify/cors": "^8.2.0", - "@timefox/bic-sydney": "^1.1.2", + "@timefox/bic-sydney": "^1.1.4", "@waylaidwanderer/fastify-sse-v2": "^3.1.0", "@waylaidwanderer/fetch-event-source": "^3.0.1", "boxen": "^7.0.1", @@ -247,9 +247,9 @@ } }, "node_modules/@timefox/bic-sydney": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@timefox/bic-sydney/-/bic-sydney-1.1.3.tgz", - "integrity": "sha512-A50xHH4ucOttShpEGhejJRGqXwPH5cjPjrFSuFyDu0Y7p6XcQ0GaXDfjnNDIDrzDptr+dRgElK0akINsZse8sg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@timefox/bic-sydney/-/bic-sydney-1.1.4.tgz", + "integrity": "sha512-ONeS0weT+ZoE471TDdzPqkKRk+VFr7sEL5+qEq1nIur6XMuVZ8cvlBicUNHfhYKIavkOM8xmBnk2dfVFQ54aiQ==", "dependencies": { "fetch-undici": "^3.0.1", "undici": "^5.22.1" @@ -4473,9 +4473,9 @@ } }, "@timefox/bic-sydney": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@timefox/bic-sydney/-/bic-sydney-1.1.3.tgz", - "integrity": "sha512-A50xHH4ucOttShpEGhejJRGqXwPH5cjPjrFSuFyDu0Y7p6XcQ0GaXDfjnNDIDrzDptr+dRgElK0akINsZse8sg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@timefox/bic-sydney/-/bic-sydney-1.1.4.tgz", + "integrity": "sha512-ONeS0weT+ZoE471TDdzPqkKRk+VFr7sEL5+qEq1nIur6XMuVZ8cvlBicUNHfhYKIavkOM8xmBnk2dfVFQ54aiQ==", "requires": { "fetch-undici": "^3.0.1", "undici": "^5.22.1" diff --git a/package.json b/package.json index 88d93b8..c364919 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "dependencies": { "@dqbd/tiktoken": "^1.0.2", "@fastify/cors": "^8.2.0", - "@timefox/bic-sydney": "^1.1.2", + "@timefox/bic-sydney": "^1.1.4", "@waylaidwanderer/fastify-sse-v2": "^3.1.0", "@waylaidwanderer/fetch-event-source": "^3.0.1", "boxen": "^7.0.1", From 1fb625534f548ef8049586f9530c127e37467924 Mon Sep 17 00:00:00 2001 From: BobMaster Date: Fri, 2 Jun 2023 15:04:28 +0800 Subject: [PATCH 3/5] chore(docker): fix host binding in container (#399) Use prebuild image from ghcr and match working directory in dockerfile --- Dockerfile | 1 + docker-compose.yml | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index bae2600..4fbdeca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ FROM node:16-alpine +ENV API_HOST=0.0.0.0 WORKDIR /app COPY . . diff --git a/docker-compose.yml b/docker-compose.yml index c56af8a..b6da243 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,14 @@ version: '3' services: app: - build: - context: . - dockerfile: ./Dockerfile + # build: + # context: . + # dockerfile: ./Dockerfile + image: ghcr.io/waylaidwanderer/node-chatgpt-api:latest + restart: unless-stopped environment: - OPENAI_API_KEY=${OPENAI_API_KEY} volumes: - - ./settings.js:/var/chatgpt-api/settings.js:cached + - ./settings.js:/app/settings.js:cached ports: - '${APP_PORT:-3000}:3000' From b95951932edcb7882ac94b9bbb517002eb9aeacf Mon Sep 17 00:00:00 2001 From: timefox Date: Sat, 3 Jun 2023 15:58:11 +0800 Subject: [PATCH 4/5] feat (demos/bing): add an example to show how BIC works --- demos/use-bing-client.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/demos/use-bing-client.js b/demos/use-bing-client.js index 4f3f1e5..3a90070 100644 --- a/demos/use-bing-client.js +++ b/demos/use-bing-client.js @@ -58,6 +58,31 @@ response = await bingAIClient.sendMessage('Could you provide short and precise t }); console.log(JSON.stringify(response, null, 2)); // {"jailbreakConversationId":false,"conversationId":"...","conversationSignature":"...","clientId":"...","invocationId":2,"messageId":"...","conversationExpiryTime":"2023-03-08T03:20:23.463914Z","response":"Some possible takeaways from the document are... Some early users of ChatGPT and Whisper APIs include Snap Inc., Quizlet, Instacart, Shopify and Speak.","details":{ /* raw response... */ }} +/* +Let bing draw a picture for us with bing image creator. +This will return a `bic` object that you can use to obtain an image url array if bing draws them. + +Note: this requires creative tone style. + */ +bingAIClient = new BingAIClient(options); + +response = await bingAIClient.sendMessage('Tell me a story of a cool little fox and a group of github friends, and draw some images for it', { + // (Required) Set a conversation style to 'creative' + toneStyle: 'creative', + clientOptions: { + features: { + genImage: { + enable: true, + type: 'markdown_list', + }, + }, + }, + onProgress: (token) => { + process.stdout.write(token); + }, +}); +console.log(JSON.stringify(response, null, 2)); // {..., "details":{ ..., "bic": {"type": "markdown_list", "prompt": "Octocat", "images": [...]}}} + /* Activate jailbreak mode by setting `jailbreakConversationId` to `true`. This will return a `jailbreakConversationId` that you can use to continue the conversation. From 40bdd05fd3406fe5fa8f921befb8a257026fc079 Mon Sep 17 00:00:00 2001 From: timefox Date: Tue, 6 Jun 2023 14:48:29 +0800 Subject: [PATCH 5/5] fix(bing): fix the mistakenly deleted "+" symbol --- src/BingAIClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BingAIClient.js b/src/BingAIClient.js index 0bd1c06..f79aeea 100644 --- a/src/BingAIClient.js +++ b/src/BingAIClient.js @@ -122,7 +122,7 @@ export default class BingAIClient { const response = await fetch(`${this.options.host}/turing/conversation/create`, fetchOptions); const { status, headers } = response; - if (status === 200 && headers.get('content-length') < 5) { + if (status === 200 && +headers.get('content-length') < 5) { throw new Error('/turing/conversation/create: Your IP is blocked by BingAI.'); }