Skip to content

Commit

Permalink
feat: add proxy support and fix streaming mode (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
puppywang committed Feb 25, 2023
1 parent cc91e95 commit 628187f
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 13 deletions.
4 changes: 3 additions & 1 deletion service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"dotenv": "^16.0.3",
"esno": "^0.16.3",
"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": {
"@antfu/eslint-config": "^0.35.2",
Expand Down
82 changes: 80 additions & 2 deletions service/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 52 additions & 5 deletions service/src/chatgpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -30,10 +32,25 @@ let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI
apiModel = 'ChatGPTAPI'
}
else {
let options = {}

if (process.env.API_REVERSE_PROXY)
options = { apiReverseProxyUrl: process.env.API_REVERSE_PROXY }
const options = {
debug: true,
}

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,
Expand Down Expand Up @@ -65,17 +82,47 @@ 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',
data: {
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 }
24 changes: 22 additions & 2 deletions service/src/index.ts
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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()
Expand Down
18 changes: 17 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GenericAbortSignal } from 'axios'
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
import { post } from '@/utils/request'

export function fetchChatAPI<T = any>(
Expand All @@ -18,3 +18,19 @@ export function fetchChatConfig<T = any>() {
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,
})
}
2 changes: 2 additions & 0 deletions src/components/common/Setting/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface ConfigState {
timeoutMs?: number
reverseProxy?: string
apiModel?: string
socksProxy?: string
}
const props = defineProps<Props>()
Expand Down Expand Up @@ -69,6 +70,7 @@ watch(
<p>API方式:{{ config?.apiModel ?? '-' }}</p>
<p>反向代理:{{ config?.reverseProxy ?? '-' }}</p>
<p>超时时间:{{ config?.timeoutMs ?? '-' }}</p>
<p>Socks代理:{{ config?.socksProxy ?? '-' }}</p>
</div>
</NCard>
</NModal>
Expand Down

0 comments on commit 628187f

Please sign in to comment.