@measey/mycoder-agent
Version:
Agent module for mycoder - an AI-powered software development assistant
219 lines • 7.48 kB
JavaScript
/**
* 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