"use client"; import { REQUEST_TIMEOUT_MS } from "@/app/constant"; import { useChatStore } from "@/app/store"; import { ChatOptions, LLMApi, LLMModel, } from "../api"; import Locale from "../../locales"; import { EventStreamContentType, fetchEventSource, } from "@fortaine/fetch-event-source"; import { prettyObject } from "@/app/utils/format"; import { getMessageTextContent } from "@/app/utils"; import api from "@/app/api/api"; export class DeepSeekApi implements LLMApi { public baseURL: string; public apiPath: string; constructor() { // this.baseURL = 'http://192.168.3.209:18078'; this.baseURL = '/deepseek-api'; // this.apiPath = this.baseURL + '/vllm/ai/chat';//线上地址 this.apiPath = this.baseURL + '/vllm/chat'; // 测试地址 } async chat(options: ChatOptions) { const list: ChatOptions['messages'] = JSON.parse(JSON.stringify(options.messages)) || []; const backList = list.reverse(); const item = backList.find((item) => { if (item.document) { if (item.document.id) { return true; } else { return false; } } else { return false; } }); const messages = options.messages.map((item) => { return { role: item.role, content: getMessageTextContent(item), } }); const userMessages = messages.filter(item => item.content); if (userMessages.length % 2 === 0) { userMessages.unshift({ role: "user", content: "⠀", }); } const isDeepThink = useChatStore.getState().isDeepThink; // 参数 const params = { // model: 'DeepSeek-R1-Distill-Qwen-14B', model: 'Qwen3-30B', enable_think: isDeepThink, messages: userMessages, stream: true, document_id: (item && item.document) ? item.document.id : undefined, // 进阶配置 max_tokens: undefined, temperature: undefined, web_search: options.config.web_search, }; const controller = new AbortController(); options.onController?.(controller); try { const userInfo = localStorage.getItem('userInfo'); const token = userInfo ? JSON.parse(userInfo).token : ""; const chatPath = this.apiPath; const chatPayload = { method: "POST", body: JSON.stringify(params), signal: controller.signal, headers: { 'Content-Type': 'application/json', 'Authorization': token }, }; const requestTimeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); let responseText = ""; let remainText = ""; let finished = false; function animateResponseText() { if (finished || controller.signal.aborted) { responseText += remainText; if (responseText?.length === 0) { options.onError?.(new Error("请求已中止,请检查网络环境。")); } return; } if (remainText.length > 0) { const fetchCount = Math.max(1, Math.round(remainText.length / 60)); const fetchText = remainText.slice(0, fetchCount); responseText += fetchText; remainText = remainText.slice(fetchCount); options.onUpdate?.(responseText, fetchText); } requestAnimationFrame(animateResponseText); } animateResponseText(); const finish = () => { if (!finished) { finished = true; let text = responseText + remainText; options.onFinish(text); } }; controller.signal.onabort = finish; let networkInfoPromise: Promise | null = null; fetchEventSource(chatPath, { ...chatPayload, async onopen(res: any) { clearTimeout(requestTimeoutId); const contentType = res.headers.get("content-type"); if (contentType?.startsWith("text/plain")) { responseText = await res.clone().text(); return finish(); } if ( !res.ok || !res.headers.get("content-type")?.startsWith(EventStreamContentType) || res.status !== 200 ) { const responseTexts = [responseText]; let extraInfo = await res.clone().text(); try { const resJson = await res.clone().json(); extraInfo = prettyObject(resJson); } catch { } if (res.status === 401) { responseTexts.push(Locale.Error.Unauthorized); } if (extraInfo) { responseTexts.push(extraInfo); } responseText = responseTexts.join("\n\n"); return finish(); } }, onmessage: (msg) => { const info = JSON.parse(msg.data); if (info.event === 'finish') { const isNetwork = useChatStore.getState().web_search; if (isNetwork) {// 联网搜索结果 networkInfoPromise = (async () => { try { const res: any = await api.get(`bigmodel/api/web/search/${info.id}`); const networkInfo = { list: res.data.search_result, }; useChatStore.getState().updateCurrentSession((session) => { session.messages = session.messages.map((item, index) => { if (index === session.messages.length - 1 && item.role !== 'user') { return { ...item, networkInfo: networkInfo, }; } else { return { ...item, } } }); }); } catch (error) { console.error(error); } })(); } return finish(); } // 获取当前的数据 const currentData = info.data; const formatStart = '```think'; const formatEnd = 'think```'; if (currentData?.startsWith(formatStart)) { remainText += currentData.replace(formatStart, '```think\n'); } else if (currentData?.startsWith(formatEnd)) { remainText += currentData.replace(formatEnd, '```'); } else { remainText += currentData; } }, async onclose() { finish(); if (networkInfoPromise) { await networkInfoPromise; // 等待 networkInfo 加载完成 } const session = useChatStore.getState().sessions[0]; const item = session.messages.find(item => item.role === 'user'); const dialogName = item ? item.content : '新的聊天'; const data = { id: session.id, appId: '1881269958412521255', userId: undefined, dialogName: dialogName, messages: session.messages.map(item => ({ id: item.id, date: item.date, role: item.role, content: item.content, document: item.document, networkInfo: item.networkInfo, })), }; await api.post('bigmodel/api/dialog/save', data); }, onerror(e) { options.onError?.(e); throw e; }, openWhenHidden: true, }); } catch (e) { options.onError?.(e as Error); } } async usage() { return { used: 0, total: 0, }; } async models(): Promise { return []; } }