diff --git a/packages/plugins/robot/src/Main.vue b/packages/plugins/robot/src/Main.vue index a4dfbe0466..4eeba186b8 100644 --- a/packages/plugins/robot/src/Main.vue +++ b/packages/plugins/robot/src/Main.vue @@ -18,7 +18,7 @@ v-model:fullscreen="fullscreen" v-model:show="robotVisible" v-model:input="inputMessage" - :status="messageState.status" + :status="chatStatus" :prompt-items="promptItems" :bubble-renderers="bubbleRenderers" :allowFiles="isVisualModel && robotSettingState.chatMode === ChatMode.Agent" @@ -147,9 +147,9 @@ const showTeleport = ref(false) const showSetting = ref(false) const { + chatStatus, inputMessage, messages, - messageState, changeChatMode, abortRequest, initChatClient, diff --git a/packages/plugins/robot/src/components/chat/RobotChat.vue b/packages/plugins/robot/src/components/chat/RobotChat.vue index e24c375bdd..155e2f3198 100644 --- a/packages/plugins/robot/src/components/chat/RobotChat.vue +++ b/packages/plugins/robot/src/components/chat/RobotChat.vue @@ -36,8 +36,7 @@ :placeholder="GeneratingStatus.includes(props.status) ? '正在思考中...' : '请输入您的问题'" :clearable="true" :loading="GeneratingStatus.includes(props.status)" - :showWordLimit="true" - :maxLength="4000" + :showWordLimit="false" @submit="handleSendMessage" @cancel="handleAbortRequest" :allowFiles="selectedAttachments.length < 1 && props.allowFiles" diff --git a/packages/plugins/robot/src/composables/core/useConversation.ts b/packages/plugins/robot/src/composables/core/useConversation.ts index 7c0da5e7e4..ae2d70869d 100644 --- a/packages/plugins/robot/src/composables/core/useConversation.ts +++ b/packages/plugins/robot/src/composables/core/useConversation.ts @@ -11,6 +11,7 @@ export interface ConversationAdapterOptions { statusManager: { isProcessing: () => boolean setProcessing: () => void + resetProcessing: () => void } } @@ -43,7 +44,8 @@ export function useConversationAdapter(options: ConversationAdapterOptions) { const contextMessages = toRaw(messages.value.slice(0, -1)) await onFinishRequest(finishReason ?? 'unknown', messages.value, contextMessages, messageState) const lastMessage = messages.value.at(-1) - if (lastMessage) { + if (lastMessage && finishReason === 'stop' && !lastMessage.tool_calls && statusManager.isProcessing()) { + statusManager.resetProcessing() await onMessageProcessed(finishReason ?? 'unknown', lastMessage.content ?? '', messages.value, {}) } } diff --git a/packages/plugins/robot/src/composables/core/useMessageStream.ts b/packages/plugins/robot/src/composables/core/useMessageStream.ts index bbf2bd3665..3699da0853 100644 --- a/packages/plugins/robot/src/composables/core/useMessageStream.ts +++ b/packages/plugins/robot/src/composables/core/useMessageStream.ts @@ -29,6 +29,7 @@ const handleDeltaReasoning = (choice: ChatCompletionStreamResponseChoice, lastMe }) } lastMessage.renderContent.at(-1)!.content += choice.delta.reasoning_content + lastMessage.reasoning_content = (lastMessage.reasoning_content || '') + choice.delta.reasoning_content } } diff --git a/packages/plugins/robot/src/composables/features/useToolCalls.ts b/packages/plugins/robot/src/composables/features/useToolCalls.ts index 7f0ff9d9cd..191ec4a0a2 100644 --- a/packages/plugins/robot/src/composables/features/useToolCalls.ts +++ b/packages/plugins/robot/src/composables/features/useToolCalls.ts @@ -74,6 +74,11 @@ export interface ToolCallHandlerConfig { onDone: (finishReason: string, messages: any[], contextMessages: any[], messageState: any) => Promise } getMessageState: () => any + statusManager?: { + isProcessing: () => boolean + setProcessing: () => void + resetProcessing: () => void + } } /** @@ -81,7 +86,7 @@ export interface ToolCallHandlerConfig { * 使用工厂函数模式,将所有依赖通过配置注入 */ export function createToolCallHandler(config: ToolCallHandlerConfig) { - const { client, getAbortController, formatMessages, hooks, streamHandlers, getMessageState } = config + const { client, getAbortController, formatMessages, hooks, streamHandlers, getMessageState, statusManager } = config return async (tool_calls: ResponseToolCall[], messages: any[], contextMessages: RobotMessage[]) => { const hasToolCall = tool_calls?.length > 0 @@ -118,6 +123,8 @@ export function createToolCallHandler(config: ToolCallHandlerConfig) { delete currentMessage.tool_calls + statusManager?.setProcessing() + // 使用工具调用结果继续对话 await client.chatStream( { messages: toolMessages as any, options: { signal: abortController.signal } }, diff --git a/packages/plugins/robot/src/composables/useChat.ts b/packages/plugins/robot/src/composables/useChat.ts index 1772d968d1..9ba4c505a1 100644 --- a/packages/plugins/robot/src/composables/useChat.ts +++ b/packages/plugins/robot/src/composables/useChat.ts @@ -1,4 +1,4 @@ -import { nextTick } from 'vue' +import { nextTick, ref } from 'vue' import { GeneratingStatus, STATUS, type ChatMessage, type MessageState } from '@opentiny/tiny-robot-kit' import { formatMessages, removeLoading } from '../utils' import { getClientConfig as getConfig, updateClientConfig as updateConfig, client } from '../services/aiClient' @@ -39,7 +39,7 @@ enum CHAT_STATUS { FINISHED = 'finished' // 本轮对话结束 } -let chatStatus: CHAT_STATUS = CHAT_STATUS.PROCESSING +const chatStatus = ref(CHAT_STATUS.FINISHED) const abortControllerMap: Record = {} @@ -52,9 +52,9 @@ const handleStreamData = createStreamDataHandler({ onStreamTools }, statusManager: { - isStreaming: () => chatStatus === CHAT_STATUS.STREAMING, + isStreaming: () => chatStatus.value === CHAT_STATUS.STREAMING, setStreaming: () => { - chatStatus = CHAT_STATUS.STREAMING + chatStatus.value = CHAT_STATUS.STREAMING } } }) @@ -110,11 +110,15 @@ const handleFinishRequest = async ( if (finishReason === 'aborted' || messageState?.status === STATUS.ABORTED) { messageState.status = STATUS.ABORTED + } else if (finishReason === 'stop' && !lastMessage.tool_calls) { + messageState.status = STATUS.FINISHED + chatStatus.value = CHAT_STATUS.FINISHED + await onMessageProcessed(finishReason, lastMessage.content ?? '', messages.value, {}) } } const handleRequestError = async (error: Error, messages: ChatMessage[], messageState: MessageState) => { - chatStatus = CHAT_STATUS.FINISHED + chatStatus.value = CHAT_STATUS.FINISHED delete abortControllerMap.main await onRequestEnd('error', messages.at(-1).content, messages, { error }) // 本次请求结束 messageState.status = STATUS.ERROR @@ -140,12 +144,15 @@ const { if (GeneratingStatus.includes(messageManager.messageState.status)) { messageManager.messageState.status = STATUS.FINISHED } - chatStatus = CHAT_STATUS.FINISHED + chatStatus.value = CHAT_STATUS.FINISHED }, statusManager: { - isProcessing: () => chatStatus === CHAT_STATUS.PROCESSING, + isProcessing: () => chatStatus.value === CHAT_STATUS.PROCESSING, setProcessing: () => { - chatStatus = CHAT_STATUS.PROCESSING + chatStatus.value = CHAT_STATUS.PROCESSING + }, + resetProcessing: () => { + chatStatus.value = CHAT_STATUS.FINISHED } } }) @@ -168,7 +175,16 @@ const handleToolCall = createToolCallHandler({ onError: handleRequestError, onDone: handleFinishRequest }, - getMessageState: () => messageManager.messageState + getMessageState: () => messageManager.messageState, + statusManager: { + isProcessing: () => chatStatus.value === CHAT_STATUS.PROCESSING, + setProcessing: () => { + chatStatus.value = CHAT_STATUS.PROCESSING + }, + resetProcessing: () => { + chatStatus.value = CHAT_STATUS.FINISHED + } + } }) // 包装 conversation 方法,添加业务特定逻辑 @@ -235,6 +251,7 @@ const abortRequest = () => { for (const key of Object.keys(abortControllerMap)) { delete abortControllerMap[key] } + chatStatus.value = CHAT_STATUS.FINISHED onRequestEnd('aborted', messageManager.messages.value.at(-1)?.content as string, messageManager.messages.value) } @@ -254,6 +271,7 @@ const changeChatMode = (chatMode: string) => { export default function () { return { + chatStatus, initChatClient, updateConfig, ...messageManager, diff --git a/packages/plugins/robot/src/utils/chat.utils.ts b/packages/plugins/robot/src/utils/chat.utils.ts index ddc0bb865b..557641dd79 100644 --- a/packages/plugins/robot/src/utils/chat.utils.ts +++ b/packages/plugins/robot/src/utils/chat.utils.ts @@ -11,7 +11,8 @@ export const formatMessages = (messages: LLMMessage[]) => { role: message.role, content: message.content, ...(message.tool_calls ? { tool_calls: message.tool_calls } : {}), - ...(message.tool_call_id ? { tool_call_id: message.tool_call_id } : {}) + ...(message.tool_call_id ? { tool_call_id: message.tool_call_id } : {}), + ...(message.reasoning_content ? { reasoning_content: message.reasoning_content } : {}) })) }