feat: add proxy support and fix streaming mode (#122)

This commit is contained in:
puppywang
2023-02-25 17:13:19 +08:00
committed by GitHub
parent cc91e95eed
commit 628187f5c3
7 changed files with 250 additions and 12 deletions

View File

@@ -2,6 +2,8 @@ import * as dotenv from 'dotenv'
import 'isomorphic-fetch'
import type { ChatGPTAPI, ChatMessage, SendMessageOptions } from 'chatgpt'
import { ChatGPTUnofficialProxyAPI } from 'chatgpt'
import { SocksProxyAgent } from 'socks-proxy-agent'
import fetch from 'node-fetch'
import { sendResponse } from './utils'
dotenv.config()
@@ -30,10 +32,25 @@ let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI
apiModel = 'ChatGPTAPI'
}
else {
let options = {}
const options = {
debug: true,
}
if (process.env.API_REVERSE_PROXY)
options = { apiReverseProxyUrl: process.env.API_REVERSE_PROXY }
if (process.env.SOCKS_PROXY_HOST && process.env.SOCKS_PROXY_PORT) {
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({
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() {
return sendResponse({
type: 'Success',
@@ -72,10 +118,11 @@ async function chatConfig() {
apiModel,
reverseProxy: process.env.API_REVERSE_PROXY,
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 { chatReply, chatConfig }
export { chatReply, chatReplyProcess, chatConfig }

View File

@@ -1,6 +1,6 @@
import express from 'express'
import type { ChatContext } from './chatgpt'
import { chatConfig, chatReply } from './chatgpt'
import type { ChatContext, ChatMessage } from './chatgpt'
import { chatConfig, chatReply, chatReplyProcess } from './chatgpt'
const app = express()
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) => {
try {
const response = await chatConfig()