Skip to content

Commit 9e32afe

Browse files
committed
feat: implement dynamic context window detection for Anthropic models
1 parent 468c050 commit 9e32afe

File tree

1 file changed

+49
-5
lines changed

1 file changed

+49
-5
lines changed

packages/agent/src/core/llm/providers/anthropic.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import {
1212
ProviderOptions,
1313
} from '../types.js';
1414

15-
// Define model context window sizes for Anthropic models
16-
const ANTHROPIC_MODEL_LIMITS: Record<string, number> = {
15+
// Fallback model context window sizes for Anthropic models
16+
// Used only if models.list() call fails or returns incomplete data
17+
const ANTHROPIC_MODEL_LIMITS_FALLBACK: Record<string, number> = {
1718
default: 200000,
1819
'claude-3-7-sonnet-20250219': 200000,
1920
'claude-3-7-sonnet-latest': 200000,
@@ -96,15 +97,27 @@ function addCacheControlToMessages(
9697
});
9798
}
9899

99-
function tokenUsageFromMessage(message: Anthropic.Message, model: string) {
100+
// Cache for model context window sizes
101+
const modelContextWindowCache: Record<string, number> = {};
102+
103+
function tokenUsageFromMessage(
104+
message: Anthropic.Message,
105+
model: string,
106+
contextWindow?: number,
107+
) {
100108
const usage = new TokenUsage();
101109
usage.input = message.usage.input_tokens;
102110
usage.cacheWrites = message.usage.cache_creation_input_tokens ?? 0;
103111
usage.cacheReads = message.usage.cache_read_input_tokens ?? 0;
104112
usage.output = message.usage.output_tokens;
105113

106114
const totalTokens = usage.input + usage.output;
107-
const maxTokens = ANTHROPIC_MODEL_LIMITS[model] || 100000; // Default fallback
115+
// Use provided context window, or fallback to cached value, or use hardcoded fallback
116+
const maxTokens =
117+
contextWindow ||
118+
modelContextWindowCache[model] ||
119+
ANTHROPIC_MODEL_LIMITS_FALLBACK[model] ||
120+
ANTHROPIC_MODEL_LIMITS_FALLBACK.default;
108121

109122
return {
110123
usage,
@@ -123,6 +136,7 @@ export class AnthropicProvider implements LLMProvider {
123136
private client: Anthropic;
124137
private apiKey: string;
125138
private baseUrl?: string;
139+
private modelContextWindow?: number;
126140

127141
constructor(model: string, options: AnthropicOptions = {}) {
128142
this.model = model;
@@ -138,6 +152,32 @@ export class AnthropicProvider implements LLMProvider {
138152
apiKey: this.apiKey,
139153
...(this.baseUrl && { baseURL: this.baseUrl }),
140154
});
155+
156+
// Initialize model context window detection
157+
this.initializeModelContextWindow();
158+
}
159+
160+
/**
161+
* Fetches the model context window size from the Anthropic API
162+
*/
163+
private async initializeModelContextWindow(): Promise<void> {
164+
try {
165+
const response = await this.client.models.list();
166+
const model = response.data.find((m) => m.id === this.model);
167+
168+
// Using type assertion to access context_window property
169+
// The Anthropic API returns context_window but it may not be in the TypeScript definitions
170+
if (model && 'context_window' in model) {
171+
this.modelContextWindow = (model as any).context_window;
172+
// Cache the result for future use
173+
modelContextWindowCache[this.model] = (model as any).context_window;
174+
}
175+
} catch (error) {
176+
console.warn(
177+
`Failed to fetch model context window for ${this.model}: ${(error as Error).message}`,
178+
);
179+
// Will fall back to hardcoded limits
180+
}
141181
}
142182

143183
/**
@@ -198,7 +238,11 @@ export class AnthropicProvider implements LLMProvider {
198238
};
199239
});
200240

201-
const tokenInfo = tokenUsageFromMessage(response, this.model);
241+
const tokenInfo = tokenUsageFromMessage(
242+
response,
243+
this.model,
244+
this.modelContextWindow,
245+
);
202246

203247
return {
204248
text: content,

0 commit comments

Comments
 (0)