mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 13:27:08 +00:00
fix: api3传输形式优化
This commit is contained in:
parent
820cf76eaf
commit
ec82629238
2 changed files with 100 additions and 155 deletions
|
|
@ -6,6 +6,8 @@
|
|||
"@waylaidwanderer/chatgpt-api": "^1.33.2",
|
||||
"chatgpt": "^5.1.1",
|
||||
"delay": "^5.0.0",
|
||||
"eventsource": "^2.0.2",
|
||||
"http": "0.0.1-security",
|
||||
"https-proxy-agent": "5.0.1",
|
||||
"keyv": "^4.5.2",
|
||||
"keyv-file": "^0.2.0",
|
||||
|
|
@ -14,10 +16,10 @@
|
|||
"random": "^4.1.0",
|
||||
"undici": "^5.21.0",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.13.0"
|
||||
"ws": "^8.13.0",
|
||||
"eventsource-parser": "^1.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"eventsource-parser": "^1.0.0",
|
||||
"jimp": "^0.22.7",
|
||||
"puppeteer-extra": "^3.3.6",
|
||||
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
||||
|
|
|
|||
249
utils/message.js
249
utils/message.js
|
|
@ -1,62 +1,27 @@
|
|||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { Config, officialChatGPTAPI } from './config.js'
|
||||
import fetch from 'node-fetch'
|
||||
import delay from 'delay'
|
||||
import _ from 'lodash'
|
||||
// import { createParser } from 'eventsource-parser'
|
||||
let createParser
|
||||
try {
|
||||
createParser = (await import('eventsource-parser')).createParser
|
||||
} catch (e) {
|
||||
console.warn('未安装eventsource-parser,请在插件目录下执行pnpm i')
|
||||
}
|
||||
import https from 'https'
|
||||
import http from 'http'
|
||||
import { createParser } from 'eventsource-parser'
|
||||
|
||||
let proxy
|
||||
if (Config.proxy) {
|
||||
try {
|
||||
proxy = (await import('https-proxy-agent')).default
|
||||
} catch (e) {
|
||||
console.warn('未安装https-proxy-agent,请在插件目录下执行pnpm add https-proxy-agent')
|
||||
}
|
||||
}
|
||||
// API3
|
||||
export class OfficialChatGPTClient {
|
||||
constructor (opts = {}) {
|
||||
const {
|
||||
accessToken,
|
||||
apiReverseUrl,
|
||||
timeoutMs
|
||||
apiReverseUrl
|
||||
} = opts
|
||||
this._accessToken = accessToken
|
||||
this._apiReverseUrl = apiReverseUrl
|
||||
this._timeoutMs = timeoutMs
|
||||
this._fetch = (url, options = {}) => {
|
||||
const defaultOptions = Config.proxy
|
||||
? {
|
||||
agent: proxy(Config.proxy)
|
||||
}
|
||||
: {}
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}
|
||||
|
||||
return fetch(url, mergedOptions)
|
||||
}
|
||||
}
|
||||
|
||||
async sendMessage (prompt, opts = {}) {
|
||||
let {
|
||||
timeoutMs = this._timeoutMs,
|
||||
conversationId,
|
||||
parentMessageId = uuidv4(),
|
||||
messageId = uuidv4(),
|
||||
action = 'next'
|
||||
} = opts
|
||||
let abortController = null
|
||||
if (timeoutMs) {
|
||||
abortController = new AbortController()
|
||||
}
|
||||
let url = this._apiReverseUrl || officialChatGPTAPI
|
||||
if (this._apiReverseUrl && Config.proxy && !Config.apiForceUseReverse) {
|
||||
// 如果配了proxy,而且有反代,但是没开启强制反代
|
||||
|
|
@ -80,96 +45,102 @@ export class OfficialChatGPTClient {
|
|||
if (conversationId) {
|
||||
body.conversation_id = conversationId
|
||||
}
|
||||
let option = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
signal: abortController?.signal,
|
||||
headers: {
|
||||
accept: 'text/event-stream',
|
||||
'x-openai-assistant-app-id': '',
|
||||
authorization: `Bearer ${this._accessToken}`,
|
||||
'content-type': 'application/json',
|
||||
referer: 'https://chat.openai.com/chat',
|
||||
library: 'chatgpt-plugin'
|
||||
},
|
||||
referrer: 'https://chat.openai.com/chat'
|
||||
}
|
||||
const res = await this._fetch(url, option)
|
||||
if (res.status === 403) {
|
||||
await delay(500)
|
||||
return await this.sendMessage(prompt, opts)
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
let body = await res.text()
|
||||
if (body.indexOf('Conversation not found') > -1) {
|
||||
throw new Error('对话不存在,请使用指令”#结束对话“结束当前对话后重新开始对话。')
|
||||
} else {
|
||||
throw new Error(body)
|
||||
let conversationResponse
|
||||
let statusCode
|
||||
let requestP = new Promise((resolve, reject) => {
|
||||
let option = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
accept: 'text/event-stream',
|
||||
'x-openai-assistant-app-id': '',
|
||||
authorization: `Bearer ${this._accessToken}`,
|
||||
'content-type': 'application/json',
|
||||
referer: 'https://chat.openai.com/chat',
|
||||
library: 'chatgpt-plugin'
|
||||
},
|
||||
referrer: 'https://chat.openai.com/chat'
|
||||
}
|
||||
}
|
||||
if (createParser) {
|
||||
let conversationResponse
|
||||
const responseP = new Promise(
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
async (resolve, reject) => {
|
||||
let response
|
||||
function onMessage (data) {
|
||||
if (data === '[DONE]') {
|
||||
return resolve({
|
||||
error: null,
|
||||
response,
|
||||
conversationId,
|
||||
messageId,
|
||||
conversationResponse
|
||||
})
|
||||
}
|
||||
try {
|
||||
const _checkJson = JSON.parse(data)
|
||||
} catch (error) {
|
||||
// console.log('warning: parse error.')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const convoResponseEvent = JSON.parse(data)
|
||||
conversationResponse = convoResponseEvent
|
||||
if (convoResponseEvent.conversation_id) {
|
||||
conversationId = convoResponseEvent.conversation_id
|
||||
}
|
||||
|
||||
if (convoResponseEvent.message?.id) {
|
||||
messageId = convoResponseEvent.message.id
|
||||
}
|
||||
|
||||
const partialResponse =
|
||||
convoResponseEvent.message?.content?.parts?.[0]
|
||||
if (partialResponse) {
|
||||
if (Config.debug) {
|
||||
logger.info(JSON.stringify(convoResponseEvent))
|
||||
}
|
||||
response = partialResponse
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('fetchSSE onMessage unexpected error', err)
|
||||
reject(err)
|
||||
}
|
||||
let requestLib = url.startsWith('https') ? https : http
|
||||
const req = requestLib.request(url, option, (res) => {
|
||||
statusCode = res.statusCode
|
||||
let response
|
||||
function onMessage (data) {
|
||||
if (data === '[DONE]') {
|
||||
return resolve({
|
||||
error: null,
|
||||
response,
|
||||
conversationId,
|
||||
messageId,
|
||||
conversationResponse
|
||||
})
|
||||
}
|
||||
try {
|
||||
const _checkJson = JSON.parse(data)
|
||||
} catch (error) {
|
||||
// console.log('warning: parse error.')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const convoResponseEvent = JSON.parse(data)
|
||||
conversationResponse = convoResponseEvent
|
||||
if (convoResponseEvent.conversation_id) {
|
||||
conversationId = convoResponseEvent.conversation_id
|
||||
}
|
||||
|
||||
const parser = createParser((event) => {
|
||||
if (event.type === 'event') {
|
||||
onMessage(event.data)
|
||||
if (convoResponseEvent.message?.id) {
|
||||
messageId = convoResponseEvent.message.id
|
||||
}
|
||||
})
|
||||
res.body.on('readable', async () => {
|
||||
logger.mark('成功连接到chat.openai.com,准备读取数据流')
|
||||
let chunk
|
||||
while ((chunk = res.body.read()) !== null) {
|
||||
let str = chunk.toString()
|
||||
parser.feed(str)
|
||||
|
||||
const partialResponse =
|
||||
convoResponseEvent.message?.content?.parts?.[0]
|
||||
if (partialResponse) {
|
||||
if (Config.debug) {
|
||||
logger.info(JSON.stringify(convoResponseEvent))
|
||||
}
|
||||
response = partialResponse
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.warn('fetchSSE onMessage unexpected error', err)
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
let response = await responseP
|
||||
|
||||
const parser = createParser((event) => {
|
||||
if (event.type === 'event') {
|
||||
onMessage(event.data)
|
||||
}
|
||||
})
|
||||
const errBody = []
|
||||
res.on('data', (chunk) => {
|
||||
// logger.mark('成功连接到chat.openai.com,准备读取数据流')
|
||||
if (statusCode === 200) {
|
||||
let str = chunk.toString()
|
||||
parser.feed(str)
|
||||
}
|
||||
errBody.push(chunk)
|
||||
})
|
||||
|
||||
// const body = []
|
||||
// res.on('data', (chunk) => body.push(chunk))
|
||||
res.on('end', () => {
|
||||
const resString = Buffer.concat(errBody).toString()
|
||||
reject(resString)
|
||||
})
|
||||
})
|
||||
req.on('error', (err) => {
|
||||
reject(err)
|
||||
})
|
||||
|
||||
req.on('timeout', () => {
|
||||
req.destroy()
|
||||
reject(new Error('Request time out'))
|
||||
})
|
||||
|
||||
req.write(JSON.stringify(body))
|
||||
req.end()
|
||||
})
|
||||
const response = await requestP
|
||||
if (statusCode === 200) {
|
||||
return {
|
||||
text: response.response,
|
||||
conversationId: response.conversationId,
|
||||
|
|
@ -177,36 +148,8 @@ export class OfficialChatGPTClient {
|
|||
parentMessageId
|
||||
}
|
||||
} else {
|
||||
logger.warn('未安装eventsource-parser,强烈建议安装以提高API3响应性能,在插件目录下执行pnpm i或pnpm add -w eventsource-parser')
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
const bodyBytes = await res.arrayBuffer()
|
||||
const bodyText = decoder.decode(bodyBytes)
|
||||
const events = bodyText.split('\n\n').filter(item => !_.isEmpty(item))
|
||||
let fullResponse
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
let event = events[i]
|
||||
event = _.trimStart(event, 'data: ')
|
||||
try {
|
||||
let tmp = JSON.parse(event)
|
||||
if (tmp.message) {
|
||||
fullResponse = tmp
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log(event)
|
||||
}
|
||||
}
|
||||
if (Config.debug) {
|
||||
logger.mark(JSON.stringify(fullResponse))
|
||||
}
|
||||
if (!fullResponse?.message) {
|
||||
throw new Error(bodyText || 'unkown error, please check log')
|
||||
}
|
||||
return {
|
||||
text: fullResponse.message.content.parts[0],
|
||||
conversationId: fullResponse.conversation_id,
|
||||
id: fullResponse.message.id,
|
||||
parentMessageId
|
||||
}
|
||||
console.log(response)
|
||||
throw new Error(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue