@refrainai/cli 0.4.1
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/dist/ai-model-FM6GWCID.js +37 -0
- package/dist/ai-model-FM6GWCID.js.map +1 -0
- package/dist/chunk-2BVDAJZT.js +236 -0
- package/dist/chunk-2BVDAJZT.js.map +1 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-7UCVPKD4.js +902 -0
- package/dist/chunk-7UCVPKD4.js.map +1 -0
- package/dist/chunk-AG3CFMYU.js +36 -0
- package/dist/chunk-AG3CFMYU.js.map +1 -0
- package/dist/chunk-CLYJHKPY.js +1131 -0
- package/dist/chunk-CLYJHKPY.js.map +1 -0
- package/dist/chunk-D5SI2PHK.js +74 -0
- package/dist/chunk-D5SI2PHK.js.map +1 -0
- package/dist/chunk-DJVUITRB.js +9084 -0
- package/dist/chunk-DJVUITRB.js.map +1 -0
- package/dist/chunk-H47NWH7N.js +4427 -0
- package/dist/chunk-H47NWH7N.js.map +1 -0
- package/dist/chunk-HQDXLWAY.js +109 -0
- package/dist/chunk-HQDXLWAY.js.map +1 -0
- package/dist/chunk-IGFCYKHC.js +1974 -0
- package/dist/chunk-IGFCYKHC.js.map +1 -0
- package/dist/chunk-RT664YIO.js +245 -0
- package/dist/chunk-RT664YIO.js.map +1 -0
- package/dist/chunk-RYIJPYM3.js +164 -0
- package/dist/chunk-RYIJPYM3.js.map +1 -0
- package/dist/chunk-TDSM3UXI.js +40 -0
- package/dist/chunk-TDSM3UXI.js.map +1 -0
- package/dist/chunk-UGPXCQY3.js +778 -0
- package/dist/chunk-UGPXCQY3.js.map +1 -0
- package/dist/chunk-VPK2MQAZ.js +589 -0
- package/dist/chunk-VPK2MQAZ.js.map +1 -0
- package/dist/chunk-WEYR56ZN.js +953 -0
- package/dist/chunk-WEYR56ZN.js.map +1 -0
- package/dist/chunk-XMFCXPYU.js +275 -0
- package/dist/chunk-XMFCXPYU.js.map +1 -0
- package/dist/chunk-Z33FCOTZ.js +251 -0
- package/dist/chunk-Z33FCOTZ.js.map +1 -0
- package/dist/cli.js +59 -0
- package/dist/cli.js.map +1 -0
- package/dist/compose-MTSIJY5D.js +547 -0
- package/dist/compose-MTSIJY5D.js.map +1 -0
- package/dist/config-ZSUNCFXR.js +9 -0
- package/dist/config-ZSUNCFXR.js.map +1 -0
- package/dist/fix-runbook-ZSBOTLC2.js +294 -0
- package/dist/fix-runbook-ZSBOTLC2.js.map +1 -0
- package/dist/google-sheets-DRWIVEVC.js +482 -0
- package/dist/google-sheets-DRWIVEVC.js.map +1 -0
- package/dist/registry-LZLYTNDJ.js +17 -0
- package/dist/registry-LZLYTNDJ.js.map +1 -0
- package/dist/runbook-data-helpers-KRR2SH76.js +16 -0
- package/dist/runbook-data-helpers-KRR2SH76.js.map +1 -0
- package/dist/runbook-executor-K7T6RJWJ.js +1480 -0
- package/dist/runbook-executor-K7T6RJWJ.js.map +1 -0
- package/dist/runbook-generator-MPXJBQ5N.js +800 -0
- package/dist/runbook-generator-MPXJBQ5N.js.map +1 -0
- package/dist/runbook-schema-3T6TP3JJ.js +35 -0
- package/dist/runbook-schema-3T6TP3JJ.js.map +1 -0
- package/dist/runbook-store-G5GUOWRR.js +11 -0
- package/dist/runbook-store-G5GUOWRR.js.map +1 -0
- package/dist/schema-5G6UQSPT.js +91 -0
- package/dist/schema-5G6UQSPT.js.map +1 -0
- package/dist/server-AG3LXQBI.js +8778 -0
- package/dist/server-AG3LXQBI.js.map +1 -0
- package/dist/tenant-ai-config-QPFEJUVJ.js +14 -0
- package/dist/tenant-ai-config-QPFEJUVJ.js.map +1 -0
- package/dist/yaml-patcher-VGUS2JGH.js +15 -0
- package/dist/yaml-patcher-VGUS2JGH.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/harness/ai-model.ts
|
|
4
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
5
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
6
|
+
import { APICallError as APICallError2, Output, generateText } from "ai";
|
|
7
|
+
|
|
8
|
+
// src/harness/ai-metrics.ts
|
|
9
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
10
|
+
var PRICING_TABLE = {
|
|
11
|
+
// Anthropic — cacheWrite = input × 1.25, cacheRead = input × 0.10
|
|
12
|
+
"claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
13
|
+
"claude-sonnet-4-5": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
14
|
+
"claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
15
|
+
"claude-haiku-4-5": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
16
|
+
"claude-haiku-3-5": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
|
|
17
|
+
"claude-opus-4-6": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
18
|
+
"claude-opus-4-5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
19
|
+
"claude-opus-4-1": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
|
|
20
|
+
"claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
|
|
21
|
+
// OpenAI (Azure OpenAI も同名モデルのため部分一致で対応) — キャッシュ書き込み追加課金なし
|
|
22
|
+
"gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25, cacheWrite: 0 },
|
|
23
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075, cacheWrite: 0 },
|
|
24
|
+
"gpt-4.1": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
|
|
25
|
+
"gpt-4.1-mini": { input: 0.4, output: 1.6, cacheRead: 0.1, cacheWrite: 0 },
|
|
26
|
+
"gpt-4.1-nano": { input: 0.1, output: 0.4, cacheRead: 0.025, cacheWrite: 0 },
|
|
27
|
+
"o3": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
|
|
28
|
+
"o4-mini": { input: 1.1, output: 4.4, cacheRead: 0.55, cacheWrite: 0 },
|
|
29
|
+
// Google Gemini — キャッシュ書き込み追加課金なし
|
|
30
|
+
"gemini-2.5-pro": { input: 1.25, output: 10, cacheRead: 0.315, cacheWrite: 0 },
|
|
31
|
+
"gemini-2.5-flash": { input: 0.15, output: 0.6, cacheRead: 0.0375, cacheWrite: 0 },
|
|
32
|
+
"gemini-2.0-flash": { input: 0.1, output: 0.4, cacheRead: 0.025, cacheWrite: 0 }
|
|
33
|
+
};
|
|
34
|
+
var DEFAULT_PRICING = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
|
|
35
|
+
function getPricing(modelId) {
|
|
36
|
+
if (PRICING_TABLE[modelId]) return PRICING_TABLE[modelId];
|
|
37
|
+
for (const [key, pricing] of Object.entries(PRICING_TABLE)) {
|
|
38
|
+
if (modelId.includes(key)) return pricing;
|
|
39
|
+
}
|
|
40
|
+
return DEFAULT_PRICING;
|
|
41
|
+
}
|
|
42
|
+
function calculateCost(metric) {
|
|
43
|
+
const pricing = getPricing(metric.modelId);
|
|
44
|
+
return metric.inputTokens * pricing.input / 1e6 + metric.cachedInputTokens * pricing.cacheRead / 1e6 + metric.cacheCreationTokens * pricing.cacheWrite / 1e6 + metric.outputTokens * pricing.output / 1e6;
|
|
45
|
+
}
|
|
46
|
+
function computeTotalRealInput(tokens) {
|
|
47
|
+
const input = tokens.totalInputTokens ?? tokens.inputTokens ?? 0;
|
|
48
|
+
const cached = tokens.totalCachedInputTokens ?? tokens.cachedInputTokens ?? 0;
|
|
49
|
+
const creation = tokens.totalCacheCreationTokens ?? tokens.cacheCreationTokens ?? 0;
|
|
50
|
+
return input + cached + creation;
|
|
51
|
+
}
|
|
52
|
+
function computeCacheRate(tokens) {
|
|
53
|
+
const total = computeTotalRealInput(tokens);
|
|
54
|
+
const cached = tokens.totalCachedInputTokens ?? tokens.cachedInputTokens ?? 0;
|
|
55
|
+
return total > 0 ? cached / total : 0;
|
|
56
|
+
}
|
|
57
|
+
var AIMetricsCollector = class {
|
|
58
|
+
constructor() {
|
|
59
|
+
this.metrics = [];
|
|
60
|
+
}
|
|
61
|
+
record(metric) {
|
|
62
|
+
this.metrics.push(metric);
|
|
63
|
+
}
|
|
64
|
+
getSummary() {
|
|
65
|
+
const totalCalls = this.metrics.length;
|
|
66
|
+
let totalInputTokens = 0;
|
|
67
|
+
let totalOutputTokens = 0;
|
|
68
|
+
let totalCachedInputTokens = 0;
|
|
69
|
+
let totalCacheCreationTokens = 0;
|
|
70
|
+
let totalDurationMs = 0;
|
|
71
|
+
let estimatedCostUsd = 0;
|
|
72
|
+
const byPurpose = {};
|
|
73
|
+
const byModel = {};
|
|
74
|
+
for (const m of this.metrics) {
|
|
75
|
+
totalInputTokens += m.inputTokens;
|
|
76
|
+
totalOutputTokens += m.outputTokens;
|
|
77
|
+
totalCachedInputTokens += m.cachedInputTokens;
|
|
78
|
+
totalCacheCreationTokens += m.cacheCreationTokens;
|
|
79
|
+
totalDurationMs += m.durationMs;
|
|
80
|
+
const cost = calculateCost(m);
|
|
81
|
+
estimatedCostUsd += cost;
|
|
82
|
+
const bp = byPurpose[m.purpose] ??= {
|
|
83
|
+
calls: 0,
|
|
84
|
+
inputTokens: 0,
|
|
85
|
+
outputTokens: 0,
|
|
86
|
+
cachedInputTokens: 0,
|
|
87
|
+
cacheCreationTokens: 0,
|
|
88
|
+
durationMs: 0
|
|
89
|
+
};
|
|
90
|
+
bp.calls++;
|
|
91
|
+
bp.inputTokens += m.inputTokens;
|
|
92
|
+
bp.outputTokens += m.outputTokens;
|
|
93
|
+
bp.cachedInputTokens += m.cachedInputTokens;
|
|
94
|
+
bp.cacheCreationTokens += m.cacheCreationTokens;
|
|
95
|
+
bp.durationMs += m.durationMs;
|
|
96
|
+
const bm = byModel[m.modelId] ??= {
|
|
97
|
+
calls: 0,
|
|
98
|
+
inputTokens: 0,
|
|
99
|
+
outputTokens: 0,
|
|
100
|
+
cachedInputTokens: 0,
|
|
101
|
+
cacheCreationTokens: 0,
|
|
102
|
+
durationMs: 0,
|
|
103
|
+
estimatedCostUsd: 0
|
|
104
|
+
};
|
|
105
|
+
bm.calls++;
|
|
106
|
+
bm.inputTokens += m.inputTokens;
|
|
107
|
+
bm.outputTokens += m.outputTokens;
|
|
108
|
+
bm.cachedInputTokens += m.cachedInputTokens;
|
|
109
|
+
bm.cacheCreationTokens += m.cacheCreationTokens;
|
|
110
|
+
bm.durationMs += m.durationMs;
|
|
111
|
+
bm.estimatedCostUsd += cost;
|
|
112
|
+
}
|
|
113
|
+
const cacheHitRate = computeCacheRate({
|
|
114
|
+
inputTokens: totalInputTokens,
|
|
115
|
+
cachedInputTokens: totalCachedInputTokens,
|
|
116
|
+
cacheCreationTokens: totalCacheCreationTokens
|
|
117
|
+
});
|
|
118
|
+
let latencyPercentiles;
|
|
119
|
+
if (this.metrics.length > 0) {
|
|
120
|
+
const sorted = this.metrics.map((m) => m.durationMs).sort((a, b) => a - b);
|
|
121
|
+
const percentile = (p) => {
|
|
122
|
+
const idx = Math.ceil(p / 100 * sorted.length) - 1;
|
|
123
|
+
return sorted[Math.max(0, Math.min(idx, sorted.length - 1))];
|
|
124
|
+
};
|
|
125
|
+
latencyPercentiles = {
|
|
126
|
+
p50: Math.round(percentile(50)),
|
|
127
|
+
p95: Math.round(percentile(95)),
|
|
128
|
+
p99: Math.round(percentile(99))
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
totalCalls,
|
|
133
|
+
totalInputTokens,
|
|
134
|
+
totalOutputTokens,
|
|
135
|
+
totalCachedInputTokens,
|
|
136
|
+
totalCacheCreationTokens,
|
|
137
|
+
cacheHitRate,
|
|
138
|
+
estimatedCostUsd: Math.round(estimatedCostUsd * 1e6) / 1e6,
|
|
139
|
+
totalDurationMs,
|
|
140
|
+
latencyPercentiles,
|
|
141
|
+
byPurpose,
|
|
142
|
+
byModel
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** メトリクスをリセットする(eval ケース間で使用) */
|
|
146
|
+
reset() {
|
|
147
|
+
this.metrics = [];
|
|
148
|
+
}
|
|
149
|
+
/** 蓄積済みの個別メトリクスを返す(デバッグ用) */
|
|
150
|
+
getMetrics() {
|
|
151
|
+
return this.metrics;
|
|
152
|
+
}
|
|
153
|
+
/** 蓄積済みメトリクスの件数 */
|
|
154
|
+
get count() {
|
|
155
|
+
return this.metrics.length;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var globalMetrics = new AIMetricsCollector();
|
|
159
|
+
var metricsStorage = new AsyncLocalStorage();
|
|
160
|
+
var activeCollector = globalMetrics;
|
|
161
|
+
function runWithMetricsCollector(collector, fn) {
|
|
162
|
+
return metricsStorage.run(collector, fn);
|
|
163
|
+
}
|
|
164
|
+
function getActiveMetricsCollector() {
|
|
165
|
+
return metricsStorage.getStore() ?? activeCollector;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/harness/ai-error.ts
|
|
169
|
+
import { APICallError } from "ai";
|
|
170
|
+
var BILLING_PATTERNS = [
|
|
171
|
+
/credit\s*balance/i,
|
|
172
|
+
/insufficient\s*(funds|credit|balance)/i,
|
|
173
|
+
/billing/i,
|
|
174
|
+
/payment\s*required/i,
|
|
175
|
+
/quota\s*exceeded/i
|
|
176
|
+
];
|
|
177
|
+
var AUTH_PATTERNS = [
|
|
178
|
+
/unauthorized/i,
|
|
179
|
+
/invalid[\s_-]*api[\s_-]*key/i,
|
|
180
|
+
/authentication\s*failed/i,
|
|
181
|
+
/forbidden/i,
|
|
182
|
+
/permission\s*denied/i,
|
|
183
|
+
/invalid[\s_-]*x[\s_-]*api[\s_-]*key/i,
|
|
184
|
+
/invalid[\s_-]*bearer[\s_-]*token/i
|
|
185
|
+
];
|
|
186
|
+
var NETWORK_PATTERNS = [
|
|
187
|
+
/econnreset/i,
|
|
188
|
+
/etimedout/i,
|
|
189
|
+
/econnrefused/i,
|
|
190
|
+
/fetch\s*failed/i,
|
|
191
|
+
/socket\s*hang\s*up/i,
|
|
192
|
+
/network\s*error/i
|
|
193
|
+
];
|
|
194
|
+
var CONTENT_FILTER_PATTERNS = [
|
|
195
|
+
/content[\s_-]*filter/i,
|
|
196
|
+
/content[\s_-]*policy/i,
|
|
197
|
+
/safety[\s_-]*filter/i,
|
|
198
|
+
/content[\s_-]*moderation/i
|
|
199
|
+
];
|
|
200
|
+
var MODEL_NOT_FOUND_PATTERNS = [
|
|
201
|
+
/model[\s_-]*not[\s_-]*found/i,
|
|
202
|
+
/does[\s_-]*not[\s_-]*exist/i,
|
|
203
|
+
/unknown[\s_-]*model/i
|
|
204
|
+
];
|
|
205
|
+
function matchesAny(message, patterns) {
|
|
206
|
+
return patterns.some((p) => p.test(message));
|
|
207
|
+
}
|
|
208
|
+
function buildUserMessage(category, originalMessage) {
|
|
209
|
+
switch (category) {
|
|
210
|
+
case "billing":
|
|
211
|
+
return `AI API billing error: ${originalMessage}`;
|
|
212
|
+
case "auth":
|
|
213
|
+
return `AI API authentication failed: ${originalMessage}`;
|
|
214
|
+
case "rate_limit":
|
|
215
|
+
return "AI API rate limit exceeded. Retrying automatically.";
|
|
216
|
+
case "overloaded":
|
|
217
|
+
return "AI API is temporarily overloaded. Retrying automatically.";
|
|
218
|
+
case "server_error":
|
|
219
|
+
return `AI API server error: ${originalMessage}`;
|
|
220
|
+
case "network":
|
|
221
|
+
return `Network error connecting to AI API: ${originalMessage}`;
|
|
222
|
+
case "model_not_found":
|
|
223
|
+
return `AI model not found: ${originalMessage}`;
|
|
224
|
+
case "content_filter":
|
|
225
|
+
return `AI API content policy violation: ${originalMessage}`;
|
|
226
|
+
case "unknown":
|
|
227
|
+
return originalMessage;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function classifyAIError(error) {
|
|
231
|
+
const originalMessage = error instanceof Error ? error.message : String(error);
|
|
232
|
+
if (APICallError.isInstance(error)) {
|
|
233
|
+
const code = error.statusCode;
|
|
234
|
+
const body = typeof error.responseBody === "string" ? error.responseBody : "";
|
|
235
|
+
const msg = error.message + " " + body;
|
|
236
|
+
if (code !== void 0) {
|
|
237
|
+
if ((code === 400 || code === 402) && matchesAny(msg, BILLING_PATTERNS)) {
|
|
238
|
+
return { category: "billing", statusCode: code, isRetryable: false, userMessage: buildUserMessage("billing", originalMessage), originalMessage };
|
|
239
|
+
}
|
|
240
|
+
if (code === 401 || code === 403) {
|
|
241
|
+
return { category: "auth", statusCode: code, isRetryable: false, userMessage: buildUserMessage("auth", originalMessage), originalMessage };
|
|
242
|
+
}
|
|
243
|
+
if (code === 404) {
|
|
244
|
+
return { category: "model_not_found", statusCode: code, isRetryable: false, userMessage: buildUserMessage("model_not_found", originalMessage), originalMessage };
|
|
245
|
+
}
|
|
246
|
+
if (code === 429) {
|
|
247
|
+
return { category: "rate_limit", statusCode: code, isRetryable: true, userMessage: buildUserMessage("rate_limit", originalMessage), originalMessage };
|
|
248
|
+
}
|
|
249
|
+
if (code === 529) {
|
|
250
|
+
return { category: "overloaded", statusCode: code, isRetryable: true, userMessage: buildUserMessage("overloaded", originalMessage), originalMessage };
|
|
251
|
+
}
|
|
252
|
+
if (code >= 500 && code < 600) {
|
|
253
|
+
return { category: "server_error", statusCode: code, isRetryable: true, userMessage: buildUserMessage("server_error", originalMessage), originalMessage };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (matchesAny(msg, BILLING_PATTERNS)) {
|
|
257
|
+
return { category: "billing", statusCode: code, isRetryable: false, userMessage: buildUserMessage("billing", originalMessage), originalMessage };
|
|
258
|
+
}
|
|
259
|
+
if (matchesAny(msg, AUTH_PATTERNS)) {
|
|
260
|
+
return { category: "auth", statusCode: code, isRetryable: false, userMessage: buildUserMessage("auth", originalMessage), originalMessage };
|
|
261
|
+
}
|
|
262
|
+
if (matchesAny(msg, CONTENT_FILTER_PATTERNS)) {
|
|
263
|
+
return { category: "content_filter", statusCode: code, isRetryable: false, userMessage: buildUserMessage("content_filter", originalMessage), originalMessage };
|
|
264
|
+
}
|
|
265
|
+
if (matchesAny(msg, MODEL_NOT_FOUND_PATTERNS)) {
|
|
266
|
+
return { category: "model_not_found", statusCode: code, isRetryable: false, userMessage: buildUserMessage("model_not_found", originalMessage), originalMessage };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (error instanceof Error) {
|
|
270
|
+
const msg = error.message;
|
|
271
|
+
if (matchesAny(msg, BILLING_PATTERNS)) {
|
|
272
|
+
return { category: "billing", isRetryable: false, userMessage: buildUserMessage("billing", originalMessage), originalMessage };
|
|
273
|
+
}
|
|
274
|
+
if (matchesAny(msg, AUTH_PATTERNS)) {
|
|
275
|
+
return { category: "auth", isRetryable: false, userMessage: buildUserMessage("auth", originalMessage), originalMessage };
|
|
276
|
+
}
|
|
277
|
+
if (matchesAny(msg, NETWORK_PATTERNS)) {
|
|
278
|
+
return { category: "network", isRetryable: true, userMessage: buildUserMessage("network", originalMessage), originalMessage };
|
|
279
|
+
}
|
|
280
|
+
if (matchesAny(msg, CONTENT_FILTER_PATTERNS)) {
|
|
281
|
+
return { category: "content_filter", isRetryable: false, userMessage: buildUserMessage("content_filter", originalMessage), originalMessage };
|
|
282
|
+
}
|
|
283
|
+
if (matchesAny(msg, MODEL_NOT_FOUND_PATTERNS)) {
|
|
284
|
+
return { category: "model_not_found", isRetryable: false, userMessage: buildUserMessage("model_not_found", originalMessage), originalMessage };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return { category: "unknown", isRetryable: false, userMessage: originalMessage, originalMessage };
|
|
288
|
+
}
|
|
289
|
+
function isFatalAIError(error) {
|
|
290
|
+
const { category } = classifyAIError(error);
|
|
291
|
+
return category === "billing" || category === "auth" || category === "model_not_found";
|
|
292
|
+
}
|
|
293
|
+
function getUserFriendlyAIErrorFromMessage(message) {
|
|
294
|
+
if (matchesAny(message, BILLING_PATTERNS)) {
|
|
295
|
+
return buildUserMessage("billing", message);
|
|
296
|
+
}
|
|
297
|
+
if (matchesAny(message, AUTH_PATTERNS)) {
|
|
298
|
+
return buildUserMessage("auth", message);
|
|
299
|
+
}
|
|
300
|
+
if (matchesAny(message, NETWORK_PATTERNS)) {
|
|
301
|
+
return buildUserMessage("network", message);
|
|
302
|
+
}
|
|
303
|
+
if (matchesAny(message, CONTENT_FILTER_PATTERNS)) {
|
|
304
|
+
return buildUserMessage("content_filter", message);
|
|
305
|
+
}
|
|
306
|
+
if (matchesAny(message, MODEL_NOT_FOUND_PATTERNS)) {
|
|
307
|
+
return buildUserMessage("model_not_found", message);
|
|
308
|
+
}
|
|
309
|
+
if (/rate[\s_-]*limit/i.test(message) || /\b429\b/.test(message)) {
|
|
310
|
+
return buildUserMessage("rate_limit", message);
|
|
311
|
+
}
|
|
312
|
+
if (/overloaded/i.test(message) || /\b529\b/.test(message)) {
|
|
313
|
+
return buildUserMessage("overloaded", message);
|
|
314
|
+
}
|
|
315
|
+
return message;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/harness/ai-model.ts
|
|
319
|
+
var DEFAULT_MODEL_ID = "claude-sonnet-4-6";
|
|
320
|
+
var DEFAULT_PROVIDER = "anthropic";
|
|
321
|
+
var PROVIDER_DEFAULT_MODELS = {
|
|
322
|
+
anthropic: "claude-sonnet-4-6",
|
|
323
|
+
openai: "gpt-4o",
|
|
324
|
+
"openai-compatible": "gpt-4o",
|
|
325
|
+
google: "gemini-2.5-pro",
|
|
326
|
+
azure: "gpt-4o",
|
|
327
|
+
bedrock: "anthropic.claude-sonnet-4-6-v1",
|
|
328
|
+
vertex: "claude-sonnet-4-6@20250514"
|
|
329
|
+
};
|
|
330
|
+
var currentModelId = DEFAULT_MODEL_ID;
|
|
331
|
+
var currentProvider = DEFAULT_PROVIDER;
|
|
332
|
+
var currentOverrides = {};
|
|
333
|
+
var cachedModel = null;
|
|
334
|
+
var overrideModelCache = /* @__PURE__ */ new Map();
|
|
335
|
+
var modelFactory = null;
|
|
336
|
+
var modelStorage = new AsyncLocalStorage2();
|
|
337
|
+
function runWithModelContext(config, factory, fn) {
|
|
338
|
+
const model = factory(config.modelId ?? DEFAULT_MODEL_ID);
|
|
339
|
+
const ctx = {
|
|
340
|
+
modelId: config.modelId ?? DEFAULT_MODEL_ID,
|
|
341
|
+
provider: config.provider ?? DEFAULT_PROVIDER,
|
|
342
|
+
overrides: config.modelOverrides ?? {},
|
|
343
|
+
model,
|
|
344
|
+
factory,
|
|
345
|
+
overrideCache: /* @__PURE__ */ new Map()
|
|
346
|
+
};
|
|
347
|
+
return modelStorage.run(ctx, fn);
|
|
348
|
+
}
|
|
349
|
+
async function initModel(config) {
|
|
350
|
+
const modelId = config?.modelId ?? DEFAULT_MODEL_ID;
|
|
351
|
+
const provider = config?.provider ?? DEFAULT_PROVIDER;
|
|
352
|
+
currentModelId = modelId;
|
|
353
|
+
currentProvider = provider;
|
|
354
|
+
currentOverrides = config?.modelOverrides ?? {};
|
|
355
|
+
overrideModelCache.clear();
|
|
356
|
+
switch (provider) {
|
|
357
|
+
case "openai": {
|
|
358
|
+
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
359
|
+
const apiKey = config?.apiKey ?? process.env.OPENAI_API_KEY;
|
|
360
|
+
const openai = createOpenAI({
|
|
361
|
+
...apiKey ? { apiKey } : {},
|
|
362
|
+
...config?.baseURL ? { baseURL: config.baseURL } : {}
|
|
363
|
+
});
|
|
364
|
+
modelFactory = (id) => openai(id);
|
|
365
|
+
cachedModel = openai(modelId);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
case "google": {
|
|
369
|
+
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
|
370
|
+
const apiKey = config?.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
371
|
+
if (!apiKey) {
|
|
372
|
+
throw new Error(
|
|
373
|
+
"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"
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
const google = createGoogleGenerativeAI({
|
|
377
|
+
apiKey,
|
|
378
|
+
...config?.baseURL ? { baseURL: config.baseURL } : {}
|
|
379
|
+
});
|
|
380
|
+
modelFactory = (id) => google(id);
|
|
381
|
+
cachedModel = google(modelId);
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
case "azure": {
|
|
385
|
+
const { createAzure } = await import("@ai-sdk/azure");
|
|
386
|
+
const apiKey = config?.apiKey ?? process.env.AZURE_API_KEY;
|
|
387
|
+
const resourceName = config?.azureResourceName ?? process.env.AZURE_RESOURCE_NAME;
|
|
388
|
+
const apiVersion = config?.azureApiVersion ?? process.env.AZURE_API_VERSION;
|
|
389
|
+
if (!resourceName && !config?.baseURL) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
"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"
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
const azure = createAzure({
|
|
395
|
+
...resourceName ? { resourceName } : {},
|
|
396
|
+
...apiKey ? { apiKey } : {},
|
|
397
|
+
...apiVersion ? { apiVersion } : {},
|
|
398
|
+
...config?.baseURL ? { baseURL: config.baseURL } : {}
|
|
399
|
+
});
|
|
400
|
+
modelFactory = (id) => azure(id);
|
|
401
|
+
cachedModel = azure(modelId);
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
case "openai-compatible": {
|
|
405
|
+
const { createOpenAICompatible } = await import("@ai-sdk/openai-compatible");
|
|
406
|
+
const baseURL = config?.baseURL ?? process.env.OPENAI_COMPATIBLE_BASE_URL;
|
|
407
|
+
if (!baseURL) {
|
|
408
|
+
throw new Error(
|
|
409
|
+
"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"
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
const apiKey = config?.apiKey ?? process.env.OPENAI_COMPATIBLE_API_KEY ?? "";
|
|
413
|
+
const openaiCompatible = createOpenAICompatible({
|
|
414
|
+
name: "openai-compatible",
|
|
415
|
+
baseURL,
|
|
416
|
+
apiKey
|
|
417
|
+
});
|
|
418
|
+
modelFactory = (id) => openaiCompatible(id);
|
|
419
|
+
cachedModel = openaiCompatible(modelId);
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
case "bedrock": {
|
|
423
|
+
const { createAmazonBedrock } = await import("@ai-sdk/amazon-bedrock");
|
|
424
|
+
const region = config?.bedrockRegion ?? process.env.AWS_REGION;
|
|
425
|
+
if (!region) {
|
|
426
|
+
throw new Error(
|
|
427
|
+
"bedrock \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F AWS_REGION \u74B0\u5883\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
const accessKeyId = config?.bedrockAccessKeyId ?? process.env.AWS_ACCESS_KEY_ID;
|
|
431
|
+
const secretAccessKey = config?.bedrockSecretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY;
|
|
432
|
+
const sessionToken = config?.bedrockSessionToken ?? process.env.AWS_SESSION_TOKEN;
|
|
433
|
+
const bedrock = createAmazonBedrock({
|
|
434
|
+
region,
|
|
435
|
+
...accessKeyId && secretAccessKey ? { accessKeyId, secretAccessKey, ...sessionToken ? { sessionToken } : {} } : {}
|
|
436
|
+
});
|
|
437
|
+
modelFactory = (id) => bedrock(id);
|
|
438
|
+
cachedModel = bedrock(modelId);
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
case "vertex": {
|
|
442
|
+
const { createVertex } = await import("@ai-sdk/google-vertex");
|
|
443
|
+
const project = config?.vertexProject ?? process.env.GOOGLE_VERTEX_PROJECT;
|
|
444
|
+
const location = config?.vertexLocation ?? process.env.GOOGLE_VERTEX_LOCATION;
|
|
445
|
+
if (!project) {
|
|
446
|
+
throw new Error(
|
|
447
|
+
"vertex \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F GOOGLE_VERTEX_PROJECT \u74B0\u5883\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
if (!location) {
|
|
451
|
+
throw new Error(
|
|
452
|
+
"vertex \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306B\u306F GOOGLE_VERTEX_LOCATION \u74B0\u5883\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
const vertex = createVertex({ project, location });
|
|
456
|
+
modelFactory = (id) => vertex(id);
|
|
457
|
+
cachedModel = vertex(modelId);
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
case "anthropic":
|
|
461
|
+
default:
|
|
462
|
+
modelFactory = (id) => anthropic(id);
|
|
463
|
+
cachedModel = anthropic(modelId);
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function buildModelFactory(config) {
|
|
468
|
+
switch (config.provider) {
|
|
469
|
+
case "openai": {
|
|
470
|
+
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
471
|
+
const openai = createOpenAI({
|
|
472
|
+
...config.apiKey ? { apiKey: config.apiKey } : {},
|
|
473
|
+
...config.baseURL ? { baseURL: config.baseURL } : {}
|
|
474
|
+
});
|
|
475
|
+
return (id) => openai(id);
|
|
476
|
+
}
|
|
477
|
+
case "google": {
|
|
478
|
+
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
|
479
|
+
if (!config.apiKey) throw new Error("apiKey required for google provider");
|
|
480
|
+
const google = createGoogleGenerativeAI({ apiKey: config.apiKey });
|
|
481
|
+
return (id) => google(id);
|
|
482
|
+
}
|
|
483
|
+
case "azure": {
|
|
484
|
+
const { createAzure } = await import("@ai-sdk/azure");
|
|
485
|
+
const azure = createAzure({
|
|
486
|
+
...config.azureResourceName ? { resourceName: config.azureResourceName } : {},
|
|
487
|
+
...config.apiKey ? { apiKey: config.apiKey } : {},
|
|
488
|
+
...config.azureApiVersion ? { apiVersion: config.azureApiVersion } : {},
|
|
489
|
+
...config.baseURL ? { baseURL: config.baseURL } : {}
|
|
490
|
+
});
|
|
491
|
+
return (id) => azure(id);
|
|
492
|
+
}
|
|
493
|
+
case "openai-compatible": {
|
|
494
|
+
if (!config.baseURL) throw new Error("baseURL required for openai-compatible");
|
|
495
|
+
const { createOpenAICompatible } = await import("@ai-sdk/openai-compatible");
|
|
496
|
+
const compat = createOpenAICompatible({
|
|
497
|
+
name: "openai-compatible",
|
|
498
|
+
baseURL: config.baseURL,
|
|
499
|
+
apiKey: config.apiKey ?? ""
|
|
500
|
+
});
|
|
501
|
+
return (id) => compat(id);
|
|
502
|
+
}
|
|
503
|
+
case "bedrock": {
|
|
504
|
+
if (!config.bedrockRegion) throw new Error("bedrockRegion required for bedrock");
|
|
505
|
+
const { createAmazonBedrock } = await import("@ai-sdk/amazon-bedrock");
|
|
506
|
+
const bedrock = createAmazonBedrock({
|
|
507
|
+
region: config.bedrockRegion,
|
|
508
|
+
...config.bedrockAccessKeyId && config.bedrockSecretAccessKey ? {
|
|
509
|
+
accessKeyId: config.bedrockAccessKeyId,
|
|
510
|
+
secretAccessKey: config.bedrockSecretAccessKey,
|
|
511
|
+
...config.bedrockSessionToken ? { sessionToken: config.bedrockSessionToken } : {}
|
|
512
|
+
} : {}
|
|
513
|
+
});
|
|
514
|
+
return (id) => bedrock(id);
|
|
515
|
+
}
|
|
516
|
+
case "vertex": {
|
|
517
|
+
if (!config.vertexProject) throw new Error("vertexProject required for vertex");
|
|
518
|
+
if (!config.vertexLocation) throw new Error("vertexLocation required for vertex");
|
|
519
|
+
const { createVertex } = await import("@ai-sdk/google-vertex");
|
|
520
|
+
const vertex = createVertex({
|
|
521
|
+
project: config.vertexProject,
|
|
522
|
+
location: config.vertexLocation
|
|
523
|
+
});
|
|
524
|
+
return (id) => vertex(id);
|
|
525
|
+
}
|
|
526
|
+
case "anthropic":
|
|
527
|
+
default: {
|
|
528
|
+
if (config.apiKey) {
|
|
529
|
+
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
530
|
+
const client = createAnthropic({ apiKey: config.apiKey });
|
|
531
|
+
return (id) => client(id);
|
|
532
|
+
}
|
|
533
|
+
const { anthropic: anthropic2 } = await import("@ai-sdk/anthropic");
|
|
534
|
+
return (id) => anthropic2(id);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
async function initModelExplicit(config) {
|
|
539
|
+
currentModelId = config.modelId;
|
|
540
|
+
currentProvider = config.provider;
|
|
541
|
+
currentOverrides = config.modelOverrides ?? {};
|
|
542
|
+
overrideModelCache.clear();
|
|
543
|
+
const factory = await buildModelFactory(config);
|
|
544
|
+
modelFactory = factory;
|
|
545
|
+
cachedModel = factory(config.modelId);
|
|
546
|
+
}
|
|
547
|
+
function getModel(purpose) {
|
|
548
|
+
const ctx = modelStorage.getStore();
|
|
549
|
+
if (ctx) {
|
|
550
|
+
if (purpose) {
|
|
551
|
+
const overrideId = ctx.overrides[purpose];
|
|
552
|
+
if (overrideId && overrideId !== ctx.modelId) {
|
|
553
|
+
const cached = ctx.overrideCache.get(purpose);
|
|
554
|
+
if (cached) return cached;
|
|
555
|
+
const model = ctx.factory(overrideId);
|
|
556
|
+
ctx.overrideCache.set(purpose, model);
|
|
557
|
+
return model;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return ctx.model;
|
|
561
|
+
}
|
|
562
|
+
if (purpose) {
|
|
563
|
+
const overrideId = currentOverrides[purpose];
|
|
564
|
+
if (overrideId && overrideId !== currentModelId) {
|
|
565
|
+
const cached = overrideModelCache.get(purpose);
|
|
566
|
+
if (cached) return cached;
|
|
567
|
+
const factory = modelFactory ?? ((id) => anthropic(id));
|
|
568
|
+
const model = factory(overrideId);
|
|
569
|
+
overrideModelCache.set(purpose, model);
|
|
570
|
+
return model;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (!cachedModel) {
|
|
574
|
+
cachedModel = anthropic(DEFAULT_MODEL_ID);
|
|
575
|
+
}
|
|
576
|
+
return cachedModel;
|
|
577
|
+
}
|
|
578
|
+
function getModelId(purpose) {
|
|
579
|
+
const ctx = modelStorage.getStore();
|
|
580
|
+
const activeOverrides = ctx ? ctx.overrides : currentOverrides;
|
|
581
|
+
const activeModelId = ctx ? ctx.modelId : currentModelId;
|
|
582
|
+
if (purpose) {
|
|
583
|
+
const overrideId = activeOverrides[purpose];
|
|
584
|
+
if (overrideId) return overrideId;
|
|
585
|
+
}
|
|
586
|
+
return activeModelId;
|
|
587
|
+
}
|
|
588
|
+
function getModelProvider() {
|
|
589
|
+
const ctx = modelStorage.getStore();
|
|
590
|
+
return ctx ? ctx.provider : currentProvider;
|
|
591
|
+
}
|
|
592
|
+
function getModelOverrides() {
|
|
593
|
+
const ctx = modelStorage.getStore();
|
|
594
|
+
return { ...ctx ? ctx.overrides : currentOverrides };
|
|
595
|
+
}
|
|
596
|
+
function extractCachedTokens(usage) {
|
|
597
|
+
return usage.inputTokenDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? 0;
|
|
598
|
+
}
|
|
599
|
+
function extractCacheCreationTokensFromUsage(usage) {
|
|
600
|
+
return usage.inputTokenDetails?.cacheWriteTokens ?? 0;
|
|
601
|
+
}
|
|
602
|
+
function extractNonCachedInputTokens(usage) {
|
|
603
|
+
if (usage.inputTokenDetails?.noCacheTokens != null) {
|
|
604
|
+
return usage.inputTokenDetails.noCacheTokens;
|
|
605
|
+
}
|
|
606
|
+
const total = usage.inputTokens ?? 0;
|
|
607
|
+
const cached = extractCachedTokens(usage);
|
|
608
|
+
const cacheCreation = extractCacheCreationTokensFromUsage(usage);
|
|
609
|
+
return Math.max(0, total - cached - cacheCreation);
|
|
610
|
+
}
|
|
611
|
+
function recordMetrics(purpose, usage, durationMs, cacheCreationTokens, rawProviderUsage) {
|
|
612
|
+
getActiveMetricsCollector().record({
|
|
613
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
614
|
+
purpose,
|
|
615
|
+
modelId: getModelId(purpose),
|
|
616
|
+
inputTokens: extractNonCachedInputTokens(usage),
|
|
617
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
618
|
+
cachedInputTokens: extractCachedTokens(usage),
|
|
619
|
+
cacheCreationTokens: cacheCreationTokens ?? 0,
|
|
620
|
+
durationMs: Math.round(durationMs),
|
|
621
|
+
rawProviderUsage
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
function extractCacheCreationTokens(result) {
|
|
625
|
+
if (Array.isArray(result.steps) && result.steps.length > 0) {
|
|
626
|
+
let total = 0;
|
|
627
|
+
for (const step of result.steps) {
|
|
628
|
+
total += step.providerMetadata?.anthropic?.cacheCreationInputTokens ?? 0;
|
|
629
|
+
}
|
|
630
|
+
return total;
|
|
631
|
+
}
|
|
632
|
+
return result.providerMetadata?.anthropic?.cacheCreationInputTokens ?? 0;
|
|
633
|
+
}
|
|
634
|
+
async function trackedGenerateObject(purpose, params) {
|
|
635
|
+
const start = performance.now();
|
|
636
|
+
const { schema, ...rest } = params;
|
|
637
|
+
let result;
|
|
638
|
+
try {
|
|
639
|
+
result = await generateText({
|
|
640
|
+
...rest,
|
|
641
|
+
maxRetries: 0,
|
|
642
|
+
// AI SDK 内部リトライを無効化、withAIRetry に委譲
|
|
643
|
+
output: Output.object({ schema })
|
|
644
|
+
});
|
|
645
|
+
} catch (error) {
|
|
646
|
+
getActiveMetricsCollector().record({
|
|
647
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
648
|
+
purpose,
|
|
649
|
+
modelId: getModelId(purpose),
|
|
650
|
+
inputTokens: 0,
|
|
651
|
+
outputTokens: 0,
|
|
652
|
+
cachedInputTokens: 0,
|
|
653
|
+
cacheCreationTokens: 0,
|
|
654
|
+
durationMs: Math.round(performance.now() - start)
|
|
655
|
+
});
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
658
|
+
const step = result.steps?.[0];
|
|
659
|
+
const rawUsage = step?.providerMetadata?.anthropic ?? result.providerMetadata?.anthropic;
|
|
660
|
+
recordMetrics(purpose, result.usage, performance.now() - start, extractCacheCreationTokens(result), rawUsage);
|
|
661
|
+
return { ...result, object: result.output };
|
|
662
|
+
}
|
|
663
|
+
async function trackedGenerateText(purpose, params) {
|
|
664
|
+
const start = performance.now();
|
|
665
|
+
let lastStepTime = start;
|
|
666
|
+
let stepsRecorded = 0;
|
|
667
|
+
const onStepFinish = (stepResult) => {
|
|
668
|
+
const now = performance.now();
|
|
669
|
+
const stepDuration = now - lastStepTime;
|
|
670
|
+
lastStepTime = now;
|
|
671
|
+
const modelId = stepResult.response?.modelId || getModelId(purpose);
|
|
672
|
+
const rawUsage = stepResult.providerMetadata?.anthropic;
|
|
673
|
+
getActiveMetricsCollector().record({
|
|
674
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
675
|
+
purpose,
|
|
676
|
+
modelId,
|
|
677
|
+
inputTokens: extractNonCachedInputTokens(stepResult.usage ?? {}),
|
|
678
|
+
outputTokens: stepResult.usage?.outputTokens ?? 0,
|
|
679
|
+
cachedInputTokens: extractCachedTokens(stepResult.usage ?? {}),
|
|
680
|
+
cacheCreationTokens: Number(stepResult.providerMetadata?.anthropic?.cacheCreationInputTokens ?? 0),
|
|
681
|
+
durationMs: Math.round(stepDuration),
|
|
682
|
+
rawProviderUsage: rawUsage
|
|
683
|
+
});
|
|
684
|
+
stepsRecorded++;
|
|
685
|
+
};
|
|
686
|
+
try {
|
|
687
|
+
const result = await generateText({
|
|
688
|
+
...params,
|
|
689
|
+
maxRetries: 0,
|
|
690
|
+
// AI SDK 内部リトライを無効化、withAIRetry に委譲
|
|
691
|
+
onStepFinish
|
|
692
|
+
});
|
|
693
|
+
return result;
|
|
694
|
+
} catch (error) {
|
|
695
|
+
if (stepsRecorded === 0) {
|
|
696
|
+
getActiveMetricsCollector().record({
|
|
697
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
698
|
+
purpose,
|
|
699
|
+
modelId: getModelId(purpose),
|
|
700
|
+
inputTokens: 0,
|
|
701
|
+
outputTokens: 0,
|
|
702
|
+
cachedInputTokens: 0,
|
|
703
|
+
cacheCreationTokens: 0,
|
|
704
|
+
durationMs: Math.round(performance.now() - start)
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
function isRetryableError(error) {
|
|
711
|
+
if (APICallError2.isInstance(error)) {
|
|
712
|
+
if (error.isRetryable) return true;
|
|
713
|
+
const code = error.statusCode;
|
|
714
|
+
if (code !== void 0) {
|
|
715
|
+
if (code === 429 || code === 529) return true;
|
|
716
|
+
if (code >= 500 && code < 600) return true;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (error instanceof Error) {
|
|
720
|
+
const msg = error.message.toLowerCase();
|
|
721
|
+
if (msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("econnrefused") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
if (msg.includes("rate limit") || msg.includes("rate_limit")) return true;
|
|
725
|
+
if (msg.includes("overloaded")) return true;
|
|
726
|
+
if (/\b(429|529)\b/.test(msg)) return true;
|
|
727
|
+
if (/\b5\d{2}\b/.test(error.message)) return true;
|
|
728
|
+
}
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
async function withAIRetry(fn, opts) {
|
|
732
|
+
const maxRetries = opts?.maxRetries ?? 2;
|
|
733
|
+
for (let attempt = 0; ; attempt++) {
|
|
734
|
+
try {
|
|
735
|
+
return await fn();
|
|
736
|
+
} catch (error) {
|
|
737
|
+
if (attempt < maxRetries && isRetryableError(error)) {
|
|
738
|
+
const delay = Math.pow(2, attempt) * 1e3;
|
|
739
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (isFatalAIError(error)) {
|
|
743
|
+
const info = classifyAIError(error);
|
|
744
|
+
const enhanced = new Error(info.userMessage);
|
|
745
|
+
enhanced.cause = error;
|
|
746
|
+
enhanced.name = "AIAPIError";
|
|
747
|
+
throw enhanced;
|
|
748
|
+
}
|
|
749
|
+
throw error;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
export {
|
|
755
|
+
computeTotalRealInput,
|
|
756
|
+
computeCacheRate,
|
|
757
|
+
AIMetricsCollector,
|
|
758
|
+
globalMetrics,
|
|
759
|
+
runWithMetricsCollector,
|
|
760
|
+
getActiveMetricsCollector,
|
|
761
|
+
getUserFriendlyAIErrorFromMessage,
|
|
762
|
+
DEFAULT_MODEL_ID,
|
|
763
|
+
DEFAULT_PROVIDER,
|
|
764
|
+
PROVIDER_DEFAULT_MODELS,
|
|
765
|
+
runWithModelContext,
|
|
766
|
+
initModel,
|
|
767
|
+
buildModelFactory,
|
|
768
|
+
initModelExplicit,
|
|
769
|
+
getModel,
|
|
770
|
+
getModelId,
|
|
771
|
+
getModelProvider,
|
|
772
|
+
getModelOverrides,
|
|
773
|
+
trackedGenerateObject,
|
|
774
|
+
trackedGenerateText,
|
|
775
|
+
isRetryableError,
|
|
776
|
+
withAIRetry
|
|
777
|
+
};
|
|
778
|
+
//# sourceMappingURL=chunk-UGPXCQY3.js.map
|