From dc1943ff5899192ee4c20cf7200dd233037da6bb Mon Sep 17 00:00:00 2001 From: parr Date: Wed, 31 Jan 2024 23:34:30 +0000 Subject: [PATCH 1/4] Added stop feature --- src/helpers/completion.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/helpers/completion.ts b/src/helpers/completion.ts index fe98323..c4a9c65 100644 --- a/src/helpers/completion.ts +++ b/src/helpers/completion.ts @@ -9,6 +9,7 @@ import { streamToString } from './stream-to-string'; import './replace-all-polyfill'; import i18n from './i18n'; import { stripRegexPatterns } from './strip-regex-patterns'; +import readline from 'readline'; const explainInSecondRequest = true; @@ -187,6 +188,20 @@ export const readData = ) => (writer: (data: string) => void): Promise => new Promise(async (resolve) => { + const rl = readline.createInterface({ + input: process.stdin, + }); + + rl.input.setRawMode(true); + + rl.input.on('keypress', (key) => { + + if (typeof key === 'string' && key.toLowerCase().includes('q')) { + stopWriting = true; + } + }); + + let stopWriting = false; let data = ''; let content = ''; let dataStart = false; @@ -199,7 +214,7 @@ export const readData = const payloads = chunk.toString().split('\n\n'); for (const payload of payloads) { - if (payload.includes('[DONE]')) { + if (payload.includes('[DONE]' || stopWriting)) { dataStart = false; resolve(data); return; From c3c287dc5c7a8f1cff87db60460eda00bb257919 Mon Sep 17 00:00:00 2001 From: Mayday Date: Fri, 2 Feb 2024 01:24:44 +0000 Subject: [PATCH 2/4] Fix Identation --- src/helpers/completion.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/helpers/completion.ts b/src/helpers/completion.ts index c4a9c65..6306280 100644 --- a/src/helpers/completion.ts +++ b/src/helpers/completion.ts @@ -15,7 +15,7 @@ const explainInSecondRequest = true; function getOpenAi(key: string, apiEndpoint: string) { const openAi = new OpenAIApi( - new Configuration({ apiKey: key, basePath: apiEndpoint }) + new Configuration({ apiKey: key, basePath: apiEndpoint }), ); return openAi; } @@ -74,7 +74,7 @@ export async function generateCompletion({ n: Math.min(number, 10), stream: true, }, - { responseType: 'stream' } + { responseType: 'stream' }, ); return completion.data as unknown as IncomingMessage; @@ -83,7 +83,7 @@ export async function generateCompletion({ if (error.code === 'ENOTFOUND') { throw new KnownError( - `Error connecting to ${error.request.hostname} (${error.request.syscall}). Are you connected to the internet?` + `Error connecting to ${error.request.hostname} (${error.request.syscall}). Are you connected to the internet?`, ); } @@ -91,7 +91,7 @@ export async function generateCompletion({ let message = response?.data as string | object | IncomingMessage; if (response && message instanceof IncomingMessage) { message = await streamToString( - response.data as unknown as IncomingMessage + response.data as unknown as IncomingMessage, ); try { // Handle if the message is JSON. It should be but occasionally will @@ -114,7 +114,7 @@ export async function generateCompletion({ ` + '\n\n' + messageString + - '\n' + '\n', ); } else if (response && message) { throw new KnownError( @@ -123,7 +123,7 @@ export async function generateCompletion({ ` + '\n\n' + messageString + - '\n' + '\n', ); } @@ -191,11 +191,10 @@ export const readData = const rl = readline.createInterface({ input: process.stdin, }); - + rl.input.setRawMode(true); - + rl.input.on('keypress', (key) => { - if (typeof key === 'string' && key.toLowerCase().includes('q')) { stopWriting = true; } @@ -237,7 +236,7 @@ export const readData = if (dataStart && content) { const contentWithoutExcluded = stripRegexPatterns( content, - excluded + excluded, ); data += contentWithoutExcluded; From c12511b21c4fb3dce97f8ac40e443f215c7d9729 Mon Sep 17 00:00:00 2001 From: thelastmayday Date: Thu, 8 Feb 2024 18:08:45 +0000 Subject: [PATCH 3/4] Added escape key to stop & Improved code strucure --- package-lock.json | 8 +++--- package.json | 2 +- src/helpers/completion.ts | 60 ++++++++++++++++----------------------- 3 files changed, 30 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 943f90b..ab52e2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "eslint-plugin-unused-imports": "^2.0.0", "jiti": "^1.17.0", "pkgroll": "^1.9.0", - "prettier": "^2.8.7", + "prettier": "^2.8.8", "typescript": "^4.9.5" } }, @@ -2772,9 +2772,9 @@ } }, "node_modules/prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" diff --git a/package.json b/package.json index 41ff672..3f00c59 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "eslint-plugin-unused-imports": "^2.0.0", "jiti": "^1.17.0", "pkgroll": "^1.9.0", - "prettier": "^2.8.7", + "prettier": "^2.8.8", "typescript": "^4.9.5" } } diff --git a/src/helpers/completion.ts b/src/helpers/completion.ts index 6306280..efe18cc 100644 --- a/src/helpers/completion.ts +++ b/src/helpers/completion.ts @@ -15,7 +15,7 @@ const explainInSecondRequest = true; function getOpenAi(key: string, apiEndpoint: string) { const openAi = new OpenAIApi( - new Configuration({ apiKey: key, basePath: apiEndpoint }), + new Configuration({ apiKey: key, basePath: apiEndpoint }) ); return openAi; } @@ -74,7 +74,7 @@ export async function generateCompletion({ n: Math.min(number, 10), stream: true, }, - { responseType: 'stream' }, + { responseType: 'stream' } ); return completion.data as unknown as IncomingMessage; @@ -83,7 +83,7 @@ export async function generateCompletion({ if (error.code === 'ENOTFOUND') { throw new KnownError( - `Error connecting to ${error.request.hostname} (${error.request.syscall}). Are you connected to the internet?`, + `Error connecting to ${error.request.hostname} (${error.request.syscall}). Are you connected to the internet?` ); } @@ -91,7 +91,7 @@ export async function generateCompletion({ let message = response?.data as string | object | IncomingMessage; if (response && message instanceof IncomingMessage) { message = await streamToString( - response.data as unknown as IncomingMessage, + response.data as unknown as IncomingMessage ); try { // Handle if the message is JSON. It should be but occasionally will @@ -114,7 +114,7 @@ export async function generateCompletion({ ` + '\n\n' + messageString + - '\n', + '\n' ); } else if (response && message) { throw new KnownError( @@ -123,7 +123,7 @@ export async function generateCompletion({ ` + '\n\n' + messageString + - '\n', + '\n' ); } @@ -188,74 +188,64 @@ export const readData = ) => (writer: (data: string) => void): Promise => new Promise(async (resolve) => { + let stopTextStream = false; + let data = ''; + let content = ''; + let dataStart = false; + let buffer = ''; + const [excludedPrefix] = excluded; + const stopTextStreamKeys = ['q', 'escape']; + const rl = readline.createInterface({ input: process.stdin, }); - rl.input.setRawMode(true); + process.stdin.setRawMode(true); - rl.input.on('keypress', (key) => { - if (typeof key === 'string' && key.toLowerCase().includes('q')) { - stopWriting = true; + process.stdin.on('keypress', (key, data) => { + if (stopTextStreamKeys.includes(data.name)) { + stopTextStream = true; } }); - - let stopWriting = false; - let data = ''; - let content = ''; - let dataStart = false; - // This buffer will temporarily hold incoming data only for detecting the start - let buffer = ''; - - const [excludedPrefix] = excluded; - for await (const chunk of iterableStream) { const payloads = chunk.toString().split('\n\n'); - for (const payload of payloads) { - if (payload.includes('[DONE]' || stopWriting)) { + if (payload.includes('[DONE]') || stopTextStream) { dataStart = false; resolve(data); return; } - if (payload.startsWith('data:')) { content = parseContent(payload); - // Use buffer only for start detection if (!dataStart) { - // Append content to the buffer buffer += content; if (buffer.match(excludedPrefix ?? '')) { dataStart = true; - // Clear the buffer once it has served its purpose buffer = ''; if (excludedPrefix) break; } } - if (dataStart && content) { const contentWithoutExcluded = stripRegexPatterns( content, - excluded, + excluded ); - data += contentWithoutExcluded; writer(contentWithoutExcluded); } } } } - - function parseContent(payload: string): string { - const data = payload.replaceAll(/(\n)?^data:\s*/g, ''); + function parseContent(payload) { + const data2 = payload.replaceAll(/(\n)?^data:\s*/g, ''); try { - const delta = JSON.parse(data.trim()); + const delta = JSON.parse(data2.trim()); return delta.choices?.[0].delta?.content ?? ''; } catch (error) { - return `Error with JSON.parse and ${payload}.\n${error}`; + return `Error with JSON.parse and ${payload}. +${error}`; } } - resolve(data); }); From acbdc51f11e4fb5c0b35858f5901742a120b3562 Mon Sep 17 00:00:00 2001 From: thelastmayday Date: Thu, 8 Feb 2024 18:26:12 +0000 Subject: [PATCH 4/4] General fixes --- src/helpers/completion.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/helpers/completion.ts b/src/helpers/completion.ts index efe18cc..85c1591 100644 --- a/src/helpers/completion.ts +++ b/src/helpers/completion.ts @@ -192,9 +192,10 @@ export const readData = let data = ''; let content = ''; let dataStart = false; - let buffer = ''; + let buffer = ''; // This buffer will temporarily hold incoming data only for detecting the start + const [excludedPrefix] = excluded; - const stopTextStreamKeys = ['q', 'escape']; + const stopTextStreamKeys = ['q', 'escape']; //Group of keys that stop the text stream const rl = readline.createInterface({ input: process.stdin, @@ -215,37 +216,44 @@ export const readData = resolve(data); return; } + if (payload.startsWith('data:')) { content = parseContent(payload); + // Use buffer only for start detection if (!dataStart) { + // Append content to the buffer buffer += content; if (buffer.match(excludedPrefix ?? '')) { dataStart = true; + // Clear the buffer once it has served its purpose buffer = ''; if (excludedPrefix) break; } } + if (dataStart && content) { const contentWithoutExcluded = stripRegexPatterns( content, excluded ); + data += contentWithoutExcluded; writer(contentWithoutExcluded); } } } } - function parseContent(payload) { - const data2 = payload.replaceAll(/(\n)?^data:\s*/g, ''); + + function parseContent(payload: string): string { + const data = payload.replaceAll(/(\n)?^data:\s*/g, ''); try { - const delta = JSON.parse(data2.trim()); + const delta = JSON.parse(data.trim()); return delta.choices?.[0].delta?.content ?? ''; } catch (error) { - return `Error with JSON.parse and ${payload}. -${error}`; + return `Error with JSON.parse and ${payload}.\n${error}`; } } + resolve(data); });