From c8b3ac7029ce00a9819ac0db9c868bd9f5bcf4b2 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Tue, 6 Dec 2022 18:19:30 -0600 Subject: [PATCH] fix: various fixes and doc updates --- package.json | 3 ++- readme.md | 14 ++++++++++++++ src/chatgpt-api.ts | 29 +++++++++++++---------------- src/chatgpt-conversation.ts | 26 ++++++++++++++------------ src/fetch-sse.ts | 8 ++++++-- src/types.ts | 13 +++++++++++++ 6 files changed, 62 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index d27c553..3094656 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "types": "./build/index.d.ts", "exports": { ".": { + "browser": "./build/browser/index.js", "import": "./build/index.js", "types": "./build/index.d.ts", "default": "./build/index.js" @@ -26,7 +27,7 @@ "dev": "tsup --watch", "clean": "del build", "prebuild": "run-s clean", - "postbuild": "sed -i 's/ *\\?\\? *(await import(\"undici\")).fetch//' build/browser/index.js", + "postbuild": "[ -n CI ] && sed -i '' 's/ *\\?\\? *(await import(\"undici\")).fetch//' build/browser/index.js || echo 'skipping postbuild on CI'", "predev": "run-s clean", "pretest": "run-s build", "docs": "typedoc", diff --git a/readme.md b/readme.md index ed4cd90..982ef57 100644 --- a/readme.md +++ b/readme.md @@ -13,6 +13,7 @@ - [Usage](#usage) - [Docs](#docs) - [How it works](#how-it-works) +- [Compatibility](#compatibility) - [Examples](#examples) - [Credit](#credit) - [License](#license) @@ -114,6 +115,19 @@ If you want to run the built-in demo, store this value as `SESSION_TOKEN` in a l > **Note** > Prior to v1.0.0, this package used a headless browser via [Playwright](https://playwright.dev/) to automate the web UI. Here are the [docs for the initial browser version](https://github.com/transitive-bullshit/chatgpt-api/tree/v0.4.2). +## Compatibility + +This package is ESM-only. It supports: + +- Node.js >= 16.8 + - If you need Node.js 14 support, use [`v1.4.0`](https://github.com/transitive-bullshit/chatgpt-api/releases/tag/v1.4.0) + - If you need CommonJS support, use [`v1.3.0`](https://github.com/transitive-bullshit/chatgpt-api/releases/tag/v1.3.0) +- Edge runtimes like CF workers and Vercel edge functions +- Modern browsers + - This is mainly intended for chrome extensions where your code is protected to a degree + - **We do not recommend using `chatgpt` from client-side browser code** because it would expose your private session token + - If you want to build a website with `chatgpt`, we recommend using it only from your backend API + ## Examples All of these awesome projects are built using the `chatgpt` package. 🤯 diff --git a/src/chatgpt-api.ts b/src/chatgpt-api.ts index 6db89bf..bca3515 100644 --- a/src/chatgpt-api.ts +++ b/src/chatgpt-api.ts @@ -12,9 +12,6 @@ const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' export class ChatGPTAPI { - public conversationId: string = undefined - public parentMessageId: string = undefined - protected _sessionToken: string protected _markdown: boolean protected _apiBaseUrl: string @@ -86,22 +83,15 @@ export class ChatGPTAPI { * @param message - The prompt message to send * @param opts.conversationId - Optional ID of a conversation to continue * @param opts.parentMessageId - Optional ID of the previous message in the conversation - * @param opts.onProgress - Optional function which will be called every time the partial response is updated - * @param opts.onConversationResponse - Optional function which will be called every time the partial response is updated with the full conversation response - * @param opts.abortSignal - Optional function used to abort the underlying `fetch` call using an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) + * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated + * @param opts.onConversationResponse - Optional callback which will be invoked every time the partial response is updated with the full conversation response + * @param opts.abortSignal - Optional callback used to abort the underlying `fetch` call using an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) + * * @returns The response from ChatGPT */ async sendMessage( message: string, - opts: { - conversationId?: string - parentMessageId?: string - onProgress?: (partialResponse: string) => void - onConversationResponse?: ( - response: types.ConversationResponseEvent - ) => void - abortSignal?: AbortSignal - } = {} + opts: types.SendMessageOptions = {} ): Promise { const { conversationId, @@ -161,6 +151,7 @@ export class ChatGPTAPI { if (onConversationResponse) { onConversationResponse(parsedData) } + const message = parsedData.message // console.log('event', JSON.stringify(parsedData, null, 2)) @@ -201,7 +192,13 @@ export class ChatGPTAPI { cookie: `__Secure-next-auth.session-token=${this._sessionToken}`, 'user-agent': this._userAgent } - }).then((r) => r.json() as any as types.SessionResult) + }).then((r) => { + if (!r.ok) { + throw new Error(`${r.status} ${r.statusText}`) + } + + return r.json() as any as types.SessionResult + }) const accessToken = res?.accessToken diff --git a/src/chatgpt-conversation.ts b/src/chatgpt-conversation.ts index f84cae2..972917a 100644 --- a/src/chatgpt-conversation.ts +++ b/src/chatgpt-conversation.ts @@ -39,28 +39,30 @@ export class ChatGPTConversation { * for each message. * * @param message - The prompt message to send - * @param opts.onProgress - Optional listener which will be called every time the partial response is updated - * @param opts.onConversationResponse - Optional listener which will be called every time a conversation response is received + * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated + * @param opts.onConversationResponse - Optional callback which will be invoked every time the partial response is updated with the full conversation response + * @param opts.abortSignal - Optional callback used to abort the underlying `fetch` call using an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) + * * @returns The response from ChatGPT */ async sendMessage( message: string, - opts: { - onProgress?: (partialResponse: string) => void - onConversationResponse?: ( - response: types.ConversationResponseEvent - ) => void - } = {} + opts: types.SendConversationMessageOptions = {} ): Promise { - const { onProgress, onConversationResponse } = opts + const { onConversationResponse, ...rest } = opts return this.api.sendMessage(message, { + ...rest, conversationId: this.conversationId, parentMessageId: this.parentMessageId, - onProgress, onConversationResponse: (response) => { - this.conversationId = response.conversation_id - this.parentMessageId = response.message.id + if (response.conversation_id) { + this.conversationId = response.conversation_id + } + + if (response.message?.id) { + this.parentMessageId = response.message.id + } if (onConversationResponse) { return onConversationResponse(response) diff --git a/src/fetch-sse.ts b/src/fetch-sse.ts index 15f2a54..705480c 100644 --- a/src/fetch-sse.ts +++ b/src/fetch-sse.ts @@ -8,14 +8,18 @@ export async function fetchSSE( options: Parameters[1] & { onMessage: (data: string) => void } ) { const { onMessage, ...fetchOptions } = options - const resp = await fetch(url, fetchOptions) + const res = await fetch(url, fetchOptions) + if (!res.ok) { + throw new Error(`ChatGPTAPI error ${res.status || res.statusText}`) + } + const parser = createParser((event) => { if (event.type === 'event') { onMessage(event.data) } }) - for await (const chunk of streamAsyncIterable(resp.body)) { + for await (const chunk of streamAsyncIterable(res.body)) { const str = new TextDecoder().decode(chunk) parser.feed(str) } diff --git a/src/types.ts b/src/types.ts index b26da3b..80e7004 100644 --- a/src/types.ts +++ b/src/types.ts @@ -273,3 +273,16 @@ export type MessageContent = { } export type MessageMetadata = any + +export type SendMessageOptions = { + conversationId?: string + parentMessageId?: string + onProgress?: (partialResponse: string) => void + onConversationResponse?: (response: ConversationResponseEvent) => void + abortSignal?: AbortSignal +} + +export type SendConversationMessageOptions = Omit< + SendMessageOptions, + 'conversationId' | 'parentMessageId' +>