Files
chatGPTBox/src/background/apis/openai-api.mjs
T
2023-03-15 16:18:51 +08:00

155 lines
4.7 KiB
JavaScript

// api version
import { maxResponseTokenLength, Models, getUserConfig } from '../../config'
import { fetchSSE } from '../../utils/fetch-sse'
import { getConversationPairs } from '../../utils/get-conversation-pairs'
import { isEmpty } from 'lodash-es'
const getChatgptPromptBase = async () => {
return `You are a helpful, creative, clever, and very friendly assistant. You are familiar with various languages in the world.`
}
const getGptPromptBase = async () => {
return (
`The following is a conversation with an AI assistant.` +
`The assistant is helpful, creative, clever, and very friendly. The assistant is familiar with various languages in the world.\n\n` +
`Human: Hello, who are you?\n` +
`AI: I am an AI created by OpenAI. How can I help you today?\n` +
`Human: 谢谢\n` +
`AI: 不客气\n`
)
}
/**
* @param {Browser.Runtime.Port} port
* @param {string} question
* @param {Session} session
* @param {string} apiKey
* @param {string} modelName
*/
export async function generateAnswersWithGptCompletionApi(
port,
question,
session,
apiKey,
modelName,
) {
const controller = new AbortController()
port.onDisconnect.addListener(() => {
console.debug('port disconnected')
controller.abort()
})
const prompt =
(await getGptPromptBase()) +
getConversationPairs(session.conversationRecords, false) +
`Human:${question}\nAI:`
const apiUrl = (await getUserConfig()).customOpenAiApiUrl
let answer = ''
await fetchSSE(`${apiUrl}/v1/completions`, {
method: 'POST',
signal: controller.signal,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
prompt: prompt,
model: Models[modelName].value,
stream: true,
max_tokens: maxResponseTokenLength,
}),
onMessage(message) {
console.debug('sse message', message)
if (message === '[DONE]') {
session.conversationRecords.push({ question: question, answer: answer })
console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session })
return
}
let data
try {
data = JSON.parse(message)
} catch (error) {
console.debug('json error', error)
return
}
answer += data.choices[0].text
port.postMessage({ answer: answer, done: false, session: null })
},
async onStart() {},
async onEnd() {},
async onError(resp) {
if (resp.status === 403) {
throw new Error('CLOUDFLARE')
}
const error = await resp.json().catch(() => ({}))
throw new Error(!isEmpty(error) ? JSON.stringify(error) : `${resp.status} ${resp.statusText}`)
},
})
}
/**
* @param {Browser.Runtime.Port} port
* @param {string} question
* @param {Session} session
* @param {string} apiKey
* @param {string} modelName
*/
export async function generateAnswersWithChatgptApi(port, question, session, apiKey, modelName) {
const controller = new AbortController()
port.onDisconnect.addListener(() => {
console.debug('port disconnected')
controller.abort()
})
const prompt = getConversationPairs(session.conversationRecords, true)
prompt.unshift({ role: 'system', content: await getChatgptPromptBase() })
prompt.push({ role: 'user', content: question })
const apiUrl = (await getUserConfig()).customOpenAiApiUrl
let answer = ''
await fetchSSE(`${apiUrl}/v1/chat/completions`, {
method: 'POST',
signal: controller.signal,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
messages: prompt,
model: Models[modelName].value,
stream: true,
max_tokens: maxResponseTokenLength,
}),
onMessage(message) {
console.debug('sse message', message)
if (message === '[DONE]') {
session.conversationRecords.push({ question: question, answer: answer })
console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session })
return
}
let data
try {
data = JSON.parse(message)
} catch (error) {
console.debug('json error', error)
return
}
if ('content' in data.choices[0].delta) answer += data.choices[0].delta.content
port.postMessage({ answer: answer, done: false, session: null })
},
async onStart() {},
async onEnd() {},
async onError(resp) {
if (resp.status === 403) {
throw new Error('CLOUDFLARE')
}
const error = await resp.json().catch(() => ({}))
throw new Error(!isEmpty(error) ? JSON.stringify(error) : `${resp.status} ${resp.statusText}`)
},
})
}