From 505024f898a9aa0cff7f4257f23b5ce5c05d55b5 Mon Sep 17 00:00:00 2001 From: ishan dutta Date: Sun, 23 Jul 2023 13:44:16 +0530 Subject: [PATCH] revered to old chatgpt provider --- build.mjs | 4 +- package-lock.json | 48 ++++---- package.json | 1 + src/background/providers/chatgpt.ts | 140 ++++++++++++++++++++++-- src/background/providers/chatgpt_new.ts | 139 +++++++++++++++++++++++ src/utils/consts.ts | 4 + 6 files changed, 306 insertions(+), 30 deletions(-) create mode 100644 src/background/providers/chatgpt_new.ts create mode 100644 src/utils/consts.ts diff --git a/build.mjs b/build.mjs index 44ed6e1..9463782 100644 --- a/build.mjs +++ b/build.mjs @@ -26,8 +26,8 @@ async function runEsbuild() { bundle: true, outdir: outdir, treeShaking: true, - minify: true, - drop: ['console', 'debugger'], + // minify: true, + // drop: ['console', 'debugger'], legalComments: 'none', define: { 'process.env.NODE_ENV': '"production"', diff --git a/package-lock.json b/package-lock.json index b00502e..36a7c89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "@geist-ui/core": "^2.3.8", "@geist-ui/icons": "^1.0.2", "@primer/octicons-react": "^17.9.0", + "dayjs": "^1.11.9", "eventsource-parser": "^0.0.5", "expiry-map": "^2.0.0", "github-markdown-css": "^5.1.0", @@ -709,9 +710,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -835,9 +836,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -898,9 +899,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1901,6 +1902,11 @@ "url": "https://github.com/sponsors/MobileFirstLLC" } }, + "node_modules/dayjs": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", + "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5929,9 +5935,9 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -6220,9 +6226,9 @@ } }, "node_modules/superagent/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6756,9 +6762,9 @@ "dev": true }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6838,9 +6844,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", - "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", "dev": true, "engines": { "node": ">= 14" diff --git a/package.json b/package.json index d1cb43e..0dfc6a8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@geist-ui/core": "^2.3.8", "@geist-ui/icons": "^1.0.2", "@primer/octicons-react": "^17.9.0", + "dayjs": "^1.11.9", "eventsource-parser": "^0.0.5", "expiry-map": "^2.0.0", "github-markdown-css": "^5.1.0", diff --git a/src/background/providers/chatgpt.ts b/src/background/providers/chatgpt.ts index 6911d80..3db251f 100644 --- a/src/background/providers/chatgpt.ts +++ b/src/background/providers/chatgpt.ts @@ -1,7 +1,10 @@ +import dayjs from 'dayjs' import ExpiryMap from 'expiry-map' import { v4 as uuidv4 } from 'uuid' +import { ADAY, APPSHORTNAME, HALFHOUR } from '../../utils/consts' import { fetchSSE } from '../fetch-sse' import { GenerateAnswerParams, Provider } from '../types' +dayjs().format() async function request(token: string, method: string, path: string, data?: unknown) { return fetch(`https://chat.openai.com/backend-api${path}`, { @@ -14,6 +17,34 @@ async function request(token: string, method: string, path: string, data?: unkno }) } +async function request_new( + token: string, + method: string, + path: string, + data?: unknown, + callback?: unknown, +) { + return fetch(`https://chat.openai.com/backend-api${path}`, { + method, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: data === undefined ? undefined : JSON.stringify(data), + }) + .then(function (response) { + console.log('fetch', token != null, method, path, 'response', response) + return response.json() + }) + .then(function (data) { + console.log('response data', data) + if (callback) callback(token, data) + }) + .catch((error) => { + console.error('fetch', token, method, path, 'error', error) + }) +} + export async function sendMessageFeedback(token: string, data: unknown) { await request(token, 'POST', '/conversation/message_feedback', data) } @@ -26,6 +57,61 @@ export async function setConversationProperty( await request(token, 'PATCH', `/conversation/${conversationId}`, propertyObject) } +const browsertabIdConversationIdMap = new Map() +const windowIdConversationIdMap = new Map() + +function deleteRecentConversations(token, data) { + const now = dayjs() + const startTime = dayjs(performance.timeOrigin) + console.log('startTime', startTime) + const convs = data.items + console.log('convs', convs) + for (let i = 0; i < convs.length; i++) { + const conv_i_time = dayjs(convs[i].create_time) + console.log( + 'conv' + i, + convs[i].id, + conv_i_time, + conv_i_time - startTime, + now - conv_i_time, + now - conv_i_time < ADAY, + ) + if ( + HALFHOUR < now - conv_i_time && + now - conv_i_time < ADAY && + convs[i].title.indexOf(APPSHORTNAME + ':') != -1 + ) { + setTimeout(function () { + console.log('Deleting', token != null, convs[i].id) + setConversationProperty(token, convs[i].id, { is_visible: false }) + const cloneBTCMap = new Map(browsertabIdConversationIdMap) + cloneBTCMap.forEach((ConversationId, tabId, map) => { + console.log('Looking for', ConversationId, tabId, 'in', map) + if (ConversationId == convs[i].id) { + console.log('Deleting ', ConversationId, tabId, 'from', map) + browsertabIdConversationIdMap.delete(tabid) + console.log( + 'browsertabIdConversationIdMap after Deleting ', + browsertabIdConversationIdMap, + ) + } + }) + const cloneWCMap = new Map(windowIdConversationIdMap) + cloneWCMap.forEach((conversationIdsConcatinated, windowId, map) => { + console.log('Looking for', conversationIdsConcatinated, windowId, 'in', map) + if (conversationIdsConcatinated.indexOf(convs[i].id) != -1) { + console.log('Deleting ', convs[i].id, windowId, 'from', map) + conversationIdsConcatinated = conversationIdsConcatinated.replace(convs[i].id, '') + conversationIdsConcatinated = conversationIdsConcatinated.replace(',,', ',') + windowIdConversationIdMap.set(windowid, conversationIdsConcatinated) + console.log('windowIdConversationIdMap after Deleting ', windowIdConversationIdMap) + } + }) + }, i * 1000) + } + } +} + const KEY_ACCESS_TOKEN = 'accessToken' const cache = new ExpiryMap(10 * 1000) @@ -49,6 +135,14 @@ export async function getChatGPTAccessToken(): Promise { export class ChatGPTProvider implements Provider { constructor(private token: string) { this.token = token + //Brute: + request_new( + token, + 'GET', + '/conversations?offset=0&limit=100&order=updated', + undefined, + deleteRecentConversations, + ) } private async fetchModels(): Promise< @@ -71,14 +165,43 @@ export class ChatGPTProvider implements Provider { async generateAnswer(params: GenerateAnswerParams) { let conversationId: string | undefined + const countWords = (text) => { + return text.trim().split(/\s+/).length + } + + const getConversationTitle = (bigtext: string) => { + let ret = bigtext.split('\n', 1)[0] + console.log('ret', ret) + try { + ret = ret.split('of the problem:')[1] + } catch (e) { + console.log(e) + } + ret = ret.split('.', 1)[0] + // console.log('ret',ret) + // try { + // ret = APPSHORTNAME + ':' + ret.split(':')[1].trim() + // } catch (e) { + // console.log(e) + // ret = APPSHORTNAME + ':' + ret.trim().slice(0, 8) + '..' + // } + console.log('ret', ret) + return APPSHORTNAME + ':' + ret + } + + const renameConversationTitle = (convId: string) => { + const titl: string = getConversationTitle(params.prompt) + console.log('renameConversationTitle:', this.token, convId, titl) + setConversationProperty(this.token, convId, { title: titl }) + } const cleanup = () => { if (conversationId) { - setConversationProperty(this.token, conversationId, { is_visible: false }) + // setConversationProperty(this.token, conversationId, { is_visible: false }) } } const modelName = await this.getModelName() - console.log('Using model:', modelName, params.conversationId, params.parentMessageId) + console.debug('Using model:', modelName) await fetchSSE('https://chat.openai.com/backend-api/conversation', { method: 'POST', @@ -114,21 +237,24 @@ export class ChatGPTProvider implements Provider { try { data = JSON.parse(message) } catch (err) { - if (new Date(message) !== 'Invalid Date' && !isNaN(new Date(message))) - console.debug('This is known issue') - else console.error(err) + console.error(err) return } - const text = data.message?.content?.parts?.[0] + '✏' + const text = data.message?.content?.parts?.[0] if (text) { + if (countWords(text) == 1 && data.message?.author?.role == 'assistant') { + if (params.prompt.indexOf('of the problem:') !== -1) { + renameConversationTitle(data.conversation_id) + } + } conversationId = data.conversation_id params.onEvent({ type: 'answer', data: { text, messageId: data.message.id, - conversationId: data.conversation_id, parentMessageId: data.parent_message_id, + conversationId: data.conversation_id, }, }) } diff --git a/src/background/providers/chatgpt_new.ts b/src/background/providers/chatgpt_new.ts new file mode 100644 index 0000000..6911d80 --- /dev/null +++ b/src/background/providers/chatgpt_new.ts @@ -0,0 +1,139 @@ +import ExpiryMap from 'expiry-map' +import { v4 as uuidv4 } from 'uuid' +import { fetchSSE } from '../fetch-sse' +import { GenerateAnswerParams, Provider } from '../types' + +async function request(token: string, method: string, path: string, data?: unknown) { + return fetch(`https://chat.openai.com/backend-api${path}`, { + method, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: data === undefined ? undefined : JSON.stringify(data), + }) +} + +export async function sendMessageFeedback(token: string, data: unknown) { + await request(token, 'POST', '/conversation/message_feedback', data) +} + +export async function setConversationProperty( + token: string, + conversationId: string, + propertyObject: object, +) { + await request(token, 'PATCH', `/conversation/${conversationId}`, propertyObject) +} + +const KEY_ACCESS_TOKEN = 'accessToken' + +const cache = new ExpiryMap(10 * 1000) + +export async function getChatGPTAccessToken(): Promise { + if (cache.get(KEY_ACCESS_TOKEN)) { + return cache.get(KEY_ACCESS_TOKEN) + } + const resp = await fetch('https://chat.openai.com/api/auth/session') + if (resp.status === 403) { + throw new Error('CLOUDFLARE') + } + const data = await resp.json().catch(() => ({})) + if (!data.accessToken) { + throw new Error('UNAUTHORIZED') + } + cache.set(KEY_ACCESS_TOKEN, data.accessToken) + return data.accessToken +} + +export class ChatGPTProvider implements Provider { + constructor(private token: string) { + this.token = token + } + + private async fetchModels(): Promise< + { slug: string; title: string; description: string; max_tokens: number }[] + > { + const resp = await request(this.token, 'GET', '/models').then((r) => r.json()) + return resp.models + } + + private async getModelName(): Promise { + try { + const models = await this.fetchModels() + return models[0].slug + } catch (err) { + console.error(err) + return 'text-davinci-002-render' + } + } + + async generateAnswer(params: GenerateAnswerParams) { + let conversationId: string | undefined + + const cleanup = () => { + if (conversationId) { + setConversationProperty(this.token, conversationId, { is_visible: false }) + } + } + + const modelName = await this.getModelName() + console.log('Using model:', modelName, params.conversationId, params.parentMessageId) + + await fetchSSE('https://chat.openai.com/backend-api/conversation', { + method: 'POST', + signal: params.signal, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.token}`, + }, + body: JSON.stringify({ + action: 'next', + messages: [ + { + id: uuidv4(), + role: 'user', + content: { + content_type: 'text', + parts: [params.prompt], + }, + }, + ], + model: modelName, + parent_message_id: params.parentMessageId || uuidv4(), + conversation_id: params.conversationId, + }), + onMessage(message: string) { + console.debug('sse message', message) + if (message === '[DONE]') { + params.onEvent({ type: 'done' }) + cleanup() + return + } + let data + try { + data = JSON.parse(message) + } catch (err) { + if (new Date(message) !== 'Invalid Date' && !isNaN(new Date(message))) + console.debug('This is known issue') + else console.error(err) + return + } + const text = data.message?.content?.parts?.[0] + '✏' + if (text) { + conversationId = data.conversation_id + params.onEvent({ + type: 'answer', + data: { + text, + messageId: data.message.id, + conversationId: data.conversation_id, + parentMessageId: data.parent_message_id, + }, + }) + } + }, + }) + return { cleanup } + } +} diff --git a/src/utils/consts.ts b/src/utils/consts.ts new file mode 100644 index 0000000..3413f3e --- /dev/null +++ b/src/utils/consts.ts @@ -0,0 +1,4 @@ +export const APPSHORTNAME = 'CPGPT' +export const HALFHOUR = 30 * 60 * 1000 +export const ANHOUR = 60 * 60 * 1000 +export const ADAY = 24 * 60 * 60 * 1000