diff --git a/CHANGELOG.md b/CHANGELOG.md index 4962b16..d207517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## v0.0.18 - 2023-04-27 +### Fixed +- Fix using chatgpt web +- Enhance error message + ## v0.0.17 - 2023-04-25 ### Added - Support new bing as backend engine diff --git a/package.json b/package.json index 357847c..67fb1d4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "notionai-plus", "displayName": "NotionAI Plus", - "version": "0.0.17", + "version": "0.0.18", "description": "NotionAI Plus is a browser extension that brings the power of NotionAI to any website you visit.", "scripts": { "dev": "plasmo dev", diff --git a/src/background/ports/stream.ts b/src/background/ports/stream.ts index a260409..4cdef1b 100644 --- a/src/background/ports/stream.ts +++ b/src/background/ports/stream.ts @@ -2,9 +2,9 @@ import type { PlasmoMessaging } from "@plasmohq/messaging" import { BardChat } from "~lib/api/bard" import { BingChat } from "~lib/api/bing" -import { ChatStream } from "~lib/api/chatgpt-api" -import { PostChatGPTStream } from "~lib/api/chatgpt-web" -import { PostNotionStream } from "~lib/api/notion-completion" +import { ChatGPTApiChat } from "~lib/api/chatgpt-api" +import { ChatGPTWebChat } from "~lib/api/chatgpt-web" +import { NotionCompletion } from "~lib/api/notion-completion" import { EngineEnum } from "~lib/enums" import { RequestBody, @@ -22,13 +22,13 @@ const handler: PlasmoMessaging.PortHandler = async (req, res) => { switch (body.engine) { case EngineEnum.ChatGPTWeb: - await PostChatGPTStream(`${instruction}\n\n${prompt}`, res) + await ChatGPTWebChat(`${instruction}\n\n${prompt}`, res) break case EngineEnum.Bing: await BingChat(`${instruction}\n\n${prompt}`, res) break case EngineEnum.ChatGPTAPI: - await ChatStream( + await ChatGPTApiChat( OPENAI_API_URL, instruction, prompt, @@ -40,7 +40,7 @@ const handler: PlasmoMessaging.PortHandler = async (req, res) => { await BardChat(`${instruction}\n\n${prompt}`, res) break case EngineEnum.NotionBoy: - await ChatStream( + await ChatGPTApiChat( NOTIONBOY_API_URL, instruction, prompt, @@ -49,7 +49,7 @@ const handler: PlasmoMessaging.PortHandler = async (req, res) => { ) break case EngineEnum.NotionAI: - await PostNotionStream( + await NotionCompletion( res, body.builtinPrompt, body.context, diff --git a/src/lib/api/bard.ts b/src/lib/api/bard.ts index ac7fb91..e1cdfb7 100644 --- a/src/lib/api/bard.ts +++ b/src/lib/api/bard.ts @@ -33,10 +33,7 @@ function generateReqId() { return Math.floor(Math.random() * 900000) + 100000 } -export async function BardChat( - prompt: string, - res: PlasmoMessaging.Response -) { +async function chat(prompt: string, res: PlasmoMessaging.Response) { const requestParams = await fetchRequestParams() let contextIds = ["", "", ""] const resp = await ofetch( @@ -62,3 +59,17 @@ export async function BardChat( contextIds = ids res.send(text) } + +export async function BardChat( + prompt: string, + res: PlasmoMessaging.Response +) { + try { + await chat(prompt, res) + } catch (err) { + console.error(err) + res.send( + "Sorry, Bard Chat is not available at the moment. error: " + err.message + ) + } +} diff --git a/src/lib/api/bing.ts b/src/lib/api/bing.ts index 9153f09..824fcdd 100644 --- a/src/lib/api/bing.ts +++ b/src/lib/api/bing.ts @@ -232,10 +232,7 @@ export async function createConversation(): Promise { return resp } -export async function BingChat( - prompt: string, - res: PlasmoMessaging.Response -) { +async function chat(prompt: string, res: PlasmoMessaging.Response) { const conversation = await createConversation() const bingConversationStyle = BingConversationStyle.Balanced const conversationContext = { @@ -286,3 +283,17 @@ export async function BingChat( await wsp.open() wsp.sendPacked({ protocol: "json", version: 1 }) } + +export async function BingChat( + prompt: string, + res: PlasmoMessaging.Response +) { + try { + chat(prompt, res) + } catch (err) { + console.error("BingChat", err) + res.send( + "Sorry, Bing Chat is not available at the moment. error: " + err.message + ) + } +} diff --git a/src/lib/api/chatgpt-api.ts b/src/lib/api/chatgpt-api.ts index 6b49ff2..f8dfb2c 100644 --- a/src/lib/api/chatgpt-api.ts +++ b/src/lib/api/chatgpt-api.ts @@ -1,42 +1,18 @@ +import { ofetch } from "ofetch" + import type { PlasmoMessaging } from "@plasmohq/messaging" import { parseSSEResponse } from "~lib/utils/sse" const MODEL = "gpt-3.5-turbo" -type ChatGPTResponseChoice = { - index: number - message: { - role: string - content: string - } - finish_reason: string -} - -type ChatGPTResponseUsgae = { - prompt_tokens: number - completion_tokens: number - total_tokens: number -} - -type ChatGPTResponse = { - id: string - object: string - created: number - choices: ChatGPTResponseChoice[] - usage: ChatGPTResponseUsgae -} - -async function ChatStream( +async function chat( url: string, instraction: string, prompt: string, api_key: string, res: PlasmoMessaging.Response ) { - if (api_key == "") { - return "Please set your OpenAI API key in the extension options page." - } const data = { model: MODEL, stream: true, @@ -45,9 +21,7 @@ async function ChatStream( { role: "user", content: prompt } ] } - // console.log(`ChatGPTAPI request: ${JSON.stringify(data)}`) - - const resp = await fetch(url, { + const resp = await ofetch(url, { method: "POST", headers: { "Content-Type": "application/json", @@ -57,31 +31,51 @@ async function ChatStream( }) let content: string = "" - if (resp.status == 200) { - await parseSSEResponse(resp, (message) => { - if (message === "[DONE]") { - return - } - - try { - const data = JSON.parse(message) - // console.log(content) - if (data?.choices?.length) { - const delta = data.choices[0].delta - if (delta?.content) { - content += delta.content - res.send(content) - } + await parseSSEResponse(resp, (message) => { + if (message === "[DONE]") { + return + } + try { + const data = JSON.parse(message) + // console.log(content) + if (data?.choices?.length) { + const delta = data.choices[0].delta + if (delta?.content) { + content += delta.content + res.send(content) } - } catch (err) { - console.error(err) - res.send(err) - return } - }) - } else { - res.send(`ChatGPT return error, status: ${resp.status}`) + } catch (err) { + console.error(err) + res.send(err) + return + } + }) +} + +async function ChatGPTApiChat( + url: string, + instraction: string, + prompt: string, + api_key: string, + res: PlasmoMessaging.Response +) { + console.log(`ChatStream: ${url}, ${instraction}, ${prompt}, ${api_key}`) + if (!api_key) { + res.send("Please set your OpenAI API key in the extension options page.") + return + } + let message = "" + for (let i = 0; i < 3; i++) { + try { + await chat(url, instraction, prompt, api_key, res) + return + } catch (err) { + console.error(err) + message = err.message + } } + res.send(message) } -export { ChatStream } +export { ChatGPTApiChat } diff --git a/src/lib/api/chatgpt-web.ts b/src/lib/api/chatgpt-web.ts index 39ed311..7b697e5 100644 --- a/src/lib/api/chatgpt-web.ts +++ b/src/lib/api/chatgpt-web.ts @@ -1,3 +1,4 @@ +import { ofetch } from "ofetch" import { v4 as uuidv4 } from "uuid" import type { PlasmoMessaging } from "@plasmohq/messaging" @@ -17,22 +18,35 @@ async function getAccessToken(): Promise { return cacheToken as string } - const resp = await fetch(`${CHATGPT_HOST}/api/auth/session`) - if (resp.status === 403) { - return "403 FORBIDDEN" - } - const data = await resp.json().catch(() => ({})) + const resp = await ofetch(`${CHATGPT_HOST}/api/auth/session`) + const data = await resp.json() if (!data.accessToken) { - return "401 UNAUTHORIZED" + throw new Error("401 UNAUTHORIZED") } await storage.set(CACHE_KEY_TOKEN, data.accessToken) return data.accessToken } -async function PostChatGPTStream( +async function ChatGPTWebChat( prompt: string, res: PlasmoMessaging.Response ) { + let message = "" + for (let i = 0; i < 3; i++) { + try { + await chat(prompt, res) + return + } catch (err) { + await storage.remove(CACHE_KEY_TOKEN) + console.error(err) + message = err.message + } + console.log(message) + } + res.send(message) +} + +async function chat(prompt: string, res: PlasmoMessaging.Response) { const accessToken = await getAccessToken() const cacheConversationId = await storage.get(CACHE_KEY_CONVERSATION_ID) @@ -54,7 +68,7 @@ async function PostChatGPTStream( model: CHATGPT_MODEL } const url = `${CHATGPT_HOST}/backend-api/conversation` - const resp = await fetch(url, { + const resp = await ofetch(url, { method: "POST", headers: { "Content-Type": "application/json", @@ -65,30 +79,26 @@ async function PostChatGPTStream( let conversationId: string = "" - if (resp.status == 200) { - await parseSSEResponse(resp, (message) => { - if (message === "[DONE]") { - // console.debug("chatgpt sse message done, start remove conversation") - removeConversation(conversationId) - return + await parseSSEResponse(resp, (message) => { + if (message === "[DONE]") { + // console.debug("chatgpt sse message done, start remove conversation") + removeConversation(conversationId) + return + } + try { + const data = JSON.parse(message) + const text = data.message?.content?.parts?.[0] + if (text) { + // console.debug("chatgpt sse message", text) + res.send(text) + conversationId = data.conversation_id } - try { - const data = JSON.parse(message) - const text = data.message?.content?.parts?.[0] - if (text) { - // console.debug("chatgpt sse message", text) - res.send(text) - conversationId = data.conversation_id - } - } catch (err) { - console.error(err) - res.send(err) - return - } - }) - } else { - res.send(resp.statusText) - } + } catch (err) { + console.error(err) + res.send(`ChatGPT return error, error: ${err.message}`) + return + } + }) } async function removeConversation(id: string) { @@ -110,4 +120,4 @@ async function removeConversation(id: string) { } } -export { PostChatGPTStream } +export { ChatGPTWebChat } diff --git a/src/lib/api/notion-completion.ts b/src/lib/api/notion-completion.ts index 8c2d7b1..865ed68 100644 --- a/src/lib/api/notion-completion.ts +++ b/src/lib/api/notion-completion.ts @@ -1,3 +1,4 @@ +import { ofetch } from "ofetch" import { v4 as uuidv4 } from "uuid" import type { PlasmoMessaging } from "@plasmohq/messaging" @@ -8,7 +9,7 @@ import { processNdjsonResp } from "~lib/utils/ndjson" const MODEL = "openai-3" const HOST = "https://www.notion.so" -async function PostNotionStream( +async function complation( res: PlasmoMessaging.Response, promptType: string, context: string, @@ -16,11 +17,7 @@ async function PostNotionStream( prompt?: string, language?: string, tone?: string -): Promise { - if (!notionSpaceId) { - return "Please set notionSpaceId in options page" - } - +) { const url = `${HOST}/api/v3/getCompletion` const data = { id: uuidv4(), @@ -65,10 +62,11 @@ async function PostNotionStream( accept: "application/x-ndjson" } - const resp = await fetch(url, { + const resp = await ofetch(url, { method: "POST", headers: headers, - body: JSON.stringify(data) + body: JSON.stringify(data), + retry: 3 }) let fullMessage: string = "" const onMessage = (msg: any) => { @@ -80,13 +78,41 @@ async function PostNotionStream( res.send(msg.completion) } } + await processNdjsonResp(resp, onMessage) +} - if (resp.status == 200) { - await processNdjsonResp(resp, onMessage) - } else { - console.log(`fail: ${resp.status}`) - res.send(resp.statusText) +async function NotionCompletion( + res: PlasmoMessaging.Response, + promptType: string, + context: string, + notionSpaceId: string, + prompt?: string, + language?: string, + tone?: string +): Promise { + if (!notionSpaceId) { + res.send("Please set notionSpaceId in options page") + return + } + let message = "" + for (let i = 0; i < 3; i++) { + try { + await complation( + res, + promptType, + context, + notionSpaceId, + prompt, + language, + tone + ) + return + } catch (err) { + console.log(err) + message = err.message + } } + res.send(message) } -export { PostNotionStream } +export { NotionCompletion }