From e690da840c4d1c2257096463c8ef9325df2929b6 Mon Sep 17 00:00:00 2001 From: lizhixuan Date: Tue, 22 Jul 2025 21:58:22 +0800 Subject: [PATCH] chore: bump @cherrystudio/ai-core version to 1.0.0-alpha.6 and refactor web search tool - Updated version in package.json to 1.0.0-alpha.6. - Simplified response structure in ToolCallChunkHandler by removing unnecessary nesting. - Refactored input schema for web search tool to enhance type safety and clarity. - Cleaned up commented-out code in MessageTool for improved readability. --- packages/aiCore/package.json | 2 +- .../src/aiCore/chunk/handleTooCallChunk.ts | 5 +- .../src/aiCore/tools/WebSearchTool.ts | 10 +- src/renderer/src/components/Spinner.tsx | 22 ---- .../pages/home/Messages/Tools/MessageTool.tsx | 101 +++++++++--------- .../Messages/Tools/MessageWebSearchTool.tsx | 65 +++++++++++ 6 files changed, 121 insertions(+), 84 deletions(-) create mode 100644 src/renderer/src/pages/home/Messages/Tools/MessageWebSearchTool.tsx diff --git a/packages/aiCore/package.json b/packages/aiCore/package.json index 42246a4761..1b855d3880 100644 --- a/packages/aiCore/package.json +++ b/packages/aiCore/package.json @@ -1,6 +1,6 @@ { "name": "@cherrystudio/ai-core", - "version": "1.0.0-alpha.5", + "version": "1.0.0-alpha.6", "description": "Cherry Studio AI Core - Unified AI Provider Interface Based on Vercel AI SDK", "main": "dist/index.js", "module": "dist/index.mjs", diff --git a/src/renderer/src/aiCore/chunk/handleTooCallChunk.ts b/src/renderer/src/aiCore/chunk/handleTooCallChunk.ts index 33b881e2cc..2de808ab87 100644 --- a/src/renderer/src/aiCore/chunk/handleTooCallChunk.ts +++ b/src/renderer/src/aiCore/chunk/handleTooCallChunk.ts @@ -229,10 +229,7 @@ export class ToolCallChunkHandler { tool: toolCallInfo.tool, arguments: input, status: 'done', - response: { - data: output, - success: true - }, + response: output, toolCallId: toolCallId } // 从活跃调用中移除(交互结束后整个实例会被丢弃) diff --git a/src/renderer/src/aiCore/tools/WebSearchTool.ts b/src/renderer/src/aiCore/tools/WebSearchTool.ts index c6fbf618bb..cb1b118b11 100644 --- a/src/renderer/src/aiCore/tools/WebSearchTool.ts +++ b/src/renderer/src/aiCore/tools/WebSearchTool.ts @@ -3,7 +3,7 @@ import WebSearchService from '@renderer/services/WebSearchService' import { Assistant, Message, WebSearchProvider } from '@renderer/types' import { UserMessageStatus } from '@renderer/types/newMessage' import { ExtractResults } from '@renderer/utils/extract' -import { type InferToolOutput, tool } from 'ai' +import { type InferToolInput, type InferToolOutput, tool } from 'ai' import { z } from 'zod' // import { AiSdkTool, ToolCallResult } from './types' @@ -18,15 +18,16 @@ const WebSearchProviderResult = z.object({ }) ) }) +const webSearchToolInputSchema = z.object({ + query: z.string().describe('The query to search for') +}) export const webSearchTool = (webSearchProviderId: WebSearchProvider['id']) => { const webSearchService = WebSearchService.getInstance(webSearchProviderId) return tool({ name: 'builtin_web_search', description: 'Search the web for information', - inputSchema: z.object({ - query: z.string().describe('The query to search for') - }), + inputSchema: webSearchToolInputSchema, outputSchema: WebSearchProviderResult, execute: async ({ query }) => { console.log('webSearchTool', query) @@ -36,6 +37,7 @@ export const webSearchTool = (webSearchProviderId: WebSearchProvider['id']) => { } }) } +export type WebSearchToolInput = InferToolInput> export type WebSearchToolOutput = InferToolOutput> export const webSearchToolWithExtraction = ( diff --git a/src/renderer/src/components/Spinner.tsx b/src/renderer/src/components/Spinner.tsx index 4431a9b69e..b50eb4bad3 100644 --- a/src/renderer/src/components/Spinner.tsx +++ b/src/renderer/src/components/Spinner.tsx @@ -33,28 +33,6 @@ export default function Spinner({ text }: Props) { ) } - -// const baseContainer = css` -// display: flex; -// flex-direction: row; -// align-items: center; -// ` - -// const Container = styled.div` -// ${baseContainer} -// background-color: var(--color-background-mute); -// padding: 10px; -// border-radius: 10px; -// margin-bottom: 10px; -// gap: 10px; -// ` - -// const StatusText = styled.div` -// font-size: 14px; -// line-height: 1.6; -// text-decoration: none; -// color: var(--color-text-1); -// ` const SearchWrapper = styled.div` display: flex; align-items: center; diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx index 14333dfd24..272e68a439 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx @@ -1,48 +1,45 @@ -import Spinner from '@renderer/components/Spinner' -import i18n from '@renderer/i18n' -import type { MCPToolResponse } from '@renderer/types' import type { ToolMessageBlock } from '@renderer/types/newMessage' import { Collapse } from 'antd' -import { useMemo } from 'react' -import styled from 'styled-components' + +import { MessageWebSearchToolBody, MessageWebSearchToolTitle } from './MessageWebSearchTool' interface Props { block: ToolMessageBlock } -const toolNameMapText = { - web_search: i18n.t('message.searching') -} -const toolDoneNameMapText = (args: Record) => { - const count = args.count ?? 0 - return i18n.t('message.websearch.fetch_complete', { count }) -} +// const toolNameMapText = { +// web_search: i18n.t('message.searching') +// } +// const toolDoneNameMapText = (args: Record) => { +// const count = args.count ?? 0 +// return i18n.t('message.websearch.fetch_complete', { count }) +// } -const PrepareTool = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { - const toolNameText = useMemo( - () => toolNameMapText[toolResponse.tool.name] || toolResponse.tool.name, - [toolResponse.tool] - ) +// const PrepareTool = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { +// const toolNameText = useMemo( +// () => toolNameMapText[toolResponse.tool.name] || toolResponse.tool.name, +// [toolResponse.tool] +// ) - return ( - - {toolNameText} - {JSON.stringify(toolResponse.arguments)} - - } - /> - ) -} +// return ( +// +// {toolNameText} +// {JSON.stringify(toolResponse.arguments)} +// +// } +// /> +// ) +// } -const DoneTool = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { - const toolDoneNameText = useMemo( - () => toolDoneNameMapText({ count: toolResponse.response?.data?.length ?? 0 }), - [toolResponse.response] - ) - return

{toolDoneNameText}

-} +// const DoneTool = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { +// const toolDoneNameText = useMemo( +// () => toolDoneNameMapText({ count: toolResponse.response?.data?.length ?? 0 }), +// [toolResponse.response] +// ) +// return

