Fetch方案
node.js
服务
const OpenAI = require('openai')
const gptClient = new OpenAI({
apiKey: 'sk-key',
baseURL: 'url'
})
type CompletionOptions = {
model?: string,
tools?: any[],
temperature?: number,
isJson?: boolean,
stream?: boolean,
messages: Message[], response_format?: { type: 'json_object' }
}
exports.onCompletion = function (messages: Message[], options = {} as CompletionOptions) {
console.log('onCompletion')
const body: CompletionOptions = {
model: options.model || 'gpt-4o',
messages
}
if (options.tools) {
body.tools = options.tools
}
if (options.isJson) {
body.response_format = {
type: 'json_object'
}
}
if (options.stream) {
body.stream = true
}
return gptClient.chat.completions.create(body)
}
接口
router.post('/onCompletion', async (req: ExpressRequest, res: ExpressResponse) => {
const { prompt, stream = true, needEncode } = req.body
if (!prompt) {
res.status(403).json({ success: false, message: 'no prompt' })
}
try {
const headers = {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache'
}
console.log('Application对话开始')
if (stream) {
res.writeHead(200, headers)
}
onCompletion([{ role: 'system', content: '你是一名优秀的人工智能顾问,可以友善且专业的回答我提出的问题。', }, { role: 'user', content: prompt }], { stream })
.then(async (ret: any) => {
if (!stream) {
console.log(JSON.stringify(ret))
return res.send(ret.choices[0].message.content)
}
for await (const chunk of ret) {
if (chunk.choices[0].finish_reason) {
console.log('Application对话结束')
res.end()
} else {
res.write(chunk.choices[0].delta.content)
}
}
})
.catch(async (error: any) => {
let message = error.message || error
console.error({ error: message })
res.status(500).json({ success: false, message })
})
} catch (error) {
res.status(500).send('获取页面签名失败')
}
})
前端
服务
async functionsse(path, body, streamCallback) {
let hostname = window.baseUrl
if (path.startsWith('http')) {
hostname = ''
}
const response = await fetch(window.baseUrl + path, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
Accept: 'application/json;charset=UTF-8',
sessiontoken: AV.User.current().getSessionToken()
},
body: JSON.stringify(body)
})
if (response.status < 200 || response.status >= 300) {
const json = await response.json()
throw json.error || json.message || json || '请求失败'
}
const tDecoder = new TextDecoder('utf-8')
const reader = response.body?.getReader()
let text = ''
let isAdd = false
try {
while (true) {
const { done, value } = await reader.read()
if (isAdd) {
text += tDecoder.decode(value, { stream: true })
} else {
text = value ? tDecoder.decode(value, { stream: true }) : text
}
if (done) {
console.log('done!!!')
streamCallback && streamCallback('', done)
break
}
streamCallback && streamCallback(text, done)
}
} catch (e) {
console.error('请求post错误', e)
return []
}
}
调用
this.result = ''
try {
http.sse('/api/onCompletion', {
prompt: this.prompt,
}, (text, done) => {
this.result += text
if(done) {
this.$store.config.loading = false
}
})
} catch(error) {
showError(error)
this.$store.config.loading = false
}