UNPKG

@measey/mycoder-agent

Version:

Agent module for mycoder - an AI-powered software development assistant

219 lines 7.48 kB
/** * Anthropic provider implementation */ import Anthropic from '@anthropic-ai/sdk'; import { TokenUsage } from '../../tokens.js'; const ANTHROPIC_CONTEXT_WINDOWS = { 'claude-3-7-sonnet-20250219': 200000, 'claude-3-7-sonnet-latest': 200000, 'claude-3-5-sonnet-20241022': 200000, 'claude-3-5-sonnet-latest': 200000, 'claude-3-haiku-20240307': 200000, 'claude-3-opus-20240229': 200000, 'claude-3-sonnet-20240229': 200000, 'claude-2.1': 100000, 'claude-2.0': 100000, 'claude-instant-1.2': 100000, }; // a function that takes a list of messages and returns a list of messages but with the last message having a cache_control of ephemeral function addCacheControlToTools(messages) { return messages.map((m, i) => ({ ...m, ...(i === messages.length - 1 ? { cache_control: { type: 'ephemeral' } } : {}), })); } function addCacheControlToContentBlocks(content) { return content.map((c, i) => { if (i === content.length - 1) { if (c.type === 'text' || c.type === 'document' || c.type === 'image' || c.type === 'tool_use' || c.type === 'tool_result' || c.type === 'thinking' || c.type === 'redacted_thinking') { return { ...c, cache_control: { type: 'ephemeral' } }; } } return c; }); } function addCacheControlToMessages(messages) { return messages.map((m, i) => { if (typeof m.content === 'string') { return { ...m, content: i >= messages.length - 2 ? [ { type: 'text', text: m.content, cache_control: { type: 'ephemeral' }, }, ] : m.content, }; } return { ...m, content: i >= messages.length - 2 ? addCacheControlToContentBlocks(m.content) : m.content, }; }); } function tokenUsageFromMessage(message, model, contextWindow) { const usage = new TokenUsage(); usage.input = message.usage.input_tokens; usage.cacheWrites = message.usage.cache_creation_input_tokens ?? 0; usage.cacheReads = message.usage.cache_read_input_tokens ?? 0; usage.output = message.usage.output_tokens; const totalTokens = usage.input + usage.output; return { usage, totalTokens, contextWindow, }; } /** * Anthropic provider implementation */ export class AnthropicProvider { name = 'anthropic'; provider = 'anthropic.messages'; model; options; client; apiKey; baseUrl; constructor(model, options = {}) { this.model = model; this.options = options; this.apiKey = options.apiKey ?? ''; this.baseUrl = options.baseUrl; if (!this.apiKey) { throw new Error('Anthropic API key is required'); } // Initialize Anthropic client this.client = new Anthropic({ apiKey: this.apiKey, ...(this.baseUrl && { baseURL: this.baseUrl }), }); } /** * Generate text using Anthropic API */ async generateText(options) { // Use configuration contextWindow if provided, otherwise use model-specific value let modelContextWindow = ANTHROPIC_CONTEXT_WINDOWS[this.model]; if (!modelContextWindow && this.options.contextWindow) { modelContextWindow = this.options.contextWindow; } const { messages, functions, temperature = 0.7, maxTokens, topP } = options; // Extract system message const systemMessage = messages.find((msg) => msg.role === 'system'); const nonSystemMessages = messages.filter((msg) => msg.role !== 'system'); const formattedMessages = this.formatMessages(nonSystemMessages); const tools = addCacheControlToTools((functions ?? []).map((fn) => ({ name: fn.name, description: fn.description, input_schema: fn.parameters, }))); const requestOptions = { model: this.model, messages: addCacheControlToMessages(formattedMessages), temperature, max_tokens: maxTokens || 1024, system: systemMessage?.content ? [ { type: 'text', text: systemMessage?.content, cache_control: { type: 'ephemeral' }, }, ] : undefined, top_p: topP, tools, stream: false, }; const response = await this.client.messages.create(requestOptions); // Extract content and tool calls const content = response.content.find((c) => c.type === 'text')?.text || ''; const toolCalls = response.content .filter((c) => { const contentType = c.type; return contentType === 'tool_use'; }) .map((c) => { const toolUse = c; return { id: toolUse.id, name: toolUse.name, content: JSON.stringify(toolUse.input), }; }); const tokenInfo = tokenUsageFromMessage(response, this.model, modelContextWindow); return { text: content, toolCalls: toolCalls, tokenUsage: tokenInfo.usage, totalTokens: tokenInfo.totalTokens, contextWindow: tokenInfo.contextWindow, }; } /** * Format messages for Anthropic API */ formatMessages(messages) { // Format messages for Anthropic API return messages.map((msg) => { if (msg.role === 'user') { return { role: 'user', content: msg.content, }; } else if (msg.role === 'assistant') { return { role: 'assistant', content: msg.content, }; } else if (msg.role === 'tool_result') { // Anthropic expects tool responses as an assistant message with tool_results return { role: 'user', content: [ { type: 'tool_result', tool_use_id: msg.tool_use_id, // Use name as the tool_use_id content: msg.content, is_error: msg.is_error, }, ], }; } else if (msg.role === 'tool_use') { return { role: 'assistant', content: [ { type: 'tool_use', name: msg.name, id: msg.id, input: JSON.parse(msg.content), }, ], }; } return { role: 'user', content: msg.content, }; }); } } //# sourceMappingURL=anthropic.js.map