mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-18 06:17:06 +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",
|
"@waylaidwanderer/chatgpt-api": "^1.33.2",
|
||||||
"chatgpt": "^5.1.1",
|
"chatgpt": "^5.1.1",
|
||||||
"delay": "^5.0.0",
|
"delay": "^5.0.0",
|
||||||
|
"eventsource": "^2.0.2",
|
||||||
|
"http": "0.0.1-security",
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"keyv": "^4.5.2",
|
"keyv": "^4.5.2",
|
||||||
"keyv-file": "^0.2.0",
|
"keyv-file": "^0.2.0",
|
||||||
|
|
@ -14,10 +16,10 @@
|
||||||
"random": "^4.1.0",
|
"random": "^4.1.0",
|
||||||
"undici": "^5.21.0",
|
"undici": "^5.21.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0",
|
||||||
|
"eventsource-parser": "^1.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"eventsource-parser": "^1.0.0",
|
|
||||||
"jimp": "^0.22.7",
|
"jimp": "^0.22.7",
|
||||||
"puppeteer-extra": "^3.3.6",
|
"puppeteer-extra": "^3.3.6",
|
||||||
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
"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 { v4 as uuidv4 } from 'uuid'
|
||||||
import { Config, officialChatGPTAPI } from './config.js'
|
import { Config, officialChatGPTAPI } from './config.js'
|
||||||
import fetch from 'node-fetch'
|
import https from 'https'
|
||||||
import delay from 'delay'
|
import http from 'http'
|
||||||
import _ from 'lodash'
|
import { createParser } from 'eventsource-parser'
|
||||||
// import { createParser } from 'eventsource-parser'
|
|
||||||
let createParser
|
|
||||||
try {
|
|
||||||
createParser = (await import('eventsource-parser')).createParser
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('未安装eventsource-parser,请在插件目录下执行pnpm i')
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// API3
|
||||||
export class OfficialChatGPTClient {
|
export class OfficialChatGPTClient {
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
const {
|
const {
|
||||||
accessToken,
|
accessToken,
|
||||||
apiReverseUrl,
|
apiReverseUrl
|
||||||
timeoutMs
|
|
||||||
} = opts
|
} = opts
|
||||||
this._accessToken = accessToken
|
this._accessToken = accessToken
|
||||||
this._apiReverseUrl = apiReverseUrl
|
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 = {}) {
|
async sendMessage (prompt, opts = {}) {
|
||||||
let {
|
let {
|
||||||
timeoutMs = this._timeoutMs,
|
|
||||||
conversationId,
|
conversationId,
|
||||||
parentMessageId = uuidv4(),
|
parentMessageId = uuidv4(),
|
||||||
messageId = uuidv4(),
|
messageId = uuidv4(),
|
||||||
action = 'next'
|
action = 'next'
|
||||||
} = opts
|
} = opts
|
||||||
let abortController = null
|
|
||||||
if (timeoutMs) {
|
|
||||||
abortController = new AbortController()
|
|
||||||
}
|
|
||||||
let url = this._apiReverseUrl || officialChatGPTAPI
|
let url = this._apiReverseUrl || officialChatGPTAPI
|
||||||
if (this._apiReverseUrl && Config.proxy && !Config.apiForceUseReverse) {
|
if (this._apiReverseUrl && Config.proxy && !Config.apiForceUseReverse) {
|
||||||
// 如果配了proxy,而且有反代,但是没开启强制反代
|
// 如果配了proxy,而且有反代,但是没开启强制反代
|
||||||
|
|
@ -80,96 +45,102 @@ export class OfficialChatGPTClient {
|
||||||
if (conversationId) {
|
if (conversationId) {
|
||||||
body.conversation_id = conversationId
|
body.conversation_id = conversationId
|
||||||
}
|
}
|
||||||
let option = {
|
let conversationResponse
|
||||||
method: 'POST',
|
let statusCode
|
||||||
body: JSON.stringify(body),
|
let requestP = new Promise((resolve, reject) => {
|
||||||
signal: abortController?.signal,
|
let option = {
|
||||||
headers: {
|
method: 'POST',
|
||||||
accept: 'text/event-stream',
|
headers: {
|
||||||
'x-openai-assistant-app-id': '',
|
accept: 'text/event-stream',
|
||||||
authorization: `Bearer ${this._accessToken}`,
|
'x-openai-assistant-app-id': '',
|
||||||
'content-type': 'application/json',
|
authorization: `Bearer ${this._accessToken}`,
|
||||||
referer: 'https://chat.openai.com/chat',
|
'content-type': 'application/json',
|
||||||
library: 'chatgpt-plugin'
|
referer: 'https://chat.openai.com/chat',
|
||||||
},
|
library: 'chatgpt-plugin'
|
||||||
referrer: 'https://chat.openai.com/chat'
|
},
|
||||||
}
|
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 requestLib = url.startsWith('https') ? https : http
|
||||||
if (createParser) {
|
const req = requestLib.request(url, option, (res) => {
|
||||||
let conversationResponse
|
statusCode = res.statusCode
|
||||||
const responseP = new Promise(
|
let response
|
||||||
// eslint-disable-next-line no-async-promise-executor
|
function onMessage (data) {
|
||||||
async (resolve, reject) => {
|
if (data === '[DONE]') {
|
||||||
let response
|
return resolve({
|
||||||
function onMessage (data) {
|
error: null,
|
||||||
if (data === '[DONE]') {
|
response,
|
||||||
return resolve({
|
conversationId,
|
||||||
error: null,
|
messageId,
|
||||||
response,
|
conversationResponse
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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 (convoResponseEvent.message?.id) {
|
||||||
if (event.type === 'event') {
|
messageId = convoResponseEvent.message.id
|
||||||
onMessage(event.data)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
res.body.on('readable', async () => {
|
const partialResponse =
|
||||||
logger.mark('成功连接到chat.openai.com,准备读取数据流')
|
convoResponseEvent.message?.content?.parts?.[0]
|
||||||
let chunk
|
if (partialResponse) {
|
||||||
while ((chunk = res.body.read()) !== null) {
|
if (Config.debug) {
|
||||||
let str = chunk.toString()
|
logger.info(JSON.stringify(convoResponseEvent))
|
||||||
parser.feed(str)
|
}
|
||||||
|
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 {
|
return {
|
||||||
text: response.response,
|
text: response.response,
|
||||||
conversationId: response.conversationId,
|
conversationId: response.conversationId,
|
||||||
|
|
@ -177,36 +148,8 @@ export class OfficialChatGPTClient {
|
||||||
parentMessageId
|
parentMessageId
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warn('未安装eventsource-parser,强烈建议安装以提高API3响应性能,在插件目录下执行pnpm i或pnpm add -w eventsource-parser')
|
console.log(response)
|
||||||
const decoder = new TextDecoder('utf-8')
|
throw new Error(response)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue