UNPKG

doomiaichat

Version:

Doomisoft OpenAI

130 lines (129 loc) 6.45 kB
/** * 火山方舟-豆包大模型引擎 */ import { request, requestStream } from "./declare"; import GptBase from "./gptbase" export default class DouBaoAI extends GptBase { protected apiKey: string; protected apiOption: any = {} /** * 构造函数 */ constructor(apiKey: string, apiOption: any = {}) { super(); this.apiKey = apiKey; this.apiOption = apiOption; } /** * 请求接口 */ public async chatRequest(chatText: string | Array<any>, callChatOption: any, axiosOption: any = {}): Promise<any> { if (!chatText) return { successed: false, error: { errcode: 2, errmsg: '缺失聊天的内容' } }; const callParams = this.assembleApiParams(chatText, false, callChatOption, axiosOption); try { const response = await request(callParams) if (response.successed && !response.data.code) return { successed: true, message: response.data.choices, usage: response.data.usage } return { successed: false, ...response.data }; } catch (error) { console.log('result is error ', error) return { successed: false, error }; } } /** * 组装最后的调用参数 * @param callChatOption * @returns */ private assembleApiParams(chatText: string | Array<any>, streamCall: boolean = false, callChatOption: any, axiosOption: any = {}): any { let messages: Array<any> = typeof (chatText) == 'string' ? [{ role: 'user', content: chatText }] : chatText; let params: any = {}; if (callChatOption?.temperature || this.apiOption.temperature) params.temperature = Number(callChatOption?.temperature || this.apiOption.temperature); params.max_tokens = Number(callChatOption?.maxtoken || this.apiOption.maxtoken); if (callChatOption?.top_p || this.apiOption.top_p) params.top_p = Number(callChatOption?.top_p || this.apiOption.top_p); if (callChatOption?.presence_penalty || this.apiOption.presence_penalty) params.presence_penalty = Number(callChatOption?.presence_penalty || this.apiOption.presence_penalty); if (callChatOption?.frequency_penalty || this.apiOption.frequency_penalty) params.frequency_penalty = Number(callChatOption?.frequency_penalty || this.apiOption.frequency_penalty); if (callChatOption?.top_logprobs || this.apiOption.top_logprobs) { params.logprobs = true; params.top_logprobs = Number(callChatOption?.top_logprobs || this.apiOption.top_logprobs); } params.tools = (callChatOption?.enableToolCall === 1 && callChatOption?.tools) ? callChatOption.tools : undefined; params.tool_choice = callChatOption?.enableToolCall === 1 ? 'auto' : undefined; const axiosParams = { ...axiosOption, method: "post", headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${this.apiKey}` }, data: { model: callChatOption?.model || this.apiOption.model, ...params, messages, stream: streamCall }, url: 'https://ark.cn-beijing.volces.com/api/v3/chat/completions' }; if (streamCall) axiosParams.responseType = 'stream'; return axiosParams; } /** * 流式的聊天模式 * @param chatText * @param _paramOption * @param axiosOption */ override async chatRequestInStream(chatText: string | Array<any>, callChatOption: any, attach?: any, axiosOption?: any): Promise<any> { if (!chatText) this.emit('chaterror', { successed: false, error: 'no text in chat' }); axiosOption = Object.assign({}, axiosOption || { timeout: 10000 }) const callParams = this.assembleApiParams(chatText, true, callChatOption, axiosOption); let requestid = Math.ceil(Math.random() * (new Date().getTime() * Math.random()) / 1000), replytext: string[] = []; try { requestStream(callParams, (chunk: any) => { let streamText = chunk.toString().replace('[DONE]', '').replace(/[\r\n]+/gm, '') this.processChunkData(streamText.split(/data: /), requestid, replytext, attach) }) return { successed: true, requestid } } catch (error) { this.emit('requesterror', { successed: false, requestid, error: 'call axios faied ' + error }); return { successed: false, requestid } } } /** * 处理每次流式返回的对话片段 * @param chunks * @param requestid * @param replytext * @param attach */ processChunkData(chunks: string[], requestid: Number, replytext: string[], attach: any) { let has_tool_calls = 0, currentIndex, previous_index = -1, tool_calls: any[] = [];// 使用数组来存储工具调用 for (const splitString of chunks) { if (!splitString) continue; const chunk = JSON.parse(splitString); const [choice] = chunk.choices, { finish_reason: finishreason, index, usage } = choice, { content, tool_calls: toolCalls } = choice.delta; if (toolCalls && toolCalls.length) { currentIndex = toolCalls[0].index; has_tool_calls = 1; if (currentIndex !== previous_index) { tool_calls.push({ id: toolCalls[0].id, type: 'function', function: { name: toolCalls[0].function.name, arguments: toolCalls[0].function.arguments } }); // 更新previousIndex以供下次比较 previous_index = currentIndex; } else { tool_calls[previous_index].function.arguments += toolCalls[0].function.arguments } } else replytext.push(content); let output = { successed: true, requestid, segment: content, text: replytext.join(''), finish_reason: finishreason, index, usage, has_tool_calls: has_tool_calls, tool_calls: tool_calls }; if (attach) output = Object.assign({}, output, attach); this.emit(finishreason ? 'chatdone' : 'chattext', output) } } }