diff --git a/package.json b/package.json index e5ba6a555f..1e93aba55a 100644 --- a/package.json +++ b/package.json @@ -74,9 +74,10 @@ "format:check": "biome format && biome lint", "prepare": "git config blame.ignoreRevsFile .git-blame-ignore-revs && husky", "claude": "dotenv -e .env -- claude", - "release:aicore:alpha": "yarn workspace @cherrystudio/ai-core version prerelease --immediate && yarn workspace @cherrystudio/ai-core npm publish --tag alpha --access public", - "release:aicore:beta": "yarn workspace @cherrystudio/ai-core version prerelease --immediate && yarn workspace @cherrystudio/ai-core npm publish --tag beta --access public", - "release:aicore": "yarn workspace @cherrystudio/ai-core version patch --immediate && yarn workspace @cherrystudio/ai-core npm publish --access public", + "release:aicore:alpha": "yarn workspace @cherrystudio/ai-core version prerelease --preid alpha --immediate && yarn workspace @cherrystudio/ai-core build && yarn workspace @cherrystudio/ai-core npm publish --tag alpha --access public", + "release:aicore:beta": "yarn workspace @cherrystudio/ai-core version prerelease --preid beta --immediate && yarn workspace @cherrystudio/ai-core build && yarn workspace @cherrystudio/ai-core npm publish --tag beta --access public", + "release:aicore": "yarn workspace @cherrystudio/ai-core version patch --immediate && yarn workspace @cherrystudio/ai-core build && yarn workspace @cherrystudio/ai-core npm publish --access public", + "release:ai-sdk-provider": "yarn workspace @cherrystudio/ai-sdk-provider version patch --immediate && yarn workspace @cherrystudio/ai-sdk-provider build && yarn workspace @cherrystudio/ai-sdk-provider npm publish --access public", "rebuild": "electron-rebuild -f -w better-sqlite3", "postinstall": "electron-builder install-app-deps" }, @@ -118,6 +119,7 @@ "@ai-sdk/google-vertex": "^3.0.68", "@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.8#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.8-d4d0aaac93.patch", "@ai-sdk/mistral": "^2.0.23", + "@ai-sdk/openai": "patch:@ai-sdk/openai@npm%3A2.0.64#~/.yarn/patches/@ai-sdk-openai-npm-2.0.64-48f99f5bf3.patch", "@ai-sdk/perplexity": "^2.0.17", "@ant-design/v5-patch-for-react-19": "^1.0.3", "@anthropic-ai/sdk": "^0.41.0", @@ -126,7 +128,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.910.0", "@aws-sdk/client-s3": "^3.910.0", "@biomejs/biome": "2.2.4", - "@cherrystudio/ai-core": "workspace:^1.0.0-alpha.18", + "@cherrystudio/ai-core": "workspace:^1.0.9", "@cherrystudio/embedjs": "^0.1.31", "@cherrystudio/embedjs-libsql": "^0.1.31", "@cherrystudio/embedjs-loader-csv": "^0.1.31", diff --git a/packages/ai-sdk-provider/package.json b/packages/ai-sdk-provider/package.json index fd0aac2643..bf509ee963 100644 --- a/packages/ai-sdk-provider/package.json +++ b/packages/ai-sdk-provider/package.json @@ -1,6 +1,6 @@ { "name": "@cherrystudio/ai-sdk-provider", - "version": "0.1.0", + "version": "0.1.2", "description": "Cherry Studio AI SDK provider bundle with CherryIN routing.", "keywords": [ "ai-sdk", diff --git a/packages/aiCore/README.md b/packages/aiCore/README.md index 4ca5ea6640..1380019094 100644 --- a/packages/aiCore/README.md +++ b/packages/aiCore/README.md @@ -71,7 +71,7 @@ Cherry Studio AI Core 是一个基于 Vercel AI SDK 的统一 AI Provider 接口 ## 安装 ```bash -npm install @cherrystudio/ai-core ai +npm install @cherrystudio/ai-core ai @ai-sdk/google @ai-sdk/openai ``` ### React Native diff --git a/packages/aiCore/package.json b/packages/aiCore/package.json index 12249cfb7b..fbbea52d40 100644 --- a/packages/aiCore/package.json +++ b/packages/aiCore/package.json @@ -1,6 +1,6 @@ { "name": "@cherrystudio/ai-core", - "version": "1.0.1", + "version": "1.0.9", "description": "Cherry Studio AI Core - Unified AI Provider Interface Based on Vercel AI SDK", "main": "dist/index.js", "module": "dist/index.mjs", @@ -33,19 +33,19 @@ }, "homepage": "https://github.com/CherryHQ/cherry-studio#readme", "peerDependencies": { + "@ai-sdk/google": "^2.0.36", + "@ai-sdk/openai": "^2.0.64", + "@cherrystudio/ai-sdk-provider": "^0.1.2", "ai": "^5.0.26" }, "dependencies": { "@ai-sdk/anthropic": "^2.0.43", "@ai-sdk/azure": "^2.0.66", "@ai-sdk/deepseek": "^1.0.27", - "@ai-sdk/google": "patch:@ai-sdk/google@npm%3A2.0.36#~/.yarn/patches/@ai-sdk-google-npm-2.0.36-6f3cc06026.patch", - "@ai-sdk/openai": "patch:@ai-sdk/openai@npm%3A2.0.64#~/.yarn/patches/@ai-sdk-openai-npm-2.0.64-48f99f5bf3.patch", "@ai-sdk/openai-compatible": "^1.0.26", "@ai-sdk/provider": "^2.0.0", "@ai-sdk/provider-utils": "^3.0.16", "@ai-sdk/xai": "^2.0.31", - "@cherrystudio/ai-sdk-provider": "workspace:*", "zod": "^4.1.5" }, "devDependencies": { diff --git a/packages/aiCore/src/core/plugins/built-in/index.ts b/packages/aiCore/src/core/plugins/built-in/index.ts index 1f8916b09a..d7f35d0cd1 100644 --- a/packages/aiCore/src/core/plugins/built-in/index.ts +++ b/packages/aiCore/src/core/plugins/built-in/index.ts @@ -4,12 +4,7 @@ */ export const BUILT_IN_PLUGIN_PREFIX = 'built-in:' -export { googleToolsPlugin } from './googleToolsPlugin' -export { createLoggingPlugin } from './logging' -export { createPromptToolUsePlugin } from './toolUsePlugin/promptToolUsePlugin' -export type { - PromptToolUseConfig, - ToolUseRequestContext, - ToolUseResult -} from './toolUsePlugin/type' -export { webSearchPlugin, type WebSearchPluginConfig } from './webSearchPlugin' +export * from './googleToolsPlugin' +export * from './toolUsePlugin/promptToolUsePlugin' +export * from './toolUsePlugin/type' +export * from './webSearchPlugin' diff --git a/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/index.ts b/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/index.ts index 23ea952323..75692cdf36 100644 --- a/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/index.ts +++ b/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/index.ts @@ -32,7 +32,7 @@ export const webSearchPlugin = (config: WebSearchPluginConfig = DEFAULT_WEB_SEAR }) // 导出类型定义供开发者使用 -export type { WebSearchPluginConfig, WebSearchToolOutputSchema } from './helper' +export * from './helper' // 默认导出 export default webSearchPlugin diff --git a/packages/aiCore/src/core/providers/index.ts b/packages/aiCore/src/core/providers/index.ts index 3ac445cb22..b9ebd6f682 100644 --- a/packages/aiCore/src/core/providers/index.ts +++ b/packages/aiCore/src/core/providers/index.ts @@ -44,7 +44,7 @@ export { // ==================== 基础数据和类型 ==================== // 基础Provider数据源 -export { baseProviderIds, baseProviders } from './schemas' +export { baseProviderIds, baseProviders, isBaseProvider } from './schemas' // 类型定义和Schema export type { diff --git a/packages/aiCore/src/core/providers/schemas.ts b/packages/aiCore/src/core/providers/schemas.ts index 778b1b705a..43a370af9b 100644 --- a/packages/aiCore/src/core/providers/schemas.ts +++ b/packages/aiCore/src/core/providers/schemas.ts @@ -7,7 +7,6 @@ import { createAzure } from '@ai-sdk/azure' import { type AzureOpenAIProviderSettings } from '@ai-sdk/azure' import { createDeepSeek } from '@ai-sdk/deepseek' import { createGoogleGenerativeAI } from '@ai-sdk/google' -import { createHuggingFace } from '@ai-sdk/huggingface' import { createOpenAI, type OpenAIProviderSettings } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import type { LanguageModelV2 } from '@ai-sdk/provider' @@ -33,8 +32,7 @@ export const baseProviderIds = [ 'deepseek', 'openrouter', 'cherryin', - 'cherryin-chat', - 'huggingface' + 'cherryin-chat' ] as const /** @@ -158,12 +156,6 @@ export const baseProviders = [ }) }, supportsImageGeneration: true - }, - { - id: 'huggingface', - name: 'HuggingFace', - creator: createHuggingFace, - supportsImageGeneration: true } ] as const satisfies BaseProvider[] diff --git a/src/main/services/agents/services/claudecode/__tests__/transform.test.ts b/src/main/services/agents/services/claudecode/__tests__/transform.test.ts index 8f8c1df038..e38f897f2a 100644 --- a/src/main/services/agents/services/claudecode/__tests__/transform.test.ts +++ b/src/main/services/agents/services/claudecode/__tests__/transform.test.ts @@ -25,7 +25,7 @@ describe('stripLocalCommandTags', () => { describe('Claude → AiSDK transform', () => { it('handles tool call streaming lifecycle', () => { - const state = new ClaudeStreamState() + const state = new ClaudeStreamState({ agentSessionId: baseStreamMetadata.session_id }) const parts: ReturnType[number][] = [] const messages: SDKMessage[] = [ @@ -182,14 +182,14 @@ describe('Claude → AiSDK transform', () => { (typeof parts)[number], { type: 'tool-result' } > - expect(toolResult.toolCallId).toBe('tool-1') + expect(toolResult.toolCallId).toBe('session-123:tool-1') expect(toolResult.toolName).toBe('Bash') expect(toolResult.input).toEqual({ command: 'ls' }) expect(toolResult.output).toBe('ok') }) it('handles streaming text completion', () => { - const state = new ClaudeStreamState() + const state = new ClaudeStreamState({ agentSessionId: baseStreamMetadata.session_id }) const parts: ReturnType[number][] = [] const messages: SDKMessage[] = [ diff --git a/src/main/services/agents/services/claudecode/claude-stream-state.ts b/src/main/services/agents/services/claudecode/claude-stream-state.ts index 078f048ce8..19a664a8d1 100644 --- a/src/main/services/agents/services/claudecode/claude-stream-state.ts +++ b/src/main/services/agents/services/claudecode/claude-stream-state.ts @@ -10,8 +10,21 @@ * Every Claude turn gets its own instance. `resetStep` should be invoked once the finish event has * been emitted to avoid leaking state into the next turn. */ +import { loggerService } from '@logger' import type { FinishReason, LanguageModelUsage, ProviderMetadata } from 'ai' +/** + * Builds a namespaced tool call ID by combining session ID with raw tool call ID. + * This ensures tool calls from different sessions don't conflict even if they have + * the same raw ID from the SDK. + * + * @param sessionId - The agent session ID + * @param rawToolCallId - The raw tool call ID from SDK (e.g., "WebFetch_0") + */ +export function buildNamespacedToolCallId(sessionId: string, rawToolCallId: string): string { + return `${sessionId}:${rawToolCallId}` +} + /** * Shared fields for every block that Claude can stream (text, reasoning, tool). */ @@ -34,6 +47,7 @@ type ReasoningBlockState = BaseBlockState & { type ToolBlockState = BaseBlockState & { kind: 'tool' toolCallId: string + rawToolCallId: string toolName: string inputBuffer: string providerMetadata?: ProviderMetadata @@ -48,12 +62,17 @@ type PendingUsageState = { } type PendingToolCall = { + rawToolCallId: string toolCallId: string toolName: string input: unknown providerMetadata?: ProviderMetadata } +type ClaudeStreamStateOptions = { + agentSessionId: string +} + /** * Tracks the lifecycle of Claude streaming blocks (text, thinking, tool calls) * across individual websocket events. The transformer relies on this class to @@ -61,12 +80,20 @@ type PendingToolCall = { * usage/finish metadata once Anthropic closes a message. */ export class ClaudeStreamState { + private logger + private readonly agentSessionId: string private blocksByIndex = new Map() - private toolIndexById = new Map() + private toolIndexByNamespacedId = new Map() private pendingUsage: PendingUsageState = {} private pendingToolCalls = new Map() private stepActive = false + constructor(options: ClaudeStreamStateOptions) { + this.logger = loggerService.withContext('ClaudeStreamState') + this.agentSessionId = options.agentSessionId + this.logger.silly('ClaudeStreamState', options) + } + /** Marks the beginning of a new AiSDK step. */ beginStep(): void { this.stepActive = true @@ -104,19 +131,21 @@ export class ClaudeStreamState { /** Caches tool metadata so subsequent input deltas and results can find it. */ openToolBlock( index: number, - params: { toolCallId: string; toolName: string; providerMetadata?: ProviderMetadata } + params: { rawToolCallId: string; toolName: string; providerMetadata?: ProviderMetadata } ): ToolBlockState { + const toolCallId = buildNamespacedToolCallId(this.agentSessionId, params.rawToolCallId) const block: ToolBlockState = { kind: 'tool', - id: params.toolCallId, + id: toolCallId, index, - toolCallId: params.toolCallId, + toolCallId, + rawToolCallId: params.rawToolCallId, toolName: params.toolName, inputBuffer: '', providerMetadata: params.providerMetadata } this.blocksByIndex.set(index, block) - this.toolIndexById.set(params.toolCallId, index) + this.toolIndexByNamespacedId.set(toolCallId, index) return block } @@ -125,13 +154,17 @@ export class ClaudeStreamState { } getToolBlockById(toolCallId: string): ToolBlockState | undefined { - const index = this.toolIndexById.get(toolCallId) + const index = this.toolIndexByNamespacedId.get(toolCallId) if (index === undefined) return undefined const block = this.blocksByIndex.get(index) if (!block || block.kind !== 'tool') return undefined return block } + getToolBlockByRawId(rawToolCallId: string): ToolBlockState | undefined { + return this.getToolBlockById(buildNamespacedToolCallId(this.agentSessionId, rawToolCallId)) + } + /** Appends streamed text to a text block, returning the updated state when present. */ appendTextDelta(index: number, text: string): TextBlockState | undefined { const block = this.blocksByIndex.get(index) @@ -158,10 +191,12 @@ export class ClaudeStreamState { /** Records a tool call to be consumed once its result arrives from the user. */ registerToolCall( - toolCallId: string, + rawToolCallId: string, payload: { toolName: string; input: unknown; providerMetadata?: ProviderMetadata } ): void { - this.pendingToolCalls.set(toolCallId, { + const toolCallId = buildNamespacedToolCallId(this.agentSessionId, rawToolCallId) + this.pendingToolCalls.set(rawToolCallId, { + rawToolCallId, toolCallId, toolName: payload.toolName, input: payload.input, @@ -170,10 +205,10 @@ export class ClaudeStreamState { } /** Retrieves and clears the buffered tool call metadata for the given id. */ - consumePendingToolCall(toolCallId: string): PendingToolCall | undefined { - const entry = this.pendingToolCalls.get(toolCallId) + consumePendingToolCall(rawToolCallId: string): PendingToolCall | undefined { + const entry = this.pendingToolCalls.get(rawToolCallId) if (entry) { - this.pendingToolCalls.delete(toolCallId) + this.pendingToolCalls.delete(rawToolCallId) } return entry } @@ -183,12 +218,12 @@ export class ClaudeStreamState { * completion so that downstream tool results can reference the original call. */ completeToolBlock(toolCallId: string, input: unknown, providerMetadata?: ProviderMetadata): void { + const block = this.getToolBlockByRawId(toolCallId) this.registerToolCall(toolCallId, { - toolName: this.getToolBlockById(toolCallId)?.toolName ?? 'unknown', + toolName: block?.toolName ?? 'unknown', input, providerMetadata }) - const block = this.getToolBlockById(toolCallId) if (block) { block.resolvedInput = input } @@ -200,7 +235,7 @@ export class ClaudeStreamState { if (!block) return undefined this.blocksByIndex.delete(index) if (block.kind === 'tool') { - this.toolIndexById.delete(block.toolCallId) + this.toolIndexByNamespacedId.delete(block.toolCallId) } return block } @@ -227,7 +262,7 @@ export class ClaudeStreamState { /** Drops cached block metadata for the currently active message. */ resetBlocks(): void { this.blocksByIndex.clear() - this.toolIndexById.clear() + this.toolIndexByNamespacedId.clear() } /** Resets the entire step lifecycle after emitting a terminal frame. */ @@ -236,6 +271,10 @@ export class ClaudeStreamState { this.resetPendingUsage() this.stepActive = false } + + getNamespacedToolCallId(rawToolCallId: string): string { + return buildNamespacedToolCallId(this.agentSessionId, rawToolCallId) + } } export type { PendingToolCall } diff --git a/src/main/services/agents/services/claudecode/index.ts b/src/main/services/agents/services/claudecode/index.ts index a8f3f54fa8..327031d2f3 100644 --- a/src/main/services/agents/services/claudecode/index.ts +++ b/src/main/services/agents/services/claudecode/index.ts @@ -13,6 +13,7 @@ import { app } from 'electron' import type { GetAgentSessionResponse } from '../..' import type { AgentServiceInterface, AgentStream, AgentStreamEvent } from '../../interfaces/AgentStreamInterface' import { sessionService } from '../SessionService' +import { buildNamespacedToolCallId } from './claude-stream-state' import { promptForToolApproval } from './tool-permissions' import { ClaudeStreamState, transformSDKMessageToStreamParts } from './transform' @@ -150,7 +151,10 @@ class ClaudeCodeService implements AgentServiceInterface { return { behavior: 'allow', updatedInput: input } } - return promptForToolApproval(toolName, input, options) + return promptForToolApproval(toolName, input, { + ...options, + toolCallId: buildNamespacedToolCallId(session.id, options.toolUseID) + }) } // Build SDK options from parameters @@ -346,7 +350,7 @@ class ClaudeCodeService implements AgentServiceInterface { const jsonOutput: SDKMessage[] = [] let hasCompleted = false const startTime = Date.now() - const streamState = new ClaudeStreamState() + const streamState = new ClaudeStreamState({ agentSessionId: sessionId }) try { for await (const message of query({ prompt: promptStream, options })) { diff --git a/src/main/services/agents/services/claudecode/tool-permissions.ts b/src/main/services/agents/services/claudecode/tool-permissions.ts index c95f4c679e..5b50f4567e 100644 --- a/src/main/services/agents/services/claudecode/tool-permissions.ts +++ b/src/main/services/agents/services/claudecode/tool-permissions.ts @@ -37,6 +37,7 @@ type RendererPermissionRequestPayload = { requestId: string toolName: string toolId: string + toolCallId: string description?: string requiresPermissions: boolean input: Record @@ -206,10 +207,19 @@ const ensureIpcHandlersRegistered = () => { }) } +type PromptForToolApprovalOptions = { + signal: AbortSignal + suggestions?: PermissionUpdate[] + + // NOTICE: This ID is namespaced with session ID, not the raw SDK tool call ID. + // Format: `${sessionId}:${rawToolCallId}`, e.g., `session_123:WebFetch_0` + toolCallId: string +} + export async function promptForToolApproval( toolName: string, input: Record, - options?: { signal: AbortSignal; suggestions?: PermissionUpdate[] } + options: PromptForToolApprovalOptions ): Promise { if (shouldAutoApproveTools) { logger.debug('promptForToolApproval auto-approving tool for test', { @@ -245,6 +255,7 @@ export async function promptForToolApproval( logger.info('Requesting user approval for tool usage', { requestId, toolName, + toolCallId: options.toolCallId, description: toolMetadata?.description }) @@ -252,6 +263,7 @@ export async function promptForToolApproval( requestId, toolName, toolId: toolMetadata?.id ?? toolName, + toolCallId: options.toolCallId, description: toolMetadata?.description, requiresPermissions: toolMetadata?.requirePermissions ?? false, input: sanitizedInput, @@ -266,6 +278,7 @@ export async function promptForToolApproval( logger.debug('Registering tool permission request', { requestId, toolName, + toolCallId: options.toolCallId, requiresPermissions: requestPayload.requiresPermissions, timeoutMs: TOOL_APPROVAL_TIMEOUT_MS, suggestionCount: sanitizedSuggestions.length @@ -273,7 +286,11 @@ export async function promptForToolApproval( return new Promise((resolve) => { const timeout = setTimeout(() => { - logger.info('User tool permission request timed out', { requestId, toolName }) + logger.info('User tool permission request timed out', { + requestId, + toolName, + toolCallId: options.toolCallId + }) finalizeRequest(requestId, { behavior: 'deny', message: 'Timed out waiting for approval' }, 'timeout') }, TOOL_APPROVAL_TIMEOUT_MS) @@ -287,7 +304,11 @@ export async function promptForToolApproval( if (options?.signal) { const abortListener = () => { - logger.info('Tool permission request aborted before user responded', { requestId, toolName }) + logger.info('Tool permission request aborted before user responded', { + requestId, + toolName, + toolCallId: options.toolCallId + }) finalizeRequest(requestId, defaultDenyUpdate, 'aborted') } diff --git a/src/main/services/agents/services/claudecode/transform.ts b/src/main/services/agents/services/claudecode/transform.ts index 41285175b4..cbd2f735d6 100644 --- a/src/main/services/agents/services/claudecode/transform.ts +++ b/src/main/services/agents/services/claudecode/transform.ts @@ -243,9 +243,10 @@ function handleAssistantToolUse( state: ClaudeStreamState, chunks: AgentStreamPart[] ): void { + const toolCallId = state.getNamespacedToolCallId(block.id) chunks.push({ type: 'tool-call', - toolCallId: block.id, + toolCallId, toolName: block.name, input: block.input, providerExecuted: true, @@ -331,10 +332,11 @@ function handleUserMessage( if (block.type === 'tool_result') { const toolResult = block as ToolResultContent const pendingCall = state.consumePendingToolCall(toolResult.tool_use_id) + const toolCallId = pendingCall?.toolCallId ?? state.getNamespacedToolCallId(toolResult.tool_use_id) if (toolResult.is_error) { chunks.push({ type: 'tool-error', - toolCallId: toolResult.tool_use_id, + toolCallId, toolName: pendingCall?.toolName ?? 'unknown', input: pendingCall?.input, error: toolResult.content, @@ -343,7 +345,7 @@ function handleUserMessage( } else { chunks.push({ type: 'tool-result', - toolCallId: toolResult.tool_use_id, + toolCallId, toolName: pendingCall?.toolName ?? 'unknown', input: pendingCall?.input, output: toolResult.content, @@ -514,7 +516,7 @@ function handleContentBlockStart( } case 'tool_use': { const block = state.openToolBlock(index, { - toolCallId: contentBlock.id, + rawToolCallId: contentBlock.id, toolName: contentBlock.name, providerMetadata }) diff --git a/src/renderer/src/aiCore/utils/options.ts b/src/renderer/src/aiCore/utils/options.ts index 128a0f5269..7f4cd33608 100644 --- a/src/renderer/src/aiCore/utils/options.ts +++ b/src/renderer/src/aiCore/utils/options.ts @@ -99,9 +99,6 @@ export function buildProviderOptions( serviceTier: serviceTierSetting } break - case 'huggingface': - providerSpecificOptions = buildOpenAIProviderOptions(assistant, model, capabilities) - break case 'anthropic': providerSpecificOptions = buildAnthropicProviderOptions(assistant, model, capabilities) break @@ -144,6 +141,9 @@ export function buildProviderOptions( case 'bedrock': providerSpecificOptions = buildBedrockProviderOptions(assistant, model, capabilities) break + case 'huggingface': + providerSpecificOptions = buildOpenAIProviderOptions(assistant, model, capabilities) + break default: // 对于其他 provider,使用通用的构建逻辑 providerSpecificOptions = { @@ -162,13 +162,17 @@ export function buildProviderOptions( ...getCustomParameters(assistant) } - const rawProviderKey = + let rawProviderKey = { 'google-vertex': 'google', 'google-vertex-anthropic': 'anthropic', 'ai-gateway': 'gateway' }[rawProviderId] || rawProviderId + if (rawProviderKey === 'cherryin') { + rawProviderKey = { gemini: 'google' }[actualProvider.type] || actualProvider.type + } + // 返回 AI Core SDK 要求的格式:{ 'providerId': providerOptions } return { [rawProviderKey]: providerSpecificOptions diff --git a/src/renderer/src/components/Popups/agent/AgentModal.tsx b/src/renderer/src/components/Popups/agent/AgentModal.tsx index d504699399..2574cbe669 100644 --- a/src/renderer/src/components/Popups/agent/AgentModal.tsx +++ b/src/renderer/src/components/Popups/agent/AgentModal.tsx @@ -1,5 +1,4 @@ import { loggerService } from '@logger' -import ClaudeIcon from '@renderer/assets/images/models/claude.png' import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { TopView } from '@renderer/components/TopView' import { permissionModeCards } from '@renderer/config/agent' @@ -9,7 +8,6 @@ import SelectAgentBaseModelButton from '@renderer/pages/home/components/SelectAg import type { AddAgentForm, AgentEntity, - AgentType, ApiModel, BaseAgentForm, PermissionMode, @@ -17,30 +15,22 @@ import type { UpdateAgentForm } from '@renderer/types' import { AgentConfigurationSchema, isAgentType } from '@renderer/types' -import { Avatar, Button, Input, Modal, Select } from 'antd' +import { Button, Input, Modal, Select } from 'antd' import { AlertTriangleIcon } from 'lucide-react' import type { ChangeEvent, FormEvent } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' -import type { BaseOption } from './shared' - const { TextArea } = Input const logger = loggerService.withContext('AddAgentPopup') -interface AgentTypeOption extends BaseOption { - type: 'type' - key: AgentEntity['type'] - name: AgentEntity['name'] -} - type AgentWithTools = AgentEntity & { tools?: Tool[] } const buildAgentForm = (existing?: AgentWithTools): BaseAgentForm => ({ type: existing?.type ?? 'claude-code', - name: existing?.name ?? 'Claude Code', + name: existing?.name ?? 'Agent', description: existing?.description, instructions: existing?.instructions, model: existing?.model ?? '', @@ -100,54 +90,6 @@ const PopupContainer: React.FC = ({ agent, afterSubmit, resolve }) => { }) }, []) - // add supported agents type here. - const agentConfig = useMemo( - () => - [ - { - type: 'type', - key: 'claude-code', - label: 'Claude Code', - name: 'Claude Code', - avatar: ClaudeIcon - } - ] as const satisfies AgentTypeOption[], - [] - ) - - const agentOptions = useMemo( - () => - agentConfig.map((option) => ({ - value: option.key, - label: ( - - - {option.label} - - ) - })), - [agentConfig] - ) - - const onAgentTypeChange = useCallback( - (value: AgentType) => { - const prevConfig = agentConfig.find((config) => config.key === form.type) - let newName: string | undefined = form.name - if (prevConfig && prevConfig.name === form.name) { - const newConfig = agentConfig.find((config) => config.key === value) - if (newConfig) { - newName = newConfig.name - } - } - setForm((prev) => ({ - ...prev, - type: value, - name: newName - })) - }, - [agentConfig, form.name, form.type] - ) - const onNameChange = useCallback((e: ChangeEvent) => { setForm((prev) => ({ ...prev, @@ -155,12 +97,12 @@ const PopupContainer: React.FC = ({ agent, afterSubmit, resolve }) => { })) }, []) - const onDescChange = useCallback((e: ChangeEvent) => { - setForm((prev) => ({ - ...prev, - description: e.target.value - })) - }, []) + // const onDescChange = useCallback((e: ChangeEvent) => { + // setForm((prev) => ({ + // ...prev, + // description: e.target.value + // })) + // }, []) const onInstChange = useCallback((e: ChangeEvent) => { setForm((prev) => ({ @@ -334,16 +276,6 @@ const PopupContainer: React.FC = ({ agent, afterSubmit, resolve }) => { - - -