diff --git a/src/renderer/src/aiCore/clients/BaseApiClient.ts b/src/renderer/src/aiCore/clients/BaseApiClient.ts index 4e39e9464d..083dbda872 100644 --- a/src/renderer/src/aiCore/clients/BaseApiClient.ts +++ b/src/renderer/src/aiCore/clients/BaseApiClient.ts @@ -37,7 +37,7 @@ import { } from '@renderer/types/sdk' import { isJSON, parseJSON } from '@renderer/utils' import { addAbortController, removeAbortController } from '@renderer/utils/abortController' -import { findFileBlocks, getMainTextContent } from '@renderer/utils/messageUtils/find' +import { findFileBlocks, getContentWithTools, getMainTextContent } from '@renderer/utils/messageUtils/find' import { defaultTimeout } from '@shared/config/constant' import Logger from 'electron-log/renderer' import { isEmpty } from 'lodash' @@ -209,7 +209,7 @@ export abstract class BaseApiClient< } public async getMessageContent(message: Message): Promise { - const content = getMainTextContent(message) + const content = getContentWithTools(message) if (isEmpty(content)) { return '' } diff --git a/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts b/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts index 7e7e7e4f20..ebe76d8152 100644 --- a/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts +++ b/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts @@ -52,7 +52,7 @@ import { TextDeltaChunk, ThinkingDeltaChunk } from '@renderer/types/chunk' -import type { Message } from '@renderer/types/newMessage' +import { type Message } from '@renderer/types/newMessage' import { AnthropicSdkMessageParam, AnthropicSdkParams, diff --git a/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts b/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts index b3637fa4e7..e4bfafe14d 100644 --- a/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts +++ b/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts @@ -243,6 +243,7 @@ export class GeminiAPIClient extends BaseApiClient< private async convertMessageToSdkParam(message: Message): Promise { const role = message.role === 'user' ? 'user' : 'model' const parts: Part[] = [{ text: await this.getMessageContent(message) }] + // Add any generated images from previous responses const imageBlocks = findImageBlocks(message) for (const imageBlock of imageBlocks) { diff --git a/src/renderer/src/pages/home/Messages/Blocks/ToolBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/ToolBlock.tsx index 94b8d5cb23..865f9c2948 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/ToolBlock.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/ToolBlock.tsx @@ -8,7 +8,7 @@ interface Props { } const ToolBlock: React.FC = ({ block }) => { - return + return } export default React.memo(ToolBlock) diff --git a/src/renderer/src/pages/home/Messages/MessageTools.tsx b/src/renderer/src/pages/home/Messages/MessageTools.tsx index 4490c3000c..186b81d6a8 100644 --- a/src/renderer/src/pages/home/Messages/MessageTools.tsx +++ b/src/renderer/src/pages/home/Messages/MessageTools.tsx @@ -8,17 +8,17 @@ import { useTranslation } from 'react-i18next' import styled from 'styled-components' interface Props { - blocks: ToolMessageBlock + block: ToolMessageBlock } -const MessageTools: FC = ({ blocks }) => { +const MessageTools: FC = ({ block }) => { const [activeKeys, setActiveKeys] = useState([]) const [copiedMap, setCopiedMap] = useState>({}) const [expandedResponse, setExpandedResponse] = useState<{ content: string; title: string } | null>(null) const { t } = useTranslation() const { messageFont, fontSize } = useSettings() - const toolResponse = blocks.metadata?.rawMcpToolResponse + const toolResponse = block.metadata?.rawMcpToolResponse const resultString = useMemo(() => { try { diff --git a/src/renderer/src/utils/messageUtils/find.ts b/src/renderer/src/utils/messageUtils/find.ts index 9fcc80b463..e5beeaa73f 100644 --- a/src/renderer/src/utils/messageUtils/find.ts +++ b/src/renderer/src/utils/messageUtils/find.ts @@ -195,13 +195,50 @@ export const findTranslationBlocks = (message: Message): TranslationMessageBlock const translationBlocks: TranslationMessageBlock[] = [] for (const blockId of message.blocks) { const block = messageBlocksSelectors.selectById(state, blockId) - if (block && block.type === 'translation') { + if (block && block.type === MessageBlockType.TRANSLATION) { translationBlocks.push(block as TranslationMessageBlock) } } return translationBlocks } +/** + * 构造带工具调用结果的消息内容 + * @param blocks + * @returns + */ +export function getContentWithTools(message: Message) { + const blocks = findAllBlocks(message) + let constructedContent = '' + for (const block of blocks) { + if (block.type === MessageBlockType.MAIN_TEXT || block.type === MessageBlockType.TOOL) { + if (block.type === MessageBlockType.MAIN_TEXT) { + constructedContent += block.content + } else if (block.type === MessageBlockType.TOOL) { + // 如果是工具调用结果,为其添加文本消息 + let resultString = + '\n\nAssistant called a tool.\nTool Name:' + + block.metadata?.rawMcpToolResponse?.tool.name + + '\nTool call result: \n```json\n' + try { + resultString += JSON.stringify( + { + params: block.metadata?.rawMcpToolResponse?.arguments, + response: block.metadata?.rawMcpToolResponse?.response + }, + null, + 2 + ) + } catch (e) { + resultString += 'Invalid Result' + } + constructedContent += resultString + '\n```\n\n' + } + } + } + return constructedContent +} + /** * Finds the WebSearchMessageBlock associated with a given message. * Assumes only one web search block per message.