@basemachina/agentic-browser-cli 0.3.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.
@@ -0,0 +1,607 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/harness/ai-model.ts
4
+ import { anthropic } from "@ai-sdk/anthropic";
5
+ import { APICallError, Output, generateText } from "ai";
6
+
7
+ // src/harness/ai-metrics.ts
8
+ import { AsyncLocalStorage } from "async_hooks";
9
+ var PRICING_TABLE = {
10
+ // Anthropic — cacheWrite = input × 1.25, cacheRead = input × 0.10
11
+ "claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
12
+ "claude-sonnet-4-5": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
13
+ "claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
14
+ "claude-haiku-4-5": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
15
+ "claude-haiku-3-5": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
16
+ "claude-opus-4-6": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
17
+ "claude-opus-4-5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
18
+ "claude-opus-4-1": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
19
+ "claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
20
+ // OpenAI (Azure OpenAI も同名モデルのため部分一致で対応) — キャッシュ書き込み追加課金なし
21
+ "gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25, cacheWrite: 0 },
22
+ "gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075, cacheWrite: 0 },
23
+ "gpt-4.1": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
24
+ "gpt-4.1-mini": { input: 0.4, output: 1.6, cacheRead: 0.1, cacheWrite: 0 },
25
+ "gpt-4.1-nano": { input: 0.1, output: 0.4, cacheRead: 0.025, cacheWrite: 0 },
26
+ "o3": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
27
+ "o4-mini": { input: 1.1, output: 4.4, cacheRead: 0.55, cacheWrite: 0 },
28
+ // Google Gemini — キャッシュ書き込み追加課金なし
29
+ "gemini-2.5-pro": { input: 1.25, output: 10, cacheRead: 0.315, cacheWrite: 0 },
30
+ "gemini-2.5-flash": { input: 0.15, output: 0.6, cacheRead: 0.0375, cacheWrite: 0 },
31
+ "gemini-2.0-flash": { input: 0.1, output: 0.4, cacheRead: 0.025, cacheWrite: 0 }
32
+ };
33
+ var DEFAULT_PRICING = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
34
+ function getPricing(modelId) {
35
+ if (PRICING_TABLE[modelId]) return PRICING_TABLE[modelId];
36
+ for (const [key, pricing] of Object.entries(PRICING_TABLE)) {
37
+ if (modelId.includes(key)) return pricing;
38
+ }
39
+ return DEFAULT_PRICING;
40
+ }
41
+ function calculateCost(metric) {
42
+ const pricing = getPricing(metric.modelId);
43
+ return metric.inputTokens * pricing.input / 1e6 + metric.cachedInputTokens * pricing.cacheRead / 1e6 + metric.cacheCreationTokens * pricing.cacheWrite / 1e6 + metric.outputTokens * pricing.output / 1e6;
44
+ }
45
+ function computeTotalRealInput(tokens) {
46
+ const input = tokens.totalInputTokens ?? tokens.inputTokens ?? 0;
47
+ const cached = tokens.totalCachedInputTokens ?? tokens.cachedInputTokens ?? 0;
48
+ const creation = tokens.totalCacheCreationTokens ?? tokens.cacheCreationTokens ?? 0;
49
+ return input + cached + creation;
50
+ }
51
+ function computeCacheRate(tokens) {
52
+ const total = computeTotalRealInput(tokens);
53
+ const cached = tokens.totalCachedInputTokens ?? tokens.cachedInputTokens ?? 0;
54
+ return total > 0 ? cached / total : 0;
55
+ }
56
+ var AIMetricsCollector = class {
57
+ constructor() {
58
+ this.metrics = [];
59
+ }
60
+ record(metric) {
61
+ this.metrics.push(metric);
62
+ }
63
+ getSummary() {
64
+ const totalCalls = this.metrics.length;
65
+ let totalInputTokens = 0;
66
+ let totalOutputTokens = 0;
67
+ let totalCachedInputTokens = 0;
68
+ let totalCacheCreationTokens = 0;
69
+ let totalDurationMs = 0;
70
+ let estimatedCostUsd = 0;
71
+ const byPurpose = {};
72
+ const byModel = {};
73
+ for (const m of this.metrics) {
74
+ totalInputTokens += m.inputTokens;
75
+ totalOutputTokens += m.outputTokens;
76
+ totalCachedInputTokens += m.cachedInputTokens;
77
+ totalCacheCreationTokens += m.cacheCreationTokens;
78
+ totalDurationMs += m.durationMs;
79
+ const cost = calculateCost(m);
80
+ estimatedCostUsd += cost;
81
+ const bp = byPurpose[m.purpose] ??= {
82
+ calls: 0,
83
+ inputTokens: 0,
84
+ outputTokens: 0,
85
+ cachedInputTokens: 0,
86
+ cacheCreationTokens: 0,
87
+ durationMs: 0
88
+ };
89
+ bp.calls++;
90
+ bp.inputTokens += m.inputTokens;
91
+ bp.outputTokens += m.outputTokens;
92
+ bp.cachedInputTokens += m.cachedInputTokens;
93
+ bp.cacheCreationTokens += m.cacheCreationTokens;
94
+ bp.durationMs += m.durationMs;
95
+ const bm = byModel[m.modelId] ??= {
96
+ calls: 0,
97
+ inputTokens: 0,
98
+ outputTokens: 0,
99
+ cachedInputTokens: 0,
100
+ cacheCreationTokens: 0,
101
+ durationMs: 0,
102
+ estimatedCostUsd: 0
103
+ };
104
+ bm.calls++;
105
+ bm.inputTokens += m.inputTokens;
106
+ bm.outputTokens += m.outputTokens;
107
+ bm.cachedInputTokens += m.cachedInputTokens;
108
+ bm.cacheCreationTokens += m.cacheCreationTokens;
109
+ bm.durationMs += m.durationMs;
110
+ bm.estimatedCostUsd += cost;
111
+ }
112
+ const cacheHitRate = computeCacheRate({
113
+ inputTokens: totalInputTokens,
114
+ cachedInputTokens: totalCachedInputTokens,
115
+ cacheCreationTokens: totalCacheCreationTokens
116
+ });
117
+ let latencyPercentiles;
118
+ if (this.metrics.length > 0) {
119
+ const sorted = this.metrics.map((m) => m.durationMs).sort((a, b) => a - b);
120
+ const percentile = (p) => {
121
+ const idx = Math.ceil(p / 100 * sorted.length) - 1;
122
+ return sorted[Math.max(0, Math.min(idx, sorted.length - 1))];
123
+ };
124
+ latencyPercentiles = {
125
+ p50: Math.round(percentile(50)),
126
+ p95: Math.round(percentile(95)),
127
+ p99: Math.round(percentile(99))
128
+ };
129
+ }
130
+ return {
131
+ totalCalls,
132
+ totalInputTokens,
133
+ totalOutputTokens,
134
+ totalCachedInputTokens,
135
+ totalCacheCreationTokens,
136
+ cacheHitRate,
137
+ estimatedCostUsd: Math.round(estimatedCostUsd * 1e6) / 1e6,
138
+ totalDurationMs,
139
+ latencyPercentiles,
140
+ byPurpose,
141
+ byModel
142
+ };
143
+ }
144
+ /** メトリクスをリセットする(eval ケース間で使用) */
145
+ reset() {
146
+ this.metrics = [];
147
+ }
148
+ /** 蓄積済みの個別メトリクスを返す(デバッグ用) */
149
+ getMetrics() {
150
+ return this.metrics;
151
+ }
152
+ /** 蓄積済みメトリクスの件数 */
153
+ get count() {
154
+ return this.metrics.length;
155
+ }
156
+ };
157
+ var globalMetrics = new AIMetricsCollector();
158
+ var metricsStorage = new AsyncLocalStorage();
159
+ var activeCollector = globalMetrics;
160
+ function getActiveMetricsCollector() {
161
+ return metricsStorage.getStore() ?? activeCollector;
162
+ }
163
+
164
+ // src/harness/ai-model.ts
165
+ var DEFAULT_MODEL_ID = "claude-sonnet-4-6";
166
+ var DEFAULT_PROVIDER = "anthropic";
167
+ var DEFAULT_PURPOSE_MODELS = {
168
+ selector: "claude-haiku-4-5-20251001",
169
+ extraction: "claude-haiku-4-5-20251001",
170
+ "exploration-light": "claude-haiku-4-5-20251001"
171
+ };
172
+ var currentModelId = DEFAULT_MODEL_ID;
173
+ var currentProvider = DEFAULT_PROVIDER;
174
+ var currentOverrides = {};
175
+ var cachedModel = null;
176
+ var overrideModelCache = /* @__PURE__ */ new Map();
177
+ var modelFactory = null;
178
+ async function initModel(config) {
179
+ const modelId = config?.modelId ?? DEFAULT_MODEL_ID;
180
+ const provider = config?.provider ?? DEFAULT_PROVIDER;
181
+ currentModelId = modelId;
182
+ currentProvider = provider;
183
+ currentOverrides = config?.modelOverrides ?? {};
184
+ overrideModelCache.clear();
185
+ switch (provider) {
186
+ case "openai": {
187
+ const { createOpenAI } = await import("@ai-sdk/openai");
188
+ const apiKey = config?.apiKey ?? process.env.OPENAI_API_KEY;
189
+ const openai = createOpenAI({
190
+ ...apiKey ? { apiKey } : {},
191
+ ...config?.baseURL ? { baseURL: config.baseURL } : {}
192
+ });
193
+ modelFactory = (id) => openai(id);
194
+ cachedModel = openai(modelId);
195
+ break;
196
+ }
197
+ case "google": {
198
+ const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
199
+ const apiKey = config?.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY;
200
+ if (!apiKey) {
201
+ throw new Error(
202
+ "google \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F GOOGLE_GENERATIVE_AI_API_KEY \u74B0\u5883\u5909\u6570\u307E\u305F\u306F apiKey \u304C\u5FC5\u8981\u3067\u3059"
203
+ );
204
+ }
205
+ const google = createGoogleGenerativeAI({
206
+ apiKey,
207
+ ...config?.baseURL ? { baseURL: config.baseURL } : {}
208
+ });
209
+ modelFactory = (id) => google(id);
210
+ cachedModel = google(modelId);
211
+ break;
212
+ }
213
+ case "azure": {
214
+ const { createAzure } = await import("@ai-sdk/azure");
215
+ const apiKey = config?.apiKey ?? process.env.AZURE_API_KEY;
216
+ const resourceName = config?.azureResourceName ?? process.env.AZURE_RESOURCE_NAME;
217
+ const apiVersion = config?.azureApiVersion ?? process.env.AZURE_API_VERSION;
218
+ if (!resourceName && !config?.baseURL) {
219
+ throw new Error(
220
+ "azure \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F AZURE_RESOURCE_NAME \u74B0\u5883\u5909\u6570\uFF08\u307E\u305F\u306F --model-base-url\uFF09\u304C\u5FC5\u8981\u3067\u3059"
221
+ );
222
+ }
223
+ const azure = createAzure({
224
+ ...resourceName ? { resourceName } : {},
225
+ ...apiKey ? { apiKey } : {},
226
+ ...apiVersion ? { apiVersion } : {},
227
+ ...config?.baseURL ? { baseURL: config.baseURL } : {}
228
+ });
229
+ modelFactory = (id) => azure(id);
230
+ cachedModel = azure(modelId);
231
+ break;
232
+ }
233
+ case "openai-compatible": {
234
+ const { createOpenAICompatible } = await import("@ai-sdk/openai-compatible");
235
+ const baseURL = config?.baseURL ?? process.env.OPENAI_COMPATIBLE_BASE_URL;
236
+ if (!baseURL) {
237
+ throw new Error(
238
+ "openai-compatible \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F OPENAI_COMPATIBLE_BASE_URL \u74B0\u5883\u5909\u6570\u307E\u305F\u306F --model-base-url \u304C\u5FC5\u8981\u3067\u3059"
239
+ );
240
+ }
241
+ const apiKey = config?.apiKey ?? process.env.OPENAI_COMPATIBLE_API_KEY ?? "";
242
+ const openaiCompatible = createOpenAICompatible({
243
+ name: "openai-compatible",
244
+ baseURL,
245
+ apiKey
246
+ });
247
+ modelFactory = (id) => openaiCompatible(id);
248
+ cachedModel = openaiCompatible(modelId);
249
+ break;
250
+ }
251
+ case "bedrock": {
252
+ const { createAmazonBedrock } = await import("@ai-sdk/amazon-bedrock");
253
+ const region = config?.bedrockRegion ?? process.env.AWS_REGION;
254
+ if (!region) {
255
+ throw new Error(
256
+ "bedrock \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F AWS_REGION \u74B0\u5883\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
257
+ );
258
+ }
259
+ const accessKeyId = config?.bedrockAccessKeyId ?? process.env.AWS_ACCESS_KEY_ID;
260
+ const secretAccessKey = config?.bedrockSecretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY;
261
+ const sessionToken = config?.bedrockSessionToken ?? process.env.AWS_SESSION_TOKEN;
262
+ const bedrock = createAmazonBedrock({
263
+ region,
264
+ ...accessKeyId && secretAccessKey ? { accessKeyId, secretAccessKey, ...sessionToken ? { sessionToken } : {} } : {}
265
+ });
266
+ modelFactory = (id) => bedrock(id);
267
+ cachedModel = bedrock(modelId);
268
+ break;
269
+ }
270
+ case "vertex": {
271
+ const { createVertex } = await import("@ai-sdk/google-vertex");
272
+ const project = config?.vertexProject ?? process.env.GOOGLE_VERTEX_PROJECT;
273
+ const location = config?.vertexLocation ?? process.env.GOOGLE_VERTEX_LOCATION;
274
+ if (!project) {
275
+ throw new Error(
276
+ "vertex \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F GOOGLE_VERTEX_PROJECT \u74B0\u5883\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
277
+ );
278
+ }
279
+ if (!location) {
280
+ throw new Error(
281
+ "vertex \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F GOOGLE_VERTEX_LOCATION \u74B0\u5883\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
282
+ );
283
+ }
284
+ const vertex = createVertex({ project, location });
285
+ modelFactory = (id) => vertex(id);
286
+ cachedModel = vertex(modelId);
287
+ break;
288
+ }
289
+ case "anthropic":
290
+ default:
291
+ modelFactory = (id) => anthropic(id);
292
+ cachedModel = anthropic(modelId);
293
+ break;
294
+ }
295
+ }
296
+ async function initModelExplicit(config) {
297
+ const modelId = config.modelId;
298
+ const provider = config.provider;
299
+ currentModelId = modelId;
300
+ currentProvider = provider;
301
+ currentOverrides = config.modelOverrides ?? {};
302
+ overrideModelCache.clear();
303
+ switch (provider) {
304
+ case "openai": {
305
+ const { createOpenAI } = await import("@ai-sdk/openai");
306
+ const openai = createOpenAI({
307
+ ...config.apiKey ? { apiKey: config.apiKey } : {},
308
+ ...config.baseURL ? { baseURL: config.baseURL } : {}
309
+ });
310
+ modelFactory = (id) => openai(id);
311
+ cachedModel = openai(modelId);
312
+ break;
313
+ }
314
+ case "google": {
315
+ if (!config.apiKey) {
316
+ throw new Error("apiKey is required for google provider in SDK mode");
317
+ }
318
+ const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
319
+ const google = createGoogleGenerativeAI({
320
+ apiKey: config.apiKey,
321
+ ...config.baseURL ? { baseURL: config.baseURL } : {}
322
+ });
323
+ modelFactory = (id) => google(id);
324
+ cachedModel = google(modelId);
325
+ break;
326
+ }
327
+ case "azure": {
328
+ if (!config.azureResourceName && !config.baseURL) {
329
+ throw new Error("azureResourceName or baseURL is required for azure provider in SDK mode");
330
+ }
331
+ const { createAzure } = await import("@ai-sdk/azure");
332
+ const azure = createAzure({
333
+ ...config.azureResourceName ? { resourceName: config.azureResourceName } : {},
334
+ ...config.apiKey ? { apiKey: config.apiKey } : {},
335
+ ...config.azureApiVersion ? { apiVersion: config.azureApiVersion } : {},
336
+ ...config.baseURL ? { baseURL: config.baseURL } : {}
337
+ });
338
+ modelFactory = (id) => azure(id);
339
+ cachedModel = azure(modelId);
340
+ break;
341
+ }
342
+ case "openai-compatible": {
343
+ if (!config.baseURL) {
344
+ throw new Error("baseURL is required for openai-compatible provider in SDK mode");
345
+ }
346
+ if (!config.apiKey) {
347
+ throw new Error("apiKey is required for openai-compatible provider in SDK mode");
348
+ }
349
+ const { createOpenAICompatible } = await import("@ai-sdk/openai-compatible");
350
+ const openaiCompatible = createOpenAICompatible({
351
+ name: "openai-compatible",
352
+ baseURL: config.baseURL,
353
+ apiKey: config.apiKey
354
+ });
355
+ modelFactory = (id) => openaiCompatible(id);
356
+ cachedModel = openaiCompatible(modelId);
357
+ break;
358
+ }
359
+ case "bedrock": {
360
+ if (!config.bedrockRegion) {
361
+ throw new Error("bedrockRegion is required for bedrock provider in SDK mode");
362
+ }
363
+ const { createAmazonBedrock } = await import("@ai-sdk/amazon-bedrock");
364
+ const bedrock = createAmazonBedrock({
365
+ region: config.bedrockRegion,
366
+ ...config.bedrockAccessKeyId && config.bedrockSecretAccessKey ? {
367
+ accessKeyId: config.bedrockAccessKeyId,
368
+ secretAccessKey: config.bedrockSecretAccessKey,
369
+ ...config.bedrockSessionToken ? { sessionToken: config.bedrockSessionToken } : {}
370
+ } : {}
371
+ });
372
+ modelFactory = (id) => bedrock(id);
373
+ cachedModel = bedrock(modelId);
374
+ break;
375
+ }
376
+ case "vertex": {
377
+ if (!config.vertexProject) {
378
+ throw new Error("vertexProject is required for vertex provider in SDK mode");
379
+ }
380
+ if (!config.vertexLocation) {
381
+ throw new Error("vertexLocation is required for vertex provider in SDK mode");
382
+ }
383
+ const { createVertex } = await import("@ai-sdk/google-vertex");
384
+ const vertex = createVertex({
385
+ project: config.vertexProject,
386
+ location: config.vertexLocation
387
+ });
388
+ modelFactory = (id) => vertex(id);
389
+ cachedModel = vertex(modelId);
390
+ break;
391
+ }
392
+ case "anthropic":
393
+ default: {
394
+ if (config.apiKey) {
395
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
396
+ const client = createAnthropic({ apiKey: config.apiKey });
397
+ modelFactory = (id) => client(id);
398
+ cachedModel = client(modelId);
399
+ } else {
400
+ modelFactory = (id) => anthropic(id);
401
+ cachedModel = anthropic(modelId);
402
+ }
403
+ break;
404
+ }
405
+ }
406
+ }
407
+ function getModel(purpose) {
408
+ if (purpose) {
409
+ const overrideId = currentOverrides[purpose] ?? DEFAULT_PURPOSE_MODELS[purpose];
410
+ if (overrideId && overrideId !== currentModelId) {
411
+ const cached = overrideModelCache.get(purpose);
412
+ if (cached) return cached;
413
+ const factory = modelFactory ?? ((id) => anthropic(id));
414
+ const model = factory(overrideId);
415
+ overrideModelCache.set(purpose, model);
416
+ return model;
417
+ }
418
+ }
419
+ if (!cachedModel) {
420
+ cachedModel = anthropic(DEFAULT_MODEL_ID);
421
+ }
422
+ return cachedModel;
423
+ }
424
+ function getModelId(purpose) {
425
+ if (purpose) {
426
+ const overrideId = currentOverrides[purpose] ?? DEFAULT_PURPOSE_MODELS[purpose];
427
+ if (overrideId) return overrideId;
428
+ }
429
+ return currentModelId;
430
+ }
431
+ function getModelProvider() {
432
+ return currentProvider;
433
+ }
434
+ function getModelOverrides() {
435
+ return { ...currentOverrides };
436
+ }
437
+ function extractCachedTokens(usage) {
438
+ return usage.inputTokenDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? 0;
439
+ }
440
+ function extractCacheCreationTokensFromUsage(usage) {
441
+ return usage.inputTokenDetails?.cacheWriteTokens ?? 0;
442
+ }
443
+ function extractNonCachedInputTokens(usage) {
444
+ if (usage.inputTokenDetails?.noCacheTokens != null) {
445
+ return usage.inputTokenDetails.noCacheTokens;
446
+ }
447
+ const total = usage.inputTokens ?? 0;
448
+ const cached = extractCachedTokens(usage);
449
+ const cacheCreation = extractCacheCreationTokensFromUsage(usage);
450
+ return Math.max(0, total - cached - cacheCreation);
451
+ }
452
+ function recordMetrics(purpose, usage, durationMs, cacheCreationTokens, rawProviderUsage) {
453
+ getActiveMetricsCollector().record({
454
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
455
+ purpose,
456
+ modelId: getModelId(purpose),
457
+ inputTokens: extractNonCachedInputTokens(usage),
458
+ outputTokens: usage.outputTokens ?? 0,
459
+ cachedInputTokens: extractCachedTokens(usage),
460
+ cacheCreationTokens: cacheCreationTokens ?? 0,
461
+ durationMs: Math.round(durationMs),
462
+ rawProviderUsage
463
+ });
464
+ }
465
+ function extractCacheCreationTokens(result) {
466
+ if (Array.isArray(result.steps) && result.steps.length > 0) {
467
+ let total = 0;
468
+ for (const step of result.steps) {
469
+ total += step.providerMetadata?.anthropic?.cacheCreationInputTokens ?? 0;
470
+ }
471
+ return total;
472
+ }
473
+ return result.providerMetadata?.anthropic?.cacheCreationInputTokens ?? 0;
474
+ }
475
+ async function trackedGenerateObject(purpose, params) {
476
+ const start = performance.now();
477
+ const { schema, ...rest } = params;
478
+ let result;
479
+ try {
480
+ result = await generateText({
481
+ ...rest,
482
+ maxRetries: 0,
483
+ // AI SDK 内部リトライを無効化、withAIRetry に委譲
484
+ output: Output.object({ schema })
485
+ });
486
+ } catch (error) {
487
+ getActiveMetricsCollector().record({
488
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
489
+ purpose,
490
+ modelId: getModelId(purpose),
491
+ inputTokens: 0,
492
+ outputTokens: 0,
493
+ cachedInputTokens: 0,
494
+ cacheCreationTokens: 0,
495
+ durationMs: Math.round(performance.now() - start)
496
+ });
497
+ throw error;
498
+ }
499
+ const step = result.steps?.[0];
500
+ const rawUsage = step?.providerMetadata?.anthropic ?? result.providerMetadata?.anthropic;
501
+ recordMetrics(purpose, result.usage, performance.now() - start, extractCacheCreationTokens(result), rawUsage);
502
+ return { ...result, object: result.output };
503
+ }
504
+ async function trackedGenerateText(purpose, params) {
505
+ const start = performance.now();
506
+ let lastStepTime = start;
507
+ let stepsRecorded = 0;
508
+ const onStepFinish = (stepResult) => {
509
+ const now = performance.now();
510
+ const stepDuration = now - lastStepTime;
511
+ lastStepTime = now;
512
+ const modelId = stepResult.response?.modelId || getModelId(purpose);
513
+ const rawUsage = stepResult.providerMetadata?.anthropic;
514
+ getActiveMetricsCollector().record({
515
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
516
+ purpose,
517
+ modelId,
518
+ inputTokens: extractNonCachedInputTokens(stepResult.usage ?? {}),
519
+ outputTokens: stepResult.usage?.outputTokens ?? 0,
520
+ cachedInputTokens: extractCachedTokens(stepResult.usage ?? {}),
521
+ cacheCreationTokens: Number(stepResult.providerMetadata?.anthropic?.cacheCreationInputTokens ?? 0),
522
+ durationMs: Math.round(stepDuration),
523
+ rawProviderUsage: rawUsage
524
+ });
525
+ stepsRecorded++;
526
+ };
527
+ try {
528
+ const result = await generateText({
529
+ ...params,
530
+ maxRetries: 0,
531
+ // AI SDK 内部リトライを無効化、withAIRetry に委譲
532
+ onStepFinish
533
+ });
534
+ return result;
535
+ } catch (error) {
536
+ if (stepsRecorded === 0) {
537
+ getActiveMetricsCollector().record({
538
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
539
+ purpose,
540
+ modelId: getModelId(purpose),
541
+ inputTokens: 0,
542
+ outputTokens: 0,
543
+ cachedInputTokens: 0,
544
+ cacheCreationTokens: 0,
545
+ durationMs: Math.round(performance.now() - start)
546
+ });
547
+ }
548
+ throw error;
549
+ }
550
+ }
551
+ function isRetryableError(error) {
552
+ if (APICallError.isInstance(error)) {
553
+ if (error.isRetryable) return true;
554
+ const code = error.statusCode;
555
+ if (code !== void 0) {
556
+ if (code === 429 || code === 529) return true;
557
+ if (code >= 500 && code < 600) return true;
558
+ }
559
+ }
560
+ if (error instanceof Error) {
561
+ const msg = error.message.toLowerCase();
562
+ if (msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("econnrefused") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
563
+ return true;
564
+ }
565
+ if (msg.includes("rate limit") || msg.includes("rate_limit")) return true;
566
+ if (msg.includes("overloaded")) return true;
567
+ if (/\b(429|529)\b/.test(msg)) return true;
568
+ if (/\b5\d{2}\b/.test(error.message)) return true;
569
+ }
570
+ return false;
571
+ }
572
+ async function withAIRetry(fn, opts) {
573
+ const maxRetries = opts?.maxRetries ?? 2;
574
+ for (let attempt = 0; ; attempt++) {
575
+ try {
576
+ return await fn();
577
+ } catch (error) {
578
+ if (attempt < maxRetries && isRetryableError(error)) {
579
+ const delay = Math.pow(2, attempt) * 1e3;
580
+ await new Promise((resolve) => setTimeout(resolve, delay));
581
+ continue;
582
+ }
583
+ throw error;
584
+ }
585
+ }
586
+ }
587
+
588
+ export {
589
+ computeTotalRealInput,
590
+ computeCacheRate,
591
+ globalMetrics,
592
+ getActiveMetricsCollector,
593
+ DEFAULT_MODEL_ID,
594
+ DEFAULT_PROVIDER,
595
+ DEFAULT_PURPOSE_MODELS,
596
+ initModel,
597
+ initModelExplicit,
598
+ getModel,
599
+ getModelId,
600
+ getModelProvider,
601
+ getModelOverrides,
602
+ trackedGenerateObject,
603
+ trackedGenerateText,
604
+ isRetryableError,
605
+ withAIRetry
606
+ };
607
+ //# sourceMappingURL=chunk-XAEHXRUC.js.map