import Anthropic from '@anthropic-ai/sdk'; import type { AiProvider, PingRequest, PingResponse, ToolCallRequest, ToolCallResponse } from './provider.interface'; /** * Anthropic 格式提供商(官方 API + 兼容 Anthropic 格式的中转站) */ export class AnthropicProvider implements AiProvider { readonly format = 'anthropic' as const; private client: Anthropic; constructor(apiKey: string, baseURL?: string) { this.client = new Anthropic({ apiKey, ...(baseURL && baseURL !== 'https://api.anthropic.com' ? { baseURL } : {}), }); } async callTool(req: ToolCallRequest): Promise { const toolChoice = req.forceTool ? ({ type: 'tool', name: req.tool.name, disable_parallel_tool_use: true, } as any) : { type: 'auto' as const }; const response = await this.client.messages.create({ model: req.model, max_tokens: req.maxTokens, system: req.systemPrompt, tools: [ { name: req.tool.name, description: req.tool.description, input_schema: req.tool.inputSchema, }, ], tool_choice: toolChoice, messages: [{ role: 'user', content: req.userPrompt }], }); const toolUse = response.content.find( (c): c is Anthropic.ToolUseBlock => c.type === 'tool_use', ); if (!toolUse) { throw new Error(this.describeMissingToolUse(response)); } return { toolName: toolUse.name, toolInput: toolUse.input as Record, inputTokens: response.usage.input_tokens, outputTokens: response.usage.output_tokens, rawModel: response.model, }; } async ping(req: PingRequest): Promise { const resp = await this.client.messages.create({ model: req.model, max_tokens: 16, messages: [{ role: 'user', content: 'ping' }], }); return { ok: true, inputTokens: resp.usage.input_tokens, outputTokens: resp.usage.output_tokens, }; } private describeMissingToolUse(response: Anthropic.Message): string { const contentSummary = response.content .map((block) => { if (block.type === 'text') { const text = block.text.replace(/\s+/g, ' ').slice(0, 160); return `text("${text}")`; } return block.type; }) .join(', '); return [ 'Anthropic 未通过 tool_use 返回结果', `stop_reason=${response.stop_reason || 'unknown'}`, `content=${contentSummary || 'empty'}`, ].join(';'); } }