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..9d191c1 100644 --- a/packages/agent/src/core/llm/providers/anthropic.ts +++ b/packages/agent/src/core/llm/providers/anthropic.ts @@ -12,8 +12,9 @@ import { ProviderOptions, } from '../types.js'; -// Define model context window sizes for Anthropic models -const ANTHROPIC_MODEL_LIMITS: Record = { +// Fallback model context window sizes for Anthropic models +// Used only if models.list() call fails or returns incomplete data +const ANTHROPIC_MODEL_LIMITS_FALLBACK: Record = { default: 200000, 'claude-3-7-sonnet-20250219': 200000, 'claude-3-7-sonnet-latest': 200000, @@ -96,7 +97,14 @@ function addCacheControlToMessages( }); } -function tokenUsageFromMessage(message: Anthropic.Message, model: string) { +// Cache for model context window sizes +const modelContextWindowCache: Record = {}; + +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 +112,12 @@ 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, or use hardcoded fallback + const maxTokens = + contextWindow || + modelContextWindowCache[model] || + ANTHROPIC_MODEL_LIMITS_FALLBACK[model] || + ANTHROPIC_MODEL_LIMITS_FALLBACK.default; return { usage, @@ -123,6 +136,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 +152,72 @@ 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, we'll fall back to hardcoded limits + this.initializeModelContextWindow().catch((error) => { + console.warn( + `Failed to initialize model context window: ${error.message}`, + ); + }); + } + + /** + * Fetches the model context window size from the Anthropic API + * + * @returns The context window size if successfully fetched, otherwise undefined + */ + private async initializeModelContextWindow(): Promise { + try { + const response = await this.client.models.list(); + + if (!response?.data || !Array.isArray(response.data)) { + console.warn(`Invalid response from models.list() for ${this.model}`); + return undefined; + } + + // 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 { + console.warn(`No context window information found for ${this.model}`); + return undefined; + } + } catch (error) { + console.warn( + `Failed to fetch model context window for ${this.model}: ${(error as Error).message}`, + ); + // Will fall back to hardcoded limits + return undefined; + } } /** @@ -198,7 +278,11 @@ export class AnthropicProvider implements LLMProvider { }; }); - const tokenInfo = tokenUsageFromMessage(response, this.model); + const tokenInfo = tokenUsageFromMessage( + response, + this.model, + this.modelContextWindow, + ); return { text: content,