diff --git a/packages/agent/CHANGELOG.md b/packages/agent/CHANGELOG.md index dfd1dd9..3dffbed 100644 --- a/packages/agent/CHANGELOG.md +++ b/packages/agent/CHANGELOG.md @@ -1,3 +1,14 @@ +# [mycoder-agent-v1.7.0](https://github.com/drivecore/mycoder/compare/mycoder-agent-v1.6.0...mycoder-agent-v1.7.0) (2025-03-21) + +### Bug Fixes + +- Fix TypeScript errors and tests for message compaction feature ([d4f1fb5](https://github.com/drivecore/mycoder/commit/d4f1fb5d197e623bf98f2221352f9132dcb3e5de)) + +### Features + +- Add automatic compaction of historical messages for agents ([a5caf46](https://github.com/drivecore/mycoder/commit/a5caf464a0a8dca925c7b46023ebde4727e211f8)), closes [#338](https://github.com/drivecore/mycoder/issues/338) +- Improve message compaction with proactive suggestions ([6276bc0](https://github.com/drivecore/mycoder/commit/6276bc0bc5fa27c4f1e9be61ff4375690ad04c62)) + # [mycoder-agent-v1.6.0](https://github.com/drivecore/mycoder/compare/mycoder-agent-v1.5.0...mycoder-agent-v1.6.0) (2025-03-21) ### Features diff --git a/packages/agent/package.json b/packages/agent/package.json index 7af27a4..2a35330 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -1,6 +1,6 @@ { "name": "mycoder-agent", - "version": "1.6.0", + "version": "1.7.0", "description": "Agent module for mycoder - an AI-powered software development assistant", "type": "module", "main": "dist/index.js", diff --git a/packages/agent/src/core/llm/providers/anthropic.ts b/packages/agent/src/core/llm/providers/anthropic.ts index 95a0458..97a35d9 100644 --- a/packages/agent/src/core/llm/providers/anthropic.ts +++ b/packages/agent/src/core/llm/providers/anthropic.ts @@ -12,20 +12,8 @@ import { ProviderOptions, } from '../types.js'; -// Define model context window sizes for Anthropic models -const ANTHROPIC_MODEL_LIMITS: Record = { - default: 200000, - '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, -}; +// Cache for model context window sizes +const modelContextWindowCache: Record = {}; /** * Anthropic-specific options @@ -96,7 +84,11 @@ function addCacheControlToMessages( }); } -function tokenUsageFromMessage(message: Anthropic.Message, model: string) { +function tokenUsageFromMessage( + message: Anthropic.Message, + model: string, + contextWindow?: number, +) { const usage = new TokenUsage(); usage.input = message.usage.input_tokens; usage.cacheWrites = message.usage.cache_creation_input_tokens ?? 0; @@ -104,7 +96,15 @@ function tokenUsageFromMessage(message: Anthropic.Message, model: string) { usage.output = message.usage.output_tokens; const totalTokens = usage.input + usage.output; - const maxTokens = ANTHROPIC_MODEL_LIMITS[model] || 100000; // Default fallback + + // Use provided context window or fallback to cached value + const maxTokens = contextWindow || modelContextWindowCache[model]; + + if (!maxTokens) { + throw new Error( + `Context window size not available for model: ${model}. Make sure to initialize the model properly.`, + ); + } return { usage, @@ -123,6 +123,7 @@ export class AnthropicProvider implements LLMProvider { private client: Anthropic; private apiKey: string; private baseUrl?: string; + private modelContextWindow?: number; constructor(model: string, options: AnthropicOptions = {}) { this.model = model; @@ -138,6 +139,73 @@ export class AnthropicProvider implements LLMProvider { apiKey: this.apiKey, ...(this.baseUrl && { baseURL: this.baseUrl }), }); + + // Initialize model context window detection + // This is async but we don't need to await it here + // If it fails, an error will be thrown when the model is used + this.initializeModelContextWindow().catch((error) => { + console.error( + `Failed to initialize model context window: ${error.message}. The model will not work until context window information is available.`, + ); + }); + } + + /** + * Fetches the model context window size from the Anthropic API + * + * @returns The context window size + * @throws Error if the context window size cannot be determined + */ + private async initializeModelContextWindow(): Promise { + try { + const response = await this.client.models.list(); + + if (!response?.data || !Array.isArray(response.data)) { + throw new Error( + `Invalid response from models.list() for ${this.model}`, + ); + } + + // Try to find the exact model + let model = response.data.find((m) => m.id === this.model); + + // If not found, try to find a model that starts with the same name + // This helps with model aliases like 'claude-3-sonnet-latest' + if (!model) { + // Split by '-latest' or '-20' to get the base model name + const parts = this.model.split('-latest'); + const modelPrefix = + parts.length > 1 ? parts[0] : this.model.split('-20')[0]; + + if (modelPrefix) { + model = response.data.find((m) => m.id.startsWith(modelPrefix)); + + if (model) { + console.info( + `Model ${this.model} not found, using ${model.id} for context window size`, + ); + } + } + } + + // Using type assertion to access context_window property + // The Anthropic API returns context_window but it may not be in the TypeScript definitions + if (model && 'context_window' in model) { + const contextWindow = (model as any).context_window; + this.modelContextWindow = contextWindow; + // Cache the result for future use + modelContextWindowCache[this.model] = contextWindow; + return contextWindow; + } else { + throw new Error( + `No context window information found for model: ${this.model}`, + ); + } + } catch (error) { + throw new Error( + `Failed to determine context window size for model ${this.model}: ${(error as Error).message}`, + ); + } } /** @@ -198,7 +266,11 @@ export class AnthropicProvider implements LLMProvider { }; }); - const tokenInfo = tokenUsageFromMessage(response, this.model); + const tokenInfo = tokenUsageFromMessage( + response, + this.model, + this.modelContextWindow, + ); return { text: content,