From 915569fe794a75e091582d8f4f7f85b5357f8390 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Thu, 3 Apr 2025 11:43:34 +0800 Subject: [PATCH] Test mongo log (#4443) * feat: mongodb-log (#4426) * perf: mongo log * feat: completions stop reasoner * mongo db log --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> --- env.d.ts | 40 ++++++++++ packages/global/core/ai/constants.ts | 12 +++ packages/global/core/ai/type.d.ts | 9 +++ .../global/core/workflow/runtime/type.d.ts | 2 + packages/service/common/mongo/index.ts | 28 ++++++- packages/service/common/mongo/init.ts | 49 ++++++------ packages/service/common/mongo/type.d.ts | 1 + packages/service/common/system/log/schema.ts | 2 +- packages/service/core/ai/utils.ts | 77 +++++++++++++++---- .../workflow/dispatch/agent/runTool/index.ts | 6 +- .../dispatch/agent/runTool/promptCall.ts | 29 ++++--- .../dispatch/agent/runTool/toolChoice.ts | 24 ++++-- .../workflow/dispatch/agent/runTool/type.d.ts | 3 +- .../core/workflow/dispatch/chat/oneapi.ts | 33 +++++--- packages/service/tsconfig.json | 4 +- packages/web/i18n/en/chat.json | 8 ++ packages/web/i18n/zh-CN/chat.json | 8 ++ packages/web/i18n/zh-Hant/chat.json | 8 ++ projects/app/.env.template | 3 +- .../core/ai/SettingLLMModel/index.tsx | 1 - .../chat/components/WholeResponseModal.tsx | 8 ++ projects/app/src/instrumentation.ts | 5 +- .../src/pages/api/admin/clearInvalidData.ts | 2 - projects/app/src/pages/api/admin/initv47.ts | 2 - projects/app/src/pages/api/admin/initv471.ts | 2 - projects/app/src/pages/api/admin/initv4810.ts | 2 - projects/app/src/pages/api/admin/initv4823.ts | 2 - projects/app/src/pages/api/admin/initv486.ts | 3 - projects/app/src/pages/api/admin/initv488.ts | 2 - .../app/src/pages/api/common/file/read.ts | 3 - .../pages/api/common/file/read/[filename].ts | 3 - .../src/pages/api/common/file/uploadImage.ts | 2 - .../src/pages/api/common/system/unlockTask.ts | 3 +- .../src/pages/api/common/tools/urlFetch.ts | 3 +- .../api/core/ai/agent/createQuestionGuide.ts | 3 +- .../api/core/chat/feedback/adminUpdate.ts | 3 +- .../api/core/chat/feedback/closeCustom.ts | 3 +- .../src/pages/api/core/chat/item/getSpeech.ts | 3 +- .../app/src/pages/api/lafApi/[...path].ts | 3 +- .../app/src/pages/api/proApi/[...path].ts | 3 +- .../user/account/updatePasswordByOld.ts | 3 +- .../user/team/limit/datasetSizeLimit.ts | 3 +- .../support/user/team/limit/webSyncLimit.ts | 4 +- projects/app/src/pages/api/system/img/[id].ts | 3 +- projects/app/src/pages/chat/share.tsx | 3 +- projects/app/src/service/mongo.ts | 10 --- test/setup.ts | 9 ++- 47 files changed, 305 insertions(+), 137 deletions(-) create mode 100644 env.d.ts diff --git a/env.d.ts b/env.d.ts new file mode 100644 index 000000000..6492948b4 --- /dev/null +++ b/env.d.ts @@ -0,0 +1,40 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + LOG_DEPTH: string; + DEFAULT_ROOT_PSW: string; + DB_MAX_LINK: string; + TOKEN_KEY: string; + FILE_TOKEN_KEY: string; + ROOT_KEY: string; + OPENAI_BASE_URL: string; + CHAT_API_KEY: string; + AIPROXY_API_ENDPOINT: string; + AIPROXY_API_TOKEN: string; + MULTIPLE_DATA_TO_BASE64: string; + MONGODB_URI: string; + MONGODB_LOG_URI?: string; + PG_URL: string; + OCEANBASE_URL: string; + MILVUS_ADDRESS: string; + MILVUS_TOKEN: string; + SANDBOX_URL: string; + PRO_URL: string; + FE_DOMAIN: string; + FILE_DOMAIN: string; + NEXT_PUBLIC_BASE_URL: string; + LOG_LEVEL: string; + STORE_LOG_LEVEL: string; + USE_IP_LIMIT: string; + WORKFLOW_MAX_RUN_TIMES: string; + WORKFLOW_MAX_LOOP_TIMES: string; + CHECK_INTERNAL_IP: string; + CHAT_LOG_URL: string; + CHAT_LOG_INTERVAL: string; + CHAT_LOG_SOURCE_ID_PREFIX: string; + ALLOWED_ORIGINS: string; + } + } +} + +export {}; diff --git a/packages/global/core/ai/constants.ts b/packages/global/core/ai/constants.ts index 45e7155f1..538fc1098 100644 --- a/packages/global/core/ai/constants.ts +++ b/packages/global/core/ai/constants.ts @@ -1,3 +1,5 @@ +import { i18nT } from '../../../web/i18n/utils'; + export enum ChatCompletionRequestMessageRoleEnum { 'System' = 'system', 'User' = 'user', @@ -28,3 +30,13 @@ export enum EmbeddingTypeEnm { query = 'query', db = 'db' } + +export const completionFinishReasonMap = { + close: i18nT('chat:completion_finish_close'), + stop: i18nT('chat:completion_finish_stop'), + length: i18nT('chat:completion_finish_length'), + tool_calls: i18nT('chat:completion_finish_tool_calls'), + content_filter: i18nT('chat:completion_finish_content_filter'), + function_call: i18nT('chat:completion_finish_function_call'), + null: i18nT('chat:completion_finish_null') +}; diff --git a/packages/global/core/ai/type.d.ts b/packages/global/core/ai/type.d.ts index c87cd931c..c7d1fa740 100644 --- a/packages/global/core/ai/type.d.ts +++ b/packages/global/core/ai/type.d.ts @@ -73,6 +73,15 @@ export type ChatCompletionMessageFunctionCall = export type StreamChatType = Stream; export type UnStreamChatType = openai.Chat.Completions.ChatCompletion; +export type CompletionFinishReason = + | 'close' + | 'stop' + | 'length' + | 'tool_calls' + | 'content_filter' + | 'function_call' + | null; + export default openai; export * from 'openai'; diff --git a/packages/global/core/workflow/runtime/type.d.ts b/packages/global/core/workflow/runtime/type.d.ts index 70e43a182..80a4c0897 100644 --- a/packages/global/core/workflow/runtime/type.d.ts +++ b/packages/global/core/workflow/runtime/type.d.ts @@ -22,6 +22,7 @@ import { UserSelectOptionType } from '../template/system/userSelect/type'; import { WorkflowResponseType } from '../../../../service/core/workflow/dispatch/type'; import { AiChatQuoteRoleType } from '../template/system/aiChat/type'; import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type'; +import { CompletionFinishReason } from '../../ai/type'; export type ExternalProviderType = { openaiAccount?: OpenaiAccountType; @@ -130,6 +131,7 @@ export type DispatchNodeResponseType = { obj: `${ChatRoleEnum}`; value: string; }[]; // completion context array. history will slice + finishReason?: CompletionFinishReason; // dataset search similarity?: number; diff --git a/packages/service/common/mongo/index.ts b/packages/service/common/mongo/index.ts index 02b4213e6..af431ad96 100644 --- a/packages/service/common/mongo/index.ts +++ b/packages/service/common/mongo/index.ts @@ -1,17 +1,26 @@ import { addLog } from '../../common/system/log'; -import mongoose, { Model } from 'mongoose'; +import mongoose, { Model, Mongoose } from 'mongoose'; export default mongoose; export * from 'mongoose'; +export const MONGO_URL = process.env.MONGODB_URI as string; +export const MONGO_LOG_URL = (process.env.MONGODB_LOG_URI ?? process.env.MONGODB_URI) as string; + export const connectionMongo = (() => { if (!global.mongodb) { - global.mongodb = mongoose; + global.mongodb = new Mongoose(); } - return global.mongodb; })(); +export const connectionLogMongo = (() => { + if (!global.mongodbLog) { + global.mongodbLog = new Mongoose(); + } + return global.mongodbLog; +})(); + const addCommonMiddleware = (schema: mongoose.Schema) => { const operations = [ /^find/, @@ -71,6 +80,19 @@ export const getMongoModel = (name: string, schema: mongoose.Schema) => { return model; }; +export const getMongoLogModel = (name: string, schema: mongoose.Schema) => { + if (connectionLogMongo.models[name]) return connectionLogMongo.models[name] as Model; + console.log('Load model======', name); + addCommonMiddleware(schema); + + const model = connectionLogMongo.model(name, schema); + + // Sync index + syncMongoIndex(model); + + return model; +}; + const syncMongoIndex = async (model: Model) => { if (process.env.SYNC_INDEX !== '0' && process.env.NODE_ENV !== 'test') { try { diff --git a/packages/service/common/mongo/init.ts b/packages/service/common/mongo/init.ts index 50cb8f463..1202d42a9 100644 --- a/packages/service/common/mongo/init.ts +++ b/packages/service/common/mongo/init.ts @@ -1,6 +1,5 @@ import { delay } from '@fastgpt/global/common/system/utils'; import { addLog } from '../system/log'; -import { connectionMongo } from './index'; import type { Mongoose } from 'mongoose'; const maxConnecting = Math.max(30, Number(process.env.DB_MAX_LINK || 20)); @@ -8,41 +7,41 @@ const maxConnecting = Math.max(30, Number(process.env.DB_MAX_LINK || 20)); /** * connect MongoDB and init data */ -export async function connectMongo(): Promise { +export async function connectMongo(db: Mongoose, url: string): Promise { /* Connecting, connected will return */ - if (connectionMongo.connection.readyState !== 0) { - return connectionMongo; + if (db.connection.readyState !== 0) { + return db; } - console.log('mongo start connect'); + console.log('MongoDB start connect'); try { // Remove existing listeners to prevent duplicates - connectionMongo.connection.removeAllListeners('error'); - connectionMongo.connection.removeAllListeners('disconnected'); - connectionMongo.set('strictQuery', 'throw'); + db.connection.removeAllListeners('error'); + db.connection.removeAllListeners('disconnected'); + db.set('strictQuery', 'throw'); - connectionMongo.connection.on('error', async (error) => { + db.connection.on('error', async (error) => { console.log('mongo error', error); try { - if (connectionMongo.connection.readyState !== 0) { - await connectionMongo.disconnect(); + if (db.connection.readyState !== 0) { + await db.disconnect(); await delay(1000); - await connectMongo(); + await connectMongo(db, url); } } catch (error) {} }); - connectionMongo.connection.on('disconnected', async () => { + db.connection.on('disconnected', async () => { console.log('mongo disconnected'); try { - if (connectionMongo.connection.readyState !== 0) { - await connectionMongo.disconnect(); + if (db.connection.readyState !== 0) { + await db.disconnect(); await delay(1000); - await connectMongo(); + await connectMongo(db, url); } } catch (error) {} }); - await connectionMongo.connect(process.env.MONGODB_URI as string, { + const options = { bufferCommands: true, maxConnecting: maxConnecting, maxPoolSize: maxConnecting, @@ -53,18 +52,18 @@ export async function connectMongo(): Promise { maxIdleTimeMS: 300000, retryWrites: true, retryReads: true + }; - // readPreference: 'secondaryPreferred', - // readConcern: { level: 'local' }, - // writeConcern: { w: 'majority', j: true } - }); + db.connect(url, options); console.log('mongo connected'); - return connectionMongo; + return db; } catch (error) { - addLog.error('mongo connect error', error); - await connectionMongo.disconnect(); + addLog.error('Mongo connect error', error); + + await db.disconnect(); + await delay(1000); - return connectMongo(); + return connectMongo(db, url); } } diff --git a/packages/service/common/mongo/type.d.ts b/packages/service/common/mongo/type.d.ts index e75ccfdbf..31704203d 100644 --- a/packages/service/common/mongo/type.d.ts +++ b/packages/service/common/mongo/type.d.ts @@ -3,4 +3,5 @@ import type { Logger } from 'winston'; declare global { var mongodb: Mongoose | undefined; + var mongodbLog: Mongoose | undefined; } diff --git a/packages/service/common/system/log/schema.ts b/packages/service/common/system/log/schema.ts index c14fabba1..e96d493e6 100644 --- a/packages/service/common/system/log/schema.ts +++ b/packages/service/common/system/log/schema.ts @@ -1,4 +1,4 @@ -import { getMongoModel, Schema } from '../../../common/mongo'; +import { getMongoLogModel as getMongoModel, Schema } from '../../../common/mongo'; import { SystemLogType } from './type'; import { LogLevelEnum } from './constant'; diff --git a/packages/service/core/ai/utils.ts b/packages/service/core/ai/utils.ts index 6b950b2e3..3cae8b086 100644 --- a/packages/service/core/ai/utils.ts +++ b/packages/service/core/ai/utils.ts @@ -2,6 +2,7 @@ import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import { ChatCompletionCreateParamsNonStreaming, ChatCompletionCreateParamsStreaming, + CompletionFinishReason, StreamChatType } from '@fastgpt/global/core/ai/type'; import { getLLMModel } from './model'; @@ -142,26 +143,40 @@ export const parseReasoningStreamContent = () => { content?: string; reasoning_content?: string; }; + finish_reason?: CompletionFinishReason; }[]; }, parseThinkTag = false - ): [string, string] => { + ): { + reasoningContent: string; + content: string; + finishReason: CompletionFinishReason; + } => { const content = part.choices?.[0]?.delta?.content || ''; + const finishReason = part.choices?.[0]?.finish_reason || null; // @ts-ignore const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || ''; if (reasoningContent || !parseThinkTag) { isInThinkTag = false; - return [reasoningContent, content]; + return { reasoningContent, content, finishReason }; } if (!content) { - return ['', '']; + return { + reasoningContent: '', + content: '', + finishReason + }; } // 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content if (isInThinkTag === false) { - return ['', content]; + return { + reasoningContent: '', + content, + finishReason + }; } // 检测是否为 think 标签开头的数据 @@ -170,17 +185,29 @@ export const parseReasoningStreamContent = () => { startTagBuffer += content; // 太少内容时候,暂时不解析 if (startTagBuffer.length < startTag.length) { - return ['', '']; + return { + reasoningContent: '', + content: '', + finishReason + }; } if (startTagBuffer.startsWith(startTag)) { isInThinkTag = true; - return [startTagBuffer.slice(startTag.length), '']; + return { + reasoningContent: startTagBuffer.slice(startTag.length), + content: '', + finishReason + }; } // 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content isInThinkTag = false; - return ['', startTagBuffer]; + return { + reasoningContent: '', + content: startTagBuffer, + finishReason + }; } // 确认是 think 标签内容,开始返回 think 内容,并实时检测 @@ -201,19 +228,35 @@ export const parseReasoningStreamContent = () => { if (endTagBuffer.includes(endTag)) { isInThinkTag = false; const answer = endTagBuffer.slice(endTag.length); - return ['', answer]; + return { + reasoningContent: '', + content: answer, + finishReason + }; } else if (endTagBuffer.length >= endTag.length) { // 缓存内容超出尾标签长度,且仍未命中 ,则认为本次猜测 失败,仍处于 think 阶段。 const tmp = endTagBuffer; endTagBuffer = ''; - return [tmp, '']; + return { + reasoningContent: tmp, + content: '', + finishReason + }; } - return ['', '']; + return { + reasoningContent: '', + content: '', + finishReason + }; } else if (content.includes(endTag)) { // 返回内容,完整命中,直接结束 isInThinkTag = false; const [think, answer] = content.split(endTag); - return [think, answer]; + return { + reasoningContent: think, + content: answer, + finishReason + }; } else { // 无 buffer,且未命中 ,开始疑似 检测。 for (let i = 1; i < endTag.length; i++) { @@ -222,13 +265,21 @@ export const parseReasoningStreamContent = () => { if (content.endsWith(partialEndTag)) { const think = content.slice(0, -partialEndTag.length); endTagBuffer += partialEndTag; - return [think, '']; + return { + reasoningContent: think, + content: '', + finishReason + }; } } } // 完全未命中尾标签,还是 think 阶段。 - return [content, '']; + return { + reasoningContent: content, + content: '', + finishReason + }; }; const getStartTagBuffer = () => startTagBuffer; diff --git a/packages/service/core/workflow/dispatch/agent/runTool/index.ts b/packages/service/core/workflow/dispatch/agent/runTool/index.ts index ccf03ba6b..08c9ff85b 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/index.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/index.ts @@ -176,7 +176,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< toolNodeOutputTokens, completeMessages = [], // The actual message sent to AI(just save text) assistantResponses = [], // FastGPT system store assistant.value response - runTimes + runTimes, + finish_reason } = await (async () => { const adaptMessages = chats2GPTMessages({ messages, @@ -276,7 +277,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< useVision ), toolDetail: childToolResponse, - mergeSignId: nodeId + mergeSignId: nodeId, + finishReason: finish_reason }, [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ // 工具调用本身的积分消耗 diff --git a/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts b/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts index b14ecf1f5..808006a7f 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts @@ -1,6 +1,10 @@ import { createChatCompletion } from '../../../../ai/config'; import { filterGPTMessageByMaxContext, loadRequestMessages } from '../../../../chat/utils'; -import { StreamChatType, ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type'; +import { + StreamChatType, + ChatCompletionMessageParam, + CompletionFinishReason +} from '@fastgpt/global/core/ai/type'; import { NextApiResponse } from 'next'; import { responseWriteController } from '../../../../../common/response'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; @@ -252,9 +256,9 @@ export const runToolWithPromptCall = async ( } }); - const { answer, reasoning } = await (async () => { + const { answer, reasoning, finish_reason } = await (async () => { if (res && isStreamResponse) { - const { answer, reasoning } = await streamResponse({ + const { answer, reasoning, finish_reason } = await streamResponse({ res, toolNodes, stream: aiResponse, @@ -262,8 +266,9 @@ export const runToolWithPromptCall = async ( aiChatReasoning }); - return { answer, reasoning }; + return { answer, reasoning, finish_reason }; } else { + const finish_reason = aiResponse.choices?.[0]?.finish_reason as CompletionFinishReason; const content = aiResponse.choices?.[0]?.message?.content || ''; const reasoningContent: string = aiResponse.choices?.[0]?.message?.reasoning_content || ''; @@ -271,14 +276,16 @@ export const runToolWithPromptCall = async ( if (reasoningContent || !aiChatReasoning) { return { answer: content, - reasoning: reasoningContent + reasoning: reasoningContent, + finish_reason }; } const [think, answer] = parseReasoningContent(content); return { answer, - reasoning: think + reasoning: think, + finish_reason }; } })(); @@ -525,7 +532,8 @@ ANSWER: `; toolNodeInputTokens, toolNodeOutputTokens, assistantResponses: toolNodeAssistants, - runTimes + runTimes, + finish_reason } ); }; @@ -550,15 +558,18 @@ async function streamResponse({ let startResponseWrite = false; let answer = ''; let reasoning = ''; + let finish_reason: CompletionFinishReason = null; const { parsePart, getStartTagBuffer } = parseReasoningStreamContent(); for await (const part of stream) { if (res.closed) { stream.controller?.abort(); + finish_reason = 'close'; break; } - const [reasoningContent, content] = parsePart(part, aiChatReasoning); + const { reasoningContent, content, finishReason } = parsePart(part, aiChatReasoning); + finish_reason = finish_reason || finishReason; answer += content; reasoning += reasoningContent; @@ -618,7 +629,7 @@ async function streamResponse({ } } - return { answer, reasoning }; + return { answer, reasoning, finish_reason }; } const parseAnswer = ( diff --git a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts index 5bbfc838f..429329ccf 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts @@ -7,7 +7,8 @@ import { ChatCompletionToolMessageParam, ChatCompletionMessageParam, ChatCompletionTool, - ChatCompletionAssistantMessageParam + ChatCompletionAssistantMessageParam, + CompletionFinishReason } from '@fastgpt/global/core/ai/type'; import { NextApiResponse } from 'next'; import { responseWriteController } from '../../../../../common/response'; @@ -300,7 +301,7 @@ export const runToolWithToolChoice = async ( } }); - const { answer, toolCalls } = await (async () => { + const { answer, toolCalls, finish_reason } = await (async () => { if (res && isStreamResponse) { return streamResponse({ res, @@ -310,6 +311,7 @@ export const runToolWithToolChoice = async ( }); } else { const result = aiResponse as ChatCompletion; + const finish_reason = result.choices?.[0]?.finish_reason as CompletionFinishReason; const calls = result.choices?.[0]?.message?.tool_calls || []; const answer = result.choices?.[0]?.message?.content || ''; @@ -350,7 +352,8 @@ export const runToolWithToolChoice = async ( return { answer, - toolCalls: toolCalls + toolCalls: toolCalls, + finish_reason }; } })(); @@ -549,8 +552,9 @@ export const runToolWithToolChoice = async ( toolNodeOutputTokens, completeMessages, assistantResponses: toolNodeAssistants, + toolWorkflowInteractiveResponse, runTimes, - toolWorkflowInteractiveResponse + finish_reason }; } @@ -565,7 +569,8 @@ export const runToolWithToolChoice = async ( toolNodeInputTokens, toolNodeOutputTokens, assistantResponses: toolNodeAssistants, - runTimes + runTimes, + finish_reason } ); } else { @@ -588,7 +593,8 @@ export const runToolWithToolChoice = async ( completeMessages, assistantResponses: [...assistantResponses, ...toolNodeAssistant.value], - runTimes: (response?.runTimes || 0) + 1 + runTimes: (response?.runTimes || 0) + 1, + finish_reason }; } }; @@ -612,14 +618,18 @@ async function streamResponse({ let textAnswer = ''; let callingTool: { name: string; arguments: string } | null = null; let toolCalls: ChatCompletionMessageToolCall[] = []; + let finishReason: CompletionFinishReason = null; for await (const part of stream) { if (res.closed) { stream.controller?.abort(); + finishReason = 'close'; break; } const responseChoice = part.choices?.[0]?.delta; + const finish_reason = part.choices?.[0]?.finish_reason as CompletionFinishReason; + finishReason = finishReason || finish_reason; if (responseChoice?.content) { const content = responseChoice.content || ''; @@ -705,5 +715,5 @@ async function streamResponse({ } } - return { answer: textAnswer, toolCalls }; + return { answer: textAnswer, toolCalls, finish_reason: finishReason }; } diff --git a/packages/service/core/workflow/dispatch/agent/runTool/type.d.ts b/packages/service/core/workflow/dispatch/agent/runTool/type.d.ts index 61cb6b217..52ec7c4bc 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/type.d.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/type.d.ts @@ -1,4 +1,4 @@ -import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type'; +import { ChatCompletionMessageParam, CompletionFinishReason } from '@fastgpt/global/core/ai/type'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import type { ModuleDispatchProps, @@ -43,6 +43,7 @@ export type RunToolResponse = { assistantResponses?: AIChatItemValueItemType[]; toolWorkflowInteractiveResponse?: WorkflowInteractiveResponseType; [DispatchNodeResponseKeyEnum.runTimes]: number; + finish_reason?: CompletionFinishReason; }; export type ToolNodeItemType = RuntimeNodeItemType & { toolParams: RuntimeNodeItemType['inputs']; diff --git a/packages/service/core/workflow/dispatch/chat/oneapi.ts b/packages/service/core/workflow/dispatch/chat/oneapi.ts index 743c0f30f..1e60ec388 100644 --- a/packages/service/core/workflow/dispatch/chat/oneapi.ts +++ b/packages/service/core/workflow/dispatch/chat/oneapi.ts @@ -6,7 +6,11 @@ import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/cons import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils'; import { parseReasoningContent, parseReasoningStreamContent } from '../../../ai/utils'; import { createChatCompletion } from '../../../ai/config'; -import type { ChatCompletionMessageParam, StreamChatType } from '@fastgpt/global/core/ai/type.d'; +import type { + ChatCompletionMessageParam, + CompletionFinishReason, + StreamChatType +} from '@fastgpt/global/core/ai/type.d'; import { formatModelChars2Points } from '../../../../support/wallet/usage/utils'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import { postTextCensor } from '../../../../common/api/requestPlusApi'; @@ -101,7 +105,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise { + const { answerText, reasoningText, finish_reason } = await (async () => { if (isStreamResponse) { if (!res) { return { answerText: '', - reasoningText: '' + reasoningText: '', + finish_reason: 'close' as const }; } // sse response - const { answer, reasoning } = await streamResponse({ + const { answer, reasoning, finish_reason } = await streamResponse({ res, stream: response, aiChatReasoning, @@ -215,9 +220,12 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise { const content = response.choices?.[0]?.message?.content || ''; // @ts-ignore @@ -260,7 +268,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise oceanbase > milvus # PG 向量库连接参数 PG_URL=postgresql://username:password@host:port/postgres diff --git a/projects/app/src/components/core/ai/SettingLLMModel/index.tsx b/projects/app/src/components/core/ai/SettingLLMModel/index.tsx index 1ebf9e67f..5002d522f 100644 --- a/projects/app/src/components/core/ai/SettingLLMModel/index.tsx +++ b/projects/app/src/components/core/ai/SettingLLMModel/index.tsx @@ -99,7 +99,6 @@ const SettingLLMModel = ({ { - console.log(e); onChange(e); onCloseAIChatSetting(); }} diff --git a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx index 60a76d998..babef946c 100644 --- a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx +++ b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx @@ -17,6 +17,7 @@ import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { getFileIcon } from '@fastgpt/global/common/file/icon'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; +import { completionFinishReasonMap } from '@fastgpt/global/core/ai/constants'; type sideTabItemType = { moduleLogo?: string; @@ -196,6 +197,13 @@ export const WholeResponseContent = ({ label={t('common:core.chat.response.module maxToken')} value={activeModule?.maxToken} /> + {activeModule?.finishReason && ( + + )} + { /* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); await authCert({ req, authRoot: true }); const { start = -2, end = -360 * 24 } = req.body as { start: number; end: number }; diff --git a/projects/app/src/pages/api/admin/initv486.ts b/projects/app/src/pages/api/admin/initv486.ts index c804ec330..ef53b629c 100644 --- a/projects/app/src/pages/api/admin/initv486.ts +++ b/projects/app/src/pages/api/admin/initv486.ts @@ -1,14 +1,11 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { PgClient } from '@fastgpt/service/common/vectorStore/pg'; import { MongoApp } from '@fastgpt/service/core/app/schema'; /* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); await authCert({ req, authRoot: true }); await MongoApp.updateMany( diff --git a/projects/app/src/pages/api/admin/initv488.ts b/projects/app/src/pages/api/admin/initv488.ts index f27569a78..84f662558 100644 --- a/projects/app/src/pages/api/admin/initv488.ts +++ b/projects/app/src/pages/api/admin/initv488.ts @@ -1,6 +1,5 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant'; @@ -8,7 +7,6 @@ import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/ /* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); await authCert({ req, authRoot: true }); await MongoDataset.updateMany( diff --git a/projects/app/src/pages/api/common/file/read.ts b/projects/app/src/pages/api/common/file/read.ts index 3a0c4311a..0f84b5e95 100644 --- a/projects/app/src/pages/api/common/file/read.ts +++ b/projects/app/src/pages/api/common/file/read.ts @@ -1,6 +1,5 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; import { authFileToken } from '@fastgpt/service/support/permission/controller'; import { getDownloadStream, getFileById } from '@fastgpt/service/common/file/gridfs/controller'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; @@ -23,8 +22,6 @@ const previewableExtensions = [ // Abandoned, use: file/read/[filename].ts export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); - const { token } = req.query as { token: string }; const { fileId, bucketName } = await authFileToken(token); diff --git a/projects/app/src/pages/api/common/file/read/[filename].ts b/projects/app/src/pages/api/common/file/read/[filename].ts index 2dd6e78a1..ba715fbe9 100644 --- a/projects/app/src/pages/api/common/file/read/[filename].ts +++ b/projects/app/src/pages/api/common/file/read/[filename].ts @@ -1,6 +1,5 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; import { authFileToken } from '@fastgpt/service/support/permission/controller'; import { getDownloadStream, getFileById } from '@fastgpt/service/common/file/gridfs/controller'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; @@ -21,8 +20,6 @@ const previewableExtensions = [ ]; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); - const { token, filename } = req.query as { token: string; filename: string }; const { fileId, bucketName } = await authFileToken(token); diff --git a/projects/app/src/pages/api/common/file/uploadImage.ts b/projects/app/src/pages/api/common/file/uploadImage.ts index ef1835c41..eedff2784 100644 --- a/projects/app/src/pages/api/common/file/uploadImage.ts +++ b/projects/app/src/pages/api/common/file/uploadImage.ts @@ -1,5 +1,4 @@ import type { NextApiRequest, NextApiResponse } from 'next'; -import { connectToDatabase } from '@/service/mongo'; import { uploadMongoImg } from '@fastgpt/service/common/file/image/controller'; import { UploadImgProps } from '@fastgpt/global/common/file/api'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; @@ -9,7 +8,6 @@ import { NextAPI } from '@/service/middleware/entry'; Upload avatar image */ async function handler(req: NextApiRequest, res: NextApiResponse): Promise { - await connectToDatabase(); const body = req.body as UploadImgProps; const { teamId } = await authCert({ req, authToken: true }); diff --git a/projects/app/src/pages/api/common/system/unlockTask.ts b/projects/app/src/pages/api/common/system/unlockTask.ts index 8089d154d..5a8f97936 100644 --- a/projects/app/src/pages/api/common/system/unlockTask.ts +++ b/projects/app/src/pages/api/common/system/unlockTask.ts @@ -1,12 +1,11 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { startTrainingQueue } from '@/service/core/dataset/training/utils'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); await authCert({ req, authToken: true }); startTrainingQueue(); } catch (error) {} diff --git a/projects/app/src/pages/api/common/tools/urlFetch.ts b/projects/app/src/pages/api/common/tools/urlFetch.ts index e75600fb4..801572ca3 100644 --- a/projects/app/src/pages/api/common/tools/urlFetch.ts +++ b/projects/app/src/pages/api/common/tools/urlFetch.ts @@ -2,13 +2,12 @@ import { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { connectToDatabase } from '@/service/mongo'; + import { UrlFetchParams, UrlFetchResponse } from '@fastgpt/global/common/file/api.d'; import { urlsFetch } from '@fastgpt/service/common/string/cheerio'; const fetchContent = async (req: NextApiRequest, res: NextApiResponse) => { try { - await connectToDatabase(); let { urlList = [], selector } = req.body as UrlFetchParams; if (!urlList || urlList.length === 0) { diff --git a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts index 625b99009..43a5e0aa6 100644 --- a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts +++ b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts @@ -1,6 +1,6 @@ import type { NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push'; import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide'; import { ApiRequestProps } from '@fastgpt/service/type/next'; @@ -27,7 +27,6 @@ async function handler( res: NextApiResponse ) { try { - await connectToDatabase(); const { messages } = req.body; const { tmbId, teamId } = await authChatCert({ diff --git a/projects/app/src/pages/api/core/chat/feedback/adminUpdate.ts b/projects/app/src/pages/api/core/chat/feedback/adminUpdate.ts index a707db570..b44154c6a 100644 --- a/projects/app/src/pages/api/core/chat/feedback/adminUpdate.ts +++ b/projects/app/src/pages/api/core/chat/feedback/adminUpdate.ts @@ -1,6 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import type { AdminUpdateFeedbackParams } from '@/global/core/chat/api.d'; import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema'; import { authChatCrud } from '@/service/support/permission/auth/chat'; @@ -8,7 +8,6 @@ import { authChatCrud } from '@/service/support/permission/auth/chat'; /* 初始化我的聊天框,需要身份验证 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); const { appId, chatId, dataId, datasetId, feedbackDataId, q, a } = req.body as AdminUpdateFeedbackParams; diff --git a/projects/app/src/pages/api/core/chat/feedback/closeCustom.ts b/projects/app/src/pages/api/core/chat/feedback/closeCustom.ts index 60baf0c55..6106466c4 100644 --- a/projects/app/src/pages/api/core/chat/feedback/closeCustom.ts +++ b/projects/app/src/pages/api/core/chat/feedback/closeCustom.ts @@ -1,6 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { authCert } from '@fastgpt/service/support/permission/auth/common'; import type { CloseCustomFeedbackParams } from '@/global/core/chat/api.d'; import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema'; @@ -10,7 +10,6 @@ import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; /* remove custom feedback */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); const { appId, chatId, dataId, index } = req.body as CloseCustomFeedbackParams; if (!dataId || !appId || !chatId) { diff --git a/projects/app/src/pages/api/core/chat/item/getSpeech.ts b/projects/app/src/pages/api/core/chat/item/getSpeech.ts index facfcd7a3..d256f5f04 100644 --- a/projects/app/src/pages/api/core/chat/item/getSpeech.ts +++ b/projects/app/src/pages/api/core/chat/item/getSpeech.ts @@ -1,6 +1,6 @@ import type { NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { GetChatSpeechProps } from '@/global/core/chat/api.d'; import { text2Speech } from '@fastgpt/service/core/ai/audio/speech'; import { pushAudioSpeechUsage } from '@/service/support/wallet/usage/push'; @@ -17,7 +17,6 @@ import { ApiRequestProps } from '@fastgpt/service/type/next'; */ async function handler(req: ApiRequestProps, res: NextApiResponse) { try { - await connectToDatabase(); const { ttsConfig, input } = req.body; if (!ttsConfig.model || !ttsConfig.voice) { diff --git a/projects/app/src/pages/api/lafApi/[...path].ts b/projects/app/src/pages/api/lafApi/[...path].ts index a0fd7e2f8..cb0b87c78 100644 --- a/projects/app/src/pages/api/lafApi/[...path].ts +++ b/projects/app/src/pages/api/lafApi/[...path].ts @@ -1,11 +1,10 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { request } from 'https'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); const { path = [], ...query } = req.query as any; const queryStr = new URLSearchParams(query).toString(); diff --git a/projects/app/src/pages/api/proApi/[...path].ts b/projects/app/src/pages/api/proApi/[...path].ts index 261955d1c..038b8d15e 100644 --- a/projects/app/src/pages/api/proApi/[...path].ts +++ b/projects/app/src/pages/api/proApi/[...path].ts @@ -1,12 +1,11 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { request } from 'http'; import { FastGPTProUrl } from '@fastgpt/service/common/system/constants'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); const { path = [], ...query } = req.query as any; const requestPath = `/api/${path?.join('/')}?${new URLSearchParams(query).toString()}`; diff --git a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts index 86712d749..78ba89dd5 100644 --- a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts +++ b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts @@ -2,12 +2,11 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { MongoUser } from '@fastgpt/service/support/user/schema'; -import { connectToDatabase } from '@/service/mongo'; + import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); const { oldPsw, newPsw } = req.body as { oldPsw: string; newPsw: string }; if (!oldPsw || !newPsw) { diff --git a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts index 4075be2e9..5bb6b0b51 100644 --- a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts +++ b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts @@ -1,12 +1,11 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); const { size } = req.query as { size: string; }; diff --git a/projects/app/src/pages/api/support/user/team/limit/webSyncLimit.ts b/projects/app/src/pages/api/support/user/team/limit/webSyncLimit.ts index cd70f4ec8..8ee8a4191 100644 --- a/projects/app/src/pages/api/support/user/team/limit/webSyncLimit.ts +++ b/projects/app/src/pages/api/support/user/team/limit/webSyncLimit.ts @@ -1,13 +1,11 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { checkWebSyncLimit } from '@fastgpt/service/support/user/utils'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); - // 凭证校验 const { teamId } = await authCert({ req, authToken: true }); diff --git a/projects/app/src/pages/api/system/img/[id].ts b/projects/app/src/pages/api/system/img/[id].ts index 904455884..882d21ab3 100644 --- a/projects/app/src/pages/api/system/img/[id].ts +++ b/projects/app/src/pages/api/system/img/[id].ts @@ -1,12 +1,11 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; + import { readMongoImg } from '@fastgpt/service/common/file/image/controller'; // get the models available to the system export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - await connectToDatabase(); const { id } = req.query as { id: string }; const { binary, mime } = await readMongoImg({ id }); diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx index e57b29d4d..7d58175b1 100644 --- a/projects/app/src/pages/chat/share.tsx +++ b/projects/app/src/pages/chat/share.tsx @@ -17,7 +17,7 @@ import { getInitOutLinkChatInfo } from '@/web/core/chat/api'; import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils'; import { MongoOutLink } from '@fastgpt/service/support/outLink/schema'; import { addLog } from '@fastgpt/service/common/system/log'; -import { connectToDatabase } from '@/service/mongo'; + import NextHead from '@/components/common/NextHead'; import { useContextSelector } from 'use-context-selector'; import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext'; @@ -391,7 +391,6 @@ export async function getServerSideProps(context: any) { const app = await (async () => { try { - await connectToDatabase(); return MongoOutLink.findOne( { shareId diff --git a/projects/app/src/service/mongo.ts b/projects/app/src/service/mongo.ts index eb1b5bd33..865df52f4 100644 --- a/projects/app/src/service/mongo.ts +++ b/projects/app/src/service/mongo.ts @@ -1,19 +1,9 @@ -import { PRICE_SCALE } from '@fastgpt/global/support/wallet/constants'; import { MongoUser } from '@fastgpt/service/support/user/schema'; -import { connectMongo } from '@fastgpt/service/common/mongo/init'; import { hashStr } from '@fastgpt/global/common/string/tools'; import { createDefaultTeam } from '@fastgpt/service/support/user/team/controller'; import { exit } from 'process'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; -/** - * This function is equivalent to the entry to the service - * connect MongoDB and init data - */ -export function connectToDatabase() { - return connectMongo(); -} - export async function initRootUser(retry = 3): Promise { try { const rootUser = await MongoUser.findOne({ diff --git a/test/setup.ts b/test/setup.ts index 5e636ddcf..fc9e3ca2d 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,6 +1,12 @@ import { existsSync, readFileSync } from 'fs'; import mongoose from '@fastgpt/service/common/mongo'; import { connectMongo } from '@fastgpt/service/common/mongo/init'; +import { + connectionMongo, + connectionLogMongo, + MONGO_URL, + MONGO_LOG_URL +} from '@fastgpt/service/common/mongo'; import { initGlobalVariables } from '@/service/common/system'; import { afterAll, beforeAll, vi } from 'vitest'; import { setup, teardown } from 'vitest-mongodb'; @@ -63,7 +69,8 @@ beforeAll(async () => { }); vi.stubEnv('MONGODB_URI', (globalThis as any).__MONGO_URI__); initGlobalVariables(); - await connectMongo(); + await connectMongo(connectionMongo, MONGO_URL); + await connectMongo(connectionLogMongo, MONGO_LOG_URL); // await getInitConfig(); if (existsSync('projects/app/.env.local')) {