{toolDoneNameText}

+// } export default function MessageTool({ block }: Props) { const toolResponse = block.metadata?.rawMcpToolResponse @@ -54,27 +51,25 @@ export default function MessageTool({ block }: Props) { items={[ { key: '1', - label: - toolResponse.status !== 'done' ? ( - - ) : ( - - ), - children: ( -

{JSON.stringify(toolResponse.status !== 'done' ? toolResponse.arguments : toolResponse.response)}

- ), - showArrow: false + label: , + children: , + showArrow: false, + styles: { + header: { + paddingLeft: '0' + } + } } ]} + size="small" ghost /> ) } -const PrepareToolWrapper = styled.span` - display: flex; - align-items: center; - gap: 4px; - font-size: 14px; - padding: 10px; - padding-left: 0; -` +// const PrepareToolWrapper = styled.span` +// display: flex; +// align-items: center; +// gap: 4px; +// font-size: 14px; +// padding-left: 0; +// ` diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageWebSearchTool.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageWebSearchTool.tsx new file mode 100644 index 0000000000..f361d8a927 --- /dev/null +++ b/src/renderer/src/pages/home/Messages/Tools/MessageWebSearchTool.tsx @@ -0,0 +1,65 @@ +import { WebSearchToolInput, WebSearchToolOutput } from '@renderer/aiCore/tools/WebSearchTool' +import Spinner from '@renderer/components/Spinner' +import i18n from '@renderer/i18n' +import { MCPToolResponse } from '@renderer/types' +import { Typography } from 'antd' +import { Search } from 'lucide-react' +import styled from 'styled-components' + +const { Text, Link } = Typography + +export const MessageWebSearchToolTitle = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { + const toolInput = toolResponse.arguments as WebSearchToolInput + const toolOutput = toolResponse.response as WebSearchToolOutput + + return toolResponse.status !== 'done' ? ( + + {i18n.t('message.searching')} + {toolInput?.query ?? ''} + + } + /> + ) : ( + + + {i18n.t('message.websearch.fetch_complete', { count: toolOutput.results.length ?? 0 })} + + ) +} + +export const MessageWebSearchToolBody = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { + const toolOutput = toolResponse.response as WebSearchToolOutput + + return toolResponse.status === 'done' ? ( + + {toolOutput.results.map((result) => ( +
  • + {result.title} +
  • + ))} +
    + ) : null +} + +const PrepareToolWrapper = styled.span` + display: flex; + align-items: center; + gap: 4px; + font-size: 14px; + padding-left: 0; +` + +const MessageWebSearchToolTitleTextWrapper = styled(Text)` + display: flex; + align-items: center; + gap: 4px; +` + +const MessageWebSearchToolBodyUlWrapper = styled.ul` + display: flex; + flex-direction: column; + gap: 4px; + padding: 0; +`