Skip to content

Commit 45defaf

Browse files
authored
fix(js/plugins/anthropic): add model name map for older models (#3874)
1 parent 6478f54 commit 45defaf

File tree

5 files changed

+106
-10
lines changed

5 files changed

+106
-10
lines changed

js/plugins/anthropic/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"build:watch": "tsup-node --watch",
6666
"test": "tsx --test tests/*_test.ts",
6767
"test:file": "tsx --test",
68+
"test:live": "tsx --test tests/live_test.ts",
6869
"test:coverage": "check-node-version --node '>=22' && tsx --test --experimental-test-coverage --test-coverage-include='src/**/*.ts' ./tests/**/*_test.ts"
6970
}
7071
}

js/plugins/anthropic/src/models.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ function commonRef(
6666
});
6767
}
6868

69+
/**
70+
* Maps short model names to their full API model IDs.
71+
* Claude 3.x models require versioned names (e.g., claude-3-5-haiku-20241022).
72+
* Claude 4.x models have API aliases that work directly.
73+
*/
74+
const MODEL_VERSION_MAP: Record<string, string> = {
75+
'claude-3-haiku': 'claude-3-haiku-20240307',
76+
'claude-3-5-haiku': 'claude-3-5-haiku-20241022',
77+
};
78+
6979
export const KNOWN_CLAUDE_MODELS: Record<
7080
string,
7181
ModelReference<
@@ -94,14 +104,16 @@ export const KNOWN_CLAUDE_MODELS: Record<
94104
};
95105

96106
/**
97-
* Gets the un-prefixed model name from a modelReference.
107+
* Gets the API model ID from a model name.
108+
* Maps short names to full versioned names for Claude 3.x models.
109+
* Claude 4.x models pass through unchanged as they have API aliases.
98110
*/
99111
export function extractVersion(
100112
model: ModelReference<ConfigSchemaType> | undefined,
101113
modelName: string
102114
): string {
103-
// Extract from model name (remove 'anthropic/' prefix if present)
104-
return modelName.replace(/^anthropic\//, '');
115+
const cleanName = modelName.replace(/^anthropic\//, '');
116+
return MODEL_VERSION_MAP[cleanName] ?? cleanName;
105117
}
106118

107119
/**

js/plugins/anthropic/tests/beta_runner_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ describe('BetaRunner', () => {
499499
true
500500
);
501501

502-
assert.strictEqual(body.model, 'claude-3-5-haiku');
502+
assert.strictEqual(body.model, 'claude-3-5-haiku-20241022');
503503
assert.ok(Array.isArray(body.system));
504504
assert.strictEqual(body.max_tokens, 128);
505505
assert.strictEqual(body.top_k, 4);
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Live integration tests that call the real Anthropic API.
19+
* Only runs when ANTHROPIC_API_KEY environment variable is set.
20+
*
21+
* Run with: ANTHROPIC_API_KEY=your-key pnpm test:live
22+
*/
23+
24+
import * as assert from 'assert';
25+
import { genkit } from 'genkit';
26+
import { describe, it } from 'node:test';
27+
import { anthropic } from '../src/index.js';
28+
29+
const API_KEY = process.env.ANTHROPIC_API_KEY;
30+
31+
describe('Live Anthropic API Tests', { skip: !API_KEY }, () => {
32+
it('should work with short model name claude-3-5-haiku', async () => {
33+
const ai = genkit({
34+
plugins: [anthropic({ apiKey: API_KEY })],
35+
});
36+
37+
const result = await ai.generate({
38+
model: 'anthropic/claude-3-5-haiku',
39+
prompt: 'Say "hello" and nothing else.',
40+
});
41+
42+
assert.ok(result.text.toLowerCase().includes('hello'));
43+
});
44+
45+
it('should work with short model name claude-3-haiku', async () => {
46+
const ai = genkit({
47+
plugins: [anthropic({ apiKey: API_KEY })],
48+
});
49+
50+
const result = await ai.generate({
51+
model: 'anthropic/claude-3-haiku',
52+
prompt: 'Say "hello" and nothing else.',
53+
});
54+
55+
assert.ok(result.text.toLowerCase().includes('hello'));
56+
});
57+
58+
it('should work with full versioned model name', async () => {
59+
const ai = genkit({
60+
plugins: [anthropic({ apiKey: API_KEY })],
61+
});
62+
63+
const result = await ai.generate({
64+
model: 'anthropic/claude-3-5-haiku-20241022',
65+
prompt: 'Say "hello" and nothing else.',
66+
});
67+
68+
assert.ok(result.text.toLowerCase().includes('hello'));
69+
});
70+
71+
it('should work with anthropic.model() helper', async () => {
72+
const ai = genkit({
73+
plugins: [anthropic({ apiKey: API_KEY })],
74+
});
75+
76+
const result = await ai.generate({
77+
model: anthropic.model('claude-3-5-haiku'),
78+
prompt: 'Say "hello" and nothing else.',
79+
});
80+
81+
assert.ok(result.text.toLowerCase().includes('hello'));
82+
});
83+
});

js/plugins/anthropic/tests/stable_runner_test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ describe('toAnthropicRequestBody', () => {
969969
role: 'user',
970970
},
971971
],
972-
model: 'claude-3-5-haiku',
972+
model: 'claude-3-5-haiku-20241022',
973973
metadata: {
974974
user_id: 'exampleUser123',
975975
},
@@ -1003,7 +1003,7 @@ describe('toAnthropicRequestBody', () => {
10031003
role: 'user',
10041004
},
10051005
],
1006-
model: 'claude-3-haiku',
1006+
model: 'claude-3-haiku-20240307',
10071007
metadata: {
10081008
user_id: 'exampleUser123',
10091009
},
@@ -1220,7 +1220,7 @@ describe('toAnthropicStreamingRequestBody', () => {
12201220
);
12211221

12221222
assert.strictEqual(output.stream, true);
1223-
assert.strictEqual(output.model, 'claude-3-5-haiku');
1223+
assert.strictEqual(output.model, 'claude-3-5-haiku-20241022');
12241224
assert.strictEqual(output.max_tokens, 4096);
12251225
});
12261226

@@ -1291,7 +1291,7 @@ describe('claudeRunner', () => {
12911291
assert.strictEqual(createStub.mock.calls.length, 1);
12921292
assert.deepStrictEqual(createStub.mock.calls[0].arguments, [
12931293
{
1294-
model: 'claude-3-5-haiku',
1294+
model: 'claude-3-5-haiku-20241022',
12951295
max_tokens: 4096,
12961296
messages: [],
12971297
},
@@ -1342,7 +1342,7 @@ describe('claudeRunner', () => {
13421342
assert.strictEqual(streamStub.mock.calls.length, 1);
13431343
assert.deepStrictEqual(streamStub.mock.calls[0].arguments, [
13441344
{
1345-
model: 'claude-3-5-haiku',
1345+
model: 'claude-3-5-haiku-20241022',
13461346
max_tokens: 4096,
13471347
messages: [],
13481348
stream: true,
@@ -2202,7 +2202,7 @@ describe('Runner request bodies and error branches', () => {
22022202
true
22032203
);
22042204

2205-
assert.strictEqual(body.model, 'claude-3-5-haiku');
2205+
assert.strictEqual(body.model, 'claude-3-5-haiku-20241022');
22062206
assert.ok(Array.isArray(body.system));
22072207
assert.strictEqual(body.system?.[0].cache_control?.type, 'ephemeral');
22082208
assert.strictEqual(body.max_tokens, 256);

0 commit comments

Comments
 (0)