fix: Ensure tool call results are included in the conversation context (#7463)

* refactor(aiCore): 统一消息内容处理逻辑,优化工具调用结果显示

重构各AI客户端的消息内容处理逻辑,使用新的getContentWithTools函数统一处理
将blocks参数重命名为block以符合语义
使用MessageBlockType枚举替代硬编码字符串

* fix(aiCore): 修复工具调用结果消息的格式问题

调整工具调用结果消息的换行格式,使其显示更清晰

* refactor(aiCore): 将getContentWithTools工具函数移至messageUtils模块

重构代码,将getContentWithTools函数从aiCore/clients/utils.ts移动到messageUtils/find.ts模块中
统一消息处理工具函数的存放位置,提高代码组织性
删除不再使用的utils.ts文件

* refactor(aiCore): 统一使用getMessageContent获取消息内容

将各API客户端中直接调用getContentWithTools改为通过基类的getMessageContent方法获取消息内容,保持行为一致性

* fix(find): 移除冗余的条件判断
This commit is contained in:
Wang Jiyuan 2025-07-01 12:34:11 +08:00 committed by GitHub
parent c37176fe98
commit 68d0b13a64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 46 additions and 8 deletions

View File

@ -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<string> {
const content = getMainTextContent(message)
const content = getContentWithTools(message)
if (isEmpty(content)) {
return ''
}

View File

@ -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,

View File

@ -243,6 +243,7 @@ export class GeminiAPIClient extends BaseApiClient<
private async convertMessageToSdkParam(message: Message): Promise<Content> {
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) {

View File

@ -8,7 +8,7 @@ interface Props {
}
const ToolBlock: React.FC<Props> = ({ block }) => {
return <MessageTools blocks={block} />
return <MessageTools block={block} />
}
export default React.memo(ToolBlock)

View File

@ -8,17 +8,17 @@ import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
interface Props {
blocks: ToolMessageBlock
block: ToolMessageBlock
}
const MessageTools: FC<Props> = ({ blocks }) => {
const MessageTools: FC<Props> = ({ block }) => {
const [activeKeys, setActiveKeys] = useState<string[]>([])
const [copiedMap, setCopiedMap] = useState<Record<string, boolean>>({})
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 {

View File

@ -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.