feat: add proxy support and fix streaming mode (#122)
This commit is contained in:
parent
cc91e95eed
commit
628187f5c3
|
@ -28,7 +28,9 @@
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"esno": "^0.16.3",
|
"esno": "^0.16.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"isomorphic-fetch": "^3.0.0"
|
"isomorphic-fetch": "^3.0.0",
|
||||||
|
"node-fetch": "^3.3.0",
|
||||||
|
"socks-proxy-agent": "^7.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^0.35.2",
|
"@antfu/eslint-config": "^0.35.2",
|
||||||
|
|
|
@ -10,7 +10,9 @@ specifiers:
|
||||||
esno: ^0.16.3
|
esno: ^0.16.3
|
||||||
express: ^4.18.2
|
express: ^4.18.2
|
||||||
isomorphic-fetch: ^3.0.0
|
isomorphic-fetch: ^3.0.0
|
||||||
|
node-fetch: ^3.3.0
|
||||||
rimraf: ^4.1.2
|
rimraf: ^4.1.2
|
||||||
|
socks-proxy-agent: ^7.0.0
|
||||||
tsup: ^6.6.3
|
tsup: ^6.6.3
|
||||||
typescript: ^4.9.5
|
typescript: ^4.9.5
|
||||||
|
|
||||||
|
@ -20,6 +22,8 @@ dependencies:
|
||||||
esno: 0.16.3
|
esno: 0.16.3
|
||||||
express: 4.18.2
|
express: 4.18.2
|
||||||
isomorphic-fetch: 3.0.0
|
isomorphic-fetch: 3.0.0
|
||||||
|
node-fetch: 3.3.0
|
||||||
|
socks-proxy-agent: 7.0.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@antfu/eslint-config': 0.35.2_7kw3g6rralp5ps6mg3uyzz6azm
|
'@antfu/eslint-config': 0.35.2_7kw3g6rralp5ps6mg3uyzz6azm
|
||||||
|
@ -641,6 +645,15 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/agent-base/6.0.2:
|
||||||
|
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||||
|
engines: {node: '>= 6.0.0'}
|
||||||
|
dependencies:
|
||||||
|
debug: 4.3.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ajv-formats/2.1.1:
|
/ajv-formats/2.1.1:
|
||||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
|
@ -999,6 +1012,11 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/data-uri-to-buffer/4.0.1:
|
||||||
|
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/debounce-fn/5.1.2:
|
/debounce-fn/5.1.2:
|
||||||
resolution: {integrity: sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==}
|
resolution: {integrity: sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -1038,7 +1056,6 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
dev: true
|
|
||||||
|
|
||||||
/deep-is/0.1.4:
|
/deep-is/0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
@ -1748,6 +1765,14 @@ packages:
|
||||||
reusify: 1.0.4
|
reusify: 1.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/fetch-blob/3.2.0:
|
||||||
|
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||||
|
engines: {node: ^12.20 || >= 14.13}
|
||||||
|
dependencies:
|
||||||
|
node-domexception: 1.0.0
|
||||||
|
web-streams-polyfill: 3.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/file-entry-cache/6.0.1:
|
/file-entry-cache/6.0.1:
|
||||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||||
engines: {node: ^10.12.0 || >=12.0.0}
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
|
@ -1819,6 +1844,13 @@ packages:
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/formdata-polyfill/4.0.10:
|
||||||
|
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||||
|
engines: {node: '>=12.20.0'}
|
||||||
|
dependencies:
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/forwarded/0.2.0:
|
/forwarded/0.2.0:
|
||||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
@ -2086,6 +2118,10 @@ packages:
|
||||||
side-channel: 1.0.4
|
side-channel: 1.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ip/2.0.0:
|
||||||
|
resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ipaddr.js/1.9.1:
|
/ipaddr.js/1.9.1:
|
||||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
@ -2509,7 +2545,6 @@ packages:
|
||||||
|
|
||||||
/ms/2.1.2:
|
/ms/2.1.2:
|
||||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/ms/2.1.3:
|
/ms/2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
@ -2535,6 +2570,11 @@ packages:
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/node-domexception/1.0.0:
|
||||||
|
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||||
|
engines: {node: '>=10.5.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/node-fetch/2.6.9:
|
/node-fetch/2.6.9:
|
||||||
resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
|
resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
|
||||||
engines: {node: 4.x || >=6.0.0}
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
@ -2547,6 +2587,15 @@ packages:
|
||||||
whatwg-url: 5.0.0
|
whatwg-url: 5.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/node-fetch/3.3.0:
|
||||||
|
resolution: {integrity: sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
dependencies:
|
||||||
|
data-uri-to-buffer: 4.0.1
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
formdata-polyfill: 4.0.10
|
||||||
|
dev: false
|
||||||
|
|
||||||
/normalize-package-data/2.5.0:
|
/normalize-package-data/2.5.0:
|
||||||
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3083,6 +3132,30 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/smart-buffer/4.2.0:
|
||||||
|
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||||
|
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/socks-proxy-agent/7.0.0:
|
||||||
|
resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
dependencies:
|
||||||
|
agent-base: 6.0.2
|
||||||
|
debug: 4.3.4
|
||||||
|
socks: 2.7.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/socks/2.7.1:
|
||||||
|
resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==}
|
||||||
|
engines: {node: '>= 10.13.0', npm: '>= 3.0.0'}
|
||||||
|
dependencies:
|
||||||
|
ip: 2.0.0
|
||||||
|
smart-buffer: 4.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/source-map-support/0.5.21:
|
/source-map-support/0.5.21:
|
||||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3440,6 +3513,11 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/web-streams-polyfill/3.2.1:
|
||||||
|
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/webidl-conversions/3.0.1:
|
/webidl-conversions/3.0.1:
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
|
@ -2,6 +2,8 @@ import * as dotenv from 'dotenv'
|
||||||
import 'isomorphic-fetch'
|
import 'isomorphic-fetch'
|
||||||
import type { ChatGPTAPI, ChatMessage, SendMessageOptions } from 'chatgpt'
|
import type { ChatGPTAPI, ChatMessage, SendMessageOptions } from 'chatgpt'
|
||||||
import { ChatGPTUnofficialProxyAPI } from 'chatgpt'
|
import { ChatGPTUnofficialProxyAPI } from 'chatgpt'
|
||||||
|
import { SocksProxyAgent } from 'socks-proxy-agent'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
import { sendResponse } from './utils'
|
import { sendResponse } from './utils'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
@ -30,10 +32,25 @@ let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI
|
||||||
apiModel = 'ChatGPTAPI'
|
apiModel = 'ChatGPTAPI'
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let options = {}
|
const options = {
|
||||||
|
debug: true,
|
||||||
|
}
|
||||||
|
|
||||||
if (process.env.API_REVERSE_PROXY)
|
if (process.env.SOCKS_PROXY_HOST && process.env.SOCKS_PROXY_PORT) {
|
||||||
options = { apiReverseProxyUrl: process.env.API_REVERSE_PROXY }
|
const agent = new SocksProxyAgent({
|
||||||
|
hostname: process.env.SOCKS_PROXY_HOST,
|
||||||
|
port: process.env.SOCKS_PROXY_PORT,
|
||||||
|
})
|
||||||
|
globalThis.console.log(`Using socks proxy: ${process.env.SOCKS_PROXY_HOST}:${process.env.SOCKS_PROXY_PORT}`)
|
||||||
|
options.fetch = (url, options) => {
|
||||||
|
return fetch(url, { agent, ...options })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.API_REVERSE_PROXY) {
|
||||||
|
options.apiReverseProxyUrl = process.env.API_REVERSE_PROXY
|
||||||
|
globalThis.console.log(`Using api reverse proxy: ${process.env.API_REVERSE_PROXY}`)
|
||||||
|
}
|
||||||
|
|
||||||
api = new ChatGPTUnofficialProxyAPI({
|
api = new ChatGPTUnofficialProxyAPI({
|
||||||
accessToken: process.env.OPENAI_ACCESS_TOKEN,
|
accessToken: process.env.OPENAI_ACCESS_TOKEN,
|
||||||
|
@ -65,6 +82,35 @@ async function chatReply(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 实验性质的函数,用于处理聊天过程中的中间结果 */
|
||||||
|
async function chatReplyProcess(
|
||||||
|
message: string,
|
||||||
|
lastContext?: { conversationId?: string; parentMessageId?: string },
|
||||||
|
process?: (chat: ChatMessage) => void,
|
||||||
|
) {
|
||||||
|
if (!message)
|
||||||
|
return sendResponse({ type: 'Fail', message: 'Message is empty' })
|
||||||
|
|
||||||
|
try {
|
||||||
|
let options: SendMessageOptions = { timeoutMs }
|
||||||
|
|
||||||
|
if (lastContext)
|
||||||
|
options = { ...lastContext }
|
||||||
|
|
||||||
|
const response = await api.sendMessage(message, {
|
||||||
|
...options,
|
||||||
|
onProgress: (partialResponse) => {
|
||||||
|
process?.(partialResponse)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return sendResponse({ type: 'Success', data: response })
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
return sendResponse({ type: 'Fail', message: error.message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function chatConfig() {
|
async function chatConfig() {
|
||||||
return sendResponse({
|
return sendResponse({
|
||||||
type: 'Success',
|
type: 'Success',
|
||||||
|
@ -72,10 +118,11 @@ async function chatConfig() {
|
||||||
apiModel,
|
apiModel,
|
||||||
reverseProxy: process.env.API_REVERSE_PROXY,
|
reverseProxy: process.env.API_REVERSE_PROXY,
|
||||||
timeoutMs,
|
timeoutMs,
|
||||||
|
socksProxy: (process.env.SOCKS_PROXY_HOST && process.env.SOCKS_PROXY_PORT) ? (`${process.env.SOCKS_PROXY_HOST}:${process.env.SOCKS_PROXY_PORT}`) : '-',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { ChatContext, ChatMessage }
|
export type { ChatContext, ChatMessage }
|
||||||
|
|
||||||
export { chatReply, chatConfig }
|
export { chatReply, chatReplyProcess, chatConfig }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import type { ChatContext } from './chatgpt'
|
import type { ChatContext, ChatMessage } from './chatgpt'
|
||||||
import { chatConfig, chatReply } from './chatgpt'
|
import { chatConfig, chatReply, chatReplyProcess } from './chatgpt'
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
@ -26,6 +26,26 @@ router.post('/chat', async (req, res) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** 实验性质的函数,用于处理聊天过程中的中间结果 */
|
||||||
|
router.post('/chat-process', async (req, res) => {
|
||||||
|
res.setHeader('Content-type', 'application/octet-stream')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { prompt, options = {} } = req.body as { prompt: string; options?: ChatContext }
|
||||||
|
let firstChunk = true
|
||||||
|
await chatReplyProcess(prompt, options, (chat: ChatMessage) => {
|
||||||
|
res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`)
|
||||||
|
firstChunk = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
res.write(JSON.stringify(error))
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
res.end()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
router.post('/config', async (req, res) => {
|
router.post('/config', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const response = await chatConfig()
|
const response = await chatConfig()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { GenericAbortSignal } from 'axios'
|
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
|
||||||
import { post } from '@/utils/request'
|
import { post } from '@/utils/request'
|
||||||
|
|
||||||
export function fetchChatAPI<T = any>(
|
export function fetchChatAPI<T = any>(
|
||||||
|
@ -18,3 +18,19 @@ export function fetchChatConfig<T = any>() {
|
||||||
url: '/config',
|
url: '/config',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 实验性质的函数,用于处理聊天过程中的中间结果 */
|
||||||
|
export function fetchChatAPIProcess<T = any>(
|
||||||
|
params: {
|
||||||
|
prompt: string
|
||||||
|
options?: { conversationId?: string; parentMessageId?: string }
|
||||||
|
signal?: GenericAbortSignal
|
||||||
|
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
|
||||||
|
) {
|
||||||
|
return post<T>({
|
||||||
|
url: '/chat-process',
|
||||||
|
data: { prompt: params.prompt, options: params.options },
|
||||||
|
signal: params.signal,
|
||||||
|
onDownloadProgress: params.onDownloadProgress,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ interface ConfigState {
|
||||||
timeoutMs?: number
|
timeoutMs?: number
|
||||||
reverseProxy?: string
|
reverseProxy?: string
|
||||||
apiModel?: string
|
apiModel?: string
|
||||||
|
socksProxy?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
@ -69,6 +70,7 @@ watch(
|
||||||
<p>API方式:{{ config?.apiModel ?? '-' }}</p>
|
<p>API方式:{{ config?.apiModel ?? '-' }}</p>
|
||||||
<p>反向代理:{{ config?.reverseProxy ?? '-' }}</p>
|
<p>反向代理:{{ config?.reverseProxy ?? '-' }}</p>
|
||||||
<p>超时时间:{{ config?.timeoutMs ?? '-' }}</p>
|
<p>超时时间:{{ config?.timeoutMs ?? '-' }}</p>
|
||||||
|
<p>Socks代理:{{ config?.socksProxy ?? '-' }}</p>
|
||||||
</div>
|
</div>
|
||||||
</NCard>
|
</NCard>
|
||||||
</NModal>
|
</NModal>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useChat } from './hooks/useChat'
|
||||||
import { HoverButton, SvgIcon } from '@/components/common'
|
import { HoverButton, SvgIcon } from '@/components/common'
|
||||||
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||||
import { useChatStore } from '@/store'
|
import { useChatStore } from '@/store'
|
||||||
import { fetchChatAPI } from '@/api'
|
import { fetchChatAPIProcess } from '@/api'
|
||||||
|
|
||||||
let controller = new AbortController()
|
let controller = new AbortController()
|
||||||
|
|
||||||
|
@ -82,6 +82,42 @@ async function onConversation() {
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await fetchChatAPIProcess<Chat.ConversationResponse>({
|
||||||
|
prompt: message,
|
||||||
|
options,
|
||||||
|
signal: controller.signal,
|
||||||
|
onDownloadProgress: ({ event }) => {
|
||||||
|
const xhr = event.target
|
||||||
|
const { responseText } = xhr
|
||||||
|
// Always process the final line
|
||||||
|
const lastIndex = responseText.lastIndexOf('\n')
|
||||||
|
let chunk = responseText
|
||||||
|
if (lastIndex !== -1)
|
||||||
|
chunk = responseText.substring(lastIndex)
|
||||||
|
try {
|
||||||
|
globalThis.console.log(`trunk = ${chunk}`)
|
||||||
|
const data = JSON.parse(chunk)
|
||||||
|
updateChat(
|
||||||
|
+uuid,
|
||||||
|
dataSources.value.length - 1,
|
||||||
|
{
|
||||||
|
dateTime: new Date().toLocaleString(),
|
||||||
|
text: data.text ?? '',
|
||||||
|
inversion: false,
|
||||||
|
error: false,
|
||||||
|
loading: false,
|
||||||
|
conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
||||||
|
requestOptions: { prompt: message, options: { ...options } },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
scrollToBottom()
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
/*
|
||||||
const { data } = await fetchChatAPI<Chat.ConversationResponse>(message, options, controller.signal)
|
const { data } = await fetchChatAPI<Chat.ConversationResponse>(message, options, controller.signal)
|
||||||
updateChat(
|
updateChat(
|
||||||
+uuid,
|
+uuid,
|
||||||
|
@ -97,6 +133,7 @@ async function onConversation() {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
let errorMessage = error?.message ?? 'Something went wrong, please try again later.'
|
let errorMessage = error?.message ?? 'Something went wrong, please try again later.'
|
||||||
|
@ -156,7 +193,21 @@ async function onRegenerate(index: number) {
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await fetchChatAPI<Chat.ConversationResponse>(message, options, controller.signal)
|
await fetchChatAPIProcess<Chat.ConversationResponse>({
|
||||||
|
prompt: message,
|
||||||
|
options,
|
||||||
|
signal: controller.signal,
|
||||||
|
onDownloadProgress: ({ event }) => {
|
||||||
|
const xhr = event.target
|
||||||
|
const { responseText } = xhr
|
||||||
|
// Always process the final line
|
||||||
|
const lastIndex = responseText.lastIndexOf('\n')
|
||||||
|
let chunk = responseText
|
||||||
|
if (lastIndex !== -1)
|
||||||
|
chunk = responseText.substring(lastIndex)
|
||||||
|
try {
|
||||||
|
globalThis.console.log(`trunk = ${chunk}`)
|
||||||
|
const data = JSON.parse(chunk)
|
||||||
updateChat(
|
updateChat(
|
||||||
+uuid,
|
+uuid,
|
||||||
index,
|
index,
|
||||||
|
@ -171,8 +222,30 @@ async function onRegenerate(index: number) {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
/*
|
||||||
|
const { data } = await fetchChatAPI<Chat.ConversationResponse>(message, options, controller.signal)
|
||||||
|
updateChat(
|
||||||
|
+uuid,
|
||||||
|
index,
|
||||||
|
{
|
||||||
|
dateTime: new Date().toLocaleString(),
|
||||||
|
text: data.text ?? '',
|
||||||
|
inversion: false,
|
||||||
|
error: false,
|
||||||
|
loading: false,
|
||||||
|
conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
||||||
|
requestOptions: { prompt: message, ...options },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
let errorMessage = 'Something went wrong, please try again later.'
|
let errorMessage = error?.message ?? 'Something went wrong, please try again later.'
|
||||||
|
|
||||||
if (error.message === 'canceled')
|
if (error.message === 'canceled')
|
||||||
errorMessage = 'Request canceled. Please try again.'
|
errorMessage = 'Request canceled. Please try again.'
|
||||||
|
|
Loading…
Reference in New Issue