@gajae-code/ai 0.18.11 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/README.md +1 -1
- package/dist/types/auth-broker/wire-schemas.d.ts +30 -0
- package/dist/types/auth-storage.d.ts +98 -13
- package/dist/types/bedrock-claude-cache-policy.d.ts +1 -1
- package/dist/types/model-cache.d.ts +6 -4
- package/dist/types/model-manager.d.ts +2 -0
- package/dist/types/model-pricing.d.ts +8 -0
- package/dist/types/model-thinking.d.ts +11 -0
- package/dist/types/openai-completions-compat.d.ts +2 -1
- package/dist/types/provider-models/google.d.ts +2 -0
- package/dist/types/providers/amazon-bedrock.d.ts +17 -2
- package/dist/types/providers/anthropic.d.ts +25 -0
- package/dist/types/providers/aws-credential-config.d.ts +5 -0
- package/dist/types/providers/google-auth.d.ts +7 -0
- package/dist/types/providers/google-gemini-headers.d.ts +11 -6
- package/dist/types/providers/kiro-api-key.d.ts +5 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +2 -1
- package/dist/types/providers/openai-completions.d.ts +1 -1
- package/dist/types/providers/openai-responses-shared.d.ts +2 -1
- package/dist/types/stream.d.ts +33 -1
- package/dist/types/types.d.ts +26 -3
- package/dist/types/usage.d.ts +3 -0
- package/dist/types/utils/discovery/antigravity.d.ts +16 -1
- package/dist/types/utils/idle-iterator.d.ts +3 -2
- package/dist/types/utils/oauth/glm-zcode.d.ts +0 -30
- package/dist/types/utils/oauth/types.d.ts +9 -0
- package/package.json +4 -4
- package/src/auth-broker/remote-store.ts +62 -11
- package/src/auth-broker/wire-schemas.ts +2 -0
- package/src/auth-gateway/server.ts +3 -2
- package/src/auth-storage.ts +908 -246
- package/src/bedrock-claude-cache-policy.d.ts +1 -1
- package/src/bedrock-claude-cache-policy.ts +2 -2
- package/src/model-cache.ts +45 -10
- package/src/model-manager.ts +18 -6
- package/src/model-pricing.d.ts +8 -0
- package/src/model-pricing.ts +87 -0
- package/src/model-thinking.d.ts +11 -0
- package/src/model-thinking.ts +97 -12
- package/src/models.json +3176 -1670
- package/src/models.ts +10 -3
- package/src/openai-completions-compat.d.ts +2 -1
- package/src/openai-completions-compat.ts +5 -0
- package/src/provider-models/google.ts +11 -1
- package/src/provider-models/openai-compat.ts +26 -0
- package/src/providers/amazon-bedrock.d.ts +17 -2
- package/src/providers/amazon-bedrock.ts +76 -16
- package/src/providers/anthropic.d.ts +25 -0
- package/src/providers/anthropic.ts +230 -18
- package/src/providers/aws-credential-config.d.ts +5 -0
- package/src/providers/aws-credential-config.ts +29 -1
- package/src/providers/google-auth.d.ts +7 -0
- package/src/providers/google-auth.ts +165 -4
- package/src/providers/google-gemini-headers.d.ts +11 -6
- package/src/providers/google-gemini-headers.ts +16 -11
- package/src/providers/google-vertex.ts +2 -4
- package/src/providers/kiro-api-key.d.ts +5 -0
- package/src/providers/kiro-api-key.ts +379 -68
- package/src/providers/ollama.ts +19 -9
- package/src/providers/openai-anthropic-shim.ts +1 -0
- package/src/providers/openai-chat-server-schema.ts +1 -1
- package/src/providers/openai-chat-server.ts +14 -2
- package/src/providers/openai-codex-responses.ts +21 -14
- package/src/providers/openai-completions.d.ts +1 -1
- package/src/providers/openai-completions.ts +65 -4
- package/src/providers/openai-responses-server.ts +10 -1
- package/src/providers/openai-responses-shared.d.ts +2 -1
- package/src/providers/openai-responses-shared.ts +42 -1
- package/src/providers/openai-responses.ts +51 -10
- package/src/providers/pi-native-server.ts +1 -1
- package/src/providers/transform-messages.ts +32 -1
- package/src/stream.d.ts +33 -1
- package/src/stream.ts +111 -17
- package/src/types.d.ts +26 -3
- package/src/types.ts +55 -2
- package/src/usage/claude.ts +4 -2
- package/src/usage.ts +3 -0
- package/src/utils/discovery/antigravity.ts +192 -7
- package/src/utils/idle-iterator.d.ts +3 -2
- package/src/utils/idle-iterator.ts +30 -10
- package/src/utils/oauth/anthropic.ts +23 -22
- package/src/utils/oauth/api-key-validation.ts +34 -11
- package/src/utils/oauth/glm-zcode.d.ts +0 -30
- package/src/utils/oauth/glm-zcode.ts +15 -4
- package/src/utils/oauth/openai-codex.ts +41 -6
- package/src/utils/oauth/types.d.ts +9 -0
- package/src/utils/oauth/types.ts +9 -0
- package/src/utils/parse-bind.d.ts +35 -0
- package/src/utils/proxy.ts +35 -6
|
@@ -10,7 +10,7 @@ export type BedrockClaudeGeneration = {
|
|
|
10
10
|
* - family-first (3.x era): anthropic.claude-3-5-haiku-20241022-v1:0
|
|
11
11
|
* - kind-first (4+ era): anthropic.claude-opus-4-20250514-v1:0,
|
|
12
12
|
* anthropic.claude-haiku-4-5-20251001-v1:0
|
|
13
|
-
* Cross-region profiles (us./eu./au./jp./apac./global. prefixes) and inference-profile
|
|
13
|
+
* Cross-region profiles (us./eu./au./jp./apac./global./in. prefixes) and inference-profile
|
|
14
14
|
* ARNs keep the canonical model id as their final path segment.
|
|
15
15
|
*/
|
|
16
16
|
export declare function parseBedrockClaudeGeneration(id: string): BedrockClaudeGeneration | undefined;
|
|
@@ -20,13 +20,13 @@ function extractBedrockModelId(id: string): string | undefined {
|
|
|
20
20
|
* - family-first (3.x era): anthropic.claude-3-5-haiku-20241022-v1:0
|
|
21
21
|
* - kind-first (4+ era): anthropic.claude-opus-4-20250514-v1:0,
|
|
22
22
|
* anthropic.claude-haiku-4-5-20251001-v1:0
|
|
23
|
-
* Cross-region profiles (us./eu./au./jp./apac./global. prefixes) and inference-profile
|
|
23
|
+
* Cross-region profiles (us./eu./au./jp./apac./global./in. prefixes) and inference-profile
|
|
24
24
|
* ARNs keep the canonical model id as their final path segment.
|
|
25
25
|
*/
|
|
26
26
|
export function parseBedrockClaudeGeneration(id: string): BedrockClaudeGeneration | undefined {
|
|
27
27
|
const modelId = extractBedrockModelId(id);
|
|
28
28
|
if (modelId === undefined) return undefined;
|
|
29
|
-
const prefix = "(?:(?:us|eu|au|jp|apac|global)\\.)?anthropic\\.claude-";
|
|
29
|
+
const prefix = "(?:(?:us|eu|au|jp|apac|global|in)\\.)?anthropic\\.claude-";
|
|
30
30
|
const component = "(?:0|[1-9]\\d?)";
|
|
31
31
|
const suffix = "(?:-(?:[a-z][a-z0-9]*(?::[a-z0-9]+)?|\\d{8}))*";
|
|
32
32
|
const familyFirst = new RegExp(`^${prefix}([1-9]\\d?)(?:-(${component}))?-([a-z][a-z0-9]*)${suffix}$`).exec(modelId);
|
package/src/model-cache.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Database } from "bun:sqlite";
|
|
|
6
6
|
import { getModelDbPath } from "@gajae-code/utils/dirs";
|
|
7
7
|
import type { Api, Model } from "./types";
|
|
8
8
|
|
|
9
|
-
const CACHE_SCHEMA_VERSION =
|
|
9
|
+
const CACHE_SCHEMA_VERSION = 6;
|
|
10
10
|
|
|
11
11
|
interface CacheRow {
|
|
12
12
|
provider_id: string;
|
|
@@ -16,6 +16,7 @@ interface CacheRow {
|
|
|
16
16
|
static_fingerprint: string;
|
|
17
17
|
dynamic_model_ids: string | null;
|
|
18
18
|
dynamic_model_provenance: string | null;
|
|
19
|
+
discovery_user_agent: string | null;
|
|
19
20
|
models: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -38,6 +39,8 @@ interface CacheEntry<TApi extends Api = Api> {
|
|
|
38
39
|
/** IDs returned by the authoritative dynamic provider catalog, when retained. */
|
|
39
40
|
dynamicModelIds: string[] | undefined;
|
|
40
41
|
dynamicModelProvenance: string | undefined;
|
|
42
|
+
/** User-Agent used during discovery; invalidates cache if it changes. */
|
|
43
|
+
discoveryUserAgent: string | undefined;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
let sharedDb: Database | null = null;
|
|
@@ -63,6 +66,7 @@ function getDb(dbPath?: string): Database {
|
|
|
63
66
|
static_fingerprint TEXT NOT NULL DEFAULT '',
|
|
64
67
|
dynamic_model_ids TEXT,
|
|
65
68
|
dynamic_model_provenance TEXT,
|
|
69
|
+
discovery_user_agent TEXT,
|
|
66
70
|
models TEXT NOT NULL
|
|
67
71
|
)
|
|
68
72
|
`);
|
|
@@ -84,7 +88,13 @@ export function closeModelCache(dbPath?: string): boolean {
|
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
function migrateCacheSchema(db: Database): void {
|
|
87
|
-
const
|
|
91
|
+
const tableInfo = db.query("PRAGMA table_info(model_cache)");
|
|
92
|
+
let columns: TableInfoRow[];
|
|
93
|
+
try {
|
|
94
|
+
columns = tableInfo.all() as TableInfoRow[];
|
|
95
|
+
} finally {
|
|
96
|
+
tableInfo.finalize();
|
|
97
|
+
}
|
|
88
98
|
if (!columns.some(column => column.name === "static_fingerprint")) {
|
|
89
99
|
db.run("ALTER TABLE model_cache ADD COLUMN static_fingerprint TEXT NOT NULL DEFAULT ''");
|
|
90
100
|
}
|
|
@@ -94,7 +104,10 @@ function migrateCacheSchema(db: Database): void {
|
|
|
94
104
|
if (!columns.some(column => column.name === "dynamic_model_provenance")) {
|
|
95
105
|
db.run("ALTER TABLE model_cache ADD COLUMN dynamic_model_provenance TEXT");
|
|
96
106
|
}
|
|
97
|
-
|
|
107
|
+
if (!columns.some(column => column.name === "discovery_user_agent")) {
|
|
108
|
+
db.run("ALTER TABLE model_cache ADD COLUMN discovery_user_agent TEXT");
|
|
109
|
+
}
|
|
110
|
+
db.run("UPDATE model_cache SET version = ? WHERE version IN (2, 3, 4, 5)", [CACHE_SCHEMA_VERSION]);
|
|
98
111
|
}
|
|
99
112
|
|
|
100
113
|
export function readModelCache<TApi extends Api>(
|
|
@@ -102,13 +115,24 @@ export function readModelCache<TApi extends Api>(
|
|
|
102
115
|
ttlMs: number,
|
|
103
116
|
now: () => number,
|
|
104
117
|
dbPath?: string,
|
|
118
|
+
expectedDiscoveryUserAgent?: string,
|
|
105
119
|
): CacheEntry<TApi> | null {
|
|
106
120
|
try {
|
|
107
121
|
const db = getDb(dbPath);
|
|
108
|
-
const
|
|
122
|
+
const select = db.query<CacheRow, [string]>("SELECT * FROM model_cache WHERE provider_id = ?");
|
|
123
|
+
let row: CacheRow | null;
|
|
124
|
+
try {
|
|
125
|
+
row = select.get(providerId);
|
|
126
|
+
} finally {
|
|
127
|
+
select.finalize();
|
|
128
|
+
}
|
|
109
129
|
if (!row || row.version !== CACHE_SCHEMA_VERSION) {
|
|
110
130
|
return null;
|
|
111
131
|
}
|
|
132
|
+
// Invalidate cache if discovery User-Agent has changed
|
|
133
|
+
if (expectedDiscoveryUserAgent !== undefined && row.discovery_user_agent !== expectedDiscoveryUserAgent) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
112
136
|
const models = JSON.parse(row.models) as Model<TApi>[];
|
|
113
137
|
const ageMs = now() - row.updated_at;
|
|
114
138
|
const fresh = Number.isFinite(ageMs) && ageMs >= 0 && ageMs <= ttlMs;
|
|
@@ -120,6 +144,7 @@ export function readModelCache<TApi extends Api>(
|
|
|
120
144
|
staticFingerprint: row.static_fingerprint ?? "",
|
|
121
145
|
dynamicModelIds: row.dynamic_model_ids === null ? undefined : (JSON.parse(row.dynamic_model_ids) as string[]),
|
|
122
146
|
dynamicModelProvenance: row.dynamic_model_provenance ?? undefined,
|
|
147
|
+
discoveryUserAgent: row.discovery_user_agent ?? undefined,
|
|
123
148
|
};
|
|
124
149
|
} catch {
|
|
125
150
|
return null;
|
|
@@ -135,12 +160,13 @@ export function writeModelCache<TApi extends Api>(
|
|
|
135
160
|
dbPath?: string,
|
|
136
161
|
dynamicModelIds?: readonly string[],
|
|
137
162
|
dynamicModelProvenance?: string,
|
|
163
|
+
discoveryUserAgent?: string,
|
|
138
164
|
): void {
|
|
139
165
|
try {
|
|
140
166
|
const db = getDb(dbPath);
|
|
141
167
|
db.run(
|
|
142
|
-
`INSERT OR REPLACE INTO model_cache (provider_id, version, updated_at, authoritative, static_fingerprint, dynamic_model_ids, dynamic_model_provenance, models)
|
|
143
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
168
|
+
`INSERT OR REPLACE INTO model_cache (provider_id, version, updated_at, authoritative, static_fingerprint, dynamic_model_ids, dynamic_model_provenance, discovery_user_agent, models)
|
|
169
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
144
170
|
[
|
|
145
171
|
providerId,
|
|
146
172
|
CACHE_SCHEMA_VERSION,
|
|
@@ -149,6 +175,7 @@ export function writeModelCache<TApi extends Api>(
|
|
|
149
175
|
staticFingerprint,
|
|
150
176
|
dynamicModelIds === undefined ? null : JSON.stringify(dynamicModelIds),
|
|
151
177
|
dynamicModelProvenance ?? null,
|
|
178
|
+
discoveryUserAgent ?? null,
|
|
152
179
|
JSON.stringify(models),
|
|
153
180
|
],
|
|
154
181
|
);
|
|
@@ -166,11 +193,12 @@ export function insertModelCacheIfAbsent<TApi extends Api>(
|
|
|
166
193
|
dbPath?: string,
|
|
167
194
|
dynamicModelIds?: readonly string[],
|
|
168
195
|
dynamicModelProvenance?: string,
|
|
196
|
+
discoveryUserAgent?: string,
|
|
169
197
|
): boolean {
|
|
170
198
|
try {
|
|
171
199
|
const result = getDb(dbPath).run(
|
|
172
|
-
`INSERT INTO model_cache (provider_id, version, updated_at, authoritative, static_fingerprint, dynamic_model_ids, dynamic_model_provenance, models)
|
|
173
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
200
|
+
`INSERT INTO model_cache (provider_id, version, updated_at, authoritative, static_fingerprint, dynamic_model_ids, dynamic_model_provenance, discovery_user_agent, models)
|
|
201
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
174
202
|
ON CONFLICT(provider_id) DO NOTHING`,
|
|
175
203
|
[
|
|
176
204
|
providerId,
|
|
@@ -180,6 +208,7 @@ export function insertModelCacheIfAbsent<TApi extends Api>(
|
|
|
180
208
|
staticFingerprint,
|
|
181
209
|
dynamicModelIds === undefined ? null : JSON.stringify(dynamicModelIds),
|
|
182
210
|
dynamicModelProvenance ?? null,
|
|
211
|
+
discoveryUserAgent ?? null,
|
|
183
212
|
JSON.stringify(models),
|
|
184
213
|
],
|
|
185
214
|
);
|
|
@@ -195,6 +224,7 @@ export function updateModelCacheIfUnchanged<TApi extends Api>(
|
|
|
195
224
|
expectedDynamicModelIds: readonly string[] | undefined,
|
|
196
225
|
expectedDynamicModelProvenance: string | undefined,
|
|
197
226
|
expectedModels: readonly Model<TApi>[],
|
|
227
|
+
expectedDiscoveryUserAgent: string | undefined,
|
|
198
228
|
updatedAt: number,
|
|
199
229
|
models: Model<TApi>[],
|
|
200
230
|
authoritative: boolean,
|
|
@@ -202,30 +232,35 @@ export function updateModelCacheIfUnchanged<TApi extends Api>(
|
|
|
202
232
|
dbPath?: string,
|
|
203
233
|
dynamicModelIds?: readonly string[],
|
|
204
234
|
dynamicModelProvenance?: string,
|
|
235
|
+
discoveryUserAgent?: string,
|
|
205
236
|
): boolean {
|
|
206
237
|
try {
|
|
207
238
|
const expectedIds = expectedDynamicModelIds === undefined ? null : JSON.stringify(expectedDynamicModelIds);
|
|
208
239
|
const provenance = expectedDynamicModelProvenance ?? null;
|
|
240
|
+
const expectedUserAgent = expectedDiscoveryUserAgent ?? null;
|
|
209
241
|
const nextIds = dynamicModelIds === undefined ? null : JSON.stringify(dynamicModelIds);
|
|
210
242
|
const nextProvenance = dynamicModelProvenance ?? null;
|
|
211
243
|
const expectedModelsJson = JSON.stringify(expectedModels);
|
|
212
244
|
const result = getDb(dbPath).run(
|
|
213
245
|
`UPDATE model_cache
|
|
214
246
|
SET updated_at = ?, authoritative = ?, static_fingerprint = ?, dynamic_model_ids = ?,
|
|
215
|
-
dynamic_model_provenance = ?, models = ?
|
|
247
|
+
dynamic_model_provenance = ?, discovery_user_agent = ?, models = ?
|
|
216
248
|
WHERE provider_id = ? AND updated_at = ?
|
|
217
|
-
AND dynamic_model_ids IS ? AND dynamic_model_provenance IS ?
|
|
249
|
+
AND dynamic_model_ids IS ? AND dynamic_model_provenance IS ?
|
|
250
|
+
AND discovery_user_agent IS ? AND models = ?`,
|
|
218
251
|
[
|
|
219
252
|
updatedAt,
|
|
220
253
|
authoritative ? 1 : 0,
|
|
221
254
|
staticFingerprint,
|
|
222
255
|
nextIds,
|
|
223
256
|
nextProvenance,
|
|
257
|
+
discoveryUserAgent ?? null,
|
|
224
258
|
JSON.stringify(models),
|
|
225
259
|
providerId,
|
|
226
260
|
expectedUpdatedAt,
|
|
227
261
|
expectedIds,
|
|
228
262
|
provenance,
|
|
263
|
+
expectedUserAgent,
|
|
229
264
|
expectedModelsJson,
|
|
230
265
|
],
|
|
231
266
|
);
|
package/src/model-manager.ts
CHANGED
|
@@ -60,6 +60,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
60
60
|
canPublishCache?: () => boolean;
|
|
61
61
|
/** Credential-and-endpoint identity required to reuse dynamic catalog IDs. */
|
|
62
62
|
cacheDynamicModelProvenance?: string;
|
|
63
|
+
/** Discovery User-Agent (e.g., for Antigravity): cache invalidates on change. */
|
|
64
|
+
discoveryUserAgent?: string;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
/**
|
|
@@ -146,7 +148,13 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
146
148
|
const provenance = options.cacheDynamicModelProvenance;
|
|
147
149
|
const now = options.now ?? Date.now;
|
|
148
150
|
const ttlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
149
|
-
const legacyCache = readModelCache<TApi>(
|
|
151
|
+
const legacyCache = readModelCache<TApi>(
|
|
152
|
+
options.providerId,
|
|
153
|
+
ttlMs,
|
|
154
|
+
now,
|
|
155
|
+
options.cacheDbPath,
|
|
156
|
+
options.discoveryUserAgent,
|
|
157
|
+
);
|
|
150
158
|
if (
|
|
151
159
|
strategy !== "online-if-uncached" ||
|
|
152
160
|
typeof options.fetchDynamicModels !== "function" ||
|
|
@@ -185,7 +193,7 @@ async function resolveProviderModelsUncoalesced<TApi extends Api = Api, TModelsD
|
|
|
185
193
|
const staticModels = passModelList<TApi>(
|
|
186
194
|
options.staticModels ?? getBundledModels(options.providerId as GeneratedProvider),
|
|
187
195
|
);
|
|
188
|
-
const cache = readModelCache<TApi>(options.providerId, ttlMs, now, dbPath);
|
|
196
|
+
const cache = readModelCache<TApi>(options.providerId, ttlMs, now, dbPath, options.discoveryUserAgent);
|
|
189
197
|
const dynamicFetcher = options.fetchDynamicModels;
|
|
190
198
|
const hasDynamicFetcher = typeof dynamicFetcher === "function";
|
|
191
199
|
const cacheDynamicModelIdsCurrent =
|
|
@@ -262,6 +270,7 @@ async function resolveProviderModelsUncoalesced<TApi extends Api = Api, TModelsD
|
|
|
262
270
|
dbPath,
|
|
263
271
|
cache.dynamicModelIds,
|
|
264
272
|
cache.dynamicModelProvenance,
|
|
273
|
+
options.discoveryUserAgent,
|
|
265
274
|
);
|
|
266
275
|
}
|
|
267
276
|
return {
|
|
@@ -315,6 +324,7 @@ async function resolveProviderModelsUncoalesced<TApi extends Api = Api, TModelsD
|
|
|
315
324
|
dbPath,
|
|
316
325
|
dynamicModels.map(model => model.id),
|
|
317
326
|
options.cacheDynamicModelProvenance,
|
|
327
|
+
options.discoveryUserAgent,
|
|
318
328
|
);
|
|
319
329
|
}
|
|
320
330
|
} else {
|
|
@@ -322,10 +332,9 @@ async function resolveProviderModelsUncoalesced<TApi extends Api = Api, TModelsD
|
|
|
322
332
|
// stale state remains visible while retry backoff still applies. Re-read
|
|
323
333
|
// the current row and trust only that row's provenance and dynamic IDs,
|
|
324
334
|
// not the initial cacheProvenanceMismatch snapshot. A concurrent writer
|
|
325
|
-
// may have replaced the provider row
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
const latestCache = readModelCache<TApi>(options.providerId, ttlMs, now, dbPath);
|
|
335
|
+
// may have replaced the provider row. This reread filters mismatched
|
|
336
|
+
// discovery User-Agents, while the checks below enforce dynamic provenance.
|
|
337
|
+
const latestCache = readModelCache<TApi>(options.providerId, ttlMs, now, dbPath, options.discoveryUserAgent);
|
|
329
338
|
const latestCacheMatchesCurrentContext =
|
|
330
339
|
options.cacheDynamicModelProvenance === undefined
|
|
331
340
|
? latestCache !== null && latestCache.dynamicModelIds === undefined
|
|
@@ -353,6 +362,7 @@ async function resolveProviderModelsUncoalesced<TApi extends Api = Api, TModelsD
|
|
|
353
362
|
latestCache.dynamicModelIds,
|
|
354
363
|
latestCache.dynamicModelProvenance,
|
|
355
364
|
latestCache.models,
|
|
365
|
+
latestCache.discoveryUserAgent,
|
|
356
366
|
now(),
|
|
357
367
|
snapshotModels,
|
|
358
368
|
false,
|
|
@@ -360,6 +370,7 @@ async function resolveProviderModelsUncoalesced<TApi extends Api = Api, TModelsD
|
|
|
360
370
|
dbPath,
|
|
361
371
|
options.cacheDynamicModelProvenance === undefined ? undefined : [],
|
|
362
372
|
options.cacheDynamicModelProvenance,
|
|
373
|
+
options.discoveryUserAgent,
|
|
363
374
|
);
|
|
364
375
|
if (!updated) cacheModels = [];
|
|
365
376
|
} else if (latestCache == null) {
|
|
@@ -372,6 +383,7 @@ async function resolveProviderModelsUncoalesced<TApi extends Api = Api, TModelsD
|
|
|
372
383
|
dbPath,
|
|
373
384
|
options.cacheDynamicModelProvenance === undefined ? undefined : [],
|
|
374
385
|
options.cacheDynamicModelProvenance,
|
|
386
|
+
options.discoveryUserAgent,
|
|
375
387
|
);
|
|
376
388
|
if (!inserted) cacheModels = [];
|
|
377
389
|
}
|
package/src/model-pricing.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { Api, Model, ModelCost } from "./types";
|
|
2
2
|
export declare function getOpenAIModelCost<TApi extends Api>(model: Model<TApi>, inputTokens: number): ModelCost | undefined;
|
|
3
|
+
export declare function getAnthropicModelCost<TApi extends Api>(model: Model<TApi>, inputTokens: number): ModelCost | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* $/million rate for 1-hour cache writes, or undefined when the model has a single
|
|
6
|
+
* cache-write rate. Cost calculation applies it to `usage.cttl.ephemeral1h` and bills
|
|
7
|
+
* the remaining cache-write tokens at `ModelCost.cacheWrite`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getAnthropicCacheWriteOneHourRate<TApi extends Api>(model: Model<TApi>, inputTokens: number): number | undefined;
|
|
10
|
+
export declare function applyAnthropicModelPricing<TApi extends Api>(model: Model<TApi>): void;
|
|
3
11
|
export declare function applyOpenAIModelPricing<TApi extends Api>(model: Model<TApi>): void;
|
package/src/model-pricing.ts
CHANGED
|
@@ -6,6 +6,17 @@ interface TieredPricing {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
const LONG_CONTEXT_THRESHOLD = 272_000;
|
|
9
|
+
// Anthropic Haiku 5.5's direct Messages API charges five times the base rates
|
|
10
|
+
// when prompt input exceeds 100K tokens, including cached input.
|
|
11
|
+
// https://platform.claude.com/docs/en/models/haiku-5-5/overview#pricing
|
|
12
|
+
const ANTHROPIC_HAIKU_5_5_LONG_CONTEXT_PRICING: LongContextPricing = {
|
|
13
|
+
threshold: 100_000,
|
|
14
|
+
cost: { input: 0.5, output: 2.5, cacheRead: 0.05, cacheWrite: 0.625 },
|
|
15
|
+
};
|
|
16
|
+
// `ModelCost.cacheWrite` is the 5-minute cache-write rate. Haiku 5.5 charges 1-hour
|
|
17
|
+
// writes at twice the base input rate in each prompt-length tier ($0.20 up to 100K
|
|
18
|
+
// tokens, $1 above), and the provider defaults to `ttl: "1h"`.
|
|
19
|
+
const ANTHROPIC_HAIKU_5_5_CACHE_WRITE_1H = { standard: 0.2, longContext: 1 };
|
|
9
20
|
|
|
10
21
|
const GPT_5_6_SOL_PRICING: TieredPricing = {
|
|
11
22
|
cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
@@ -95,6 +106,82 @@ export function getOpenAIModelCost<TApi extends Api>(model: Model<TApi>, inputTo
|
|
|
95
106
|
return inputTokens > pricing.longContextPricing.threshold ? pricing.longContextPricing.cost : pricing.cost;
|
|
96
107
|
}
|
|
97
108
|
|
|
109
|
+
const BEDROCK_HAIKU_5_5_ID = /^(?:(?:au|eu|global|jp|us).)?anthropic.claude-haiku-5-5$/;
|
|
110
|
+
// Bedrock bills Haiku 5.5 prompts above 100K tokens at higher "Long Context" rates.
|
|
111
|
+
// AWS documents the threshold but the per-token rates only on its pricing page,
|
|
112
|
+
// which is not machine-readable; mirror Anthropic's published 5x multiplier on each
|
|
113
|
+
// inference profile's own base rates.
|
|
114
|
+
// https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-haiku-5-5.html#model-card-anthropic-claude-haiku-5-5-pricing
|
|
115
|
+
const BEDROCK_HAIKU_5_5_LONG_CONTEXT_MULTIPLIER = 5;
|
|
116
|
+
|
|
117
|
+
function getAnthropicLongContextPricing<TApi extends Api>(model: Model<TApi>): LongContextPricing | undefined {
|
|
118
|
+
if (model.provider === "anthropic" && model.api === "anthropic-messages" && model.id === "claude-haiku-5-5") {
|
|
119
|
+
return {
|
|
120
|
+
threshold: ANTHROPIC_HAIKU_5_5_LONG_CONTEXT_PRICING.threshold,
|
|
121
|
+
cost: { ...ANTHROPIC_HAIKU_5_5_LONG_CONTEXT_PRICING.cost },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (
|
|
125
|
+
model.provider === "amazon-bedrock" &&
|
|
126
|
+
model.api === "bedrock-converse-stream" &&
|
|
127
|
+
BEDROCK_HAIKU_5_5_ID.test(model.id)
|
|
128
|
+
) {
|
|
129
|
+
const m = BEDROCK_HAIKU_5_5_LONG_CONTEXT_MULTIPLIER;
|
|
130
|
+
const scale = (rate: number): number => Math.round(rate * m * 1e9) / 1e9;
|
|
131
|
+
return {
|
|
132
|
+
threshold: ANTHROPIC_HAIKU_5_5_LONG_CONTEXT_PRICING.threshold,
|
|
133
|
+
cost: {
|
|
134
|
+
input: scale(model.cost.input),
|
|
135
|
+
output: scale(model.cost.output),
|
|
136
|
+
cacheRead: scale(model.cost.cacheRead),
|
|
137
|
+
cacheWrite: scale(model.cost.cacheWrite),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function getAnthropicModelCost<TApi extends Api>(
|
|
145
|
+
model: Model<TApi>,
|
|
146
|
+
inputTokens: number,
|
|
147
|
+
): ModelCost | undefined {
|
|
148
|
+
const pricing = getAnthropicLongContextPricing(model);
|
|
149
|
+
if (!pricing || inputTokens <= pricing.threshold) return undefined;
|
|
150
|
+
return pricing.cost;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* $/million rate for 1-hour cache writes, or undefined when the model has a single
|
|
155
|
+
* cache-write rate. Cost calculation applies it to `usage.cttl.ephemeral1h` and bills
|
|
156
|
+
* the remaining cache-write tokens at `ModelCost.cacheWrite`.
|
|
157
|
+
*/
|
|
158
|
+
export function getAnthropicCacheWriteOneHourRate<TApi extends Api>(
|
|
159
|
+
model: Model<TApi>,
|
|
160
|
+
inputTokens: number,
|
|
161
|
+
): number | undefined {
|
|
162
|
+
const aboveThreshold = inputTokens > ANTHROPIC_HAIKU_5_5_LONG_CONTEXT_PRICING.threshold;
|
|
163
|
+
if (model.provider === "anthropic" && model.api === "anthropic-messages" && model.id === "claude-haiku-5-5") {
|
|
164
|
+
return aboveThreshold
|
|
165
|
+
? ANTHROPIC_HAIKU_5_5_CACHE_WRITE_1H.longContext
|
|
166
|
+
: ANTHROPIC_HAIKU_5_5_CACHE_WRITE_1H.standard;
|
|
167
|
+
}
|
|
168
|
+
if (
|
|
169
|
+
model.provider === "amazon-bedrock" &&
|
|
170
|
+
model.api === "bedrock-converse-stream" &&
|
|
171
|
+
BEDROCK_HAIKU_5_5_ID.test(model.id)
|
|
172
|
+
) {
|
|
173
|
+
// Same structure as direct: 1-hour writes cost twice the tier's input rate.
|
|
174
|
+
const multiplier = aboveThreshold ? BEDROCK_HAIKU_5_5_LONG_CONTEXT_MULTIPLIER : 1;
|
|
175
|
+
return Math.round(2 * model.cost.input * multiplier * 1e9) / 1e9;
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function applyAnthropicModelPricing<TApi extends Api>(model: Model<TApi>): void {
|
|
181
|
+
const pricing = getAnthropicLongContextPricing(model);
|
|
182
|
+
if (pricing) model.longContextPricing = pricing;
|
|
183
|
+
}
|
|
184
|
+
|
|
98
185
|
export function applyOpenAIModelPricing<TApi extends Api>(model: Model<TApi>): void {
|
|
99
186
|
if (model.provider !== "openai" && model.provider !== "openai-codex") {
|
|
100
187
|
return;
|
package/src/model-thinking.d.ts
CHANGED
|
@@ -87,12 +87,23 @@ export declare function mapEffortToAnthropicAdaptiveEffort<TApi extends Api>(mod
|
|
|
87
87
|
* - Thinking content is omitted by default (needs display: "summarized")
|
|
88
88
|
*/
|
|
89
89
|
export declare function hasOpus47ApiRestrictions(modelId: string): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Haiku 5+ enables adaptive thinking when the `thinking` field is omitted.
|
|
92
|
+
* An explicit user-facing Off choice must therefore send `type: "disabled"`.
|
|
93
|
+
*/
|
|
94
|
+
export declare function requiresAnthropicThinkingDisableWhenOff(modelId: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Haiku 5+ and Opus 4.7+ reject sampling parameters on the Anthropic Messages API.
|
|
97
|
+
* https://platform.claude.com/docs/en/models/haiku-5-5/migration-guide#remove-sampling-parameters
|
|
98
|
+
*/
|
|
99
|
+
export declare function hasAnthropicModelSamplingRestrictions(modelId: string): boolean;
|
|
90
100
|
/**
|
|
91
101
|
* Adaptive thinking `display` is supported starting with Anthropic Opus 4.7.
|
|
92
102
|
* Older adaptive-thinking models (Opus 4.6, Sonnet 4.6+) reject the field.
|
|
93
103
|
* Fable (5+) postdates Opus 4.7, accepts `display`, and defaults it to
|
|
94
104
|
* "omitted" — thinking tokens are billed but no content streams back — so it
|
|
95
105
|
* must opt in like Opus 4.7+ (issue #2791).
|
|
106
|
+
* Haiku 5+ also accepts `display` and defaults it to "omitted".
|
|
96
107
|
*
|
|
97
108
|
* Shares `hasOpus47ApiRestrictions` version parsing on purpose: the two
|
|
98
109
|
* predicates describe the same API generation, and a private `claude-opus-(\d+)-(\d+)`
|
package/src/model-thinking.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
isCodexGpt56Tier,
|
|
6
6
|
isCodexProductTransport,
|
|
7
7
|
} from "./context-cap-policy";
|
|
8
|
-
import { applyOpenAIModelPricing } from "./model-pricing";
|
|
8
|
+
import { applyAnthropicModelPricing, applyOpenAIModelPricing } from "./model-pricing";
|
|
9
9
|
import {
|
|
10
10
|
isAuditedOpenAIReasoningTransport,
|
|
11
11
|
parseDirectXaiReasoningEffortGeneration,
|
|
@@ -56,6 +56,17 @@ const DEFAULT_REASONING_EFFORTS_WITH_XHIGH_AND_MAX: readonly Effort[] = [
|
|
|
56
56
|
Effort.XHigh,
|
|
57
57
|
Effort.Max,
|
|
58
58
|
];
|
|
59
|
+
// Haiku 5.5 supports low, medium, high, xhigh, and max on both Anthropic
|
|
60
|
+
// Messages and Bedrock Converse; neither contract lists minimal.
|
|
61
|
+
// https://platform.claude.com/docs/en/build-with-claude/effort#effort-levels
|
|
62
|
+
// https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-haiku-5-5.html
|
|
63
|
+
const ANTHROPIC_HAIKU_5_5_EFFORTS: readonly Effort[] = [
|
|
64
|
+
Effort.Low,
|
|
65
|
+
Effort.Medium,
|
|
66
|
+
Effort.High,
|
|
67
|
+
Effort.XHigh,
|
|
68
|
+
Effort.Max,
|
|
69
|
+
];
|
|
59
70
|
const GEMINI_3_PRO_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High];
|
|
60
71
|
const GEMINI_3_FLASH_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High];
|
|
61
72
|
// Gemini 3.7 Flash dropped `minimal`; the official API returns an error for it.
|
|
@@ -95,8 +106,8 @@ type SemVer = {
|
|
|
95
106
|
patch: number;
|
|
96
107
|
};
|
|
97
108
|
|
|
98
|
-
type GeminiKind = "pro" | "flash";
|
|
99
|
-
type AnthropicKind = "opus" | "sonnet" | "fable";
|
|
109
|
+
type GeminiKind = "pro" | "flash" | "argon";
|
|
110
|
+
type AnthropicKind = "opus" | "sonnet" | "fable" | "haiku";
|
|
100
111
|
type OpenAIVariant =
|
|
101
112
|
| "astra"
|
|
102
113
|
| "base"
|
|
@@ -328,6 +339,23 @@ export function modelSupportsReasoningControl<TApi extends Api>(
|
|
|
328
339
|
export function applyGeneratedModelPolicies(models: ApiModel<Api>[]): void {
|
|
329
340
|
for (let index = 0; index < models.length; index++) {
|
|
330
341
|
const source = models[index]!;
|
|
342
|
+
const inputCost = nonNegativeFinitePrice(source.cost.input);
|
|
343
|
+
const outputCost = nonNegativeFinitePrice(source.cost.output);
|
|
344
|
+
const cacheReadCost = nonNegativeFinitePrice(source.cost.cacheRead);
|
|
345
|
+
const cacheWriteCost = nonNegativeFinitePrice(source.cost.cacheWrite);
|
|
346
|
+
if (
|
|
347
|
+
inputCost !== source.cost.input ||
|
|
348
|
+
outputCost !== source.cost.output ||
|
|
349
|
+
cacheReadCost !== source.cost.cacheRead ||
|
|
350
|
+
cacheWriteCost !== source.cost.cacheWrite
|
|
351
|
+
) {
|
|
352
|
+
source.cost = {
|
|
353
|
+
input: inputCost,
|
|
354
|
+
output: outputCost,
|
|
355
|
+
cacheRead: cacheReadCost,
|
|
356
|
+
cacheWrite: cacheWriteCost,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
331
359
|
if (source.provider === "xai" && (source.id === "grok-4.5" || source.id === "grok-4.6")) {
|
|
332
360
|
source.reasoning = true;
|
|
333
361
|
}
|
|
@@ -359,6 +387,10 @@ export function applyGeneratedModelPolicies(models: ApiModel<Api>[]): void {
|
|
|
359
387
|
}
|
|
360
388
|
}
|
|
361
389
|
|
|
390
|
+
function nonNegativeFinitePrice(value: number): number {
|
|
391
|
+
return Number.isFinite(value) && value >= 0 ? value : 0;
|
|
392
|
+
}
|
|
393
|
+
|
|
362
394
|
/**
|
|
363
395
|
* Link OpenAI model variants to their context promotion targets.
|
|
364
396
|
*
|
|
@@ -517,12 +549,34 @@ export function hasOpus47ApiRestrictions(modelId: string): boolean {
|
|
|
517
549
|
return semverGte(parsed.version, "4.7") && parsed.kind === "opus";
|
|
518
550
|
}
|
|
519
551
|
|
|
552
|
+
function isAnthropicHaikuAdaptiveModel(modelId: string): boolean {
|
|
553
|
+
const parsed = parseAnthropicModel(getCanonicalModelId(modelId));
|
|
554
|
+
return parsed?.kind === "haiku" && semverGte(parsed.version, "5.0");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Haiku 5+ enables adaptive thinking when the `thinking` field is omitted.
|
|
559
|
+
* An explicit user-facing Off choice must therefore send `type: "disabled"`.
|
|
560
|
+
*/
|
|
561
|
+
export function requiresAnthropicThinkingDisableWhenOff(modelId: string): boolean {
|
|
562
|
+
return isAnthropicHaikuAdaptiveModel(modelId);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Haiku 5+ and Opus 4.7+ reject sampling parameters on the Anthropic Messages API.
|
|
567
|
+
* https://platform.claude.com/docs/en/models/haiku-5-5/migration-guide#remove-sampling-parameters
|
|
568
|
+
*/
|
|
569
|
+
export function hasAnthropicModelSamplingRestrictions(modelId: string): boolean {
|
|
570
|
+
return hasOpus47ApiRestrictions(modelId) || isAnthropicHaikuAdaptiveModel(modelId);
|
|
571
|
+
}
|
|
572
|
+
|
|
520
573
|
/**
|
|
521
574
|
* Adaptive thinking `display` is supported starting with Anthropic Opus 4.7.
|
|
522
575
|
* Older adaptive-thinking models (Opus 4.6, Sonnet 4.6+) reject the field.
|
|
523
576
|
* Fable (5+) postdates Opus 4.7, accepts `display`, and defaults it to
|
|
524
577
|
* "omitted" — thinking tokens are billed but no content streams back — so it
|
|
525
578
|
* must opt in like Opus 4.7+ (issue #2791).
|
|
579
|
+
* Haiku 5+ also accepts `display` and defaults it to "omitted".
|
|
526
580
|
*
|
|
527
581
|
* Shares `hasOpus47ApiRestrictions` version parsing on purpose: the two
|
|
528
582
|
* predicates describe the same API generation, and a private `claude-opus-(\d+)-(\d+)`
|
|
@@ -533,7 +587,7 @@ export function hasOpus47ApiRestrictions(modelId: string): boolean {
|
|
|
533
587
|
*/
|
|
534
588
|
export function supportsAnthropicAdaptiveThinkingDisplay(modelId: string): boolean {
|
|
535
589
|
if (/claude-fable-\d/.test(modelId)) return true;
|
|
536
|
-
return hasOpus47ApiRestrictions(modelId);
|
|
590
|
+
return hasOpus47ApiRestrictions(modelId) || isAnthropicHaikuAdaptiveModel(modelId);
|
|
537
591
|
}
|
|
538
592
|
|
|
539
593
|
function anthropicModelHasRealXHighEffort<TApi extends Api>(model: ApiModel<TApi>): boolean {
|
|
@@ -555,6 +609,7 @@ function anthropicModelHasRealXHighEffort<TApi extends Api>(model: ApiModel<TApi
|
|
|
555
609
|
|
|
556
610
|
function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
|
|
557
611
|
applyOpenAIModelPricing(model);
|
|
612
|
+
applyAnthropicModelPricing(model);
|
|
558
613
|
const copilotLimits = model.provider === "github-copilot" ? COPILOT_GENERATED_LIMITS[model.id] : undefined;
|
|
559
614
|
if (copilotLimits) {
|
|
560
615
|
model.contextWindow = copilotLimits.contextWindow;
|
|
@@ -729,6 +784,17 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
|
|
|
729
784
|
) {
|
|
730
785
|
model.contextWindow = 1_000_000;
|
|
731
786
|
}
|
|
787
|
+
// Google Gemini 4 Argon supports up to 65,536 output tokens across provider
|
|
788
|
+
// aliases. Strip catalog provider prefixes and OpenRouter's batch suffix.
|
|
789
|
+
// https://ai.google.dev/gemini-api/docs/models/gemini-4-argon
|
|
790
|
+
const canonicalId = model.id
|
|
791
|
+
.toLowerCase()
|
|
792
|
+
.split("/")
|
|
793
|
+
.at(-1)
|
|
794
|
+
?.replace(/:batch$/, "");
|
|
795
|
+
if (canonicalId === "gemini-4-argon") {
|
|
796
|
+
model.maxTokens = Math.min(model.maxTokens, 65_536);
|
|
797
|
+
}
|
|
732
798
|
}
|
|
733
799
|
|
|
734
800
|
function scrubGeneratedModelName(name: string): string {
|
|
@@ -847,6 +913,19 @@ function inferDefaultEffort<TApi extends Api>(model: ApiModel<TApi>, parsedModel
|
|
|
847
913
|
if (model.provider === "omlx") {
|
|
848
914
|
return Effort.Medium;
|
|
849
915
|
}
|
|
916
|
+
if (
|
|
917
|
+
parsedModel.family === "anthropic" &&
|
|
918
|
+
parsedModel.kind === "haiku" &&
|
|
919
|
+
((model.provider === "anthropic" &&
|
|
920
|
+
model.api === "anthropic-messages" &&
|
|
921
|
+
semverGte(parsedModel.version, "5.0")) ||
|
|
922
|
+
(model.provider === "amazon-bedrock" &&
|
|
923
|
+
model.api === "bedrock-converse-stream" &&
|
|
924
|
+
semverGte(parsedModel.version, "5.5")))
|
|
925
|
+
) {
|
|
926
|
+
// Haiku 5.5 defaults to medium adaptive effort instead of the global high.
|
|
927
|
+
return Effort.Medium;
|
|
928
|
+
}
|
|
850
929
|
return undefined;
|
|
851
930
|
}
|
|
852
931
|
|
|
@@ -979,6 +1058,8 @@ function inferGeminiSupportedEfforts(model: GeminiModel): readonly Effort[] {
|
|
|
979
1058
|
if (model.kind === "pro") {
|
|
980
1059
|
return GEMINI_3_PRO_EFFORTS;
|
|
981
1060
|
}
|
|
1061
|
+
// Gemini 3.7+ Flash and Gemini 4 Argon accept low/medium/high only.
|
|
1062
|
+
// https://ai.google.dev/gemini-api/docs/models/gemini-4-argon
|
|
982
1063
|
if (semverGte(model.version, "3.7")) {
|
|
983
1064
|
return GEMINI_3_7_FLASH_EFFORTS;
|
|
984
1065
|
}
|
|
@@ -993,6 +1074,9 @@ function inferAnthropicSupportedEfforts<TApi extends Api>(
|
|
|
993
1074
|
(model.api === "anthropic-messages" || model.api === "bedrock-converse-stream") &&
|
|
994
1075
|
semverGte(parsedModel.version, "4.6")
|
|
995
1076
|
) {
|
|
1077
|
+
if (parsedModel.kind === "haiku" && semverGte(parsedModel.version, "5.5")) {
|
|
1078
|
+
return ANTHROPIC_HAIKU_5_5_EFFORTS;
|
|
1079
|
+
}
|
|
996
1080
|
if (parsedModel.kind === "fable") {
|
|
997
1081
|
// Fable exposes Anthropic's Messages-only xhigh preset; Bedrock
|
|
998
1082
|
// Converse lacks it (same split as Opus 4.7+ below).
|
|
@@ -1054,11 +1138,7 @@ function inferThinkingControlMode<TApi extends Api>(
|
|
|
1054
1138
|
case "google-generative-ai":
|
|
1055
1139
|
case "google-gemini-cli":
|
|
1056
1140
|
case "google-vertex":
|
|
1057
|
-
return parsedModel.family === "gemini" &&
|
|
1058
|
-
semverGte(parsedModel.version, "3.0") &&
|
|
1059
|
-
parsedModel.version.major === 3
|
|
1060
|
-
? "google-level"
|
|
1061
|
-
: "budget";
|
|
1141
|
+
return parsedModel.family === "gemini" && semverGte(parsedModel.version, "3.0") ? "google-level" : "budget";
|
|
1062
1142
|
|
|
1063
1143
|
case "anthropic-messages":
|
|
1064
1144
|
if (parsedModel.family === "anthropic") {
|
|
@@ -1075,7 +1155,7 @@ function inferThinkingControlMode<TApi extends Api>(
|
|
|
1075
1155
|
if (parsedModel.family === "anthropic") {
|
|
1076
1156
|
if (
|
|
1077
1157
|
semverGte(parsedModel.version, "4.6") &&
|
|
1078
|
-
(parsedModel.kind === "opus" || parsedModel.kind === "fable")
|
|
1158
|
+
(parsedModel.kind === "opus" || parsedModel.kind === "fable" || parsedModel.kind === "haiku")
|
|
1079
1159
|
) {
|
|
1080
1160
|
return "anthropic-adaptive";
|
|
1081
1161
|
}
|
|
@@ -1104,7 +1184,7 @@ function parseGeminiModel(modelId: string): GeminiModel | null {
|
|
|
1104
1184
|
if (modelId.endsWith(GEMINI_SUFFIX)) {
|
|
1105
1185
|
modelId = modelId.slice(0, -GEMINI_SUFFIX.length);
|
|
1106
1186
|
}
|
|
1107
|
-
const match = /gemini-(\d+(?:\.\d+){0,2})-(pro|flash)\b/.exec(modelId);
|
|
1187
|
+
const match = /gemini-(\d+(?:\.\d+){0,2})-(pro|flash|argon)\b/.exec(modelId);
|
|
1108
1188
|
if (!match) {
|
|
1109
1189
|
return null;
|
|
1110
1190
|
}
|
|
@@ -1116,7 +1196,7 @@ function parseGeminiModel(modelId: string): GeminiModel | null {
|
|
|
1116
1196
|
}
|
|
1117
1197
|
|
|
1118
1198
|
function parseAnthropicModel(modelId: string): AnthropicModel | null {
|
|
1119
|
-
const match = /claude-(opus|sonnet|fable)-(\d{1,2}(?:[.-]\d{1,2}){0,2})\b/.exec(modelId);
|
|
1199
|
+
const match = /claude-(opus|sonnet|fable|haiku)-(\d{1,2}(?:[.-]\d{1,2}){0,2})\b/.exec(modelId);
|
|
1120
1200
|
if (!match) {
|
|
1121
1201
|
return null;
|
|
1122
1202
|
}
|
|
@@ -1124,6 +1204,11 @@ function parseAnthropicModel(modelId: string): AnthropicModel | null {
|
|
|
1124
1204
|
if (!version) {
|
|
1125
1205
|
return null;
|
|
1126
1206
|
}
|
|
1207
|
+
// Haiku is recognized from generation 5 only. Haiku 4.5 predates the
|
|
1208
|
+
// adaptive-thinking policy and keeps the generic (unparsed) budget ladder.
|
|
1209
|
+
if (match[1] === "haiku" && !semverGte(version, "5.0")) {
|
|
1210
|
+
return null;
|
|
1211
|
+
}
|
|
1127
1212
|
return { family: "anthropic", kind: match[1] as AnthropicKind, version };
|
|
1128
1213
|
}
|
|
1129
1214
|
|