"use strict"; /** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Anthropic = void 0; const fetchWithTimeout_1 = require("../fetchWithTimeout"); const types_1 = require("../types"); class Anthropic { name = 'anthropic'; async complete(conversation, options) { const maxTokens = Math.min(options.maxTokens ?? 32_768, 32_768); const { response, error } = await create({ model: options.model, max_tokens: maxTokens, temperature: options.temperature, system: systemPrompt(conversation.systemPrompt), messages: conversation.messages.map(toAnthropicMessageParts).flat(), tools: conversation.tools.map(toAnthropicTool), thinking: options.reasoning && options.reasoning !== 'none' ? { type: 'enabled', budget_tokens: options.maxTokens ? Math.round(maxTokens / 10) : 1024, } : undefined, }, options); if (error || !response) return { result: (0, types_1.assistantMessageFromError)(error ?? 'No response from Anthropic API'), usage: (0, types_1.emptyUsage)() }; const result = toAssistantMessage(response); const usage = { input: response.usage.input_tokens, output: response.usage.output_tokens, }; return { result, usage }; } } exports.Anthropic = Anthropic; async function create(createParams, options) { const headers = { 'Content-Type': 'application/json', 'x-api-key': options.apiKey, 'anthropic-version': '2023-06-01', }; const debugBody = { ...createParams, tools: `${createParams.tools?.length ?? 0} tools` }; options.debug?.('lowire:anthropic')('Request:', JSON.stringify(debugBody, null, 2)); const response = await (0, fetchWithTimeout_1.fetchWithTimeout)(options.apiEndpoint ?? `https://api.anthropic.com/v1/messages`, { method: 'POST', headers, body: JSON.stringify(createParams), signal: options.signal, timeout: options.apiTimeout }); const responseText = await response.text(); const responseBody = JSON.parse(responseText); options.debug?.('lowire:anthropic')('Response:', responseText); if (!response.ok) { options.debug?.('lowire:anthropic')('Response:', response.status); return { error: `API error: ${response.status} ${response.statusText} ${responseText}` }; } return { response: responseBody }; } function toContentPart(block) { if (block.type === 'text') { return { type: 'text', text: block.text, }; } if (block.type === 'tool_use') { return { type: 'tool_call', name: block.name, arguments: block.input, id: block.id, }; } if (block.type === 'thinking') { return { type: 'thinking', thinking: block.thinking, signature: block.signature, }; } return null; } function toAnthropicResultParam(part) { if (part.type === 'text') { return { type: 'text', text: part.text, }; } if (part.type === 'image') { return { type: 'image', source: { type: 'base64', data: part.data, media_type: part.mimeType }, }; } throw new Error(`Unsupported content part type: ${part.type}`); } function toAssistantMessage(message) { const stopReason = { code: 'ok' }; if (message.stop_reason === 'max_tokens') stopReason.code = 'max_tokens'; return { role: 'assistant', content: message.content.map(toContentPart).filter(Boolean), stopReason, }; } function toAnthropicTool(tool) { return { name: tool.name, description: tool.description, input_schema: tool.inputSchema, }; } function toAnthropicAssistantMessageParam(message) { const content = []; const toolResults = []; for (const part of message.content) { if (part.type === 'text') { content.push({ ...part, citations: [] }); continue; } if (part.type === 'tool_call') { content.push({ type: 'tool_use', id: part.id, name: part.name, input: part.arguments }); if (part.result) toolResults.push(toAnthropicToolResultMessage(part, part.result)); continue; } if (part.type === 'thinking') { content.push({ type: 'thinking', thinking: part.thinking, signature: part.signature, }); continue; } } if (message.toolError) { toolResults.push({ role: 'user', content: [{ type: 'text', text: message.toolError, }] }); } return [{ role: 'assistant', content }, ...toolResults]; } function toAnthropicToolResultMessage(call, result) { const toolResult = { type: 'tool_result', tool_use_id: call.id, content: result.content.map(toAnthropicResultParam), is_error: result.isError, }; return { role: 'user', content: [toolResult] }; } function toAnthropicMessageParts(message) { if (message.role === 'user') { return [{ role: 'user', content: message.content }]; } if (message.role === 'assistant') return toAnthropicAssistantMessageParam(message); throw new Error(`Unsupported message role: ${message.role}`); } const systemPrompt = (prompt) => ` ### System instructions ${prompt} ### Tool calling instructions - Make sure every message contains a tool call. - When you use a tool, you may provide a brief thought or explanation in the content field immediately before the tool_call. Do not split this into separate messages. - Every reply must include a tool call. `;