@blockrun/llm 3.11.0 → 3.13.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/dist/index.cjs CHANGED
@@ -88,7 +88,7 @@ __export(index_exports, {
88
88
  importSolanaWallet: () => importSolanaWallet,
89
89
  importWallet: () => importWallet,
90
90
  listDiscoveredSolanaWallets: () => listDiscoveredSolanaWallets,
91
- listDiscoveredWallets: () => listDiscoveredWallets,
91
+ listDiscoveredWallets: () => listDiscoveredWallets2,
92
92
  loadSolanaWallet: () => loadSolanaWallet,
93
93
  loadWallet: () => loadWallet,
94
94
  logCost: () => logCost,
@@ -97,7 +97,7 @@ __export(index_exports, {
97
97
  saveToCache: () => saveToCache,
98
98
  saveWallet: () => saveWallet,
99
99
  scanSolanaWallets: () => scanSolanaWallets,
100
- scanWallets: () => scanWallets,
100
+ scanWallets: () => scanWallets2,
101
101
  setCache: () => setCache,
102
102
  setupAgentSolanaWallet: () => setupAgentSolanaWallet,
103
103
  setupAgentWallet: () => setupAgentWallet,
@@ -145,6 +145,3362 @@ var APIError = class extends BlockrunError {
145
145
  }
146
146
  };
147
147
 
148
+ // node_modules/.pnpm/@blockrun+router-core@https+++codeload.github.com+BlockRunAI+router-core+tar.gz+d4308049348e1_gni7wlazdyzf7iqax5nhqdpoqi/node_modules/@blockrun/router-core/dist/index.js
149
+ function scoreTokenCount(estimatedTokens, thresholds) {
150
+ if (estimatedTokens < thresholds.simple) {
151
+ return { name: "tokenCount", score: -1, signal: `short (${estimatedTokens} tokens)` };
152
+ }
153
+ if (estimatedTokens > thresholds.complex) {
154
+ return { name: "tokenCount", score: 1, signal: `long (${estimatedTokens} tokens)` };
155
+ }
156
+ return { name: "tokenCount", score: 0, signal: null };
157
+ }
158
+ function scoreKeywordMatch(text, keywords, name, signalLabel, thresholds, scores) {
159
+ const matches = keywords.filter((kw) => text.includes(kw.toLowerCase()));
160
+ if (matches.length >= thresholds.high) {
161
+ return {
162
+ name,
163
+ score: scores.high,
164
+ signal: `${signalLabel} (${matches.slice(0, 3).join(", ")})`
165
+ };
166
+ }
167
+ if (matches.length >= thresholds.low) {
168
+ return {
169
+ name,
170
+ score: scores.low,
171
+ signal: `${signalLabel} (${matches.slice(0, 3).join(", ")})`
172
+ };
173
+ }
174
+ return { name, score: scores.none, signal: null };
175
+ }
176
+ function scoreMultiStep(text) {
177
+ const patterns = [/first.*then/i, /step \d/i, /\d\.\s/];
178
+ const hits = patterns.filter((p) => p.test(text));
179
+ if (hits.length > 0) {
180
+ return { name: "multiStepPatterns", score: 0.5, signal: "multi-step" };
181
+ }
182
+ return { name: "multiStepPatterns", score: 0, signal: null };
183
+ }
184
+ function scoreQuestionComplexity(prompt) {
185
+ const count = (prompt.match(/\?/g) || []).length;
186
+ if (count > 3) {
187
+ return { name: "questionComplexity", score: 0.5, signal: `${count} questions` };
188
+ }
189
+ return { name: "questionComplexity", score: 0, signal: null };
190
+ }
191
+ function scoreAgenticTask(text, keywords) {
192
+ let matchCount = 0;
193
+ const signals = [];
194
+ for (const keyword of keywords) {
195
+ if (text.includes(keyword.toLowerCase())) {
196
+ matchCount++;
197
+ if (signals.length < 3) {
198
+ signals.push(keyword);
199
+ }
200
+ }
201
+ }
202
+ if (matchCount >= 4) {
203
+ return {
204
+ dimensionScore: {
205
+ name: "agenticTask",
206
+ score: 1,
207
+ signal: `agentic (${signals.join(", ")})`
208
+ },
209
+ agenticScore: 1
210
+ };
211
+ } else if (matchCount >= 3) {
212
+ return {
213
+ dimensionScore: {
214
+ name: "agenticTask",
215
+ score: 0.6,
216
+ signal: `agentic (${signals.join(", ")})`
217
+ },
218
+ agenticScore: 0.6
219
+ };
220
+ } else if (matchCount >= 1) {
221
+ return {
222
+ dimensionScore: {
223
+ name: "agenticTask",
224
+ score: 0.2,
225
+ signal: `agentic-light (${signals.join(", ")})`
226
+ },
227
+ agenticScore: 0.2
228
+ };
229
+ }
230
+ return {
231
+ dimensionScore: { name: "agenticTask", score: 0, signal: null },
232
+ agenticScore: 0
233
+ };
234
+ }
235
+ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
236
+ const userText = prompt.toLowerCase();
237
+ const dimensions = [
238
+ // Token count uses total estimated tokens (system + user) — context size matters for model selection
239
+ scoreTokenCount(estimatedTokens, config.tokenCountThresholds),
240
+ scoreKeywordMatch(
241
+ userText,
242
+ config.codeKeywords,
243
+ "codePresence",
244
+ "code",
245
+ { low: 1, high: 2 },
246
+ { none: 0, low: 0.5, high: 1 }
247
+ ),
248
+ scoreKeywordMatch(
249
+ userText,
250
+ config.reasoningKeywords,
251
+ "reasoningMarkers",
252
+ "reasoning",
253
+ { low: 1, high: 2 },
254
+ { none: 0, low: 0.7, high: 1 }
255
+ ),
256
+ scoreKeywordMatch(
257
+ userText,
258
+ config.technicalKeywords,
259
+ "technicalTerms",
260
+ "technical",
261
+ { low: 2, high: 4 },
262
+ { none: 0, low: 0.5, high: 1 }
263
+ ),
264
+ scoreKeywordMatch(
265
+ userText,
266
+ config.creativeKeywords,
267
+ "creativeMarkers",
268
+ "creative",
269
+ { low: 1, high: 2 },
270
+ { none: 0, low: 0.5, high: 0.7 }
271
+ ),
272
+ scoreKeywordMatch(
273
+ userText,
274
+ config.simpleKeywords,
275
+ "simpleIndicators",
276
+ "simple",
277
+ { low: 1, high: 2 },
278
+ { none: 0, low: -1, high: -1 }
279
+ ),
280
+ scoreMultiStep(userText),
281
+ scoreQuestionComplexity(prompt),
282
+ // 6 new dimensions
283
+ scoreKeywordMatch(
284
+ userText,
285
+ config.imperativeVerbs,
286
+ "imperativeVerbs",
287
+ "imperative",
288
+ { low: 1, high: 2 },
289
+ { none: 0, low: 0.3, high: 0.5 }
290
+ ),
291
+ scoreKeywordMatch(
292
+ userText,
293
+ config.constraintIndicators,
294
+ "constraintCount",
295
+ "constraints",
296
+ { low: 1, high: 3 },
297
+ { none: 0, low: 0.3, high: 0.7 }
298
+ ),
299
+ scoreKeywordMatch(
300
+ userText,
301
+ config.outputFormatKeywords,
302
+ "outputFormat",
303
+ "format",
304
+ { low: 1, high: 2 },
305
+ { none: 0, low: 0.4, high: 0.7 }
306
+ ),
307
+ scoreKeywordMatch(
308
+ userText,
309
+ config.referenceKeywords,
310
+ "referenceComplexity",
311
+ "references",
312
+ { low: 1, high: 2 },
313
+ { none: 0, low: 0.3, high: 0.5 }
314
+ ),
315
+ scoreKeywordMatch(
316
+ userText,
317
+ config.negationKeywords,
318
+ "negationComplexity",
319
+ "negation",
320
+ { low: 2, high: 3 },
321
+ { none: 0, low: 0.3, high: 0.5 }
322
+ ),
323
+ scoreKeywordMatch(
324
+ userText,
325
+ config.domainSpecificKeywords,
326
+ "domainSpecificity",
327
+ "domain-specific",
328
+ { low: 1, high: 2 },
329
+ { none: 0, low: 0.5, high: 0.8 }
330
+ )
331
+ ];
332
+ const agenticResult = scoreAgenticTask(userText, config.agenticTaskKeywords);
333
+ dimensions.push(agenticResult.dimensionScore);
334
+ const agenticScore = agenticResult.agenticScore;
335
+ const signals = dimensions.filter((d) => d.signal !== null).map((d) => d.signal);
336
+ const weights = config.dimensionWeights;
337
+ let weightedScore = 0;
338
+ for (const d of dimensions) {
339
+ const w = weights[d.name] ?? 0;
340
+ weightedScore += d.score * w;
341
+ }
342
+ const reasoningMatches = config.reasoningKeywords.filter(
343
+ (kw) => userText.includes(kw.toLowerCase())
344
+ );
345
+ if (reasoningMatches.length >= 2) {
346
+ const confidence2 = calibrateConfidence(
347
+ Math.max(weightedScore, 0.3),
348
+ // ensure positive for confidence calc
349
+ config.confidenceSteepness
350
+ );
351
+ return {
352
+ score: weightedScore,
353
+ tier: "REASONING",
354
+ confidence: Math.max(confidence2, 0.85),
355
+ signals,
356
+ agenticScore,
357
+ dimensions
358
+ };
359
+ }
360
+ const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;
361
+ let tier;
362
+ let distanceFromBoundary;
363
+ if (weightedScore < simpleMedium) {
364
+ tier = "SIMPLE";
365
+ distanceFromBoundary = simpleMedium - weightedScore;
366
+ } else if (weightedScore < mediumComplex) {
367
+ tier = "MEDIUM";
368
+ distanceFromBoundary = Math.min(weightedScore - simpleMedium, mediumComplex - weightedScore);
369
+ } else if (weightedScore < complexReasoning) {
370
+ tier = "COMPLEX";
371
+ distanceFromBoundary = Math.min(
372
+ weightedScore - mediumComplex,
373
+ complexReasoning - weightedScore
374
+ );
375
+ } else {
376
+ tier = "REASONING";
377
+ distanceFromBoundary = weightedScore - complexReasoning;
378
+ }
379
+ const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);
380
+ if (confidence < config.confidenceThreshold) {
381
+ return { score: weightedScore, tier: null, confidence, signals, agenticScore, dimensions };
382
+ }
383
+ return { score: weightedScore, tier, confidence, signals, agenticScore, dimensions };
384
+ }
385
+ function calibrateConfidence(distance, steepness) {
386
+ return 1 / (1 + Math.exp(-steepness * distance));
387
+ }
388
+ var BASELINE_MODEL_ID = "anthropic/claude-opus-4.7";
389
+ var BASELINE_INPUT_PRICE = 5;
390
+ var BASELINE_OUTPUT_PRICE = 25;
391
+ function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile, agenticScore) {
392
+ const tierConfig = tierConfigs[tier];
393
+ const model = tierConfig.primary;
394
+ const pricing = modelPricing.get(model);
395
+ let costEstimate;
396
+ if (pricing?.flatPrice !== void 0) {
397
+ costEstimate = pricing.flatPrice;
398
+ } else {
399
+ const inputPrice = pricing?.inputPrice ?? 0;
400
+ const outputPrice = pricing?.outputPrice ?? 0;
401
+ costEstimate = estimatedInputTokens / 1e6 * inputPrice + maxOutputTokens / 1e6 * outputPrice;
402
+ }
403
+ const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
404
+ const opusInputPrice = opusPricing?.inputPrice ?? BASELINE_INPUT_PRICE;
405
+ const opusOutputPrice = opusPricing?.outputPrice ?? BASELINE_OUTPUT_PRICE;
406
+ const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
407
+ const baselineOutput = maxOutputTokens / 1e6 * opusOutputPrice;
408
+ const baselineCost = baselineInput + baselineOutput;
409
+ const savings = routingProfile === "premium" ? 0 : baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;
410
+ return {
411
+ model,
412
+ tier,
413
+ confidence,
414
+ method,
415
+ reasoning,
416
+ costEstimate,
417
+ baselineCost,
418
+ savings,
419
+ ...agenticScore !== void 0 && { agenticScore }
420
+ };
421
+ }
422
+ function getFallbackChain(tier, tierConfigs) {
423
+ const config = tierConfigs[tier];
424
+ return [config.primary, ...config.fallback];
425
+ }
426
+ var SERVER_MARGIN_PERCENT = 5;
427
+ var MIN_PAYMENT_USD = 1e-3;
428
+ function calculateModelCost(model, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile) {
429
+ const pricing = modelPricing.get(model);
430
+ let costEstimate;
431
+ if (pricing?.flatPrice !== void 0) {
432
+ costEstimate = Math.max(pricing.flatPrice * (1 + SERVER_MARGIN_PERCENT / 100), MIN_PAYMENT_USD);
433
+ } else {
434
+ const inputPrice = pricing?.inputPrice ?? 0;
435
+ const outputPrice = pricing?.outputPrice ?? 0;
436
+ const inputCost = estimatedInputTokens / 1e6 * inputPrice;
437
+ const outputCost = maxOutputTokens / 1e6 * outputPrice;
438
+ costEstimate = Math.max(
439
+ (inputCost + outputCost) * (1 + SERVER_MARGIN_PERCENT / 100),
440
+ MIN_PAYMENT_USD
441
+ );
442
+ }
443
+ const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
444
+ const opusInputPrice = opusPricing?.inputPrice ?? BASELINE_INPUT_PRICE;
445
+ const opusOutputPrice = opusPricing?.outputPrice ?? BASELINE_OUTPUT_PRICE;
446
+ const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
447
+ const baselineOutput = maxOutputTokens / 1e6 * opusOutputPrice;
448
+ const baselineCost = baselineInput + baselineOutput;
449
+ const savings = routingProfile === "premium" ? 0 : baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;
450
+ return { costEstimate, baselineCost, savings };
451
+ }
452
+ function filterCandidatesByCapacity(models, estimatedInputTokens, requestedOutputTokens, getCapabilities) {
453
+ const filtered = models.filter((modelId) => {
454
+ const capabilities = getCapabilities(modelId);
455
+ if (!capabilities) return true;
456
+ return capabilities.contextWindow >= (estimatedInputTokens + requestedOutputTokens) * 1.1 && capabilities.maxOutput >= requestedOutputTokens;
457
+ });
458
+ return filtered;
459
+ }
460
+ function applyPromotions(tierConfigs, promotions, profile, now = /* @__PURE__ */ new Date()) {
461
+ if (!promotions || promotions.length === 0) return tierConfigs;
462
+ let result = tierConfigs;
463
+ for (const promo of promotions) {
464
+ const start = new Date(promo.startDate);
465
+ const end = new Date(promo.endDate);
466
+ if (now < start || now >= end) continue;
467
+ if (promo.profiles && !promo.profiles.includes(profile)) continue;
468
+ if (result === tierConfigs) {
469
+ result = { ...tierConfigs };
470
+ for (const t of Object.keys(result)) {
471
+ result[t] = { ...result[t] };
472
+ }
473
+ }
474
+ for (const [tier, override] of Object.entries(promo.tierOverrides)) {
475
+ if (!result[tier]) continue;
476
+ if (override.primary) result[tier].primary = override.primary;
477
+ if (override.fallback) result[tier].fallback = override.fallback;
478
+ }
479
+ }
480
+ return result;
481
+ }
482
+ var RulesStrategy = class {
483
+ name = "rules";
484
+ route(prompt, systemPrompt, maxOutputTokens, options) {
485
+ const { config, modelPricing } = options;
486
+ const fullText = `${systemPrompt ?? ""} ${prompt}`;
487
+ const estimatedTokens = Math.ceil(fullText.length / 4);
488
+ const scanLimit = Math.max(1, Math.min(8e3, config.classifier.promptTruncationChars));
489
+ const sample = (value) => {
490
+ if (value.length <= scanLimit) return value;
491
+ const prefixLength = Math.ceil(scanLimit / 2);
492
+ return `${value.slice(0, prefixLength)}
493
+ ${value.slice(-(scanLimit - prefixLength))}`;
494
+ };
495
+ const scannedPrompt = sample(prompt);
496
+ const scannedSystemPrompt = systemPrompt ? sample(systemPrompt) : void 0;
497
+ const ruleResult = classifyByRules(
498
+ scannedPrompt,
499
+ scannedSystemPrompt,
500
+ estimatedTokens,
501
+ config.scoring
502
+ );
503
+ const { routingProfile } = options;
504
+ let tierConfigs;
505
+ let profileSuffix;
506
+ let profile;
507
+ if (routingProfile === "eco") {
508
+ tierConfigs = config.ecoTiers ?? config.tiers;
509
+ profileSuffix = config.ecoTiers ? " | eco" : " | eco (default tiers)";
510
+ profile = "eco";
511
+ } else if (routingProfile === "premium") {
512
+ tierConfigs = config.premiumTiers ?? config.tiers;
513
+ profileSuffix = config.premiumTiers ? " | premium" : " | premium (default tiers)";
514
+ profile = "premium";
515
+ } else {
516
+ const agenticScore = ruleResult.agenticScore ?? 0;
517
+ const isAutoAgentic = agenticScore >= 0.5;
518
+ const agenticModeSetting = config.overrides.agenticMode;
519
+ const hasToolsInRequest = options.requiresTools ?? options.hasTools ?? false;
520
+ let useAgenticTiers;
521
+ if (agenticModeSetting === false) {
522
+ useAgenticTiers = false;
523
+ } else if (agenticModeSetting === true) {
524
+ useAgenticTiers = config.agenticTiers != null;
525
+ } else {
526
+ useAgenticTiers = (hasToolsInRequest || isAutoAgentic) && config.agenticTiers != null;
527
+ }
528
+ tierConfigs = useAgenticTiers ? config.agenticTiers : config.tiers;
529
+ profileSuffix = useAgenticTiers ? ` | agentic${hasToolsInRequest ? " (tools)" : ""}` : "";
530
+ profile = useAgenticTiers ? "agentic" : "auto";
531
+ }
532
+ tierConfigs = applyPromotions(tierConfigs, config.promotions, profile, options.now);
533
+ const agenticScoreValue = ruleResult.agenticScore;
534
+ if (estimatedTokens > config.overrides.maxTokensForceComplex) {
535
+ const decision2 = selectModel(
536
+ "COMPLEX",
537
+ 0.95,
538
+ "rules",
539
+ `Input exceeds ${config.overrides.maxTokensForceComplex} tokens${profileSuffix}`,
540
+ tierConfigs,
541
+ modelPricing,
542
+ estimatedTokens,
543
+ maxOutputTokens,
544
+ routingProfile,
545
+ agenticScoreValue
546
+ );
547
+ return { ...decision2, tierConfigs, profile };
548
+ }
549
+ const hasStructuredOutput = options.requiresStructuredOutput === true || (scannedSystemPrompt ? /json|structured|schema/i.test(scannedSystemPrompt) : false);
550
+ let tier;
551
+ let confidence;
552
+ const method = "rules";
553
+ let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(", ")}`;
554
+ if (ruleResult.tier !== null) {
555
+ tier = ruleResult.tier;
556
+ confidence = ruleResult.confidence;
557
+ } else {
558
+ tier = config.overrides.ambiguousDefaultTier;
559
+ confidence = 0.5;
560
+ reasoning += ` | ambiguous -> default: ${tier}`;
561
+ }
562
+ if (hasStructuredOutput) {
563
+ const tierRank = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };
564
+ const minTier = config.overrides.structuredOutputMinTier;
565
+ if (tierRank[tier] < tierRank[minTier]) {
566
+ reasoning += ` | upgraded to ${minTier} (structured output)`;
567
+ tier = minTier;
568
+ }
569
+ }
570
+ reasoning += profileSuffix;
571
+ const decision = selectModel(
572
+ tier,
573
+ confidence,
574
+ method,
575
+ reasoning,
576
+ tierConfigs,
577
+ modelPricing,
578
+ estimatedTokens,
579
+ maxOutputTokens,
580
+ routingProfile,
581
+ agenticScoreValue
582
+ );
583
+ return { ...decision, tierConfigs, profile };
584
+ }
585
+ };
586
+ var registry = /* @__PURE__ */ new Map();
587
+ registry.set("rules", new RulesStrategy());
588
+ function getStrategy(name) {
589
+ const strategy = registry.get(name);
590
+ if (!strategy) {
591
+ throw new Error(`Unknown routing strategy: ${name}`);
592
+ }
593
+ return strategy;
594
+ }
595
+ function registerStrategy(strategy) {
596
+ registry.set(strategy.name, strategy);
597
+ }
598
+ var DEFAULT_MODEL_CAPABILITIES = Object.freeze({
599
+ "anthropic/claude-fable-5": {
600
+ contextWindow: 1e6,
601
+ maxOutputTokens: 128e3,
602
+ supportsTools: true,
603
+ supportsVision: true
604
+ },
605
+ "anthropic/claude-haiku-4.5": {
606
+ contextWindow: 2e5,
607
+ maxOutputTokens: 8192,
608
+ supportsTools: true,
609
+ supportsVision: true
610
+ },
611
+ "anthropic/claude-opus-4.6": {
612
+ contextWindow: 1e6,
613
+ maxOutputTokens: 128e3,
614
+ supportsTools: true,
615
+ supportsVision: true
616
+ },
617
+ "anthropic/claude-opus-4.7": {
618
+ contextWindow: 1e6,
619
+ maxOutputTokens: 128e3,
620
+ supportsTools: true,
621
+ supportsVision: true
622
+ },
623
+ "anthropic/claude-opus-4.8": {
624
+ contextWindow: 1e6,
625
+ maxOutputTokens: 128e3,
626
+ supportsTools: true,
627
+ supportsVision: true
628
+ },
629
+ "anthropic/claude-opus-5": {
630
+ contextWindow: 1e6,
631
+ maxOutputTokens: 128e3,
632
+ supportsTools: true,
633
+ supportsVision: true
634
+ },
635
+ "anthropic/claude-sonnet-4.6": {
636
+ contextWindow: 2e5,
637
+ maxOutputTokens: 64e3,
638
+ supportsTools: true,
639
+ supportsVision: true
640
+ },
641
+ "anthropic/claude-sonnet-5": {
642
+ contextWindow: 1e6,
643
+ maxOutputTokens: 128e3,
644
+ supportsTools: true,
645
+ supportsVision: true
646
+ },
647
+ "deepseek/deepseek-chat": {
648
+ contextWindow: 1e6,
649
+ maxOutputTokens: 8192,
650
+ supportsTools: true,
651
+ supportsVision: false
652
+ },
653
+ "deepseek/deepseek-reasoner": {
654
+ contextWindow: 1e6,
655
+ maxOutputTokens: 8192,
656
+ supportsTools: true,
657
+ supportsVision: false
658
+ },
659
+ "deepseek/deepseek-v4-pro": {
660
+ contextWindow: 1048576,
661
+ maxOutputTokens: 65536,
662
+ supportsTools: true,
663
+ supportsVision: false
664
+ },
665
+ "free/deepseek-v4-flash": {
666
+ contextWindow: 1e6,
667
+ maxOutputTokens: 16384,
668
+ supportsTools: false,
669
+ supportsVision: false
670
+ },
671
+ "free/gpt-oss-120b": {
672
+ contextWindow: 128e3,
673
+ maxOutputTokens: 16384,
674
+ supportsTools: false,
675
+ supportsVision: false
676
+ },
677
+ "free/gpt-oss-20b": {
678
+ contextWindow: 128e3,
679
+ maxOutputTokens: 16384,
680
+ supportsTools: false,
681
+ supportsVision: false
682
+ },
683
+ "free/seed-oss-36b": {
684
+ contextWindow: 131072,
685
+ maxOutputTokens: 16384,
686
+ supportsTools: false,
687
+ supportsVision: false
688
+ },
689
+ "google/gemini-2.5-flash": {
690
+ contextWindow: 1e6,
691
+ maxOutputTokens: 65536,
692
+ supportsTools: true,
693
+ supportsVision: true
694
+ },
695
+ "google/gemini-2.5-flash-lite": {
696
+ contextWindow: 1e6,
697
+ maxOutputTokens: 65536,
698
+ supportsTools: true,
699
+ supportsVision: false
700
+ },
701
+ "google/gemini-2.5-pro": {
702
+ contextWindow: 105e4,
703
+ maxOutputTokens: 65536,
704
+ supportsTools: true,
705
+ supportsVision: true
706
+ },
707
+ "google/gemini-3-flash-preview": {
708
+ contextWindow: 1e6,
709
+ maxOutputTokens: 65536,
710
+ supportsTools: false,
711
+ supportsVision: true
712
+ },
713
+ "google/gemini-3.1-flash-lite": {
714
+ contextWindow: 1e6,
715
+ maxOutputTokens: 8192,
716
+ supportsTools: true,
717
+ supportsVision: false
718
+ },
719
+ "google/gemini-3.1-pro": {
720
+ contextWindow: 105e4,
721
+ maxOutputTokens: 65536,
722
+ supportsTools: true,
723
+ supportsVision: true
724
+ },
725
+ "google/gemini-3.5-flash": {
726
+ contextWindow: 1048576,
727
+ maxOutputTokens: 65536,
728
+ supportsTools: true,
729
+ supportsVision: true
730
+ },
731
+ "moonshot/kimi-k2.5": {
732
+ contextWindow: 262144,
733
+ maxOutputTokens: 16384,
734
+ supportsTools: true,
735
+ supportsVision: true
736
+ },
737
+ "moonshot/kimi-k2.6": {
738
+ contextWindow: 262144,
739
+ maxOutputTokens: 65536,
740
+ supportsTools: true,
741
+ supportsVision: true
742
+ },
743
+ "moonshot/kimi-k2.7": {
744
+ contextWindow: 262144,
745
+ maxOutputTokens: 65536,
746
+ supportsTools: true,
747
+ supportsVision: true
748
+ },
749
+ "moonshot/kimi-k3": {
750
+ contextWindow: 1048576,
751
+ maxOutputTokens: 65536,
752
+ supportsTools: true,
753
+ supportsVision: true
754
+ },
755
+ "openai/gpt-4.1": {
756
+ contextWindow: 128e3,
757
+ maxOutputTokens: 16384,
758
+ supportsTools: true,
759
+ supportsVision: true
760
+ },
761
+ "openai/gpt-4o-mini": {
762
+ contextWindow: 128e3,
763
+ maxOutputTokens: 16384,
764
+ supportsTools: true,
765
+ supportsVision: false
766
+ },
767
+ "openai/gpt-5-mini": {
768
+ contextWindow: 2e5,
769
+ maxOutputTokens: 65536,
770
+ supportsTools: true,
771
+ supportsVision: false
772
+ },
773
+ "openai/gpt-5.3-codex": {
774
+ contextWindow: 4e5,
775
+ maxOutputTokens: 128e3,
776
+ supportsTools: true,
777
+ supportsVision: false
778
+ },
779
+ "openai/gpt-5.4": {
780
+ contextWindow: 4e5,
781
+ maxOutputTokens: 128e3,
782
+ supportsTools: true,
783
+ supportsVision: true
784
+ },
785
+ "openai/gpt-5.4-nano": {
786
+ contextWindow: 105e4,
787
+ maxOutputTokens: 32768,
788
+ supportsTools: true,
789
+ supportsVision: false
790
+ },
791
+ "openai/gpt-5.5": {
792
+ contextWindow: 105e4,
793
+ maxOutputTokens: 128e3,
794
+ supportsTools: true,
795
+ supportsVision: true
796
+ },
797
+ "openai/gpt-5.6-terra": {
798
+ contextWindow: 105e4,
799
+ maxOutputTokens: 128e3,
800
+ supportsTools: true,
801
+ supportsVision: true
802
+ },
803
+ "openai/o3": {
804
+ contextWindow: 2e5,
805
+ maxOutputTokens: 1e5,
806
+ supportsTools: true,
807
+ supportsVision: false
808
+ },
809
+ "openai/o4-mini": {
810
+ contextWindow: 128e3,
811
+ maxOutputTokens: 65536,
812
+ supportsTools: true,
813
+ supportsVision: false
814
+ },
815
+ "qwen/qwen3.7-max": {
816
+ contextWindow: 1e6,
817
+ maxOutputTokens: 65536,
818
+ supportsTools: true,
819
+ supportsVision: false
820
+ },
821
+ "xai/grok-3-mini": {
822
+ contextWindow: 131072,
823
+ maxOutputTokens: 16384,
824
+ supportsTools: true,
825
+ supportsVision: false
826
+ },
827
+ "xai/grok-4-0709": {
828
+ contextWindow: 131072,
829
+ maxOutputTokens: 16384,
830
+ supportsTools: true,
831
+ supportsVision: false
832
+ },
833
+ "xai/grok-4-1-fast-non-reasoning": {
834
+ contextWindow: 131072,
835
+ maxOutputTokens: 16384,
836
+ supportsTools: true,
837
+ supportsVision: false
838
+ },
839
+ "xai/grok-4-1-fast-reasoning": {
840
+ contextWindow: 131072,
841
+ maxOutputTokens: 16384,
842
+ supportsTools: true,
843
+ supportsVision: false
844
+ },
845
+ "xai/grok-4-fast-non-reasoning": {
846
+ contextWindow: 131072,
847
+ maxOutputTokens: 16384,
848
+ supportsTools: true,
849
+ supportsVision: false
850
+ },
851
+ "xai/grok-4-fast-reasoning": {
852
+ contextWindow: 131072,
853
+ maxOutputTokens: 16384,
854
+ supportsTools: true,
855
+ supportsVision: false
856
+ },
857
+ "xai/grok-4.5": {
858
+ contextWindow: 5e5,
859
+ maxOutputTokens: 16384,
860
+ supportsTools: true,
861
+ supportsVision: true
862
+ },
863
+ "zai/glm-5.1": {
864
+ contextWindow: 2e5,
865
+ maxOutputTokens: 128e3,
866
+ supportsTools: true,
867
+ supportsVision: false
868
+ },
869
+ "zai/glm-5.2": {
870
+ contextWindow: 1e6,
871
+ maxOutputTokens: 262144,
872
+ supportsTools: true,
873
+ supportsVision: false
874
+ }
875
+ });
876
+ var model_profiles_generated_default = {
877
+ "openai/gpt-5.5": {
878
+ measuredAt: "2026-07-21T10:21:31Z",
879
+ latencyMs: 6243.1,
880
+ p95LatencyMs: 9865,
881
+ outputTokensPerSecond: 12.53,
882
+ errorRate: 0,
883
+ samples: 3
884
+ },
885
+ "openai/gpt-5.4-pro": {
886
+ measuredAt: "2026-07-21T10:21:31Z",
887
+ latencyMs: 13015.5,
888
+ p95LatencyMs: 23976.4,
889
+ outputTokensPerSecond: 6.42,
890
+ errorRate: 0,
891
+ samples: 3
892
+ },
893
+ "openai/gpt-5.4-mini": {
894
+ measuredAt: "2026-07-21T10:21:31Z",
895
+ latencyMs: 5550,
896
+ p95LatencyMs: 6595.7,
897
+ outputTokensPerSecond: 11.96,
898
+ errorRate: 0.3333,
899
+ samples: 3
900
+ },
901
+ "openai/gpt-5.3-codex": {
902
+ measuredAt: "2026-07-21T10:21:31Z",
903
+ latencyMs: 4617.1,
904
+ p95LatencyMs: 5800.7,
905
+ outputTokensPerSecond: 12.48,
906
+ errorRate: 0,
907
+ samples: 3
908
+ },
909
+ "anthropic/claude-opus-4.8": {
910
+ measuredAt: "2026-07-21T10:21:31Z",
911
+ latencyMs: 3915.1,
912
+ p95LatencyMs: 6130.8,
913
+ outputTokensPerSecond: 16.33,
914
+ errorRate: 0,
915
+ samples: 3
916
+ },
917
+ "anthropic/claude-opus-4.6": {
918
+ measuredAt: "2026-07-21T10:21:31Z",
919
+ latencyMs: 3765.5,
920
+ p95LatencyMs: 4257.2,
921
+ outputTokensPerSecond: 14.18,
922
+ errorRate: 0,
923
+ samples: 3
924
+ },
925
+ "anthropic/claude-sonnet-4.6": {
926
+ measuredAt: "2026-07-21T10:21:31Z",
927
+ latencyMs: 3860.6,
928
+ p95LatencyMs: 5093.5,
929
+ outputTokensPerSecond: 13.85,
930
+ errorRate: 0,
931
+ samples: 3
932
+ },
933
+ "anthropic/claude-haiku-4.5": {
934
+ measuredAt: "2026-07-21T10:21:31Z",
935
+ latencyMs: 2734.9,
936
+ p95LatencyMs: 3181.6,
937
+ outputTokensPerSecond: 19.58,
938
+ errorRate: 0,
939
+ samples: 3
940
+ },
941
+ "google/gemini-3.1-pro": {
942
+ measuredAt: "2026-07-21T10:21:31Z",
943
+ latencyMs: 13935.7,
944
+ p95LatencyMs: 26675.3,
945
+ outputTokensPerSecond: 77.47,
946
+ errorRate: 0,
947
+ samples: 3
948
+ },
949
+ "google/gemini-3.5-flash": {
950
+ measuredAt: "2026-07-21T10:21:31Z",
951
+ latencyMs: 4608.7,
952
+ p95LatencyMs: 8420.9,
953
+ outputTokensPerSecond: 57.88,
954
+ errorRate: 0,
955
+ samples: 3
956
+ },
957
+ "google/gemini-3.1-flash-lite": {
958
+ measuredAt: "2026-07-21T10:21:31Z",
959
+ latencyMs: 4619.7,
960
+ p95LatencyMs: 9927.1,
961
+ outputTokensPerSecond: 42.01,
962
+ errorRate: 0,
963
+ samples: 3
964
+ },
965
+ "google/gemini-2.5-flash": {
966
+ measuredAt: "2026-07-21T10:21:31Z",
967
+ latencyMs: 5506.9,
968
+ p95LatencyMs: 11462.5,
969
+ outputTokensPerSecond: 65.19,
970
+ errorRate: 0,
971
+ samples: 3
972
+ },
973
+ "deepseek/deepseek-v4-pro": {
974
+ measuredAt: "2026-07-21T10:21:31Z",
975
+ latencyMs: 6044.8,
976
+ p95LatencyMs: 10782.3,
977
+ outputTokensPerSecond: 22.47,
978
+ errorRate: 0,
979
+ samples: 3
980
+ },
981
+ "deepseek/deepseek-reasoner": {
982
+ measuredAt: "2026-07-21T10:21:31Z",
983
+ latencyMs: 4111.9,
984
+ p95LatencyMs: 5305.7,
985
+ outputTokensPerSecond: 16.46,
986
+ errorRate: 0,
987
+ samples: 3
988
+ },
989
+ "deepseek/deepseek-chat": {
990
+ measuredAt: "2026-07-21T10:21:31Z",
991
+ latencyMs: 2648.6,
992
+ p95LatencyMs: 3524.1,
993
+ outputTokensPerSecond: 16.73,
994
+ errorRate: 0,
995
+ samples: 3
996
+ },
997
+ "moonshot/kimi-k2.7": {
998
+ measuredAt: "2026-07-21T10:21:31Z",
999
+ latencyMs: 4295.4,
1000
+ p95LatencyMs: 6153.8,
1001
+ outputTokensPerSecond: 18.54,
1002
+ errorRate: 0,
1003
+ samples: 3
1004
+ },
1005
+ "qwen/qwen3.7-max": {
1006
+ measuredAt: "2026-07-21T10:21:31Z",
1007
+ latencyMs: 30729.4,
1008
+ p95LatencyMs: 39622,
1009
+ outputTokensPerSecond: 36.89,
1010
+ errorRate: 0.3333,
1011
+ samples: 3
1012
+ },
1013
+ "xai/grok-4.3": {
1014
+ measuredAt: "2026-07-21T10:21:31Z",
1015
+ latencyMs: 6946.1,
1016
+ p95LatencyMs: 9495.4,
1017
+ outputTokensPerSecond: 65.3,
1018
+ errorRate: 0,
1019
+ samples: 3
1020
+ },
1021
+ "xai/grok-4.20-reasoning": {
1022
+ measuredAt: "2026-07-21T10:21:31Z",
1023
+ latencyMs: 3472.4,
1024
+ p95LatencyMs: 5332.4,
1025
+ outputTokensPerSecond: 13.27,
1026
+ errorRate: 0,
1027
+ samples: 3
1028
+ },
1029
+ "xai/grok-4.20-non-reasoning": {
1030
+ measuredAt: "2026-07-21T10:21:31Z",
1031
+ latencyMs: 5174.4,
1032
+ p95LatencyMs: 6081.7,
1033
+ outputTokensPerSecond: 10.21,
1034
+ errorRate: 0.3333,
1035
+ samples: 3
1036
+ },
1037
+ "xai/grok-4-1-fast-reasoning": {
1038
+ measuredAt: "2026-07-21T10:21:31Z",
1039
+ latencyMs: 13148.2,
1040
+ p95LatencyMs: 19104.2,
1041
+ outputTokensPerSecond: 4.28,
1042
+ errorRate: 0,
1043
+ samples: 3
1044
+ },
1045
+ "minimax/minimax-m3": {
1046
+ measuredAt: "2026-07-21T10:21:31Z",
1047
+ latencyMs: 3385,
1048
+ p95LatencyMs: 4247.2,
1049
+ outputTokensPerSecond: 15.16,
1050
+ errorRate: 0,
1051
+ samples: 3
1052
+ },
1053
+ "minimax/minimax-m2.7": {
1054
+ measuredAt: "2026-07-21T10:21:31Z",
1055
+ latencyMs: 4596.7,
1056
+ p95LatencyMs: 6884.6,
1057
+ outputTokensPerSecond: 17.03,
1058
+ errorRate: 0,
1059
+ samples: 3
1060
+ },
1061
+ "zai/glm-5.2": {
1062
+ measuredAt: "2026-07-21T10:21:31Z",
1063
+ latencyMs: 4406.3,
1064
+ p95LatencyMs: 6139.7,
1065
+ outputTokensPerSecond: 10.41,
1066
+ errorRate: 0,
1067
+ samples: 3
1068
+ },
1069
+ "zai/glm-5.1": {
1070
+ measuredAt: "2026-07-21T10:21:31Z",
1071
+ latencyMs: 7775.4,
1072
+ p95LatencyMs: 9182.1,
1073
+ outputTokensPerSecond: 6.08,
1074
+ errorRate: 0,
1075
+ samples: 3
1076
+ },
1077
+ "zai/glm-5": {
1078
+ measuredAt: "2026-07-21T10:21:31Z",
1079
+ latencyMs: 4159.4,
1080
+ p95LatencyMs: 4992.7,
1081
+ outputTokensPerSecond: 10.28,
1082
+ errorRate: 0,
1083
+ samples: 3
1084
+ },
1085
+ "free/qwen3-coder-480b": {
1086
+ measuredAt: "2026-07-21T10:21:31Z",
1087
+ latencyMs: 2063.9,
1088
+ p95LatencyMs: 3646.3,
1089
+ outputTokensPerSecond: 39.8,
1090
+ errorRate: 0,
1091
+ samples: 3
1092
+ },
1093
+ "free/mistral-large-3-675b": {
1094
+ measuredAt: "2026-07-21T10:21:31Z",
1095
+ latencyMs: 3147.5,
1096
+ p95LatencyMs: 5555.3,
1097
+ outputTokensPerSecond: 27.76,
1098
+ errorRate: 0,
1099
+ samples: 3
1100
+ },
1101
+ "free/nemotron-3-nano-omni-30b-a3b-reasoning": {
1102
+ measuredAt: "2026-07-21T10:21:31Z",
1103
+ latencyMs: 6508.4,
1104
+ p95LatencyMs: 14252.7,
1105
+ outputTokensPerSecond: 68.26,
1106
+ errorRate: 0,
1107
+ samples: 3
1108
+ },
1109
+ "free/glm-4.7": {
1110
+ measuredAt: "2026-07-21T10:21:31Z",
1111
+ latencyMs: 2014.8,
1112
+ p95LatencyMs: 3039.9,
1113
+ outputTokensPerSecond: 39.92,
1114
+ errorRate: 0,
1115
+ samples: 3
1116
+ }
1117
+ };
1118
+ var LIVE_MODEL_PROFILES = Object.freeze(
1119
+ model_profiles_generated_default
1120
+ );
1121
+ var HISTORICAL_MODEL_PROFILES = Object.freeze({
1122
+ "anthropic/claude-haiku-4.5": {
1123
+ measuredAt: "2026-03-16T13:50:48Z",
1124
+ latencyMs: 2305,
1125
+ outputTokensPerSecond: 140.6
1126
+ },
1127
+ "anthropic/claude-opus-4.6": {
1128
+ measuredAt: "2026-03-16T13:50:48Z",
1129
+ latencyMs: 2139,
1130
+ outputTokensPerSecond: 119.7
1131
+ },
1132
+ "anthropic/claude-sonnet-4.6": {
1133
+ measuredAt: "2026-03-16T13:50:48Z",
1134
+ latencyMs: 2110,
1135
+ outputTokensPerSecond: 121.3
1136
+ },
1137
+ "deepseek/deepseek-chat": {
1138
+ measuredAt: "2026-03-16T13:50:48Z",
1139
+ latencyMs: 1431,
1140
+ outputTokensPerSecond: 179.2,
1141
+ intelligenceIndex: 32
1142
+ },
1143
+ "google/gemini-2.5-flash": {
1144
+ measuredAt: "2026-03-16T13:50:48Z",
1145
+ latencyMs: 1238,
1146
+ outputTokensPerSecond: 207.6,
1147
+ intelligenceIndex: 20
1148
+ },
1149
+ "google/gemini-2.5-flash-lite": {
1150
+ measuredAt: "2026-03-16T13:50:48Z",
1151
+ latencyMs: 1353,
1152
+ outputTokensPerSecond: 192.5,
1153
+ intelligenceIndex: 20
1154
+ },
1155
+ "google/gemini-2.5-pro": {
1156
+ measuredAt: "2026-03-16T13:50:48Z",
1157
+ latencyMs: 1294,
1158
+ outputTokensPerSecond: 197.8
1159
+ },
1160
+ "google/gemini-3.1-pro": {
1161
+ measuredAt: "2026-03-16T13:50:48Z",
1162
+ latencyMs: 1609,
1163
+ outputTokensPerSecond: 167.2
1164
+ },
1165
+ "moonshot/kimi-k2.5": {
1166
+ measuredAt: "2026-03-16T13:50:48Z",
1167
+ latencyMs: 1646,
1168
+ outputTokensPerSecond: 155.7
1169
+ },
1170
+ "openai/gpt-4o-mini": {
1171
+ measuredAt: "2026-03-16T13:50:48Z",
1172
+ latencyMs: 2764,
1173
+ outputTokensPerSecond: 92.8
1174
+ },
1175
+ "openai/gpt-5.3-codex": {
1176
+ measuredAt: "2026-03-16T13:50:48Z",
1177
+ latencyMs: 7935,
1178
+ outputTokensPerSecond: 32.3
1179
+ },
1180
+ "xai/grok-4-1-fast-non-reasoning": {
1181
+ measuredAt: "2026-03-16T13:50:48Z",
1182
+ latencyMs: 1244,
1183
+ outputTokensPerSecond: 205.8,
1184
+ intelligenceIndex: 41
1185
+ },
1186
+ "xai/grok-4-1-fast-reasoning": {
1187
+ measuredAt: "2026-03-16T13:50:48Z",
1188
+ latencyMs: 1454,
1189
+ outputTokensPerSecond: 176.2,
1190
+ intelligenceIndex: 41
1191
+ }
1192
+ });
1193
+ function inferToolRequirement(prompt, _systemPrompt, toolChoice) {
1194
+ if (toolChoice === "none") return false;
1195
+ if (toolChoice === "required") return true;
1196
+ if (typeof toolChoice === "object" && toolChoice !== null && toolChoice.type === "function") {
1197
+ return true;
1198
+ }
1199
+ const text = prompt;
1200
+ const explicitTool = /\b(?:use|call|invoke)\s+(?:the\s+)?[\w.-]+\s+(?:tool|function|api)\b|\btool[_ -]?call\b|使用.{0,20}(?:工具|函数|接口)|调用.{0,20}(?:工具|函数|接口)/i;
1201
+ const codeEnvironment = /\b(?:run|execute)\s+(?:the\s+)?(?:tests?|command|script|build|linter)|\b(?:edit|modify|patch|create|write|save|delete|rename|move|inspect|read)\b.{0,60}\b(?:file|repository|repo|codebase|directory|folder)\b|\b(?:terminal|shell|bash|zsh|pytest|npm test|pnpm test|git\s+(?:status|diff|commit)|docker)\b|(?:运行|执行).{0,20}(?:测试|命令|脚本|构建)|(?:修改|编辑|修复|创建|读取|检查|保存).{0,30}(?:文件|仓库|代码库|目录)/i;
1202
+ const webAction = /\b(?:browse|search|look up|fetch|open)\b.{0,80}\b(?:web|website|url|online|documentation|docs|news|weather|price)\b|(?:浏览|搜索|查询|打开).{0,30}(?:网页|网站|链接|文档|新闻|天气|价格)/i;
1203
+ const statefulAction = /\b(?:refund|cancel|book|reserve|purchase|buy|return|exchange|transfer|update|change)\b.{0,80}\b(?:order|booking|reservation|account|address|payment|subscription|ticket|flight|item)\b|(?:退款|取消|预订|购买|退货|换货|转账|更新|修改).{0,30}(?:订单|预订|账户|地址|付款|订阅|票|航班|商品)/i;
1204
+ return explicitTool.test(text) || codeEnvironment.test(text) || webAction.test(text) || statefulAction.test(text);
1205
+ }
1206
+ var DEFAULT_PORTFOLIO_WEIGHTS = {
1207
+ auto: {
1208
+ quality: 0.47,
1209
+ capability: 0.2,
1210
+ cost: 0.18,
1211
+ speed: 0.07,
1212
+ reliability: 0.03,
1213
+ legacy: 0.05
1214
+ },
1215
+ eco: { quality: 0.36, capability: 0.2, cost: 0.28, speed: 0.1, reliability: 0.04, legacy: 0.02 },
1216
+ premium: {
1217
+ quality: 0.58,
1218
+ capability: 0.2,
1219
+ cost: 0.08,
1220
+ speed: 0.06,
1221
+ reliability: 0.06,
1222
+ legacy: 0.02
1223
+ },
1224
+ highStakesBoost: { quality: 0.08, reliability: 0.05 },
1225
+ latencySensitiveSpeedBoost: 0.08,
1226
+ affinityFloorGap: { auto: 0.1, eco: 0.22, premium: 0.05 }
1227
+ };
1228
+ function likelyNeedsParallelToolCalls(prompt, needsTools, toolCount, toolNames) {
1229
+ if (!needsTools || toolCount === void 0 || toolCount < 1) return false;
1230
+ const text = prompt.trim();
1231
+ const explicitRepeat = /\b(?:in parallel|simultaneously|concurrently|for each|each of|every one|both|(?:two|three|multiple|several)\s+(?:cities|locations|items|tasks|orders|users|files))\b|并行|同时|分别|每个|各自|(?:两个|三个|多个)(?:城市|地点|项目|任务|订单|用户|文件)|cada uno|para cada|simult[aá]neamente/i.test(
1232
+ text
1233
+ );
1234
+ if (explicitRepeat) return true;
1235
+ const sentenceClauses = text.split(/[.!?。!?]+/).map((part) => part.trim()).filter((part) => part.length >= 8);
1236
+ if (/\b(?:also|additionally|furthermore)\b|另外|此外|그리고/i.test(text) && sentenceClauses.length >= 2 || /\band\s+(?:also|for the)\b/i.test(text))
1237
+ return true;
1238
+ const pairedQuantity = /\b\d+(?:\.\d+)?\s+(?:and|or)\s+\d+(?:\.\d+)?\s*(?:gb|mb|tb|kg|g|ml|oz|cups?|cores?|cpus?)\b/i.test(
1239
+ text
1240
+ );
1241
+ if (pairedQuantity) return true;
1242
+ const operationTokens = /* @__PURE__ */ new Set([
1243
+ "add",
1244
+ "delete",
1245
+ "remove",
1246
+ "cancel",
1247
+ "return",
1248
+ "exchange",
1249
+ "modify",
1250
+ "book",
1251
+ "transfer",
1252
+ "send",
1253
+ "upload",
1254
+ "download",
1255
+ "create",
1256
+ "close"
1257
+ ]);
1258
+ const lowered = text.toLowerCase();
1259
+ const matchedOperationTokens = new Set(
1260
+ (toolNames ?? []).flatMap((name) => name.toLowerCase().split(/[^a-z0-9\u3400-\u9fff]+/)).filter((token) => operationTokens.has(token) && lowered.includes(token))
1261
+ );
1262
+ if (matchedOperationTokens.size >= 2) return true;
1263
+ const nonEmptyLines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1264
+ const quantityMentions = text.match(
1265
+ /\b(?:\d+(?:\.\d+)?|one|two|three|four|five|six|seven|eight|nine|ten)\s*(?:oz|ounce|ounces|g|gram|grams|kg|ml|cups?|pieces?|tablespoons?)\b/gi
1266
+ ) ?? [];
1267
+ if (nonEmptyLines.length >= 2 && quantityMentions.length >= 2) return true;
1268
+ const repeatedLookup = /\b(?:weather|climate|clima|tiempo|temperature|snow|news|report)\b|天气|气象|温度|降雪|新闻|报告/i.test(
1269
+ text
1270
+ );
1271
+ const multiLocationConnector = /\b(?:and also|both|y|e)\b|还有|以及|和|、/i.test(text);
1272
+ const commaSeparatedLocations = (text.match(/[,,]/g) ?? []).length >= 2;
1273
+ if (repeatedLookup && (multiLocationConnector || commaSeparatedLocations)) return true;
1274
+ const distinctOrderParts = /\b(?:food|meal)\b[\s\S]*\bdrink\b|\bdrink\b[\s\S]*\b(?:food|meal)\b/i.test(text);
1275
+ const koreanParallelClauses = (text.match(/,/g) ?? []).length >= 3 && /하고|그리고/.test(text);
1276
+ return distinctOrderParts || koreanParallelClauses;
1277
+ }
1278
+ function classifyTask(prompt, systemPrompt, options) {
1279
+ const fullText = `${systemPrompt ?? ""} ${prompt}`;
1280
+ const estimatedInputTokens = Math.ceil(fullText.length / 4);
1281
+ const scanLimit = Math.max(1, Math.min(8e3, options.config.classifier.promptTruncationChars));
1282
+ const sample = (value) => {
1283
+ if (value.length <= scanLimit) return value;
1284
+ const prefixLength = Math.ceil(scanLimit / 2);
1285
+ return `${value.slice(0, prefixLength)}
1286
+ ${value.slice(-(scanLimit - prefixLength))}`;
1287
+ };
1288
+ const scannedPrompt = sample(prompt);
1289
+ const scannedSystemPrompt = sample(systemPrompt ?? "");
1290
+ const scannedFullText = `${scannedSystemPrompt} ${scannedPrompt}`;
1291
+ const text = scannedPrompt.toLowerCase();
1292
+ const explicitCodeSignal = /```|\b(?:typescript|javascript|python|rust|java|sql|stack trace|traceback|exception)\b|\.(?:ts|tsx|js|py|go|rs)\b/i.test(
1293
+ scannedPrompt
1294
+ );
1295
+ const codeConstructSignal = /\b(?:implement|refactor|debug|write|edit|modify|create|define|review|fix)\b[\s\S]{0,48}\b(?:api|function|class|method)\b|\b(?:api|function|class|method)\b[\s\S]{0,48}\b(?:code|implementation|typescript|javascript|python|rust|java)\b/i.test(
1296
+ scannedPrompt
1297
+ );
1298
+ const nativeCodeSignal = /\b(?:programmed|written|implemented?|code)\s+(?:in|using)\s+(?:c\+\+|c|rust|go)\b/i.test(
1299
+ scannedPrompt
1300
+ );
1301
+ const hasCode = explicitCodeSignal || codeConstructSignal || nativeCodeSignal;
1302
+ const toolsAvailable = options.hasTools ?? false;
1303
+ const needsTools = options.requiresTools ?? (toolsAvailable && inferToolRequirement(scannedPrompt, scannedSystemPrompt));
1304
+ const likelyParallelToolCalls = likelyNeedsParallelToolCalls(
1305
+ scannedPrompt,
1306
+ needsTools,
1307
+ options.toolCount,
1308
+ options.toolNames
1309
+ );
1310
+ const normalizedToolNames = (options.toolNames ?? []).map((name) => name.toLowerCase());
1311
+ const airlineToolSignal = normalizedToolNames.some(
1312
+ (name) => /(?:flight|reservation|airport|baggage|passenger)/.test(name)
1313
+ );
1314
+ const retailToolSignal = normalizedToolNames.some(
1315
+ (name) => /(?:order|product|item|return|exchange|address)/.test(name)
1316
+ );
1317
+ const webResearchToolSignal = normalizedToolNames.some(
1318
+ (name) => /^(?:web_?search|web_?fetch)$/.test(name)
1319
+ );
1320
+ const agentDomain = airlineToolSignal && !retailToolSignal ? "airline" : retailToolSignal && !airlineToolSignal ? "retail" : webResearchToolSignal ? "web_research" : "other";
1321
+ const clueConnectors = scannedFullText.match(
1322
+ /\b(?:after|before|while|where|whose|which|in \d{4}|as of|over \d+|another|also|furthermore)\b|(?:之后|之前|其中|截至|超过|另一个|此外)/gi
1323
+ ) ?? [];
1324
+ const entityResolutionSignal = /\b(?:identify|who (?:is|was)|what (?:is|was) the name|which (?:person|player|company|country|city)|find the (?:person|player|name|entity))\b|(?:找出|识别|是谁|哪位|名称是什么)/i.test(
1325
+ scannedFullText
1326
+ );
1327
+ const exactAnswerSignal = /\b(?:exact answer|single best-supported answer|following clues|multiple public sources)\b|(?:精确答案|根据.*线索|多个公开来源)/i.test(
1328
+ scannedFullText
1329
+ );
1330
+ const deepWebResearch = agentDomain === "web_research" && (exactAnswerSignal || entityResolutionSignal && (clueConnectors.length >= 3 || prompt.length >= 320));
1331
+ const globalOptimizationSignal = /\b(?:cheapest|lowest[- ]price|least expensive|most expensive|highest(?:[- ]priced)?|largest|smallest|maximum|minimum|best available|closest|not (?:cost|exceed))\b|最便宜|最低价|最贵|最高价|最大|最小/i.test(
1332
+ scannedPrompt
1333
+ );
1334
+ const globalScopeSignal = /\b(?:everything|all (?:(?:my|your|their|the) )?(?:future |upcoming )?(?:items|orders|passengers|flights|reservations|bookings)|every (?:item|order|passenger|flight|reservation|booking))\b|全部|所有|每个/i.test(
1335
+ scannedPrompt
1336
+ );
1337
+ const globalChoiceSignal = globalOptimizationSignal || globalScopeSignal;
1338
+ const crossRecordSignal = /\b(?:another|other|different|previous)\s+(?:order|reservation|booking|account|address)\b|另一(?:个)?(?:订单|预订|账户|地址)|其他(?:订单|预订|账户|地址)/i.test(
1339
+ scannedPrompt
1340
+ );
1341
+ const reservationIds = scannedPrompt.match(/\b[A-Z0-9]{6}\b/g) ?? [];
1342
+ const crossReservationBatchSignal = agentDomain === "airline" && (/\b(?:two|three|multiple|several)(?:\s+of\s+(?:my|our|the))?\s+(?:upcoming\s+)?(?:reservations?|bookings?)\b|\b(?:a\s+)?(?:second|third)\s+(?:reservation|booking)\b/i.test(
1343
+ scannedPrompt
1344
+ ) || new Set(reservationIds).size >= 2);
1345
+ const conditionalGlobalWorkflowSignal = agentDomain === "airline" && globalScopeSignal && /\b(?:if|that (?:contain|have)|longer than|shorter than|under|over|at (?:most|least)|wherever possible)\b|如果|超过|少于|不超过|尽可能/i.test(
1346
+ scannedPrompt
1347
+ ) && /\b(?:cancel|change|upgrade|move|book)\b[\s\S]*\b(?:cancel|change|upgrade|move|book)\b|取消[\s\S]*(?:升级|更改)|升级[\s\S]*(?:取消|更改)/i.test(
1348
+ scannedPrompt
1349
+ );
1350
+ const policyExceptionSignal = agentDomain === "retail" && /\b(?:return|refund|send back|get (?:my |the )?money back)\b|退货|退款|退回/i.test(
1351
+ scannedPrompt
1352
+ ) && /\b(?:amex|american express|visa|mastercard|credit card|debit card|different card|another card|other card)\b|信用卡|借记卡|其他卡|另一张卡/i.test(
1353
+ scannedPrompt
1354
+ );
1355
+ const singleSelectedPolicyException = policyExceptionSignal && /\b(?:return|refund|send back)\b[^.!?。!?]{0,96}\b(?:the )?(?:pricier|cheaper|more expensive|less expensive|costlier|one)\b/i.test(
1356
+ scannedPrompt
1357
+ );
1358
+ const negotiatedWorkflowSignal = agentDomain === "retail" && /\b(?:return|exchange)\b|退货|退回|换货|交换/i.test(scannedPrompt);
1359
+ const numberedSteps = (scannedPrompt.match(/(?:^|\s)\d+(?:\.\d+)*[.)]\s+/g) ?? []).length;
1360
+ const complexMultiToolPlan = likelyParallelToolCalls && ((options.toolCount ?? 0) >= 6 || numberedSteps >= 3 || prompt.length > 1200);
1361
+ let agentRisk = needsTools && singleSelectedPolicyException ? "policy_exception_simple" : needsTools && policyExceptionSignal ? "policy_exception" : (
1362
+ // Airline prompts that require a global optimum (for example the
1363
+ // cheapest itinerary across several candidates) are materially harder
1364
+ // than applying one change to every passenger in a known reservation.
1365
+ // Full-session evidence supports Sonnet for the former, while upgrading
1366
+ // the latter merely because it says "all passengers" caused a large cost
1367
+ // increase without a quality gain.
1368
+ needsTools && agentDomain === "airline" && (globalOptimizationSignal || conditionalGlobalWorkflowSignal) ? "complex_high" : needsTools && (likelyParallelToolCalls || globalChoiceSignal || crossRecordSignal || crossReservationBatchSignal || negotiatedWorkflowSignal) ? "high" : "standard"
1369
+ );
1370
+ const needsVision = options.hasVision ?? false;
1371
+ const needsStructuredOutput = options.requiresStructuredOutput ?? false;
1372
+ const latencySensitive = /\b(?:urgent|asap|fast|quick|low latency|real[- ]time)\b|尽快|马上|快速|低延迟/i.test(
1373
+ scannedFullText
1374
+ );
1375
+ const highStakes = /\b(?:production|security|payment|legal|medical|financial|audit)\b|生产|安全|支付|法律|医疗|财务|审计/i.test(
1376
+ scannedFullText
1377
+ );
1378
+ const terminalToolSignal = normalizedToolNames.some(
1379
+ (name) => /^(?:terminalexec|terminalinspect|terminalsendkeys)$/.test(name)
1380
+ );
1381
+ const simpleTerminalArtifact = /\b(?:create|write|convert|generate|build|implement|run|fix|repair|debug|make)\b[\s\S]{0,120}\b(?:file|script|csv|parquet|json|txt|server|endpoint)\b/i.test(
1382
+ scannedPrompt
1383
+ );
1384
+ const terminalComplexRepair = terminalToolSignal && /\b(?:multiple|several)\s+(?:scripts?|files?|components?)\b|\b(?:pipeline|dependencies)\b[\s\S]{0,100}\b(?:fail|issue|fix|repair|run|execute)\b|\b(?:identify|find|fix|repair)\s+(?:and\s+)?(?:fix\s+)?all\s+(?:the\s+)?issues\b/i.test(
1385
+ scannedPrompt
1386
+ );
1387
+ const mentionedTerminalRuntimes = new Set(
1388
+ (scannedPrompt.match(/\b(?:gcc|clang|rustc|javac|go\s+build|node|python)\b/gi) ?? []).map(
1389
+ (name) => name.toLowerCase().replace(/\s+/g, " ")
1390
+ )
1391
+ );
1392
+ const terminalCrossRuntimeArtifact = terminalToolSignal && (/\bpolyglot\b/i.test(scannedPrompt) || /\b(?:both|each)\b[\s\S]{0,120}\b(?:compilers?|runtimes?|toolchains?)\b/i.test(
1393
+ scannedPrompt
1394
+ ) || mentionedTerminalRuntimes.size >= 2 && /\b(?:compile|build|run|execute)\b/i.test(scannedPrompt));
1395
+ const terminalFrameworkToNativeArtifact = terminalToolSignal && /\b(?:pytorch|tensorflow|jax|onnx|state[_ -]?dict|checkpoint|safetensors?)\b|\.(?:pth|pt|onnx)\b/i.test(
1396
+ scannedPrompt
1397
+ ) && /\b(?:pure|native|programmed|written|implemented?)\s+(?:in|using)\s+(?:c\+\+|c|rust|go)\b|\b(?:c\+\+|c|rust|go)\s+(?:program|binary|executable|cli|tool|implementation)\b/i.test(
1398
+ scannedPrompt
1399
+ ) && /\b(?:inference|model|weights?|tensor|export|convert|load)\b/i.test(scannedPrompt);
1400
+ if (needsTools && (terminalComplexRepair || terminalCrossRuntimeArtifact || terminalFrameworkToNativeArtifact) && (agentRisk === "standard" || agentRisk === "high"))
1401
+ agentRisk = "complex_high";
1402
+ const complexTerminalOperation = /\b(?:git|ssh|nginx|https|certificate|authentication|credential|deploy|production|encrypt|gpg|shred|securely delete|decommission|benchmark|evaluate|embedding|chess|image|search the web|schema|statistical|statistics|aggregate|join|multiple inputs?)\b/i.test(
1403
+ scannedPrompt
1404
+ );
1405
+ const terminalCredentialSignal = /\b(?:ssh|nginx|certificate|authentication|credentials?|passwords?|api keys?|deploy|production|encrypt|gpg|shred|securely delete|decommission)\b/i.test(
1406
+ scannedPrompt
1407
+ ) || /\b(?:access|auth|authentication|bearer|secret|api)\s+tokens?\b|\btokens?\s+(?:secret|credential|authentication)\b/i.test(
1408
+ scannedPrompt
1409
+ );
1410
+ const terminalSafetySensitive = terminalToolSignal && (highStakes || terminalCredentialSignal);
1411
+ const implicitTerminalCode = needsTools && terminalToolSignal && agentRisk === "standard" && !highStakes && !complexTerminalOperation && numberedSteps < 3 && prompt.length <= 1e3 && simpleTerminalArtifact;
1412
+ const language = /[\u3400-\u9fff]/.test(scannedFullText) ? "zh" : "other";
1413
+ const multipleChoiceSignals = (scannedPrompt.match(/(?:^|\n)\s*[A-D][.)]\s+/gim) ?? []).length;
1414
+ const numericSignals = (scannedPrompt.match(/-?\d+(?:[.,]\d+)?/g) ?? []).length;
1415
+ const compactMathProblem = !hasCode && prompt.length < 2500 && numericSignals >= 2 && (/[+×÷=%$€£¥]|\b(?:total|each|per|times|half|twice|percent|how many|how much|calculate)\b/i.test(
1416
+ scannedPrompt
1417
+ ) || /[??]\s*$/.test(scannedPrompt.trim()) || numericSignals >= 3);
1418
+ let taskType = "chat";
1419
+ if (needsVision) taskType = "vision";
1420
+ else if (estimatedInputTokens > 8e4) taskType = "long_context";
1421
+ else if (needsTools && (hasCode || implicitTerminalCode)) taskType = "code_agent";
1422
+ else if (needsTools && likelyParallelToolCalls && !complexMultiToolPlan)
1423
+ taskType = "tool_agent_parallel";
1424
+ else if (needsTools) taskType = "tool_agent";
1425
+ else if (multipleChoiceSignals >= 3) taskType = "reasoning_mcq";
1426
+ else if (compactMathProblem) taskType = "reasoning_math";
1427
+ else if (/\b(?:bug|debug|error|failure|failing|regression|crash|修复|报错|错误|调试)\b/i.test(text))
1428
+ taskType = "debug";
1429
+ else if (hasCode || /\b(?:refactor|implement|patch|edit|rewrite|重构|实现|修改)\b/i.test(text))
1430
+ taskType = "code_edit";
1431
+ else if (needsStructuredOutput || /\b(?:extract|json|schema|csv|字段|提取)\b/i.test(text))
1432
+ taskType = "extraction";
1433
+ else if (/\b(?:prove|derive|theorem|formal|mathematical|reasoning|证明|推导|定理|数学)\b/i.test(text))
1434
+ taskType = "reasoning";
1435
+ return {
1436
+ taskType,
1437
+ estimatedInputTokens,
1438
+ hasCode,
1439
+ needsTools,
1440
+ toolsAvailable,
1441
+ needsVision,
1442
+ needsStructuredOutput,
1443
+ latencySensitive,
1444
+ highStakes,
1445
+ language,
1446
+ likelyParallelToolCalls,
1447
+ complexMultiToolPlan,
1448
+ agentDomain,
1449
+ deepWebResearch,
1450
+ agentRisk,
1451
+ terminalToolSignal,
1452
+ terminalSafetySensitive,
1453
+ implicitTerminalCode
1454
+ };
1455
+ }
1456
+ function affinity(modelId, task, language = "other", agentDomain = "other", deepWebResearch = false, agentRisk = "standard", terminalToolSignal = false, terminalSafetySensitive = false) {
1457
+ const id = modelId.toLowerCase();
1458
+ const modelName = id.slice(id.indexOf("/") + 1);
1459
+ const match = (values, score) => values.some((value) => modelName === value) ? score : 0;
1460
+ const base = 0.68;
1461
+ switch (task) {
1462
+ case "code_agent":
1463
+ if (terminalToolSignal && agentRisk === "complex_high") {
1464
+ return Math.max(
1465
+ base,
1466
+ match(["claude-sonnet-5"], 1),
1467
+ match(["gpt-5.3-codex"], 0.87),
1468
+ match(["gpt-5-mini"], 0.78),
1469
+ match(["gemini-3.5-flash"], 0.76)
1470
+ );
1471
+ }
1472
+ return Math.max(
1473
+ base,
1474
+ match(["gpt-5.3-codex"], 1),
1475
+ match(["claude-sonnet-5"], 0.98),
1476
+ match(["gpt-5-mini"], 0.96),
1477
+ match(["gemini-3.5-flash"], 0.92),
1478
+ match(["kimi-k3"], 0.9),
1479
+ match(["deepseek-v4-pro", "glm-5.2"], 0.88)
1480
+ );
1481
+ case "tool_agent":
1482
+ if (terminalToolSignal && agentRisk === "complex_high") {
1483
+ return Math.max(
1484
+ base,
1485
+ match(["claude-sonnet-5"], 1),
1486
+ match(["gpt-5.3-codex"], 0.87),
1487
+ match(["gpt-5-mini"], 0.78),
1488
+ match(["gemini-3.5-flash"], 0.76)
1489
+ );
1490
+ }
1491
+ if (terminalToolSignal && !terminalSafetySensitive) {
1492
+ return Math.max(
1493
+ base,
1494
+ match(["gpt-5-mini"], 1),
1495
+ match(["gpt-5.3-codex"], 0.98),
1496
+ match(["claude-sonnet-5"], 0.9),
1497
+ match(["gemini-3.5-flash"], 0.89)
1498
+ );
1499
+ }
1500
+ if (terminalToolSignal && terminalSafetySensitive) {
1501
+ return Math.max(
1502
+ base,
1503
+ match(["claude-sonnet-5"], 1),
1504
+ match(["claude-opus-4.8"], 0.9),
1505
+ match(["gpt-5.3-codex"], 0.84)
1506
+ );
1507
+ }
1508
+ if (agentDomain === "web_research") {
1509
+ return deepWebResearch ? Math.max(
1510
+ base,
1511
+ match(["claude-sonnet-5"], 1),
1512
+ match(["gpt-5-mini"], 0.88),
1513
+ match(["gemini-3.5-flash"], 0.84),
1514
+ match(["claude-opus-5"], 0.8),
1515
+ match(["claude-opus-4.8"], 0.78)
1516
+ ) : Math.max(
1517
+ base,
1518
+ match(["claude-sonnet-5"], 1),
1519
+ match(["gpt-5-mini"], 0.88),
1520
+ match(["gemini-3.5-flash"], 0.86),
1521
+ match(["claude-opus-5"], 0.84),
1522
+ match(["claude-opus-4.8"], 0.82)
1523
+ );
1524
+ }
1525
+ if (agentDomain === "retail") {
1526
+ if (agentRisk === "standard") {
1527
+ return Math.max(
1528
+ base,
1529
+ match(["gpt-5-mini"], 1),
1530
+ match(["claude-sonnet-5"], 0.88),
1531
+ match(["gemini-3.5-flash"], 0.82),
1532
+ match(["gpt-5.3-codex"], 0.81),
1533
+ match(["kimi-k3"], 0.78),
1534
+ match(["deepseek-v4-pro"], 0.76)
1535
+ );
1536
+ }
1537
+ if (agentRisk === "policy_exception") {
1538
+ return Math.max(
1539
+ base,
1540
+ match(["gpt-4.1"], 1),
1541
+ match(["claude-sonnet-5"], 0.9),
1542
+ match(["deepseek-v4-pro"], 0.82),
1543
+ match(["gpt-5-mini"], 0.8),
1544
+ match(["gpt-4o-mini"], 0.76)
1545
+ );
1546
+ }
1547
+ if (agentRisk === "policy_exception_simple") {
1548
+ return Math.max(
1549
+ base,
1550
+ match(["gpt-5-mini"], 1),
1551
+ match(["gpt-4.1"], 0.86),
1552
+ match(["deepseek-v4-pro"], 0.82),
1553
+ match(["gpt-4o-mini"], 0.8)
1554
+ );
1555
+ }
1556
+ return Math.max(
1557
+ base,
1558
+ match(["deepseek-v4-pro"], 1),
1559
+ match(["claude-sonnet-5"], 0.88),
1560
+ match(["gemini-3.5-flash"], 0.82),
1561
+ match(["gpt-5.3-codex"], 0.81),
1562
+ match(["kimi-k3"], 0.78),
1563
+ match(["gpt-5-mini"], 0.76)
1564
+ );
1565
+ }
1566
+ if (agentDomain === "airline") {
1567
+ if (agentRisk === "complex_high") {
1568
+ return Math.max(
1569
+ base,
1570
+ match(["claude-sonnet-5"], 1),
1571
+ match(["gpt-5-mini"], 0.78),
1572
+ match(["gemini-3.5-flash"], 0.76),
1573
+ match(["deepseek-v4-pro"], 0.74)
1574
+ );
1575
+ }
1576
+ return Math.max(
1577
+ base,
1578
+ match(["gpt-5-mini"], 1),
1579
+ match(["claude-sonnet-5"], 0.9),
1580
+ match(["gemini-3.5-flash"], 0.8),
1581
+ match(["deepseek-v4-pro"], 0.76)
1582
+ );
1583
+ }
1584
+ return Math.max(
1585
+ base,
1586
+ match(["claude-sonnet-5"], 1),
1587
+ match(["gemini-3.5-flash"], 0.88),
1588
+ match(["gpt-5.3-codex"], 0.87),
1589
+ match(["gpt-5-mini"], 0.84),
1590
+ match(["kimi-k3"], 0.85),
1591
+ match(["deepseek-v4-pro"], 0.82)
1592
+ );
1593
+ case "tool_agent_parallel":
1594
+ if (terminalToolSignal) {
1595
+ return terminalSafetySensitive ? Math.max(
1596
+ base,
1597
+ match(["claude-sonnet-5"], 1),
1598
+ match(["claude-opus-4.8"], 0.9),
1599
+ match(["gpt-5.3-codex"], 0.86)
1600
+ ) : Math.max(
1601
+ base,
1602
+ match(["gpt-5-mini"], 1),
1603
+ match(["gpt-5.3-codex"], 0.98),
1604
+ match(["claude-sonnet-5"], 0.92),
1605
+ match(["gemini-3.5-flash"], 0.88)
1606
+ );
1607
+ }
1608
+ if (agentDomain === "web_research") {
1609
+ return deepWebResearch ? Math.max(
1610
+ base,
1611
+ match(["claude-sonnet-5"], 1),
1612
+ match(["gpt-5-mini"], 0.88),
1613
+ match(["gemini-3.5-flash"], 0.84),
1614
+ match(["claude-opus-5"], 0.8),
1615
+ match(["claude-opus-4.8"], 0.78)
1616
+ ) : Math.max(
1617
+ base,
1618
+ match(["claude-sonnet-5"], 1),
1619
+ match(["gpt-5-mini"], 0.88),
1620
+ match(["gemini-3.5-flash"], 0.86),
1621
+ match(["claude-opus-5"], 0.84),
1622
+ match(["claude-opus-4.8"], 0.82)
1623
+ );
1624
+ }
1625
+ if (agentDomain === "retail") {
1626
+ if (agentRisk === "policy_exception") {
1627
+ return Math.max(
1628
+ base,
1629
+ match(["gpt-4.1"], 1),
1630
+ match(["claude-sonnet-5"], 0.9),
1631
+ match(["deepseek-v4-pro"], 0.82),
1632
+ match(["gpt-5-mini"], 0.8),
1633
+ match(["gpt-4o-mini"], 0.76)
1634
+ );
1635
+ }
1636
+ if (agentRisk === "policy_exception_simple") {
1637
+ return Math.max(
1638
+ base,
1639
+ match(["gpt-5-mini"], 1),
1640
+ match(["gpt-4.1"], 0.86),
1641
+ match(["deepseek-v4-pro"], 0.82),
1642
+ match(["gpt-4o-mini"], 0.8)
1643
+ );
1644
+ }
1645
+ return Math.max(
1646
+ base,
1647
+ match(["deepseek-v4-pro"], 1),
1648
+ match(["claude-sonnet-5"], 0.88),
1649
+ match(["claude-opus-4.8"], 0.84),
1650
+ match(["gpt-5-mini"], 0.78),
1651
+ match(["gemini-3.5-flash"], 0.76)
1652
+ );
1653
+ }
1654
+ if (agentDomain === "airline") {
1655
+ return agentRisk === "complex_high" ? Math.max(
1656
+ base,
1657
+ match(["claude-sonnet-5"], 1),
1658
+ match(["gpt-5-mini"], 0.78),
1659
+ match(["claude-opus-4.8"], 0.76),
1660
+ match(["gemini-3.5-flash"], 0.74)
1661
+ ) : Math.max(
1662
+ base,
1663
+ match(["gpt-5-mini"], 1),
1664
+ match(["claude-sonnet-5"], 0.9),
1665
+ match(["gemini-3.5-flash"], 0.8)
1666
+ );
1667
+ }
1668
+ return Math.max(
1669
+ base,
1670
+ match(["claude-opus-4.8"], 1),
1671
+ match(["claude-sonnet-5"], 0.84),
1672
+ match(["grok-4.5"], 0.82),
1673
+ match(["gemini-3.5-flash"], 0.8),
1674
+ match(["deepseek-v4-pro"], 0.78)
1675
+ );
1676
+ case "code_edit":
1677
+ case "debug":
1678
+ return Math.max(
1679
+ base,
1680
+ match(["gpt-5.3-codex"], 1),
1681
+ match(["claude-sonnet-4.6"], 0.94),
1682
+ match(["glm-5.2"], 0.9),
1683
+ match(["kimi-k2.7", "deepseek-v4-pro"], 0.86)
1684
+ );
1685
+ case "reasoning":
1686
+ return Math.max(
1687
+ base,
1688
+ match(["claude-sonnet-5", "claude-sonnet-4.6"], 0.98),
1689
+ match(["deepseek-v4-pro"], 0.95),
1690
+ match(["grok-4.5"], 0.94),
1691
+ match(["gemini-3.1-pro", "gemini-3.5-flash"], 0.92)
1692
+ );
1693
+ case "reasoning_mcq":
1694
+ return Math.max(
1695
+ base,
1696
+ match(["gemini-3-flash-preview"], 1),
1697
+ match(["gemini-3.5-flash"], 0.91),
1698
+ match(["grok-4.5"], 0.9),
1699
+ match(["claude-sonnet-5"], 0.88),
1700
+ match(["deepseek-v4-pro"], 0.84)
1701
+ );
1702
+ case "reasoning_math":
1703
+ return Math.max(
1704
+ base,
1705
+ match(["gemini-3.5-flash"], 1),
1706
+ match(["grok-4.5"], 0.93),
1707
+ match(["claude-sonnet-5", "deepseek-v4-pro", "kimi-k3"], 0.9),
1708
+ match(["kimi-k2.7"], 0.84)
1709
+ );
1710
+ case "vision":
1711
+ return Math.max(
1712
+ base,
1713
+ match(["gemini-3.1-pro"], 0.96),
1714
+ match(["qwen3.7-max", "claude-sonnet-4.6", "kimi-k2.7", "grok-4.3"], 0.9)
1715
+ );
1716
+ case "long_context":
1717
+ return Math.max(
1718
+ base,
1719
+ match(["gemini-3.1-pro"], 1),
1720
+ match(["qwen3.7-max", "glm-5.2"], 0.89),
1721
+ match(["gemini-3.5-flash"], 0.88),
1722
+ match(["deepseek-v4-pro"], 0.85)
1723
+ );
1724
+ case "extraction": {
1725
+ const kimiExtractionAffinity = language === "zh" ? 1 : 0.9;
1726
+ return Math.max(
1727
+ base,
1728
+ match(["gemini-3.5-flash", "gemini-2.5-flash", "gpt-4o-mini"], 0.9),
1729
+ match(["claude-sonnet-5", "claude-sonnet-4.6"], 0.9),
1730
+ match(["kimi-k3", "kimi-k2.7"], kimiExtractionAffinity)
1731
+ );
1732
+ }
1733
+ default:
1734
+ return Math.max(
1735
+ base,
1736
+ match(["gemini-3.5-flash", "gemini-2.5-flash", "kimi-k3", "kimi-k2.7"], 0.86)
1737
+ );
1738
+ }
1739
+ }
1740
+ function evidenceCandidates(task) {
1741
+ if (task === "code_agent") {
1742
+ return [
1743
+ "openai/gpt-5.3-codex",
1744
+ "anthropic/claude-sonnet-5",
1745
+ "openai/gpt-5-mini",
1746
+ "google/gemini-3.5-flash",
1747
+ "moonshot/kimi-k3",
1748
+ "deepseek/deepseek-v4-pro"
1749
+ ];
1750
+ }
1751
+ if (task === "tool_agent") {
1752
+ return [
1753
+ "anthropic/claude-sonnet-5",
1754
+ "anthropic/claude-opus-5",
1755
+ "openai/gpt-5-mini",
1756
+ "openai/gpt-4.1",
1757
+ "openai/gpt-4o-mini",
1758
+ "google/gemini-3.5-flash",
1759
+ "openai/gpt-5.3-codex",
1760
+ "moonshot/kimi-k3",
1761
+ "deepseek/deepseek-v4-pro"
1762
+ ];
1763
+ }
1764
+ if (task === "tool_agent_parallel") {
1765
+ return [
1766
+ "anthropic/claude-opus-5",
1767
+ "anthropic/claude-opus-4.8",
1768
+ "anthropic/claude-sonnet-5",
1769
+ "openai/gpt-5-mini",
1770
+ "openai/gpt-4.1",
1771
+ "openai/gpt-4o-mini",
1772
+ "xai/grok-4.5",
1773
+ "google/gemini-3.5-flash",
1774
+ "deepseek/deepseek-v4-pro"
1775
+ ];
1776
+ }
1777
+ if (task === "long_context") {
1778
+ return [
1779
+ "google/gemini-3.1-pro",
1780
+ "deepseek/deepseek-v4-pro",
1781
+ "qwen/qwen3.7-max",
1782
+ "zai/glm-5.2",
1783
+ "google/gemini-3.5-flash"
1784
+ ];
1785
+ }
1786
+ if (task === "reasoning_mcq") {
1787
+ return [
1788
+ "google/gemini-3-flash-preview",
1789
+ "google/gemini-3.5-flash",
1790
+ "xai/grok-4.5",
1791
+ "anthropic/claude-sonnet-5",
1792
+ "deepseek/deepseek-v4-pro"
1793
+ ];
1794
+ }
1795
+ if (task === "reasoning_math") {
1796
+ return [
1797
+ "google/gemini-3.5-flash",
1798
+ "xai/grok-4.5",
1799
+ "anthropic/claude-sonnet-5",
1800
+ "deepseek/deepseek-v4-pro",
1801
+ "moonshot/kimi-k3"
1802
+ ];
1803
+ }
1804
+ return [];
1805
+ }
1806
+ function isEligible(modelId, features, maxOutputTokens, options) {
1807
+ const model = options.modelCapabilities?.[modelId] ?? DEFAULT_MODEL_CAPABILITIES[modelId];
1808
+ if (!model) return true;
1809
+ if (features.needsTools && !model.supportsTools) return false;
1810
+ if (features.needsVision && !model.supportsVision) return false;
1811
+ if (features.needsStructuredOutput && !model.supportsTools) return false;
1812
+ if (model.maxOutputTokens < maxOutputTokens) return false;
1813
+ return model.contextWindow >= (features.estimatedInputTokens + maxOutputTokens) * 1.1;
1814
+ }
1815
+ function estimatedCost(modelId, options, inputTokens, outputTokens) {
1816
+ const price = options.modelPricing.get(modelId);
1817
+ if (!price) return Number.POSITIVE_INFINITY;
1818
+ if (price.flatPrice !== void 0) return price.flatPrice;
1819
+ return (inputTokens * price.inputPrice + outputTokens * price.outputPrice) / 1e6;
1820
+ }
1821
+ function profileScore(modelId, options, now) {
1822
+ const profile = options.modelPerformance?.[modelId] ?? LIVE_MODEL_PROFILES[modelId] ?? HISTORICAL_MODEL_PROFILES[modelId];
1823
+ if (!profile) return void 0;
1824
+ const measuredAt = Date.parse(profile.measuredAt);
1825
+ if (!Number.isFinite(measuredAt)) return void 0;
1826
+ const ageDays = Math.max(0, (now.getTime() - measuredAt) / 864e5);
1827
+ const sampleConfidence = profile.samples === void 0 ? 1 : Math.min(1, Math.max(0, profile.samples) / 10);
1828
+ const freshness = Math.pow(0.5, ageDays / 30) * sampleConfidence;
1829
+ const quality = profile.intelligenceIndex === void 0 ? void 0 : Math.min(1, profile.intelligenceIndex / 50);
1830
+ const speed = Math.min(
1831
+ 1,
1832
+ (2e3 / Math.max(500, profile.latencyMs) + profile.outputTokensPerSecond / 250) / 2
1833
+ );
1834
+ const tailSpeed = Math.min(1, 3e3 / Math.max(750, profile.p95LatencyMs ?? profile.latencyMs));
1835
+ const reliability = Math.max(0, 1 - (profile.errorRate ?? 0));
1836
+ return { quality, speed, tailSpeed, reliability, freshness };
1837
+ }
1838
+ var PortfolioStrategy = class {
1839
+ name = "portfolio";
1840
+ route(prompt, systemPrompt, maxOutputTokens, options) {
1841
+ const features = classifyTask(prompt, systemPrompt, options);
1842
+ const base = new RulesStrategy().route(prompt, systemPrompt, maxOutputTokens, {
1843
+ ...options,
1844
+ requiresTools: features.needsTools
1845
+ });
1846
+ const tierConfigs = base.tierConfigs;
1847
+ if (!tierConfigs) return base;
1848
+ const targetTier = (features.taskType === "reasoning_mcq" || features.taskType === "reasoning_math") && (base.tier === "SIMPLE" || base.tier === "MEDIUM") ? "REASONING" : base.tier;
1849
+ const tierConfig = tierConfigs[targetTier];
1850
+ const configuredCandidates = tierConfig ? getFallbackChain(targetTier, tierConfigs) : [];
1851
+ const chain = [
1852
+ .../* @__PURE__ */ new Set([...configuredCandidates, ...evidenceCandidates(features.taskType)])
1853
+ ].filter((model2) => typeof model2 === "string" && model2.length > 0);
1854
+ const eligible = chain.filter(
1855
+ (model2) => isEligible(model2, features, maxOutputTokens, options)
1856
+ );
1857
+ const eligibleCandidates = eligible.length > 0 ? eligible : chain;
1858
+ if (eligibleCandidates.length === 0) return base;
1859
+ const profileName = options.routingProfile === "eco" ? "eco" : options.routingProfile === "premium" ? "premium" : "auto";
1860
+ const portfolio = options.config.portfolio ?? DEFAULT_PORTFOLIO_WEIGHTS;
1861
+ const getAffinity = (model2) => affinity(
1862
+ model2,
1863
+ features.taskType,
1864
+ features.language,
1865
+ features.agentDomain,
1866
+ features.deepWebResearch,
1867
+ features.agentRisk,
1868
+ features.terminalToolSignal,
1869
+ features.terminalSafetySensitive
1870
+ );
1871
+ const bestAffinity = Math.max(...eligibleCandidates.map(getAffinity));
1872
+ const specificAffinity = eligibleCandidates.filter((model2) => getAffinity(model2) > 0.68);
1873
+ const affinityPool = specificAffinity.length > 0 ? specificAffinity : [eligibleCandidates[0]];
1874
+ const affinityFloorGap = features.terminalToolSignal ? Math.max(
1875
+ portfolio.affinityFloorGap[profileName],
1876
+ features.terminalSafetySensitive ? 0.15 : 0.12
1877
+ ) : portfolio.affinityFloorGap[profileName];
1878
+ const candidates = affinityPool.filter(
1879
+ (model2) => getAffinity(model2) >= bestAffinity - affinityFloorGap
1880
+ );
1881
+ const costs = candidates.map(
1882
+ (model2) => estimatedCost(model2, options, features.estimatedInputTokens, maxOutputTokens)
1883
+ );
1884
+ const finiteCosts = costs.filter(Number.isFinite);
1885
+ const minCost = finiteCosts.length > 0 ? Math.min(...finiteCosts) : 0;
1886
+ const maxCost = finiteCosts.length > 0 ? Math.max(...finiteCosts) : 1;
1887
+ const now = options.now ?? /* @__PURE__ */ new Date();
1888
+ const profileWeights = portfolio[profileName];
1889
+ const rankedEntries = candidates.map((model2, index) => {
1890
+ const cost = estimatedCost(model2, options, features.estimatedInputTokens, maxOutputTokens);
1891
+ const costScore = Number.isFinite(cost) && maxCost > minCost ? 1 - (cost - minCost) / (maxCost - minCost) : 0.5;
1892
+ const capabilityScore = isEligible(model2, features, maxOutputTokens, options) ? 1 : 0;
1893
+ const profile = profileScore(model2, options, now);
1894
+ const observedQuality = profile?.quality === void 0 ? getAffinity(model2) : getAffinity(model2) * (1 - profile.freshness) + profile.quality * profile.freshness;
1895
+ const observedSpeed = profile ? profile.speed * profile.freshness : 0.5;
1896
+ const observedTailSpeed = profile ? profile.tailSpeed * profile.freshness : 0.5;
1897
+ const observedReliability = profile ? profile.reliability * profile.freshness + (1 - profile.freshness) : 1;
1898
+ const legacyScore = 1 - index / Math.max(1, candidates.length - 1);
1899
+ const qualityWeight = profileWeights.quality + (features.highStakes ? portfolio.highStakesBoost.quality : 0);
1900
+ const speedScore = features.latencySensitive ? observedTailSpeed : observedSpeed;
1901
+ const speedWeight = profileWeights.speed + (features.latencySensitive ? portfolio.latencySensitiveSpeedBoost : 0);
1902
+ const reliabilityWeight = profileWeights.reliability + (features.highStakes ? portfolio.highStakesBoost.reliability : 0);
1903
+ const score = observedQuality * qualityWeight + capabilityScore * profileWeights.capability + costScore * profileWeights.cost + speedScore * speedWeight + observedReliability * reliabilityWeight + legacyScore * profileWeights.legacy;
1904
+ return {
1905
+ model: model2,
1906
+ score,
1907
+ quality: observedQuality,
1908
+ cost: costScore,
1909
+ speed: speedScore,
1910
+ reliability: observedReliability
1911
+ };
1912
+ }).sort((a, b) => b.score - a.score);
1913
+ const scoredModels = rankedEntries.map((item) => item.model);
1914
+ const webResearchFallbackOrder = [
1915
+ "anthropic/claude-sonnet-5",
1916
+ "openai/gpt-5-mini",
1917
+ "google/gemini-3.5-flash",
1918
+ "anthropic/claude-opus-5",
1919
+ "anthropic/claude-opus-4.8",
1920
+ "openai/gpt-5.3-codex"
1921
+ ];
1922
+ const ranked = features.agentDomain === "web_research" ? [
1923
+ ...scoredModels,
1924
+ ...webResearchFallbackOrder.filter(
1925
+ (model2) => eligibleCandidates.includes(model2) && !scoredModels.includes(model2)
1926
+ ),
1927
+ ...eligibleCandidates.filter(
1928
+ (model2) => !scoredModels.includes(model2) && !webResearchFallbackOrder.includes(model2)
1929
+ )
1930
+ ] : features.taskType === "tool_agent" || features.taskType === "tool_agent_parallel" && features.agentDomain !== "other" ? [
1931
+ ...scoredModels,
1932
+ ...eligibleCandidates.filter((model2) => !scoredModels.includes(model2))
1933
+ ] : [
1934
+ ...scoredModels,
1935
+ ...eligibleCandidates.filter((model2) => !scoredModels.includes(model2))
1936
+ ];
1937
+ const model = ranked[0] ?? base.model;
1938
+ const selectedTierConfigs = {
1939
+ ...tierConfigs,
1940
+ [targetTier]: { primary: model, fallback: ranked.slice(1) }
1941
+ };
1942
+ const decision = selectModel(
1943
+ targetTier,
1944
+ base.confidence,
1945
+ "portfolio",
1946
+ `${base.reasoning} | v3 task=${features.taskType} agentRisk=${features.agentRisk} deepWebResearch=${features.deepWebResearch} terminalCode=${features.implicitTerminalCode} terminalSafety=${features.terminalSafetySensitive} candidates=${ranked.length}`,
1947
+ selectedTierConfigs,
1948
+ options.modelPricing,
1949
+ features.estimatedInputTokens,
1950
+ maxOutputTokens,
1951
+ options.routingProfile,
1952
+ base.agenticScore
1953
+ );
1954
+ return {
1955
+ ...decision,
1956
+ tierConfigs: selectedTierConfigs,
1957
+ profile: base.profile,
1958
+ candidates: ranked,
1959
+ candidateScores: rankedEntries.map(({ model: model2, score, quality, cost, speed, reliability }) => ({
1960
+ model: model2,
1961
+ score,
1962
+ quality,
1963
+ cost,
1964
+ speed,
1965
+ reliability
1966
+ })),
1967
+ taskType: features.taskType,
1968
+ routerVersion: "v3-portfolio"
1969
+ };
1970
+ }
1971
+ };
1972
+ var DEFAULT_ROUTING_CONFIG = {
1973
+ version: "3.4",
1974
+ strategy: "portfolio",
1975
+ portfolio: {
1976
+ auto: {
1977
+ quality: 0.47,
1978
+ capability: 0.2,
1979
+ cost: 0.18,
1980
+ speed: 0.07,
1981
+ reliability: 0.03,
1982
+ legacy: 0.05
1983
+ },
1984
+ eco: {
1985
+ quality: 0.36,
1986
+ capability: 0.2,
1987
+ cost: 0.28,
1988
+ speed: 0.1,
1989
+ reliability: 0.04,
1990
+ legacy: 0.02
1991
+ },
1992
+ premium: {
1993
+ quality: 0.58,
1994
+ capability: 0.2,
1995
+ cost: 0.08,
1996
+ speed: 0.06,
1997
+ reliability: 0.06,
1998
+ legacy: 0.02
1999
+ },
2000
+ highStakesBoost: { quality: 0.08, reliability: 0.05 },
2001
+ latencySensitiveSpeedBoost: 0.08,
2002
+ affinityFloorGap: { auto: 0.1, eco: 0.22, premium: 0.05 }
2003
+ },
2004
+ classifier: {
2005
+ llmModel: "google/gemini-2.5-flash",
2006
+ llmMaxTokens: 10,
2007
+ llmTemperature: 0,
2008
+ promptTruncationChars: 500,
2009
+ cacheTtlMs: 36e5
2010
+ // 1 hour
2011
+ },
2012
+ scoring: {
2013
+ tokenCountThresholds: { simple: 50, complex: 500 },
2014
+ // Multilingual keywords: EN + ZH + JA + RU + DE + ES + PT + KO + AR
2015
+ codeKeywords: [
2016
+ // English
2017
+ "function",
2018
+ "class",
2019
+ "import",
2020
+ "def",
2021
+ "SELECT",
2022
+ "async",
2023
+ "await",
2024
+ "const",
2025
+ "let",
2026
+ "var",
2027
+ "return",
2028
+ "```",
2029
+ // Chinese
2030
+ "\u51FD\u6570",
2031
+ "\u7C7B",
2032
+ "\u5BFC\u5165",
2033
+ "\u5B9A\u4E49",
2034
+ "\u67E5\u8BE2",
2035
+ "\u5F02\u6B65",
2036
+ "\u7B49\u5F85",
2037
+ "\u5E38\u91CF",
2038
+ "\u53D8\u91CF",
2039
+ "\u8FD4\u56DE",
2040
+ // Japanese
2041
+ "\u95A2\u6570",
2042
+ "\u30AF\u30E9\u30B9",
2043
+ "\u30A4\u30F3\u30DD\u30FC\u30C8",
2044
+ "\u975E\u540C\u671F",
2045
+ "\u5B9A\u6570",
2046
+ "\u5909\u6570",
2047
+ // Russian
2048
+ "\u0444\u0443\u043D\u043A\u0446\u0438\u044F",
2049
+ "\u043A\u043B\u0430\u0441\u0441",
2050
+ "\u0438\u043C\u043F\u043E\u0440\u0442",
2051
+ "\u043E\u043F\u0440\u0435\u0434\u0435\u043B",
2052
+ "\u0437\u0430\u043F\u0440\u043E\u0441",
2053
+ "\u0430\u0441\u0438\u043D\u0445\u0440\u043E\u043D\u043D\u044B\u0439",
2054
+ "\u043E\u0436\u0438\u0434\u0430\u0442\u044C",
2055
+ "\u043A\u043E\u043D\u0441\u0442\u0430\u043D\u0442\u0430",
2056
+ "\u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F",
2057
+ "\u0432\u0435\u0440\u043D\u0443\u0442\u044C",
2058
+ // German
2059
+ "funktion",
2060
+ "klasse",
2061
+ "importieren",
2062
+ "definieren",
2063
+ "abfrage",
2064
+ "asynchron",
2065
+ "erwarten",
2066
+ "konstante",
2067
+ "variable",
2068
+ "zur\xFCckgeben",
2069
+ // Spanish
2070
+ "funci\xF3n",
2071
+ "clase",
2072
+ "importar",
2073
+ "definir",
2074
+ "consulta",
2075
+ "as\xEDncrono",
2076
+ "esperar",
2077
+ "constante",
2078
+ "variable",
2079
+ "retornar",
2080
+ // Portuguese
2081
+ "fun\xE7\xE3o",
2082
+ "classe",
2083
+ "importar",
2084
+ "definir",
2085
+ "consulta",
2086
+ "ass\xEDncrono",
2087
+ "aguardar",
2088
+ "constante",
2089
+ "vari\xE1vel",
2090
+ "retornar",
2091
+ // Korean
2092
+ "\uD568\uC218",
2093
+ "\uD074\uB798\uC2A4",
2094
+ "\uAC00\uC838\uC624\uAE30",
2095
+ "\uC815\uC758",
2096
+ "\uCFFC\uB9AC",
2097
+ "\uBE44\uB3D9\uAE30",
2098
+ "\uB300\uAE30",
2099
+ "\uC0C1\uC218",
2100
+ "\uBCC0\uC218",
2101
+ "\uBC18\uD658",
2102
+ // Arabic
2103
+ "\u062F\u0627\u0644\u0629",
2104
+ "\u0641\u0626\u0629",
2105
+ "\u0627\u0633\u062A\u064A\u0631\u0627\u062F",
2106
+ "\u062A\u0639\u0631\u064A\u0641",
2107
+ "\u0627\u0633\u062A\u0639\u0644\u0627\u0645",
2108
+ "\u063A\u064A\u0631 \u0645\u062A\u0632\u0627\u0645\u0646",
2109
+ "\u0627\u0646\u062A\u0638\u0627\u0631",
2110
+ "\u062B\u0627\u0628\u062A",
2111
+ "\u0645\u062A\u063A\u064A\u0631",
2112
+ "\u0625\u0631\u062C\u0627\u0639"
2113
+ ],
2114
+ reasoningKeywords: [
2115
+ // English
2116
+ "prove",
2117
+ "theorem",
2118
+ "derive",
2119
+ "step by step",
2120
+ "chain of thought",
2121
+ "formally",
2122
+ "mathematical",
2123
+ "proof",
2124
+ "logically",
2125
+ // Chinese
2126
+ "\u8BC1\u660E",
2127
+ "\u5B9A\u7406",
2128
+ "\u63A8\u5BFC",
2129
+ "\u9010\u6B65",
2130
+ "\u601D\u7EF4\u94FE",
2131
+ "\u5F62\u5F0F\u5316",
2132
+ "\u6570\u5B66",
2133
+ "\u903B\u8F91",
2134
+ // Japanese
2135
+ "\u8A3C\u660E",
2136
+ "\u5B9A\u7406",
2137
+ "\u5C0E\u51FA",
2138
+ "\u30B9\u30C6\u30C3\u30D7\u30D0\u30A4\u30B9\u30C6\u30C3\u30D7",
2139
+ "\u8AD6\u7406\u7684",
2140
+ // Russian
2141
+ "\u0434\u043E\u043A\u0430\u0437\u0430\u0442\u044C",
2142
+ "\u0434\u043E\u043A\u0430\u0436\u0438",
2143
+ "\u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432",
2144
+ "\u0442\u0435\u043E\u0440\u0435\u043C\u0430",
2145
+ "\u0432\u044B\u0432\u0435\u0441\u0442\u0438",
2146
+ "\u0448\u0430\u0433 \u0437\u0430 \u0448\u0430\u0433\u043E\u043C",
2147
+ "\u043F\u043E\u0448\u0430\u0433\u043E\u0432\u043E",
2148
+ "\u043F\u043E\u044D\u0442\u0430\u043F\u043D\u043E",
2149
+ "\u0446\u0435\u043F\u043E\u0447\u043A\u0430 \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0439",
2150
+ "\u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0435\u043D\u0438",
2151
+ "\u0444\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E",
2152
+ "\u043C\u0430\u0442\u0435\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438",
2153
+ "\u043B\u043E\u0433\u0438\u0447\u0435\u0441\u043A\u0438",
2154
+ // German
2155
+ "beweisen",
2156
+ "beweis",
2157
+ "theorem",
2158
+ "ableiten",
2159
+ "schritt f\xFCr schritt",
2160
+ "gedankenkette",
2161
+ "formal",
2162
+ "mathematisch",
2163
+ "logisch",
2164
+ // Spanish
2165
+ "demostrar",
2166
+ "teorema",
2167
+ "derivar",
2168
+ "paso a paso",
2169
+ "cadena de pensamiento",
2170
+ "formalmente",
2171
+ "matem\xE1tico",
2172
+ "prueba",
2173
+ "l\xF3gicamente",
2174
+ // Portuguese
2175
+ "provar",
2176
+ "teorema",
2177
+ "derivar",
2178
+ "passo a passo",
2179
+ "cadeia de pensamento",
2180
+ "formalmente",
2181
+ "matem\xE1tico",
2182
+ "prova",
2183
+ "logicamente",
2184
+ // Korean
2185
+ "\uC99D\uBA85",
2186
+ "\uC815\uB9AC",
2187
+ "\uB3C4\uCD9C",
2188
+ "\uB2E8\uACC4\uBCC4",
2189
+ "\uC0AC\uACE0\uC758 \uC5F0\uC1C4",
2190
+ "\uD615\uC2DD\uC801",
2191
+ "\uC218\uD559\uC801",
2192
+ "\uB17C\uB9AC\uC801",
2193
+ // Arabic
2194
+ "\u0625\u062B\u0628\u0627\u062A",
2195
+ "\u0646\u0638\u0631\u064A\u0629",
2196
+ "\u0627\u0634\u062A\u0642\u0627\u0642",
2197
+ "\u062E\u0637\u0648\u0629 \u0628\u062E\u0637\u0648\u0629",
2198
+ "\u0633\u0644\u0633\u0644\u0629 \u0627\u0644\u062A\u0641\u0643\u064A\u0631",
2199
+ "\u0631\u0633\u0645\u064A\u0627\u064B",
2200
+ "\u0631\u064A\u0627\u0636\u064A",
2201
+ "\u0628\u0631\u0647\u0627\u0646",
2202
+ "\u0645\u0646\u0637\u0642\u064A\u0627\u064B"
2203
+ ],
2204
+ simpleKeywords: [
2205
+ // English
2206
+ "what is",
2207
+ "define",
2208
+ "translate",
2209
+ "hello",
2210
+ "yes or no",
2211
+ "capital of",
2212
+ "how old",
2213
+ "who is",
2214
+ "when was",
2215
+ // Chinese
2216
+ "\u4EC0\u4E48\u662F",
2217
+ "\u5B9A\u4E49",
2218
+ "\u7FFB\u8BD1",
2219
+ "\u4F60\u597D",
2220
+ "\u662F\u5426",
2221
+ "\u9996\u90FD",
2222
+ "\u591A\u5927",
2223
+ "\u8C01\u662F",
2224
+ "\u4F55\u65F6",
2225
+ // Japanese
2226
+ "\u3068\u306F",
2227
+ "\u5B9A\u7FA9",
2228
+ "\u7FFB\u8A33",
2229
+ "\u3053\u3093\u306B\u3061\u306F",
2230
+ "\u306F\u3044\u304B\u3044\u3044\u3048",
2231
+ "\u9996\u90FD",
2232
+ "\u8AB0",
2233
+ // Russian
2234
+ "\u0447\u0442\u043E \u0442\u0430\u043A\u043E\u0435",
2235
+ "\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435",
2236
+ "\u043F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438",
2237
+ "\u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0438",
2238
+ "\u043F\u0440\u0438\u0432\u0435\u0442",
2239
+ "\u0434\u0430 \u0438\u043B\u0438 \u043D\u0435\u0442",
2240
+ "\u0441\u0442\u043E\u043B\u0438\u0446\u0430",
2241
+ "\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043B\u0435\u0442",
2242
+ "\u043A\u0442\u043E \u0442\u0430\u043A\u043E\u0439",
2243
+ "\u043A\u043E\u0433\u0434\u0430",
2244
+ "\u043E\u0431\u044A\u044F\u0441\u043D\u0438",
2245
+ // German
2246
+ "was ist",
2247
+ "definiere",
2248
+ "\xFCbersetze",
2249
+ "hallo",
2250
+ "ja oder nein",
2251
+ "hauptstadt",
2252
+ "wie alt",
2253
+ "wer ist",
2254
+ "wann",
2255
+ "erkl\xE4re",
2256
+ // Spanish
2257
+ "qu\xE9 es",
2258
+ "definir",
2259
+ "traducir",
2260
+ "hola",
2261
+ "s\xED o no",
2262
+ "capital de",
2263
+ "cu\xE1ntos a\xF1os",
2264
+ "qui\xE9n es",
2265
+ "cu\xE1ndo",
2266
+ // Portuguese
2267
+ "o que \xE9",
2268
+ "definir",
2269
+ "traduzir",
2270
+ "ol\xE1",
2271
+ "sim ou n\xE3o",
2272
+ "capital de",
2273
+ "quantos anos",
2274
+ "quem \xE9",
2275
+ "quando",
2276
+ // Korean
2277
+ "\uBB34\uC5C7",
2278
+ "\uC815\uC758",
2279
+ "\uBC88\uC5ED",
2280
+ "\uC548\uB155\uD558\uC138\uC694",
2281
+ "\uC608 \uB610\uB294 \uC544\uB2C8\uC624",
2282
+ "\uC218\uB3C4",
2283
+ "\uB204\uAD6C",
2284
+ "\uC5B8\uC81C",
2285
+ // Arabic
2286
+ "\u0645\u0627 \u0647\u0648",
2287
+ "\u062A\u0639\u0631\u064A\u0641",
2288
+ "\u062A\u0631\u062C\u0645",
2289
+ "\u0645\u0631\u062D\u0628\u0627",
2290
+ "\u0646\u0639\u0645 \u0623\u0648 \u0644\u0627",
2291
+ "\u0639\u0627\u0635\u0645\u0629",
2292
+ "\u0645\u0646 \u0647\u0648",
2293
+ "\u0645\u062A\u0649"
2294
+ ],
2295
+ technicalKeywords: [
2296
+ // English
2297
+ "algorithm",
2298
+ "optimize",
2299
+ "architecture",
2300
+ "distributed",
2301
+ "kubernetes",
2302
+ "microservice",
2303
+ "database",
2304
+ "infrastructure",
2305
+ // Chinese
2306
+ "\u7B97\u6CD5",
2307
+ "\u4F18\u5316",
2308
+ "\u67B6\u6784",
2309
+ "\u5206\u5E03\u5F0F",
2310
+ "\u5FAE\u670D\u52A1",
2311
+ "\u6570\u636E\u5E93",
2312
+ "\u57FA\u7840\u8BBE\u65BD",
2313
+ // Japanese
2314
+ "\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0",
2315
+ "\u6700\u9069\u5316",
2316
+ "\u30A2\u30FC\u30AD\u30C6\u30AF\u30C1\u30E3",
2317
+ "\u5206\u6563",
2318
+ "\u30DE\u30A4\u30AF\u30ED\u30B5\u30FC\u30D3\u30B9",
2319
+ "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9",
2320
+ // Russian
2321
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C",
2322
+ "\u043E\u043F\u0442\u0438\u043C\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
2323
+ "\u043E\u043F\u0442\u0438\u043C\u0438\u0437\u0430\u0446\u0438",
2324
+ "\u043E\u043F\u0442\u0438\u043C\u0438\u0437\u0438\u0440\u0443\u0439",
2325
+ "\u0430\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u0430",
2326
+ "\u0440\u0430\u0441\u043F\u0440\u0435\u0434\u0435\u043B\u0451\u043D\u043D\u044B\u0439",
2327
+ "\u043C\u0438\u043A\u0440\u043E\u0441\u0435\u0440\u0432\u0438\u0441",
2328
+ "\u0431\u0430\u0437\u0430 \u0434\u0430\u043D\u043D\u044B\u0445",
2329
+ "\u0438\u043D\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430",
2330
+ // German
2331
+ "algorithmus",
2332
+ "optimieren",
2333
+ "architektur",
2334
+ "verteilt",
2335
+ "kubernetes",
2336
+ "mikroservice",
2337
+ "datenbank",
2338
+ "infrastruktur",
2339
+ // Spanish
2340
+ "algoritmo",
2341
+ "optimizar",
2342
+ "arquitectura",
2343
+ "distribuido",
2344
+ "microservicio",
2345
+ "base de datos",
2346
+ "infraestructura",
2347
+ // Portuguese
2348
+ "algoritmo",
2349
+ "otimizar",
2350
+ "arquitetura",
2351
+ "distribu\xEDdo",
2352
+ "microsservi\xE7o",
2353
+ "banco de dados",
2354
+ "infraestrutura",
2355
+ // Korean
2356
+ "\uC54C\uACE0\uB9AC\uC998",
2357
+ "\uCD5C\uC801\uD654",
2358
+ "\uC544\uD0A4\uD14D\uCC98",
2359
+ "\uBD84\uC0B0",
2360
+ "\uB9C8\uC774\uD06C\uB85C\uC11C\uBE44\uC2A4",
2361
+ "\uB370\uC774\uD130\uBCA0\uC774\uC2A4",
2362
+ "\uC778\uD504\uB77C",
2363
+ // Arabic
2364
+ "\u062E\u0648\u0627\u0631\u0632\u0645\u064A\u0629",
2365
+ "\u062A\u062D\u0633\u064A\u0646",
2366
+ "\u0628\u0646\u064A\u0629",
2367
+ "\u0645\u0648\u0632\u0639",
2368
+ "\u062E\u062F\u0645\u0629 \u0645\u0635\u063A\u0631\u0629",
2369
+ "\u0642\u0627\u0639\u062F\u0629 \u0628\u064A\u0627\u0646\u0627\u062A",
2370
+ "\u0628\u0646\u064A\u0629 \u062A\u062D\u062A\u064A\u0629"
2371
+ ],
2372
+ creativeKeywords: [
2373
+ // English
2374
+ "story",
2375
+ "poem",
2376
+ "compose",
2377
+ "brainstorm",
2378
+ "creative",
2379
+ "imagine",
2380
+ "write a",
2381
+ // Chinese
2382
+ "\u6545\u4E8B",
2383
+ "\u8BD7",
2384
+ "\u521B\u4F5C",
2385
+ "\u5934\u8111\u98CE\u66B4",
2386
+ "\u521B\u610F",
2387
+ "\u60F3\u8C61",
2388
+ "\u5199\u4E00\u4E2A",
2389
+ // Japanese
2390
+ "\u7269\u8A9E",
2391
+ "\u8A69",
2392
+ "\u4F5C\u66F2",
2393
+ "\u30D6\u30EC\u30A4\u30F3\u30B9\u30C8\u30FC\u30E0",
2394
+ "\u5275\u9020\u7684",
2395
+ "\u60F3\u50CF",
2396
+ // Russian
2397
+ "\u0438\u0441\u0442\u043E\u0440\u0438\u044F",
2398
+ "\u0440\u0430\u0441\u0441\u043A\u0430\u0437",
2399
+ "\u0441\u0442\u0438\u0445\u043E\u0442\u0432\u043E\u0440\u0435\u043D\u0438\u0435",
2400
+ "\u0441\u043E\u0447\u0438\u043D\u0438\u0442\u044C",
2401
+ "\u0441\u043E\u0447\u0438\u043D\u0438",
2402
+ "\u043C\u043E\u0437\u0433\u043E\u0432\u043E\u0439 \u0448\u0442\u0443\u0440\u043C",
2403
+ "\u0442\u0432\u043E\u0440\u0447\u0435\u0441\u043A\u0438\u0439",
2404
+ "\u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C",
2405
+ "\u043F\u0440\u0438\u0434\u0443\u043C\u0430\u0439",
2406
+ "\u043D\u0430\u043F\u0438\u0448\u0438",
2407
+ // German
2408
+ "geschichte",
2409
+ "gedicht",
2410
+ "komponieren",
2411
+ "brainstorming",
2412
+ "kreativ",
2413
+ "vorstellen",
2414
+ "schreibe",
2415
+ "erz\xE4hlung",
2416
+ // Spanish
2417
+ "historia",
2418
+ "poema",
2419
+ "componer",
2420
+ "lluvia de ideas",
2421
+ "creativo",
2422
+ "imaginar",
2423
+ "escribe",
2424
+ // Portuguese
2425
+ "hist\xF3ria",
2426
+ "poema",
2427
+ "compor",
2428
+ "criativo",
2429
+ "imaginar",
2430
+ "escreva",
2431
+ // Korean
2432
+ "\uC774\uC57C\uAE30",
2433
+ "\uC2DC",
2434
+ "\uC791\uACE1",
2435
+ "\uBE0C\uB808\uC778\uC2A4\uD1A0\uBC0D",
2436
+ "\uCC3D\uC758\uC801",
2437
+ "\uC0C1\uC0C1",
2438
+ "\uC791\uC131",
2439
+ // Arabic
2440
+ "\u0642\u0635\u0629",
2441
+ "\u0642\u0635\u064A\u062F\u0629",
2442
+ "\u062A\u0623\u0644\u064A\u0641",
2443
+ "\u0639\u0635\u0641 \u0630\u0647\u0646\u064A",
2444
+ "\u0625\u0628\u062F\u0627\u0639\u064A",
2445
+ "\u062A\u062E\u064A\u0644",
2446
+ "\u0627\u0643\u062A\u0628"
2447
+ ],
2448
+ // New dimension keyword lists (multilingual)
2449
+ imperativeVerbs: [
2450
+ // English
2451
+ "build",
2452
+ "create",
2453
+ "implement",
2454
+ "design",
2455
+ "develop",
2456
+ "construct",
2457
+ "generate",
2458
+ "deploy",
2459
+ "configure",
2460
+ "set up",
2461
+ // Chinese
2462
+ "\u6784\u5EFA",
2463
+ "\u521B\u5EFA",
2464
+ "\u5B9E\u73B0",
2465
+ "\u8BBE\u8BA1",
2466
+ "\u5F00\u53D1",
2467
+ "\u751F\u6210",
2468
+ "\u90E8\u7F72",
2469
+ "\u914D\u7F6E",
2470
+ "\u8BBE\u7F6E",
2471
+ // Japanese
2472
+ "\u69CB\u7BC9",
2473
+ "\u4F5C\u6210",
2474
+ "\u5B9F\u88C5",
2475
+ "\u8A2D\u8A08",
2476
+ "\u958B\u767A",
2477
+ "\u751F\u6210",
2478
+ "\u30C7\u30D7\u30ED\u30A4",
2479
+ "\u8A2D\u5B9A",
2480
+ // Russian
2481
+ "\u043F\u043E\u0441\u0442\u0440\u043E\u0438\u0442\u044C",
2482
+ "\u043F\u043E\u0441\u0442\u0440\u043E\u0439",
2483
+ "\u0441\u043E\u0437\u0434\u0430\u0442\u044C",
2484
+ "\u0441\u043E\u0437\u0434\u0430\u0439",
2485
+ "\u0440\u0435\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u0442\u044C",
2486
+ "\u0440\u0435\u0430\u043B\u0438\u0437\u0443\u0439",
2487
+ "\u0441\u043F\u0440\u043E\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
2488
+ "\u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C",
2489
+ "\u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0430\u0439",
2490
+ "\u0441\u043A\u043E\u043D\u0441\u0442\u0440\u0443\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
2491
+ "\u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
2492
+ "\u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439",
2493
+ "\u0440\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u0442\u044C",
2494
+ "\u0440\u0430\u0437\u0432\u0435\u0440\u043D\u0438",
2495
+ "\u043D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C",
2496
+ "\u043D\u0430\u0441\u0442\u0440\u043E\u0439",
2497
+ // German
2498
+ "erstellen",
2499
+ "bauen",
2500
+ "implementieren",
2501
+ "entwerfen",
2502
+ "entwickeln",
2503
+ "konstruieren",
2504
+ "generieren",
2505
+ "bereitstellen",
2506
+ "konfigurieren",
2507
+ "einrichten",
2508
+ // Spanish
2509
+ "construir",
2510
+ "crear",
2511
+ "implementar",
2512
+ "dise\xF1ar",
2513
+ "desarrollar",
2514
+ "generar",
2515
+ "desplegar",
2516
+ "configurar",
2517
+ // Portuguese
2518
+ "construir",
2519
+ "criar",
2520
+ "implementar",
2521
+ "projetar",
2522
+ "desenvolver",
2523
+ "gerar",
2524
+ "implantar",
2525
+ "configurar",
2526
+ // Korean
2527
+ "\uAD6C\uCD95",
2528
+ "\uC0DD\uC131",
2529
+ "\uAD6C\uD604",
2530
+ "\uC124\uACC4",
2531
+ "\uAC1C\uBC1C",
2532
+ "\uBC30\uD3EC",
2533
+ "\uC124\uC815",
2534
+ // Arabic
2535
+ "\u0628\u0646\u0627\u0621",
2536
+ "\u0625\u0646\u0634\u0627\u0621",
2537
+ "\u062A\u0646\u0641\u064A\u0630",
2538
+ "\u062A\u0635\u0645\u064A\u0645",
2539
+ "\u062A\u0637\u0648\u064A\u0631",
2540
+ "\u062A\u0648\u0644\u064A\u062F",
2541
+ "\u0646\u0634\u0631",
2542
+ "\u0625\u0639\u062F\u0627\u062F"
2543
+ ],
2544
+ constraintIndicators: [
2545
+ // English
2546
+ "under",
2547
+ "at most",
2548
+ "at least",
2549
+ "within",
2550
+ "no more than",
2551
+ "o(",
2552
+ "maximum",
2553
+ "minimum",
2554
+ "limit",
2555
+ "budget",
2556
+ // Chinese
2557
+ "\u4E0D\u8D85\u8FC7",
2558
+ "\u81F3\u5C11",
2559
+ "\u6700\u591A",
2560
+ "\u5728\u5185",
2561
+ "\u6700\u5927",
2562
+ "\u6700\u5C0F",
2563
+ "\u9650\u5236",
2564
+ "\u9884\u7B97",
2565
+ // Japanese
2566
+ "\u4EE5\u4E0B",
2567
+ "\u6700\u5927",
2568
+ "\u6700\u5C0F",
2569
+ "\u5236\u9650",
2570
+ "\u4E88\u7B97",
2571
+ // Russian
2572
+ "\u043D\u0435 \u0431\u043E\u043B\u0435\u0435",
2573
+ "\u043D\u0435 \u043C\u0435\u043D\u0435\u0435",
2574
+ "\u043A\u0430\u043A \u043C\u0438\u043D\u0438\u043C\u0443\u043C",
2575
+ "\u0432 \u043F\u0440\u0435\u0434\u0435\u043B\u0430\u0445",
2576
+ "\u043C\u0430\u043A\u0441\u0438\u043C\u0443\u043C",
2577
+ "\u043C\u0438\u043D\u0438\u043C\u0443\u043C",
2578
+ "\u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u0435",
2579
+ "\u0431\u044E\u0434\u0436\u0435\u0442",
2580
+ // German
2581
+ "h\xF6chstens",
2582
+ "mindestens",
2583
+ "innerhalb",
2584
+ "nicht mehr als",
2585
+ "maximal",
2586
+ "minimal",
2587
+ "grenze",
2588
+ "budget",
2589
+ // Spanish
2590
+ "como m\xE1ximo",
2591
+ "al menos",
2592
+ "dentro de",
2593
+ "no m\xE1s de",
2594
+ "m\xE1ximo",
2595
+ "m\xEDnimo",
2596
+ "l\xEDmite",
2597
+ "presupuesto",
2598
+ // Portuguese
2599
+ "no m\xE1ximo",
2600
+ "pelo menos",
2601
+ "dentro de",
2602
+ "n\xE3o mais que",
2603
+ "m\xE1ximo",
2604
+ "m\xEDnimo",
2605
+ "limite",
2606
+ "or\xE7amento",
2607
+ // Korean
2608
+ "\uC774\uD558",
2609
+ "\uC774\uC0C1",
2610
+ "\uCD5C\uB300",
2611
+ "\uCD5C\uC18C",
2612
+ "\uC81C\uD55C",
2613
+ "\uC608\uC0B0",
2614
+ // Arabic
2615
+ "\u0639\u0644\u0649 \u0627\u0644\u0623\u0643\u062B\u0631",
2616
+ "\u0639\u0644\u0649 \u0627\u0644\u0623\u0642\u0644",
2617
+ "\u0636\u0645\u0646",
2618
+ "\u0644\u0627 \u064A\u0632\u064A\u062F \u0639\u0646",
2619
+ "\u0623\u0642\u0635\u0649",
2620
+ "\u0623\u062F\u0646\u0649",
2621
+ "\u062D\u062F",
2622
+ "\u0645\u064A\u0632\u0627\u0646\u064A\u0629"
2623
+ ],
2624
+ outputFormatKeywords: [
2625
+ // English
2626
+ "json",
2627
+ "yaml",
2628
+ "xml",
2629
+ "table",
2630
+ "csv",
2631
+ "markdown",
2632
+ "schema",
2633
+ "format as",
2634
+ "structured",
2635
+ // Chinese
2636
+ "\u8868\u683C",
2637
+ "\u683C\u5F0F\u5316\u4E3A",
2638
+ "\u7ED3\u6784\u5316",
2639
+ // Japanese
2640
+ "\u30C6\u30FC\u30D6\u30EB",
2641
+ "\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8",
2642
+ "\u69CB\u9020\u5316",
2643
+ // Russian
2644
+ "\u0442\u0430\u0431\u043B\u0438\u0446\u0430",
2645
+ "\u0444\u043E\u0440\u043C\u0430\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043A\u0430\u043A",
2646
+ "\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439",
2647
+ // German
2648
+ "tabelle",
2649
+ "formatieren als",
2650
+ "strukturiert",
2651
+ // Spanish
2652
+ "tabla",
2653
+ "formatear como",
2654
+ "estructurado",
2655
+ // Portuguese
2656
+ "tabela",
2657
+ "formatar como",
2658
+ "estruturado",
2659
+ // Korean
2660
+ "\uD14C\uC774\uBE14",
2661
+ "\uD615\uC2DD",
2662
+ "\uAD6C\uC870\uD654",
2663
+ // Arabic
2664
+ "\u062C\u062F\u0648\u0644",
2665
+ "\u062A\u0646\u0633\u064A\u0642",
2666
+ "\u0645\u0646\u0638\u0645"
2667
+ ],
2668
+ referenceKeywords: [
2669
+ // English
2670
+ "above",
2671
+ "below",
2672
+ "previous",
2673
+ "following",
2674
+ "the docs",
2675
+ "the api",
2676
+ "the code",
2677
+ "earlier",
2678
+ "attached",
2679
+ // Chinese
2680
+ "\u4E0A\u9762",
2681
+ "\u4E0B\u9762",
2682
+ "\u4E4B\u524D",
2683
+ "\u63A5\u4E0B\u6765",
2684
+ "\u6587\u6863",
2685
+ "\u4EE3\u7801",
2686
+ "\u9644\u4EF6",
2687
+ // Japanese
2688
+ "\u4E0A\u8A18",
2689
+ "\u4E0B\u8A18",
2690
+ "\u524D\u306E",
2691
+ "\u6B21\u306E",
2692
+ "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
2693
+ "\u30B3\u30FC\u30C9",
2694
+ // Russian
2695
+ "\u0432\u044B\u0448\u0435",
2696
+ "\u043D\u0438\u0436\u0435",
2697
+ "\u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0439",
2698
+ "\u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439",
2699
+ "\u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u044F",
2700
+ "\u043A\u043E\u0434",
2701
+ "\u0440\u0430\u043D\u0435\u0435",
2702
+ "\u0432\u043B\u043E\u0436\u0435\u043D\u0438\u0435",
2703
+ // German
2704
+ "oben",
2705
+ "unten",
2706
+ "vorherige",
2707
+ "folgende",
2708
+ "dokumentation",
2709
+ "der code",
2710
+ "fr\xFCher",
2711
+ "anhang",
2712
+ // Spanish
2713
+ "arriba",
2714
+ "abajo",
2715
+ "anterior",
2716
+ "siguiente",
2717
+ "documentaci\xF3n",
2718
+ "el c\xF3digo",
2719
+ "adjunto",
2720
+ // Portuguese
2721
+ "acima",
2722
+ "abaixo",
2723
+ "anterior",
2724
+ "seguinte",
2725
+ "documenta\xE7\xE3o",
2726
+ "o c\xF3digo",
2727
+ "anexo",
2728
+ // Korean
2729
+ "\uC704",
2730
+ "\uC544\uB798",
2731
+ "\uC774\uC804",
2732
+ "\uB2E4\uC74C",
2733
+ "\uBB38\uC11C",
2734
+ "\uCF54\uB4DC",
2735
+ "\uCCA8\uBD80",
2736
+ // Arabic
2737
+ "\u0623\u0639\u0644\u0627\u0647",
2738
+ "\u0623\u062F\u0646\u0627\u0647",
2739
+ "\u0627\u0644\u0633\u0627\u0628\u0642",
2740
+ "\u0627\u0644\u062A\u0627\u0644\u064A",
2741
+ "\u0627\u0644\u0648\u062B\u0627\u0626\u0642",
2742
+ "\u0627\u0644\u0643\u0648\u062F",
2743
+ "\u0645\u0631\u0641\u0642"
2744
+ ],
2745
+ negationKeywords: [
2746
+ // English
2747
+ "don't",
2748
+ "do not",
2749
+ "avoid",
2750
+ "never",
2751
+ "without",
2752
+ "except",
2753
+ "exclude",
2754
+ "no longer",
2755
+ // Chinese
2756
+ "\u4E0D\u8981",
2757
+ "\u907F\u514D",
2758
+ "\u4ECE\u4E0D",
2759
+ "\u6CA1\u6709",
2760
+ "\u9664\u4E86",
2761
+ "\u6392\u9664",
2762
+ // Japanese
2763
+ "\u3057\u306A\u3044\u3067",
2764
+ "\u907F\u3051\u308B",
2765
+ "\u6C7A\u3057\u3066",
2766
+ "\u306A\u3057\u3067",
2767
+ "\u9664\u304F",
2768
+ // Russian
2769
+ "\u043D\u0435 \u0434\u0435\u043B\u0430\u0439",
2770
+ "\u043D\u0435 \u043D\u0430\u0434\u043E",
2771
+ "\u043D\u0435\u043B\u044C\u0437\u044F",
2772
+ "\u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044C",
2773
+ "\u043D\u0438\u043A\u043E\u0433\u0434\u0430",
2774
+ "\u0431\u0435\u0437",
2775
+ "\u043A\u0440\u043E\u043C\u0435",
2776
+ "\u0438\u0441\u043A\u043B\u044E\u0447\u0438\u0442\u044C",
2777
+ "\u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435",
2778
+ // German
2779
+ "nicht",
2780
+ "vermeide",
2781
+ "niemals",
2782
+ "ohne",
2783
+ "au\xDFer",
2784
+ "ausschlie\xDFen",
2785
+ "nicht mehr",
2786
+ // Spanish
2787
+ "no hagas",
2788
+ "evitar",
2789
+ "nunca",
2790
+ "sin",
2791
+ "excepto",
2792
+ "excluir",
2793
+ // Portuguese
2794
+ "n\xE3o fa\xE7a",
2795
+ "evitar",
2796
+ "nunca",
2797
+ "sem",
2798
+ "exceto",
2799
+ "excluir",
2800
+ // Korean
2801
+ "\uD558\uC9C0 \uB9C8",
2802
+ "\uD53C\uD558\uB2E4",
2803
+ "\uC808\uB300",
2804
+ "\uC5C6\uC774",
2805
+ "\uC81C\uC678",
2806
+ // Arabic
2807
+ "\u0644\u0627 \u062A\u0641\u0639\u0644",
2808
+ "\u062A\u062C\u0646\u0628",
2809
+ "\u0623\u0628\u062F\u0627\u064B",
2810
+ "\u0628\u062F\u0648\u0646",
2811
+ "\u0628\u0627\u0633\u062A\u062B\u0646\u0627\u0621",
2812
+ "\u0627\u0633\u062A\u0628\u0639\u0627\u062F"
2813
+ ],
2814
+ domainSpecificKeywords: [
2815
+ // English
2816
+ "quantum",
2817
+ "fpga",
2818
+ "vlsi",
2819
+ "risc-v",
2820
+ "asic",
2821
+ "photonics",
2822
+ "genomics",
2823
+ "proteomics",
2824
+ "topological",
2825
+ "homomorphic",
2826
+ "zero-knowledge",
2827
+ "lattice-based",
2828
+ // Chinese
2829
+ "\u91CF\u5B50",
2830
+ "\u5149\u5B50\u5B66",
2831
+ "\u57FA\u56E0\u7EC4\u5B66",
2832
+ "\u86CB\u767D\u8D28\u7EC4\u5B66",
2833
+ "\u62D3\u6251",
2834
+ "\u540C\u6001",
2835
+ "\u96F6\u77E5\u8BC6",
2836
+ "\u683C\u5BC6\u7801",
2837
+ // Japanese
2838
+ "\u91CF\u5B50",
2839
+ "\u30D5\u30A9\u30C8\u30CB\u30AF\u30B9",
2840
+ "\u30B2\u30CE\u30DF\u30AF\u30B9",
2841
+ "\u30C8\u30DD\u30ED\u30B8\u30AB\u30EB",
2842
+ // Russian
2843
+ "\u043A\u0432\u0430\u043D\u0442\u043E\u0432\u044B\u0439",
2844
+ "\u0444\u043E\u0442\u043E\u043D\u0438\u043A\u0430",
2845
+ "\u0433\u0435\u043D\u043E\u043C\u0438\u043A\u0430",
2846
+ "\u043F\u0440\u043E\u0442\u0435\u043E\u043C\u0438\u043A\u0430",
2847
+ "\u0442\u043E\u043F\u043E\u043B\u043E\u0433\u0438\u0447\u0435\u0441\u043A\u0438\u0439",
2848
+ "\u0433\u043E\u043C\u043E\u043C\u043E\u0440\u0444\u043D\u044B\u0439",
2849
+ "\u0441 \u043D\u0443\u043B\u0435\u0432\u044B\u043C \u0440\u0430\u0437\u0433\u043B\u0430\u0448\u0435\u043D\u0438\u0435\u043C",
2850
+ "\u043D\u0430 \u043E\u0441\u043D\u043E\u0432\u0435 \u0440\u0435\u0448\u0451\u0442\u043E\u043A",
2851
+ // German
2852
+ "quanten",
2853
+ "photonik",
2854
+ "genomik",
2855
+ "proteomik",
2856
+ "topologisch",
2857
+ "homomorph",
2858
+ "zero-knowledge",
2859
+ "gitterbasiert",
2860
+ // Spanish
2861
+ "cu\xE1ntico",
2862
+ "fot\xF3nica",
2863
+ "gen\xF3mica",
2864
+ "prote\xF3mica",
2865
+ "topol\xF3gico",
2866
+ "homom\xF3rfico",
2867
+ // Portuguese
2868
+ "qu\xE2ntico",
2869
+ "fot\xF4nica",
2870
+ "gen\xF4mica",
2871
+ "prote\xF4mica",
2872
+ "topol\xF3gico",
2873
+ "homom\xF3rfico",
2874
+ // Korean
2875
+ "\uC591\uC790",
2876
+ "\uD3EC\uD1A0\uB2C9\uC2A4",
2877
+ "\uC720\uC804\uCCB4\uD559",
2878
+ "\uC704\uC0C1",
2879
+ "\uB3D9\uD615",
2880
+ // Arabic
2881
+ "\u0643\u0645\u064A",
2882
+ "\u0636\u0648\u0626\u064A\u0627\u062A",
2883
+ "\u062C\u064A\u0646\u0648\u0645\u064A\u0627\u062A",
2884
+ "\u0637\u0648\u0628\u0648\u0644\u0648\u062C\u064A",
2885
+ "\u062A\u0645\u0627\u062B\u0644\u064A"
2886
+ ],
2887
+ // Agentic task keywords - file ops, execution, multi-step, iterative work
2888
+ // Pruned: removed overly common words like "then", "first", "run", "test", "build"
2889
+ agenticTaskKeywords: [
2890
+ // English - File operations (clearly agentic)
2891
+ "read file",
2892
+ "read the file",
2893
+ "look at",
2894
+ "check the",
2895
+ "open the",
2896
+ "edit",
2897
+ "modify",
2898
+ "update the",
2899
+ "change the",
2900
+ "write to",
2901
+ "create file",
2902
+ // English - Execution (specific commands only)
2903
+ "execute",
2904
+ "deploy",
2905
+ "install",
2906
+ "npm",
2907
+ "pip",
2908
+ "compile",
2909
+ // English - Multi-step patterns (specific only)
2910
+ "after that",
2911
+ "and also",
2912
+ "once done",
2913
+ "step 1",
2914
+ "step 2",
2915
+ // English - Iterative work
2916
+ "fix",
2917
+ "debug",
2918
+ "until it works",
2919
+ "keep trying",
2920
+ "iterate",
2921
+ "make sure",
2922
+ "verify",
2923
+ "confirm",
2924
+ // Chinese (keep specific ones)
2925
+ "\u8BFB\u53D6\u6587\u4EF6",
2926
+ "\u67E5\u770B",
2927
+ "\u6253\u5F00",
2928
+ "\u7F16\u8F91",
2929
+ "\u4FEE\u6539",
2930
+ "\u66F4\u65B0",
2931
+ "\u521B\u5EFA",
2932
+ "\u6267\u884C",
2933
+ "\u90E8\u7F72",
2934
+ "\u5B89\u88C5",
2935
+ "\u7B2C\u4E00\u6B65",
2936
+ "\u7B2C\u4E8C\u6B65",
2937
+ "\u4FEE\u590D",
2938
+ "\u8C03\u8BD5",
2939
+ "\u76F4\u5230",
2940
+ "\u786E\u8BA4",
2941
+ "\u9A8C\u8BC1",
2942
+ // Spanish
2943
+ "leer archivo",
2944
+ "editar",
2945
+ "modificar",
2946
+ "actualizar",
2947
+ "ejecutar",
2948
+ "desplegar",
2949
+ "instalar",
2950
+ "paso 1",
2951
+ "paso 2",
2952
+ "arreglar",
2953
+ "depurar",
2954
+ "verificar",
2955
+ // Portuguese
2956
+ "ler arquivo",
2957
+ "editar",
2958
+ "modificar",
2959
+ "atualizar",
2960
+ "executar",
2961
+ "implantar",
2962
+ "instalar",
2963
+ "passo 1",
2964
+ "passo 2",
2965
+ "corrigir",
2966
+ "depurar",
2967
+ "verificar",
2968
+ // Korean
2969
+ "\uD30C\uC77C \uC77D\uAE30",
2970
+ "\uD3B8\uC9D1",
2971
+ "\uC218\uC815",
2972
+ "\uC5C5\uB370\uC774\uD2B8",
2973
+ "\uC2E4\uD589",
2974
+ "\uBC30\uD3EC",
2975
+ "\uC124\uCE58",
2976
+ "\uB2E8\uACC4 1",
2977
+ "\uB2E8\uACC4 2",
2978
+ "\uB514\uBC84\uADF8",
2979
+ "\uD655\uC778",
2980
+ // Arabic
2981
+ "\u0642\u0631\u0627\u0621\u0629 \u0645\u0644\u0641",
2982
+ "\u062A\u062D\u0631\u064A\u0631",
2983
+ "\u062A\u0639\u062F\u064A\u0644",
2984
+ "\u062A\u062D\u062F\u064A\u062B",
2985
+ "\u062A\u0646\u0641\u064A\u0630",
2986
+ "\u0646\u0634\u0631",
2987
+ "\u062A\u062B\u0628\u064A\u062A",
2988
+ "\u0627\u0644\u062E\u0637\u0648\u0629 1",
2989
+ "\u0627\u0644\u062E\u0637\u0648\u0629 2",
2990
+ "\u0625\u0635\u0644\u0627\u062D",
2991
+ "\u062A\u0635\u062D\u064A\u062D",
2992
+ "\u062A\u062D\u0642\u0642"
2993
+ ],
2994
+ // Dimension weights (sum to 1.0)
2995
+ dimensionWeights: {
2996
+ tokenCount: 0.08,
2997
+ codePresence: 0.15,
2998
+ reasoningMarkers: 0.18,
2999
+ technicalTerms: 0.1,
3000
+ creativeMarkers: 0.05,
3001
+ simpleIndicators: 0.02,
3002
+ // Reduced from 0.12 to make room for agenticTask
3003
+ multiStepPatterns: 0.12,
3004
+ questionComplexity: 0.05,
3005
+ imperativeVerbs: 0.03,
3006
+ constraintCount: 0.04,
3007
+ outputFormat: 0.03,
3008
+ referenceComplexity: 0.02,
3009
+ negationComplexity: 0.01,
3010
+ domainSpecificity: 0.02,
3011
+ agenticTask: 0.04
3012
+ // Reduced - agentic signals influence tier selection, not dominate it
3013
+ },
3014
+ // Tier boundaries on weighted score axis
3015
+ tierBoundaries: {
3016
+ simpleMedium: 0,
3017
+ mediumComplex: 0.3,
3018
+ // Raised from 0.18 - prevent simple tasks from reaching expensive COMPLEX tier
3019
+ complexReasoning: 0.5
3020
+ // Raised from 0.4 - reserve for true reasoning tasks
3021
+ },
3022
+ // Sigmoid steepness for confidence calibration
3023
+ confidenceSteepness: 12,
3024
+ // Below this confidence → ambiguous (null tier)
3025
+ confidenceThreshold: 0.7
3026
+ },
3027
+ // Auto (balanced) tier configs - current default smart routing
3028
+ // Benchmark-tuned 2026-03-16: balancing quality (retention) + latency
3029
+ tiers: {
3030
+ SIMPLE: {
3031
+ primary: "google/gemini-2.5-flash",
3032
+ // 1,238ms, IQ 20, 60% retention (best) — fast AND quality
3033
+ fallback: [
3034
+ "google/gemini-3-flash-preview",
3035
+ // 1,398ms, IQ 46 — smarter fallback
3036
+ "deepseek/deepseek-chat",
3037
+ // V4 Flash chat ($0.20/$0.40, 1M ctx) — repriced 2026-04-24
3038
+ "moonshot/kimi-k2.5",
3039
+ // 1,646ms, IQ 47, strong quality
3040
+ "google/gemini-3.1-flash-lite",
3041
+ // $0.25/$1.50, 1M context — newest flash-lite
3042
+ "google/gemini-2.5-flash-lite",
3043
+ // 1,353ms, $0.10/$0.40
3044
+ "openai/gpt-5.4-nano",
3045
+ // $0.20/$1.25, 1M context
3046
+ "xai/grok-4-fast-non-reasoning",
3047
+ // 1,143ms, $0.20/$0.50 — fast fallback
3048
+ "free/gpt-oss-120b"
3049
+ // 1,252ms, FREE fallback (hidden from /v1/models but direct calls work)
3050
+ ]
3051
+ },
3052
+ MEDIUM: {
3053
+ primary: "moonshot/kimi-k2.7",
3054
+ // $0.95/$4.00, 256K ctx, multi-modal + reasoning — Moonshot flagship; promoted from K2.6 (2026-06-14) after BlockRun added K2.7 + hid K2.6. Same price as K2.6.
3055
+ fallback: [
3056
+ "moonshot/kimi-k2.6",
3057
+ // identical-cost in-family hot swap (K2.6 still routable)
3058
+ "moonshot/kimi-k2.5",
3059
+ // $0.60/$3.00 — graceful-degradation backstop
3060
+ "google/gemini-3-flash-preview",
3061
+ // 1,398ms, IQ 46 — nearly same IQ, faster + cheaper
3062
+ "deepseek/deepseek-chat",
3063
+ // 1,431ms, IQ 32, 41% retention
3064
+ "google/gemini-2.5-flash",
3065
+ // 1,238ms, 60% retention
3066
+ "google/gemini-3.1-flash-lite",
3067
+ // $0.25/$1.50, 1M context
3068
+ "google/gemini-2.5-flash-lite",
3069
+ // 1,353ms, $0.10/$0.40
3070
+ "xai/grok-4-1-fast-non-reasoning",
3071
+ // 1,244ms, fast fallback
3072
+ "xai/grok-3-mini"
3073
+ // 1,202ms, $0.30/$0.50
3074
+ ]
3075
+ },
3076
+ COMPLEX: {
3077
+ primary: "google/gemini-3.1-pro",
3078
+ // 1,609ms, IQ 57 — fast flagship quality
3079
+ fallback: [
3080
+ "google/gemini-3-flash-preview",
3081
+ // 1,398ms, IQ 46 — fast + smart
3082
+ "xai/grok-4-0709",
3083
+ // 1,348ms, IQ 41
3084
+ "google/gemini-2.5-pro",
3085
+ // 1,294ms
3086
+ "anthropic/claude-sonnet-5",
3087
+ // near-Opus quality at Sonnet cost, 1M ctx
3088
+ "anthropic/claude-sonnet-4.6",
3089
+ // 2,110ms, IQ 52 — quality fallback
3090
+ "deepseek/deepseek-chat",
3091
+ // 1,431ms, IQ 32
3092
+ "google/gemini-2.5-flash",
3093
+ // 1,238ms, IQ 20 — cheap last resort
3094
+ "openai/gpt-5.6-terra",
3095
+ // GPT-5.6 balanced tier — newest generation, stable (Sol excluded: #202)
3096
+ "openai/gpt-5.5",
3097
+ // Prior OpenAI flagship — 1M+ ctx, native agent + computer use; benchmark TBD
3098
+ "openai/gpt-5.4"
3099
+ // 6,213ms, IQ 57 — previous flagship, benchmarked
3100
+ ]
3101
+ },
3102
+ REASONING: {
3103
+ primary: "xai/grok-4-1-fast-reasoning",
3104
+ // 1,454ms, $0.20/$0.50
3105
+ fallback: [
3106
+ "xai/grok-4-fast-reasoning",
3107
+ // 1,298ms, $0.20/$0.50
3108
+ "deepseek/deepseek-reasoner",
3109
+ // V4 Flash thinking ($0.20/$0.40, 1M ctx)
3110
+ "deepseek/deepseek-v4-pro",
3111
+ // V4 Pro flagship ($0.50/$1.00 promo through 2026-05-31, list $2/$4) — strongest open-weight reasoner
3112
+ "openai/o4-mini",
3113
+ // 2,328ms ($1.10/$4.40)
3114
+ "openai/o3"
3115
+ // 2,862ms
3116
+ ]
3117
+ }
3118
+ },
3119
+ // Eco tier configs - absolute cheapest (blockrun/eco)
3120
+ ecoTiers: {
3121
+ SIMPLE: {
3122
+ primary: "free/gpt-oss-120b",
3123
+ // FREE! $0.00/$0.00 — heavy user default
3124
+ fallback: [
3125
+ "free/gpt-oss-20b",
3126
+ // FREE — smaller, faster
3127
+ "free/deepseek-v4-flash",
3128
+ // FREE — 1M ctx; slow (~10 tok/s, 07-28 probe) but completes
3129
+ // seed-oss-36b sat here as the free coder until it EOL'd 2026-08-03 (HTTP 410).
3130
+ // gpt-oss-120b/20b already head this chain, so the rung is dropped, not retargeted.
3131
+ "google/gemini-3.1-flash-lite",
3132
+ // $0.25/$1.50 — newest flash-lite
3133
+ "openai/gpt-5.4-nano",
3134
+ // $0.20/$1.25 — fast nano
3135
+ "google/gemini-2.5-flash-lite",
3136
+ // $0.10/$0.40
3137
+ "xai/grok-4-fast-non-reasoning"
3138
+ // $0.20/$0.50
3139
+ ]
3140
+ },
3141
+ MEDIUM: {
3142
+ primary: "google/gemini-3.1-flash-lite",
3143
+ // $0.25/$1.50 — newest flash-lite
3144
+ fallback: [
3145
+ "openai/gpt-5.4-nano",
3146
+ // $0.20/$1.25
3147
+ "google/gemini-2.5-flash-lite",
3148
+ // $0.10/$0.40
3149
+ "xai/grok-4-fast-non-reasoning",
3150
+ "google/gemini-2.5-flash"
3151
+ ]
3152
+ },
3153
+ COMPLEX: {
3154
+ primary: "google/gemini-3.1-flash-lite",
3155
+ // $0.25/$1.50
3156
+ fallback: [
3157
+ "google/gemini-2.5-flash-lite",
3158
+ "xai/grok-4-0709",
3159
+ "google/gemini-2.5-flash",
3160
+ "deepseek/deepseek-chat"
3161
+ ]
3162
+ },
3163
+ REASONING: {
3164
+ primary: "xai/grok-4-1-fast-reasoning",
3165
+ // $0.20/$0.50
3166
+ fallback: [
3167
+ "xai/grok-4-fast-reasoning",
3168
+ "deepseek/deepseek-reasoner",
3169
+ // V4 Flash thinking — $0.20/$0.40
3170
+ "deepseek/deepseek-v4-pro"
3171
+ // V4 Pro flagship — $0.50/$1.00 promo, post-promo $2/$4
3172
+ ]
3173
+ }
3174
+ },
3175
+ // Premium tier configs - best quality (blockrun/premium)
3176
+ // codex=complex coding, kimi=simple coding, sonnet=reasoning/instructions, opus=architecture/PM/audits
3177
+ premiumTiers: {
3178
+ SIMPLE: {
3179
+ primary: "moonshot/kimi-k2.7",
3180
+ // $0.95/$4.00 - Moonshot flagship (256K ctx, multi-modal + reasoning); promoted from K2.6 (2026-06-14), same price
3181
+ fallback: [
3182
+ "moonshot/kimi-k2.6",
3183
+ // identical-cost in-family hot swap (K2.6 still routable)
3184
+ "moonshot/kimi-k2.5",
3185
+ // $0.60/$3.00 - proven reliable backstop when Moonshot direct API falters
3186
+ "google/gemini-2.5-flash",
3187
+ // 60% retention, fast growth
3188
+ "anthropic/claude-haiku-4.5",
3189
+ "google/gemini-2.5-flash-lite",
3190
+ "deepseek/deepseek-chat"
3191
+ ]
3192
+ },
3193
+ MEDIUM: {
3194
+ primary: "openai/gpt-5.3-codex",
3195
+ // $1.75/$14 - 400K context, 128K output, replaces 5.2
3196
+ fallback: [
3197
+ "moonshot/kimi-k2.7",
3198
+ // Moonshot flagship
3199
+ "moonshot/kimi-k2.6",
3200
+ "moonshot/kimi-k2.5",
3201
+ "google/gemini-2.5-flash",
3202
+ // 60% retention, good coding capability
3203
+ "google/gemini-2.5-pro",
3204
+ "xai/grok-4-0709",
3205
+ "anthropic/claude-sonnet-5",
3206
+ "anthropic/claude-sonnet-4.6"
3207
+ ]
3208
+ },
3209
+ COMPLEX: {
3210
+ // fable-5 was promoted here 2026-06-11, force-reverted 2026-06-13 when Anthropic
3211
+ // withdrew the offer, and restored 2026-07-14 now that BlockRun has relisted it.
3212
+ primary: "anthropic/claude-fable-5",
3213
+ // Best quality for complex tasks — Mythos-class flagship above Opus ($10/$50, 1M ctx, always-on thinking)
3214
+ // Fallback chain de-Gemini'd 2026-04-22: when Anthropic 503s, Gemini is
3215
+ // also prone to "high demand" 503s (correlated failure — everyone falls
3216
+ // back to Google at the same time). Prefer xAI Grok → Moonshot → OpenAI
3217
+ // flagship → DeepSeek → NVIDIA free instead.
3218
+ fallback: [
3219
+ "anthropic/claude-opus-5",
3220
+ // in-family hot swap first (half the price, 1M ctx + adaptive thinking)
3221
+ "anthropic/claude-opus-4.8",
3222
+ // in-family hot swap (identical cost to 5)
3223
+ "anthropic/claude-opus-4.7",
3224
+ // in-family hot swap (identical cost to 4.8)
3225
+ "anthropic/claude-opus-4.6",
3226
+ // in-family hot swap
3227
+ "anthropic/claude-sonnet-5",
3228
+ // Sonnet-tier drop-down, near-Opus quality
3229
+ "anthropic/claude-sonnet-4.6",
3230
+ "xai/grok-4.5",
3231
+ // xAI flagship — 503-resistant, direct-xAI SKU (added 2026-07-14)
3232
+ "xai/grok-4-0709",
3233
+ // 503-resistant flagship
3234
+ "moonshot/kimi-k2.7",
3235
+ // Moonshot flagship, independent infra
3236
+ "moonshot/kimi-k2.6",
3237
+ "moonshot/kimi-k2.5",
3238
+ "openai/gpt-5.6-terra",
3239
+ // GPT-5.6 balanced tier — newest generation, stable (Sol excluded: #202)
3240
+ "openai/gpt-5.5",
3241
+ // Prior OpenAI flagship — 1M+ ctx, native agent + computer use
3242
+ "openai/gpt-5.4",
3243
+ // Previous flagship (slow but stable, benchmarked at 6,213ms)
3244
+ "openai/gpt-5.3-codex",
3245
+ "deepseek/deepseek-chat",
3246
+ // Cheap, reliable
3247
+ "free/gpt-oss-120b"
3248
+ // NVIDIA free ultimate backstop (was seed-oss-36b; EOL'd 2026-08-03)
3249
+ ]
3250
+ },
3251
+ REASONING: {
3252
+ primary: "anthropic/claude-sonnet-4.6",
3253
+ // 2,110ms, $3/$15 - best for reasoning/instructions
3254
+ fallback: [
3255
+ "anthropic/claude-sonnet-5",
3256
+ // in-family hot swap — same cost, adaptive thinking, 1M ctx
3257
+ "anthropic/claude-opus-5",
3258
+ // Newest flagship Opus w/ adaptive thinking
3259
+ "anthropic/claude-opus-4.8",
3260
+ // Prior flagship Opus — identical cost to 5
3261
+ "anthropic/claude-opus-4.7",
3262
+ // Flagship Opus w/ adaptive thinking
3263
+ "anthropic/claude-opus-4.6",
3264
+ // 2,139ms
3265
+ "xai/grok-4-1-fast-reasoning",
3266
+ // 1,454ms, cheap fast reasoning
3267
+ "openai/o4-mini",
3268
+ // 2,328ms ($1.10/$4.40)
3269
+ "openai/o3"
3270
+ // 2,862ms
3271
+ ]
3272
+ }
3273
+ },
3274
+ // Agentic tier configs - models that excel at multi-step autonomous tasks
3275
+ agenticTiers: {
3276
+ SIMPLE: {
3277
+ primary: "openai/gpt-4o-mini",
3278
+ // $0.15/$0.60 - best tool compliance at lowest cost
3279
+ fallback: [
3280
+ "moonshot/kimi-k2.5",
3281
+ // 1,646ms, strong tool use quality
3282
+ "anthropic/claude-haiku-4.5",
3283
+ // 2,305ms
3284
+ "xai/grok-4-1-fast-non-reasoning"
3285
+ // 1,244ms, fast fallback
3286
+ ]
3287
+ },
3288
+ MEDIUM: {
3289
+ primary: "moonshot/kimi-k2.7",
3290
+ // $0.95/$4.00 — Moonshot flagship, strong tool use; promoted from K2.6 (2026-06-14) after BlockRun added K2.7 + hid K2.6. Same price.
3291
+ fallback: [
3292
+ "moonshot/kimi-k2.6",
3293
+ // identical-cost in-family hot swap (K2.6 still routable)
3294
+ "moonshot/kimi-k2.5",
3295
+ // $0.60/$3.00 — graceful-degradation backstop
3296
+ "xai/grok-4-1-fast-non-reasoning",
3297
+ // 1,244ms, fast fallback
3298
+ "openai/gpt-4o-mini",
3299
+ // 2,764ms, reliable tool calling
3300
+ "anthropic/claude-haiku-4.5",
3301
+ // 2,305ms
3302
+ "deepseek/deepseek-chat"
3303
+ // 1,431ms
3304
+ ]
3305
+ },
3306
+ COMPLEX: {
3307
+ primary: "anthropic/claude-sonnet-4.6",
3308
+ // 2,110ms — best agentic quality
3309
+ // Fallback chain de-Gemini'd 2026-04-22: Gemini's "high demand" 503s
3310
+ // correlate with Anthropic outages (everyone falls back together).
3311
+ // Prefer 503-resistant providers first.
3312
+ fallback: [
3313
+ "anthropic/claude-sonnet-5",
3314
+ // in-family hot swap — same cost, near-Opus agentic quality
3315
+ "anthropic/claude-opus-5",
3316
+ // Newest flagship Opus — in-family hot swap
3317
+ "anthropic/claude-opus-4.8",
3318
+ // Prior flagship Opus — identical cost to 5
3319
+ "anthropic/claude-opus-4.7",
3320
+ // Flagship Opus — in-family hot swap
3321
+ "anthropic/claude-opus-4.6",
3322
+ // 2,139ms
3323
+ "xai/grok-4-0709",
3324
+ // 1,348ms — strong tool use, independent infra
3325
+ "moonshot/kimi-k2.7",
3326
+ // Moonshot flagship — strong tool use, independent infra
3327
+ "moonshot/kimi-k2.5",
3328
+ // cost-stability backstop
3329
+ "openai/gpt-5.6-terra",
3330
+ // GPT-5.6 balanced tier — newest generation, stable (Sol excluded: #202)
3331
+ "openai/gpt-5.5",
3332
+ // Prior flagship — native agent + computer use (exactly the agentic-tier use case)
3333
+ "openai/gpt-5.4",
3334
+ // Previous flagship — 6,213ms, reliable
3335
+ "deepseek/deepseek-chat",
3336
+ // 1,431ms — cheap, reliable
3337
+ "free/gpt-oss-120b"
3338
+ // NVIDIA free ultimate backstop (was seed-oss-36b; EOL'd 2026-08-03)
3339
+ ]
3340
+ },
3341
+ REASONING: {
3342
+ primary: "anthropic/claude-sonnet-4.6",
3343
+ // 2,110ms — strong tool use + reasoning
3344
+ fallback: [
3345
+ "anthropic/claude-sonnet-5",
3346
+ // in-family hot swap — same cost, adaptive thinking
3347
+ "anthropic/claude-opus-5",
3348
+ // Newest flagship Opus w/ adaptive thinking
3349
+ "anthropic/claude-opus-4.8",
3350
+ // Prior flagship Opus — identical cost to 5
3351
+ "anthropic/claude-opus-4.7",
3352
+ // Flagship Opus w/ adaptive thinking
3353
+ "anthropic/claude-opus-4.6",
3354
+ // 2,139ms
3355
+ "xai/grok-4-1-fast-reasoning",
3356
+ // 1,454ms
3357
+ "deepseek/deepseek-reasoner"
3358
+ // 1,454ms
3359
+ ]
3360
+ }
3361
+ },
3362
+ // Time-windowed promotions — auto-applied when active, ignored when expired
3363
+ promotions: [
3364
+ {
3365
+ name: "GLM-5.1 Launch Promo ($0.001 flat)",
3366
+ startDate: "2026-04-01",
3367
+ endDate: "2026-05-01",
3368
+ tierOverrides: {
3369
+ SIMPLE: { primary: "zai/glm-5.1" }
3370
+ },
3371
+ profiles: ["auto"]
3372
+ // only auto profile — eco stays free, premium stays premium
3373
+ }
3374
+ ],
3375
+ overrides: {
3376
+ maxTokensForceComplex: 1e5,
3377
+ structuredOutputMinTier: "MEDIUM",
3378
+ ambiguousDefaultTier: "MEDIUM"
3379
+ // agenticMode left undefined → auto-detect via tools/agenticScore.
3380
+ // Set to `true` to force agentic tiers; `false` to disable them entirely.
3381
+ }
3382
+ };
3383
+ registerStrategy(new PortfolioStrategy());
3384
+ function route(prompt, systemPrompt, maxOutputTokens, options) {
3385
+ const strategy = getStrategy(options.config.strategy ?? "portfolio");
3386
+ return strategy.route(prompt, systemPrompt, maxOutputTokens, options);
3387
+ }
3388
+
3389
+ // src/router-adapter.ts
3390
+ var AUTO_ROUTING_PROFILES = {
3391
+ "blockrun/auto": "auto",
3392
+ "blockrun/eco": "eco",
3393
+ "blockrun/premium": "premium"
3394
+ };
3395
+ var BASE_MINIMUM_PAYMENT_USD = 2e-3;
3396
+ var SOLANA_MINIMUM_PAYMENT_USD = 1e-3;
3397
+ function isTransientError(err) {
3398
+ if (err instanceof PaymentError) return false;
3399
+ if (err instanceof APIError) {
3400
+ return [429, 502, 503, 504, 522, 524].includes(err.statusCode);
3401
+ }
3402
+ if (err instanceof Error) {
3403
+ if (err.name === "AbortError") return true;
3404
+ if (err.name === "TypeError" && /fetch|network/i.test(err.message)) return true;
3405
+ }
3406
+ return false;
3407
+ }
3408
+ function errSummary(err) {
3409
+ if (err instanceof APIError) return `APIError ${err.statusCode}`;
3410
+ if (err instanceof Error) {
3411
+ const msg = err.message.length > 80 ? err.message.slice(0, 80) : err.message;
3412
+ return `${err.name}: ${msg}`;
3413
+ }
3414
+ return String(err).slice(0, 100);
3415
+ }
3416
+ function routingProfileForModel(model) {
3417
+ return AUTO_ROUTING_PROFILES[model.toLowerCase()];
3418
+ }
3419
+ function routingText(messages) {
3420
+ const systemPrompt = messages.filter((message) => message.role === "system" && typeof message.content === "string").map((message) => message.content).join("\n") || void 0;
3421
+ const lastUser = [...messages].reverse().find((message) => message.role === "user" && typeof message.content === "string");
3422
+ const lastText = [...messages].reverse().find((message) => typeof message.content === "string");
3423
+ let conversationChars = 0;
3424
+ let hasVision = false;
3425
+ for (const message of messages) {
3426
+ if (typeof message.content === "string") {
3427
+ conversationChars += message.content.length;
3428
+ } else if (Array.isArray(message.content)) {
3429
+ for (const part of message.content) {
3430
+ if (part?.type === "image_url" || part?.type === "image") hasVision = true;
3431
+ if (typeof part?.text === "string") conversationChars += part.text.length;
3432
+ }
3433
+ }
3434
+ }
3435
+ return {
3436
+ prompt: lastUser?.content ?? lastText?.content ?? "",
3437
+ systemPrompt,
3438
+ conversationChars,
3439
+ hasVision
3440
+ };
3441
+ }
3442
+ function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, options = {}) {
3443
+ const tools = options.tools ?? [];
3444
+ const requiresTools = options.toolChoice === "none" ? false : options.toolChoice === "required" || typeof options.toolChoice === "object" ? true : void 0;
3445
+ const decision = route(prompt, systemPrompt, maxOutputTokens, {
3446
+ config: DEFAULT_ROUTING_CONFIG,
3447
+ modelPricing,
3448
+ routingProfile: options.routingProfile,
3449
+ hasTools: tools.length > 0,
3450
+ toolCount: tools.length,
3451
+ toolNames: tools.map((tool) => tool.function.name),
3452
+ requiresTools,
3453
+ hasVision: options.hasVision,
3454
+ requiresStructuredOutput: options.requiresStructuredOutput
3455
+ });
3456
+ const tierConfigs = decision.tierConfigs ?? DEFAULT_ROUTING_CONFIG.tiers;
3457
+ const ranked = decision.candidates?.length ? decision.candidates : [decision.model, ...getFallbackChain(decision.tier, tierConfigs)];
3458
+ const callable = [];
3459
+ for (const id of ranked) {
3460
+ const resolved = !id.startsWith("free/") ? id : modelPricing.has(`nvidia/${id.slice(5)}`) ? `nvidia/${id.slice(5)}` : null;
3461
+ if (resolved && !callable.includes(resolved)) callable.push(resolved);
3462
+ }
3463
+ const estimatedInputTokens = Math.ceil(
3464
+ Math.max(options.conversationChars ?? 0, `${systemPrompt ?? ""} ${prompt}`.length) / 4
3465
+ );
3466
+ const fitting = filterCandidatesByCapacity(
3467
+ callable,
3468
+ estimatedInputTokens,
3469
+ maxOutputTokens,
3470
+ (id) => {
3471
+ const caps = DEFAULT_MODEL_CAPABILITIES[id];
3472
+ return caps ? { contextWindow: caps.contextWindow, maxOutput: caps.maxOutputTokens } : void 0;
3473
+ }
3474
+ );
3475
+ const availableCandidates = fitting.length > 0 ? fitting : callable;
3476
+ const model = availableCandidates[0] ?? decision.model;
3477
+ const costs = calculateModelCost(
3478
+ model,
3479
+ modelPricing,
3480
+ estimatedInputTokens,
3481
+ maxOutputTokens,
3482
+ options.routingProfile
3483
+ );
3484
+ const minimumPaymentUsd = options.minimumPaymentUsd ?? SOLANA_MINIMUM_PAYMENT_USD;
3485
+ const entry = modelPricing.get(model);
3486
+ const isFree = entry !== void 0 && entry.inputPrice === 0 && entry.outputPrice === 0 && !entry.flatPrice;
3487
+ const costEstimate = isFree ? 0 : Math.max(costs.costEstimate, minimumPaymentUsd);
3488
+ const savings = options.routingProfile === "premium" || costs.baselineCost <= 0 ? 0 : entry !== void 0 ? Math.max(0, (costs.baselineCost - costEstimate) / costs.baselineCost) : decision.savings;
3489
+ return {
3490
+ ...decision,
3491
+ ...costs,
3492
+ costEstimate,
3493
+ savings,
3494
+ model,
3495
+ reasoning: model === decision.model ? decision.reasoning : `${decision.reasoning} | catalog fallback: ${model}`,
3496
+ candidates: availableCandidates,
3497
+ candidateScores: decision.candidateScores?.filter(
3498
+ (score) => availableCandidates.includes(score.model)
3499
+ ),
3500
+ fallbacks: availableCandidates.slice(1)
3501
+ };
3502
+ }
3503
+
148
3504
  // src/x402.ts
149
3505
  var import_accounts = require("viem/accounts");
150
3506
 
@@ -608,29 +3964,10 @@ function getCostSummary() {
608
3964
  }
609
3965
 
610
3966
  // src/version.ts
611
- var SDK_VERSION = "3.11.0";
3967
+ var SDK_VERSION = "3.13.0";
612
3968
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
613
3969
 
614
3970
  // src/client.ts
615
- function isTransientError(err) {
616
- if (err instanceof PaymentError) return false;
617
- if (err instanceof APIError) {
618
- return [429, 502, 503, 504, 522, 524].includes(err.statusCode);
619
- }
620
- if (err instanceof Error) {
621
- if (err.name === "AbortError") return true;
622
- if (err.name === "TypeError" && /fetch|network/i.test(err.message)) return true;
623
- }
624
- return false;
625
- }
626
- function errSummary(err) {
627
- if (err instanceof APIError) return `APIError ${err.statusCode}`;
628
- if (err instanceof Error) {
629
- const msg = err.message.length > 80 ? err.message.slice(0, 80) : err.message;
630
- return `${err.name}: ${msg}`;
631
- }
632
- return String(err).slice(0, 100);
633
- }
634
3971
  function mapRawToModel(m) {
635
3972
  return {
636
3973
  id: m.id,
@@ -685,7 +4022,7 @@ var LLMClient = class _LLMClient {
685
4022
  modelPricingPromise = null;
686
4023
  // Pre-auth cache: avoids the 402 round-trip on repeat requests to the same model.
687
4024
  // Key = "endpoint:model", value = cached payment header + timestamp.
688
- // TTL: 1 hour (mirrors ClawRouter's payment-preauth.ts approach).
4025
+ // TTL: 1 hour — pre-auth quotes are stable server-side on that horizon.
689
4026
  preAuthCache = /* @__PURE__ */ new Map();
690
4027
  static PRE_AUTH_TTL_MS = 36e5;
691
4028
  /**
@@ -739,11 +4076,35 @@ var LLMClient = class _LLMClient {
739
4076
  });
740
4077
  return result.choices[0].message.content || "";
741
4078
  }
4079
+ async makeRoutingDecision(prompt, systemPrompt, maxOutputTokens, options = {}) {
4080
+ return routeWithCatalog(
4081
+ prompt,
4082
+ systemPrompt,
4083
+ maxOutputTokens,
4084
+ await this.getModelPricing(),
4085
+ { ...options, minimumPaymentUsd: BASE_MINIMUM_PAYMENT_USD }
4086
+ );
4087
+ }
4088
+ /**
4089
+ * Inspect a local routing decision without making or paying for a model call.
4090
+ * The first invocation may fetch the public model catalog for current prices.
4091
+ */
4092
+ async route(prompt, options) {
4093
+ return this.makeRoutingDecision(
4094
+ prompt,
4095
+ options?.system,
4096
+ options?.maxOutputTokens ?? options?.maxTokens ?? DEFAULT_MAX_TOKENS,
4097
+ {
4098
+ routingProfile: options?.routingProfile,
4099
+ requiresStructuredOutput: options?.responseFormat !== void 0
4100
+ }
4101
+ );
4102
+ }
742
4103
  /**
743
4104
  * Smart chat with automatic model routing.
744
4105
  *
745
- * Uses ClawRouter's deterministic portfolio router (Router v3.4, the Auto
746
- * default since v0.12.242): it classifies the task shape locally (<1ms, no
4106
+ * Uses BlockRun's product-neutral Router Core portfolio strategy: it
4107
+ * classifies the task shape locally (no
747
4108
  * extra model call), enforces capability constraints as hard filters, and
748
4109
  * ranks an ordered candidate portfolio — the cheapest model that can handle
749
4110
  * the request wins, and the rest become the transient-error fallback chain.
@@ -771,33 +4132,8 @@ var LLMClient = class _LLMClient {
771
4132
  * ```
772
4133
  */
773
4134
  async smartChat(prompt, options) {
774
- const modelPricing = await this.getModelPricing();
775
- const maxOutputTokens = options?.maxOutputTokens || options?.maxTokens || 1024;
776
- let route;
777
- let DEFAULT_ROUTING_CONFIG;
778
- let getFallbackChain;
779
- try {
780
- ({ route, DEFAULT_ROUTING_CONFIG, getFallbackChain } = await import("@blockrun/clawrouter"));
781
- } catch (err) {
782
- throw new Error(
783
- `smartChat() requires the optional '@blockrun/clawrouter' routing engine, which is not installed or failed to load. Install it, or call chat() with an explicit model instead. Cause: ${err.message}`
784
- );
785
- }
786
- const decision = route(prompt, options?.system, maxOutputTokens, {
787
- config: DEFAULT_ROUTING_CONFIG,
788
- modelPricing,
789
- routingProfile: options?.routingProfile
790
- });
791
- const tierConfigs = decision.tierConfigs ?? DEFAULT_ROUTING_CONFIG.tiers;
792
- const ranked = decision.candidates?.length ? decision.candidates : [decision.model, ...getFallbackChain(decision.tier, tierConfigs)];
793
- const callable = [];
794
- for (const id of ranked) {
795
- const resolved = !id.startsWith("free/") ? id : modelPricing.has(`nvidia/${id.slice(5)}`) ? `nvidia/${id.slice(5)}` : null;
796
- if (resolved && !callable.includes(resolved)) callable.push(resolved);
797
- }
798
- const primary = callable[0] ?? decision.model;
799
- const fallbacks = callable.slice(1);
800
- const response = await this.chat(primary, prompt, {
4135
+ const decision = await this.route(prompt, options);
4136
+ const response = await this.chat(decision.model, prompt, {
801
4137
  system: options?.system,
802
4138
  maxTokens: options?.maxTokens,
803
4139
  temperature: options?.temperature,
@@ -806,14 +4142,47 @@ var LLMClient = class _LLMClient {
806
4142
  searchParameters: options?.searchParameters,
807
4143
  responseFormat: options?.responseFormat,
808
4144
  stop: options?.stop,
809
- fallbackModels: fallbacks
4145
+ // An explicit caller-supplied chain wins over the routed one.
4146
+ fallbackModels: options?.fallbackModels ?? decision.fallbacks
810
4147
  });
811
4148
  return {
812
4149
  response,
813
- model: primary,
814
- routing: { ...decision, model: primary, fallbacks }
4150
+ model: decision.model,
4151
+ routing: decision
815
4152
  };
816
4153
  }
4154
+ /** Route a full Agent/tool conversation while preserving its complete response. */
4155
+ async smartChatCompletion(messages, options = {}) {
4156
+ const { prompt, systemPrompt, conversationChars, hasVision } = routingText(messages);
4157
+ const decision = await this.makeRoutingDecision(
4158
+ prompt,
4159
+ systemPrompt,
4160
+ options.maxOutputTokens ?? options.maxTokens ?? DEFAULT_MAX_TOKENS,
4161
+ {
4162
+ routingProfile: options.routingProfile,
4163
+ requiresStructuredOutput: options.responseFormat !== void 0,
4164
+ tools: options.tools,
4165
+ toolChoice: options.toolChoice,
4166
+ conversationChars,
4167
+ hasVision
4168
+ }
4169
+ );
4170
+ const response = await this.chatCompletion(decision.model, messages, {
4171
+ maxTokens: options.maxTokens,
4172
+ temperature: options.temperature,
4173
+ topP: options.topP,
4174
+ search: options.search,
4175
+ searchParameters: options.searchParameters,
4176
+ tools: options.tools,
4177
+ toolChoice: options.toolChoice,
4178
+ responseFormat: options.responseFormat,
4179
+ stop: options.stop,
4180
+ // An explicit caller-supplied chain wins over the routed one.
4181
+ fallbackModels: options.fallbackModels ?? decision.fallbacks
4182
+ });
4183
+ response.routing = decision;
4184
+ return { response, model: decision.model, routing: decision };
4185
+ }
817
4186
  /**
818
4187
  * Get model pricing map (cached).
819
4188
  * Fetches from API on first call, then returns cached result.
@@ -833,31 +4202,18 @@ var LLMClient = class _LLMClient {
833
4202
  this.modelPricingPromise = null;
834
4203
  }
835
4204
  }
836
- /**
837
- * Fetch model pricing from API.
838
- *
839
- * For flat-billed models (e.g. ZAI GLM-5 family at $0.001/call) the
840
- * router still expects per-token rates, so we synthesise an equivalent
841
- * per-token price assuming ~1500 total tokens per call. Without this,
842
- * flat models would resolve to inputPrice=outputPrice=0 and the router
843
- * would treat them as free, biasing routing decisions and reporting
844
- * inflated savings %.
845
- */
4205
+ /** Fetch model pricing from the live catalog, preserving flat billing. */
846
4206
  async fetchModelPricing() {
847
4207
  const models = await this.listModels();
848
4208
  const pricing = /* @__PURE__ */ new Map();
849
4209
  for (const model of models) {
850
- if (model.billingMode === "flat" && model.flatPrice && model.flatPrice > 0) {
851
- const perDirection = model.flatPrice * 1e6 / 1500 / 2;
852
- pricing.set(model.id, {
853
- inputPrice: perDirection,
854
- outputPrice: perDirection
855
- });
4210
+ if (model.available === false) continue;
4211
+ const inputPrice = Number.isFinite(model.inputPrice) ? model.inputPrice : 0;
4212
+ const outputPrice = Number.isFinite(model.outputPrice) ? model.outputPrice : 0;
4213
+ if (model.billingMode === "flat" && Number.isFinite(model.flatPrice) && model.flatPrice > 0) {
4214
+ pricing.set(model.id, { inputPrice, outputPrice, flatPrice: model.flatPrice });
856
4215
  } else {
857
- pricing.set(model.id, {
858
- inputPrice: model.inputPrice,
859
- outputPrice: model.outputPrice
860
- });
4216
+ pricing.set(model.id, { inputPrice, outputPrice });
861
4217
  }
862
4218
  }
863
4219
  return pricing;
@@ -877,6 +4233,13 @@ var LLMClient = class _LLMClient {
877
4233
  * @returns ChatResponse object with choices and usage
878
4234
  */
879
4235
  async chatCompletion(model, messages, options) {
4236
+ const routingProfile = routingProfileForModel(model);
4237
+ if (routingProfile) {
4238
+ return (await this.smartChatCompletion(messages, {
4239
+ ...options,
4240
+ routingProfile
4241
+ })).response;
4242
+ }
880
4243
  validateMaxTokens(options?.maxTokens);
881
4244
  const buildBody = (m) => {
882
4245
  const body = {
@@ -1149,9 +4512,28 @@ var LLMClient = class _LLMClient {
1149
4512
  */
1150
4513
  async chatCompletionStream(model, messages, options) {
1151
4514
  validateMaxTokens(options?.maxTokens);
4515
+ let requestModel = model;
4516
+ const routingProfile = routingProfileForModel(model);
4517
+ if (routingProfile) {
4518
+ const { prompt, systemPrompt, conversationChars, hasVision } = routingText(messages);
4519
+ const decision = await this.makeRoutingDecision(
4520
+ prompt,
4521
+ systemPrompt,
4522
+ options?.maxTokens ?? DEFAULT_MAX_TOKENS,
4523
+ {
4524
+ routingProfile,
4525
+ requiresStructuredOutput: options?.responseFormat !== void 0,
4526
+ tools: options?.tools,
4527
+ toolChoice: options?.toolChoice,
4528
+ conversationChars,
4529
+ hasVision
4530
+ }
4531
+ );
4532
+ requestModel = decision.model;
4533
+ }
1152
4534
  const url = `${this.apiUrl}/v1/chat/completions`;
1153
4535
  const body = {
1154
- model,
4536
+ model: requestModel,
1155
4537
  messages,
1156
4538
  max_tokens: options?.maxTokens ?? DEFAULT_MAX_TOKENS,
1157
4539
  stream: true
@@ -1162,7 +4544,7 @@ var LLMClient = class _LLMClient {
1162
4544
  if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
1163
4545
  if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
1164
4546
  if (options?.stop !== void 0) body.stop = options.stop;
1165
- const cacheKey2 = `/v1/chat/completions:${model}`;
4547
+ const cacheKey2 = `/v1/chat/completions:${requestModel}`;
1166
4548
  const cached = this.preAuthCache.get(cacheKey2);
1167
4549
  const now = Date.now();
1168
4550
  if (cached && now - cached.cachedAt < _LLMClient.PRE_AUTH_TTL_MS) {
@@ -1672,8 +5054,8 @@ var LLMClient = class _LLMClient {
1672
5054
  * @example
1673
5055
  * const results = await client.exa("search", { query: "latest AI research", numResults: 5 });
1674
5056
  */
1675
- async exa(path5, body) {
1676
- return this.requestWithPaymentRaw(`/v1/exa/${path5}`, body);
5057
+ async exa(path6, body) {
5058
+ return this.requestWithPaymentRaw(`/v1/exa/${path6}`, body);
1677
5059
  }
1678
5060
  /**
1679
5061
  * Neural web search via Exa. Returns semantically relevant URLs and metadata.
@@ -1772,7 +5154,13 @@ var LLMClient = class _LLMClient {
1772
5154
  */
1773
5155
  async getBalance() {
1774
5156
  const usdcContract = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
1775
- const rpcs = ["https://base.publicnode.com", "https://mainnet.base.org", "https://base.meowrpc.com"];
5157
+ const configuredRpc = typeof process !== "undefined" && process.env ? process.env.BASE_RPC_URL : void 0;
5158
+ const rpcs = [
5159
+ configuredRpc,
5160
+ "https://base-rpc.publicnode.com",
5161
+ "https://mainnet.base.org",
5162
+ "https://base.llamarpc.com"
5163
+ ].filter((rpc) => Boolean(rpc));
1776
5164
  const selector = "0x70a08231";
1777
5165
  const paddedAddress = this.account.address.slice(2).toLowerCase().padStart(64, "0");
1778
5166
  const data = selector + paddedAddress;
@@ -1790,7 +5178,9 @@ var LLMClient = class _LLMClient {
1790
5178
  headers: { "Content-Type": "application/json" },
1791
5179
  body: JSON.stringify(payload)
1792
5180
  });
5181
+ if (!response.ok) throw new Error(`Base RPC returned ${response.status}`);
1793
5182
  const result = await response.json();
5183
+ if (!result.result || result.error) throw new Error("Base RPC returned no balance result");
1794
5184
  const balanceRaw = parseInt(result.result || "0x0", 16);
1795
5185
  return balanceRaw / 1e6;
1796
5186
  } catch (e) {
@@ -1813,8 +5203,8 @@ var LLMClient = class _LLMClient {
1813
5203
  * const market = await client.pm("kalshi/markets/KXBTC-25MAR14");
1814
5204
  * const results = await client.pm("polymarket/search", { q: "bitcoin" });
1815
5205
  */
1816
- async pm(path5, params) {
1817
- return this.getWithPaymentRaw(`/v1/pm/${path5}`, params);
5206
+ async pm(path6, params) {
5207
+ return this.getWithPaymentRaw(`/v1/pm/${path6}`, params);
1818
5208
  }
1819
5209
  /**
1820
5210
  * Structured query for Predexon prediction market data (POST endpoints).
@@ -1830,8 +5220,8 @@ var LLMClient = class _LLMClient {
1830
5220
  * addresses: ["0xabc...", "0xdef..."],
1831
5221
  * });
1832
5222
  */
1833
- async pmQuery(path5, query) {
1834
- return this.requestWithPaymentRaw(`/v1/pm/${path5}`, query);
5223
+ async pmQuery(path6, query) {
5224
+ return this.requestWithPaymentRaw(`/v1/pm/${path6}`, query);
1835
5225
  }
1836
5226
  // ── PM convenience helpers (Predexon v2) ─────────────────────────────────
1837
5227
  // Thin wrappers over pm() / pmQuery() for the most common v2 endpoints.
@@ -1919,8 +5309,8 @@ var LLMClient = class _LLMClient {
1919
5309
  * "prices/coingecko:bitcoin,base:0x..."
1920
5310
  * @param params - Query parameters passed through to DefiLlama
1921
5311
  */
1922
- async defi(path5, params) {
1923
- return this.getWithPaymentRaw(`/v1/defillama/${path5}`, params);
5312
+ async defi(path6, params) {
5313
+ return this.getWithPaymentRaw(`/v1/defillama/${path6}`, params);
1924
5314
  }
1925
5315
  /** All DeFi protocols with TVL ($0.005/call). */
1926
5316
  async defiProtocols() {
@@ -1956,9 +5346,9 @@ var LLMClient = class _LLMClient {
1956
5346
  * @param params - Query params (chainId, sellToken, buyToken, sellAmount, taker, ...)
1957
5347
  * @param body - JSON body — pass to switch to POST (gasless/submit only)
1958
5348
  */
1959
- async dex(path5, params, body) {
1960
- if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path5}`, body);
1961
- return this.getWithPaymentRaw(`/v1/zerox/${path5}`, params);
5349
+ async dex(path6, params, body) {
5350
+ if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path6}`, body);
5351
+ return this.getWithPaymentRaw(`/v1/zerox/${path6}`, params);
1962
5352
  }
1963
5353
  /** Indicative Permit2 swap price — no commitment (free). */
1964
5354
  async dexPrice(params) {
@@ -2002,8 +5392,8 @@ var LLMClient = class _LLMClient {
2002
5392
  * "sandbox/status", "sandbox/terminate" ($0.001 each)
2003
5393
  * @param body - JSON body for the endpoint
2004
5394
  */
2005
- async modal(path5, body) {
2006
- return this.requestWithPaymentRaw(`/v1/modal/${path5}`, body ?? {});
5395
+ async modal(path6, body) {
5396
+ return this.requestWithPaymentRaw(`/v1/modal/${path6}`, body ?? {});
2007
5397
  }
2008
5398
  /** Create a sandboxed compute environment ($0.01 CPU / $0.05 GPU). */
2009
5399
  async modalSandboxCreate(body) {
@@ -3365,8 +6755,8 @@ var PhoneClient = class {
3365
6755
  return data;
3366
6756
  }
3367
6757
  // ─── Internals ─────────────────────────────────────────────────────────
3368
- async request(path5, body) {
3369
- const url = `${this.apiUrl}/v1/phone/${path5}`;
6758
+ async request(path6, body) {
6759
+ const url = `${this.apiUrl}/v1/phone/${path6}`;
3370
6760
  const response = await this.fetchWithTimeout(url, {
3371
6761
  method: "POST",
3372
6762
  headers: { "Content-Type": "application/json" },
@@ -3773,10 +7163,10 @@ var PriceClient = class {
3773
7163
  }
3774
7164
  async price(category, symbol, options) {
3775
7165
  if (!symbol) throw new Error("symbol is required");
3776
- const path5 = categoryPath(category, options?.market, "price", symbol);
7166
+ const path6 = categoryPath(category, options?.market, "price", symbol);
3777
7167
  const query = {};
3778
7168
  if (options?.session) query.session = options.session;
3779
- const data = await this.getWithPayment(path5, query);
7169
+ const data = await this.getWithPayment(path6, query);
3780
7170
  return {
3781
7171
  symbol: data.symbol ?? symbol.toUpperCase(),
3782
7172
  price: data.price,
@@ -3795,14 +7185,14 @@ var PriceClient = class {
3795
7185
  if (!options.from || options.from <= 0) {
3796
7186
  throw new Error("history requires options.from (unix seconds)");
3797
7187
  }
3798
- const path5 = categoryPath(category, options.market, "history", symbol);
7188
+ const path6 = categoryPath(category, options.market, "history", symbol);
3799
7189
  const query = {
3800
7190
  resolution: options.resolution ?? "D",
3801
7191
  from: String(options.from)
3802
7192
  };
3803
7193
  if (options.to) query.to = String(options.to);
3804
7194
  if (options.session) query.session = options.session;
3805
- const data = await this.getWithPayment(path5, query);
7195
+ const data = await this.getWithPayment(path6, query);
3806
7196
  return {
3807
7197
  symbol: data.symbol ?? symbol.toUpperCase(),
3808
7198
  resolution: data.resolution ?? (options.resolution ?? "D"),
@@ -3814,12 +7204,12 @@ var PriceClient = class {
3814
7204
  };
3815
7205
  }
3816
7206
  async listSymbols(category, options) {
3817
- const path5 = categoryPath(category, options?.market, "list");
7207
+ const path6 = categoryPath(category, options?.market, "list");
3818
7208
  const query = {
3819
7209
  limit: String(options?.limit && options.limit > 0 ? options.limit : 100)
3820
7210
  };
3821
7211
  if (options?.query) query.q = options.query;
3822
- const data = await this.getWithPayment(path5, query);
7212
+ const data = await this.getWithPayment(path6, query);
3823
7213
  if (Array.isArray(data)) {
3824
7214
  return { symbols: data, count: data.length };
3825
7215
  }
@@ -3978,19 +7368,19 @@ var SurfClient = class {
3978
7368
  * `/v1/surf` is tolerated and stripped). Query params are URL-encoded;
3979
7369
  * arrays become repeated keys (`?a=1&a=2`).
3980
7370
  */
3981
- async get(path5, params) {
3982
- const url = this.buildUrl(path5, params);
7371
+ async get(path6, params) {
7372
+ const url = this.buildUrl(path6, params);
3983
7373
  return this.requestWithPayment(url, "GET");
3984
7374
  }
3985
7375
  /**
3986
7376
  * POST a Surf endpoint with a JSON body. Same path normalization as `get`.
3987
7377
  */
3988
- async post(path5, body) {
3989
- const url = this.buildUrl(path5);
7378
+ async post(path6, body) {
7379
+ const url = this.buildUrl(path6);
3990
7380
  return this.requestWithPayment(url, "POST", body);
3991
7381
  }
3992
- buildUrl(path5, params) {
3993
- let normalized = path5.startsWith("/") ? path5 : `/${path5}`;
7382
+ buildUrl(path6, params) {
7383
+ let normalized = path6.startsWith("/") ? path6 : `/${path6}`;
3994
7384
  if (!normalized.startsWith(SURF_PREFIX)) {
3995
7385
  normalized = `${SURF_PREFIX}${normalized}`;
3996
7386
  }
@@ -4390,15 +7780,15 @@ var BlockrunClient = class {
4390
7780
  * `/api` is tolerated and stripped). Query params are URL-encoded; arrays
4391
7781
  * become repeated keys (`?a=1&a=2`); undefined/null are dropped.
4392
7782
  */
4393
- async get(path5, params) {
4394
- const url = this.buildUrl(path5, params);
7783
+ async get(path6, params) {
7784
+ const url = this.buildUrl(path6, params);
4395
7785
  return this.requestWithPayment(url, "GET");
4396
7786
  }
4397
7787
  /**
4398
7788
  * POST a BlockRun endpoint with a JSON body.
4399
7789
  */
4400
- async post(path5, body) {
4401
- const url = this.buildUrl(path5);
7790
+ async post(path6, body) {
7791
+ const url = this.buildUrl(path6);
4402
7792
  return this.requestWithPayment(url, "POST", body);
4403
7793
  }
4404
7794
  /**
@@ -4413,8 +7803,8 @@ var BlockrunClient = class {
4413
7803
  * short-circuits and returns the body. Most long-running endpoints (image,
4414
7804
  * video, music, voice) return 202 with a poll_url.
4415
7805
  */
4416
- async poll(path5, body, options) {
4417
- const submitUrl = this.buildUrl(path5);
7806
+ async poll(path6, body, options) {
7807
+ const submitUrl = this.buildUrl(path6);
4418
7808
  const budgetMs = options?.budgetMs ?? DEFAULT_POLL_BUDGET_MS;
4419
7809
  const intervalMs = options?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
4420
7810
  const resp402 = await this.fetchWithTimeout(submitUrl, {
@@ -4513,8 +7903,8 @@ var BlockrunClient = class {
4513
7903
  * process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
4514
7904
  * }
4515
7905
  */
4516
- async *stream(path5, body) {
4517
- const url = this.buildUrl(path5);
7906
+ async *stream(path6, body) {
7907
+ const url = this.buildUrl(path6);
4518
7908
  const requestBody = JSON.stringify(body ?? {});
4519
7909
  const resp402 = await this.fetchWithTimeout(url, {
4520
7910
  method: "POST",
@@ -4580,8 +7970,8 @@ var BlockrunClient = class {
4580
7970
  // --------------------------------------------------------------------
4581
7971
  // Internal: shared infrastructure
4582
7972
  // --------------------------------------------------------------------
4583
- buildUrl(path5, params) {
4584
- let normalized = path5.startsWith("/") ? path5 : `/${path5}`;
7973
+ buildUrl(path6, params) {
7974
+ let normalized = path6.startsWith("/") ? path6 : `/${path6}`;
4585
7975
  if (normalized.startsWith("/api/")) {
4586
7976
  normalized = normalized.slice(4);
4587
7977
  }
@@ -4734,46 +8124,64 @@ function sleep(ms) {
4734
8124
  }
4735
8125
 
4736
8126
  // src/wallet.ts
4737
- var import_accounts15 = require("viem/accounts");
8127
+ var import_accounts16 = require("viem/accounts");
8128
+
8129
+ // node_modules/.pnpm/@blockrun+core@0.1.1_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@3.25.76/node_modules/@blockrun/core/dist/config.js
4738
8130
  var fs2 = __toESM(require("fs"), 1);
4739
- var path2 = __toESM(require("path"), 1);
4740
8131
  var os2 = __toESM(require("os"), 1);
4741
- var USDC_BASE_CONTRACT = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
4742
- var BASE_CHAIN_ID2 = "8453";
4743
- var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
4744
- var WALLET_FILE = path2.join(WALLET_DIR, ".session");
4745
- function createWallet() {
4746
- const privateKey = (0, import_accounts15.generatePrivateKey)();
4747
- const account = (0, import_accounts15.privateKeyToAccount)(privateKey);
8132
+ var path2 = __toESM(require("path"), 1);
8133
+ function homeDir() {
8134
+ return process.env.BLOCKRUN_HOME || os2.homedir();
8135
+ }
8136
+ function blockrunDir() {
8137
+ return path2.join(homeDir(), ".blockrun");
8138
+ }
8139
+ function paths() {
8140
+ const dir = blockrunDir();
4748
8141
  return {
4749
- address: account.address,
4750
- privateKey
8142
+ dir,
8143
+ session: path2.join(dir, ".session"),
8144
+ legacy: path2.join(dir, "wallet.key"),
8145
+ solana: path2.join(dir, ".solana"),
8146
+ config: path2.join(dir, "config.json"),
8147
+ costLog: path2.join(dir, "cost_log.jsonl")
4751
8148
  };
4752
8149
  }
4753
- function saveWallet(privateKey) {
4754
- if (!fs2.existsSync(WALLET_DIR)) {
4755
- fs2.mkdirSync(WALLET_DIR, { recursive: true });
8150
+
8151
+ // node_modules/.pnpm/@blockrun+core@0.1.1_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@3.25.76/node_modules/@blockrun/core/dist/wallet.js
8152
+ var fs3 = __toESM(require("fs"), 1);
8153
+ var path3 = __toESM(require("path"), 1);
8154
+ var import_accounts15 = require("viem/accounts");
8155
+ function normalize(raw) {
8156
+ const k = raw.trim();
8157
+ return k.startsWith("0x") ? k : `0x${k}`;
8158
+ }
8159
+ function addressFromKey(raw) {
8160
+ try {
8161
+ return (0, import_accounts15.privateKeyToAccount)(normalize(raw)).address;
8162
+ } catch {
8163
+ return null;
4756
8164
  }
4757
- fs2.writeFileSync(WALLET_FILE, privateKey, { mode: 384 });
4758
- return WALLET_FILE;
4759
8165
  }
4760
8166
  function scanWallets() {
4761
- const home = os2.homedir();
8167
+ const home = homeDir();
4762
8168
  const results = [];
4763
8169
  try {
4764
- const entries = fs2.readdirSync(home, { withFileTypes: true });
4765
- for (const entry of entries) {
4766
- if (!entry.name.startsWith(".") || !entry.isDirectory()) continue;
4767
- const walletFile = path2.join(home, entry.name, "wallet.json");
4768
- if (!fs2.existsSync(walletFile)) continue;
8170
+ for (const entry of fs3.readdirSync(home, { withFileTypes: true })) {
8171
+ if (!entry.name.startsWith(".") || !entry.isDirectory())
8172
+ continue;
8173
+ const walletFile2 = path3.join(home, entry.name, "wallet.json");
8174
+ if (!fs3.existsSync(walletFile2))
8175
+ continue;
4769
8176
  try {
4770
- const data = JSON.parse(fs2.readFileSync(walletFile, "utf-8"));
8177
+ const data = JSON.parse(fs3.readFileSync(walletFile2, "utf-8"));
4771
8178
  const pk = data.privateKey || "";
4772
- const addr = data.address || "";
4773
- if (pk && addr) {
4774
- const mtime = fs2.statSync(walletFile).mtimeMs;
4775
- results.push({ mtime, privateKey: pk, address: addr, source: walletFile });
4776
- }
8179
+ if (!pk)
8180
+ continue;
8181
+ const derived = addressFromKey(pk);
8182
+ if (!derived)
8183
+ continue;
8184
+ results.push({ mtime: fs3.statSync(walletFile2).mtimeMs, privateKey: pk, address: derived, source: walletFile2 });
4777
8185
  } catch {
4778
8186
  continue;
4779
8187
  }
@@ -4784,71 +8192,97 @@ function scanWallets() {
4784
8192
  return results.map(({ privateKey, address, source }) => ({ privateKey, address, source }));
4785
8193
  }
4786
8194
  function listDiscoveredWallets() {
4787
- const listed = [];
4788
- for (const entry of scanWallets()) {
4789
- try {
4790
- listed.push({
4791
- address: (0, import_accounts15.privateKeyToAccount)(entry.privateKey).address,
4792
- source: entry.source
4793
- });
4794
- } catch {
4795
- continue;
4796
- }
4797
- }
4798
- return listed;
8195
+ return scanWallets().map(({ address, source }) => ({ address, source }));
4799
8196
  }
4800
- function importWallet(address) {
8197
+ function adoptWallet(address) {
4801
8198
  const wanted = address.trim().toLowerCase();
8199
+ const p = paths();
4802
8200
  for (const entry of scanWallets()) {
4803
- let derived;
4804
- try {
4805
- derived = (0, import_accounts15.privateKeyToAccount)(entry.privateKey).address;
4806
- } catch {
8201
+ if (entry.address.toLowerCase() !== wanted)
4807
8202
  continue;
4808
- }
4809
- if (derived.toLowerCase() !== wanted) continue;
4810
- if (fs2.existsSync(WALLET_FILE)) {
4811
- const current = fs2.readFileSync(WALLET_FILE, "utf-8").trim();
4812
- if (current && current !== entry.privateKey) {
4813
- const backup = path2.join(WALLET_DIR, `.session.backup-${Math.floor(Date.now() / 1e3)}`);
4814
- fs2.writeFileSync(backup, current, { mode: 384 });
8203
+ const privateKey = normalize(entry.privateKey);
8204
+ if (fs3.existsSync(p.session)) {
8205
+ const current = fs3.readFileSync(p.session, "utf8").trim();
8206
+ if (current && normalize(current) !== privateKey) {
8207
+ const backup = path3.join(p.dir, `.session.backup-${Math.floor(Date.now() / 1e3)}`);
8208
+ fs3.writeFileSync(backup, current, { mode: 384 });
4815
8209
  }
4816
8210
  }
4817
- saveWallet(entry.privateKey);
4818
- return derived;
8211
+ fs3.mkdirSync(p.dir, { recursive: true });
8212
+ fs3.writeFileSync(p.session, privateKey, { mode: 384 });
8213
+ return { address: entry.address, privateKey, source: "session" };
4819
8214
  }
4820
8215
  const available = listDiscoveredWallets().map((w) => w.address);
4821
- throw new Error(
4822
- `No discovered wallet controls ${address}. Available: ${available.length ? available.join(", ") : "none"}`
4823
- );
8216
+ throw new Error(`No discovered wallet controls ${address}. Available: ${available.length ? available.join(", ") : "none"}`);
4824
8217
  }
4825
- function loadWallet() {
4826
- if (fs2.existsSync(WALLET_FILE)) {
4827
- const key = fs2.readFileSync(WALLET_FILE, "utf-8").trim();
4828
- if (key) return key;
4829
- }
4830
- const legacyFile = path2.join(WALLET_DIR, "wallet.key");
4831
- if (fs2.existsSync(legacyFile)) {
4832
- const key = fs2.readFileSync(legacyFile, "utf-8").trim();
4833
- if (key) return key;
8218
+ function resolveFromFiles() {
8219
+ const p = paths();
8220
+ if (fs3.existsSync(p.session)) {
8221
+ const raw = fs3.readFileSync(p.session, "utf8").trim();
8222
+ if (raw)
8223
+ return { privateKey: normalize(raw), source: "session" };
8224
+ }
8225
+ if (fs3.existsSync(p.legacy)) {
8226
+ const raw = fs3.readFileSync(p.legacy, "utf8").trim();
8227
+ if (raw)
8228
+ return { privateKey: normalize(raw), source: "legacy" };
4834
8229
  }
4835
8230
  return null;
4836
8231
  }
8232
+ function resolvePrivateKey(env = process.env) {
8233
+ const fromEnv = env.BLOCKRUN_WALLET_KEY || env.BASE_CHAIN_WALLET_KEY;
8234
+ if (fromEnv && fromEnv.trim()) {
8235
+ return { privateKey: normalize(fromEnv), source: "env" };
8236
+ }
8237
+ return resolveFromFiles();
8238
+ }
8239
+
8240
+ // src/wallet.ts
8241
+ var fs4 = __toESM(require("fs"), 1);
8242
+ var USDC_BASE_CONTRACT = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
8243
+ var BASE_CHAIN_ID2 = "8453";
8244
+ function walletDir() {
8245
+ return paths().dir;
8246
+ }
8247
+ function walletFile() {
8248
+ return paths().session;
8249
+ }
8250
+ function createWallet() {
8251
+ const privateKey = (0, import_accounts16.generatePrivateKey)();
8252
+ const account = (0, import_accounts16.privateKeyToAccount)(privateKey);
8253
+ return {
8254
+ address: account.address,
8255
+ privateKey
8256
+ };
8257
+ }
8258
+ function saveWallet(privateKey) {
8259
+ const dir = walletDir();
8260
+ const file = walletFile();
8261
+ if (!fs4.existsSync(dir)) {
8262
+ fs4.mkdirSync(dir, { recursive: true });
8263
+ }
8264
+ fs4.writeFileSync(file, privateKey, { mode: 384 });
8265
+ return file;
8266
+ }
8267
+ function scanWallets2() {
8268
+ return scanWallets();
8269
+ }
8270
+ function listDiscoveredWallets2() {
8271
+ return listDiscoveredWallets();
8272
+ }
8273
+ function importWallet(address) {
8274
+ return adoptWallet(address).address;
8275
+ }
8276
+ function loadWallet() {
8277
+ return resolveFromFiles()?.privateKey ?? null;
8278
+ }
4837
8279
  function formatWalletMigrationNotice(newAddress) {
4838
- let discovered;
8280
+ let addresses;
4839
8281
  try {
4840
- discovered = scanWallets();
8282
+ addresses = listDiscoveredWallets().map((w) => w.address);
4841
8283
  } catch {
4842
8284
  return null;
4843
8285
  }
4844
- const addresses = [];
4845
- for (const entry of discovered) {
4846
- try {
4847
- addresses.push((0, import_accounts15.privateKeyToAccount)(entry.privateKey).address);
4848
- } catch {
4849
- continue;
4850
- }
4851
- }
4852
8286
  if (addresses.length === 0) return null;
4853
8287
  const found = addresses.map((addr) => ` ${addr}`).join("\n");
4854
8288
  return `
@@ -4875,30 +8309,18 @@ BLOCKRUN_WALLET_KEY=<private-key> for a single run without changing anything.
4875
8309
  `;
4876
8310
  }
4877
8311
  function getOrCreateWallet() {
4878
- const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4879
- if (envKey) {
4880
- const account = (0, import_accounts15.privateKeyToAccount)(envKey);
4881
- return { address: account.address, privateKey: envKey, isNew: false };
4882
- }
4883
- const fileKey = loadWallet();
4884
- if (fileKey) {
4885
- const account = (0, import_accounts15.privateKeyToAccount)(fileKey);
4886
- return { address: account.address, privateKey: fileKey, isNew: false };
8312
+ const resolved = resolvePrivateKey();
8313
+ if (resolved) {
8314
+ const account = (0, import_accounts16.privateKeyToAccount)(resolved.privateKey);
8315
+ return { address: account.address, privateKey: resolved.privateKey, isNew: false };
4887
8316
  }
4888
8317
  const { address, privateKey } = createWallet();
4889
8318
  saveWallet(privateKey);
4890
8319
  return { address, privateKey, isNew: true };
4891
8320
  }
4892
8321
  function getWalletAddress() {
4893
- const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4894
- if (envKey) {
4895
- return (0, import_accounts15.privateKeyToAccount)(envKey).address;
4896
- }
4897
- const fileKey = loadWallet();
4898
- if (fileKey) {
4899
- return (0, import_accounts15.privateKeyToAccount)(fileKey).address;
4900
- }
4901
- return null;
8322
+ const resolved = resolvePrivateKey();
8323
+ return resolved ? (0, import_accounts16.privateKeyToAccount)(resolved.privateKey).address : null;
4902
8324
  }
4903
8325
  function getEip681Uri(address, amountUsdc = 1) {
4904
8326
  const amountWei = Math.floor(amountUsdc * 1e6);
@@ -4960,15 +8382,15 @@ function formatFundingMessageCompact(address) {
4960
8382
  return `I need a little top-up to keep helping you! Send USDC on Base to: ${address}
4961
8383
  Check my balance: ${links.basescan}`;
4962
8384
  }
4963
- var WALLET_FILE_PATH = WALLET_FILE;
4964
- var WALLET_DIR_PATH = WALLET_DIR;
8385
+ var WALLET_FILE_PATH = walletFile();
8386
+ var WALLET_DIR_PATH = walletDir();
4965
8387
 
4966
8388
  // src/solana-wallet.ts
4967
- var fs3 = __toESM(require("fs"), 1);
4968
- var path3 = __toESM(require("path"), 1);
8389
+ var fs5 = __toESM(require("fs"), 1);
8390
+ var path4 = __toESM(require("path"), 1);
4969
8391
  var os3 = __toESM(require("os"), 1);
4970
- var WALLET_DIR2 = path3.join(os3.homedir(), ".blockrun");
4971
- var SOLANA_WALLET_FILE = path3.join(WALLET_DIR2, ".solana-session");
8392
+ var WALLET_DIR = path4.join(os3.homedir(), ".blockrun");
8393
+ var SOLANA_WALLET_FILE = path4.join(WALLET_DIR, ".solana-session");
4972
8394
  async function createSolanaWallet() {
4973
8395
  const { Keypair } = await loadSolanaWeb3("Solana wallet operations");
4974
8396
  const bs58 = await import("bs58");
@@ -5032,39 +8454,39 @@ async function solanaPublicKey(privateKey) {
5032
8454
  return Keypair.fromSecretKey(bytes).publicKey.toBase58();
5033
8455
  }
5034
8456
  function saveSolanaWallet(privateKey) {
5035
- if (!fs3.existsSync(WALLET_DIR2)) fs3.mkdirSync(WALLET_DIR2, { recursive: true });
5036
- fs3.writeFileSync(SOLANA_WALLET_FILE, privateKey, { mode: 384 });
8457
+ if (!fs5.existsSync(WALLET_DIR)) fs5.mkdirSync(WALLET_DIR, { recursive: true });
8458
+ fs5.writeFileSync(SOLANA_WALLET_FILE, privateKey, { mode: 384 });
5037
8459
  return SOLANA_WALLET_FILE;
5038
8460
  }
5039
8461
  function scanSolanaWallets() {
5040
8462
  const home = os3.homedir();
5041
8463
  const results = [];
5042
8464
  try {
5043
- const entries = fs3.readdirSync(home, { withFileTypes: true });
8465
+ const entries = fs5.readdirSync(home, { withFileTypes: true });
5044
8466
  for (const entry of entries) {
5045
8467
  if (!entry.name.startsWith(".") || !entry.isDirectory()) continue;
5046
- const solanaWalletFile = path3.join(home, entry.name, "solana-wallet.json");
5047
- if (fs3.existsSync(solanaWalletFile)) {
8468
+ const solanaWalletFile = path4.join(home, entry.name, "solana-wallet.json");
8469
+ if (fs5.existsSync(solanaWalletFile)) {
5048
8470
  try {
5049
- const data = JSON.parse(fs3.readFileSync(solanaWalletFile, "utf-8"));
8471
+ const data = JSON.parse(fs5.readFileSync(solanaWalletFile, "utf-8"));
5050
8472
  const pk = data.privateKey || "";
5051
8473
  const addr = data.address || "";
5052
8474
  if (pk && addr) {
5053
- const mtime = fs3.statSync(solanaWalletFile).mtimeMs;
8475
+ const mtime = fs5.statSync(solanaWalletFile).mtimeMs;
5054
8476
  results.push({ mtime, secretKey: pk, publicKey: addr, source: solanaWalletFile });
5055
8477
  }
5056
8478
  } catch {
5057
8479
  }
5058
8480
  }
5059
8481
  if (entry.name === ".brcc") {
5060
- const brccWalletFile = path3.join(home, entry.name, "wallet.json");
5061
- if (fs3.existsSync(brccWalletFile)) {
8482
+ const brccWalletFile = path4.join(home, entry.name, "wallet.json");
8483
+ if (fs5.existsSync(brccWalletFile)) {
5062
8484
  try {
5063
- const data = JSON.parse(fs3.readFileSync(brccWalletFile, "utf-8"));
8485
+ const data = JSON.parse(fs5.readFileSync(brccWalletFile, "utf-8"));
5064
8486
  const pk = data.privateKey || "";
5065
8487
  const addr = data.address || "";
5066
8488
  if (pk && addr) {
5067
- const mtime = fs3.statSync(brccWalletFile).mtimeMs;
8489
+ const mtime = fs5.statSync(brccWalletFile).mtimeMs;
5068
8490
  results.push({ mtime, secretKey: pk, publicKey: addr, source: brccWalletFile });
5069
8491
  }
5070
8492
  } catch {
@@ -5078,8 +8500,8 @@ function scanSolanaWallets() {
5078
8500
  return results.map(({ secretKey, publicKey, source }) => ({ secretKey, publicKey, source }));
5079
8501
  }
5080
8502
  function loadSolanaWallet() {
5081
- if (fs3.existsSync(SOLANA_WALLET_FILE)) {
5082
- const key = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
8503
+ if (fs5.existsSync(SOLANA_WALLET_FILE)) {
8504
+ const key = fs5.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
5083
8505
  if (key) return key;
5084
8506
  }
5085
8507
  return null;
@@ -5105,14 +8527,14 @@ async function importSolanaWallet(address) {
5105
8527
  continue;
5106
8528
  }
5107
8529
  if (derived !== wanted) continue;
5108
- if (fs3.existsSync(SOLANA_WALLET_FILE)) {
5109
- const current = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
8530
+ if (fs5.existsSync(SOLANA_WALLET_FILE)) {
8531
+ const current = fs5.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
5110
8532
  if (current && current !== entry.secretKey) {
5111
- const backup = path3.join(
5112
- WALLET_DIR2,
8533
+ const backup = path4.join(
8534
+ WALLET_DIR,
5113
8535
  `.solana-session.backup-${Math.floor(Date.now() / 1e3)}`
5114
8536
  );
5115
- fs3.writeFileSync(backup, current, { mode: 384 });
8537
+ fs5.writeFileSync(backup, current, { mode: 384 });
5116
8538
  }
5117
8539
  }
5118
8540
  saveSolanaWallet(entry.secretKey);
@@ -5180,8 +8602,8 @@ async function getOrCreateSolanaWallet() {
5180
8602
  );
5181
8603
  return { privateKey: envKey, address: address2, isNew: false };
5182
8604
  }
5183
- if (fs3.existsSync(SOLANA_WALLET_FILE)) {
5184
- const fileKey = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
8605
+ if (fs5.existsSync(SOLANA_WALLET_FILE)) {
8606
+ const fileKey = fs5.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
5185
8607
  if (fileKey) {
5186
8608
  const address2 = await solanaPublicKeyFrom(fileKey, SOLANA_WALLET_FILE);
5187
8609
  return { privateKey: fileKey, address: address2, isNew: false };
@@ -5228,6 +8650,8 @@ var SolanaLLMClient = class {
5228
8650
  sessionTotalUsd = 0;
5229
8651
  sessionCalls = 0;
5230
8652
  addressCache = null;
8653
+ modelPricingCache = null;
8654
+ modelPricingPromise = null;
5231
8655
  constructor(options = {}) {
5232
8656
  const envKey = typeof process !== "undefined" && process.env ? process.env.SOLANA_WALLET_KEY : void 0;
5233
8657
  const privateKey = options.privateKey || envKey;
@@ -5262,25 +8686,141 @@ var SolanaLLMClient = class {
5262
8686
  temperature: options?.temperature,
5263
8687
  topP: options?.topP,
5264
8688
  search: options?.search,
5265
- searchParameters: options?.searchParameters
8689
+ searchParameters: options?.searchParameters,
8690
+ responseFormat: options?.responseFormat,
8691
+ stop: options?.stop,
8692
+ fallbackModels: options?.fallbackModels
5266
8693
  });
5267
8694
  return result.choices[0].message.content || "";
5268
8695
  }
5269
8696
  /** Full chat completion (OpenAI-compatible). */
5270
8697
  async chatCompletion(model, messages, options) {
8698
+ const routingProfile = routingProfileForModel(model);
8699
+ if (routingProfile) {
8700
+ return (await this.smartChatCompletion(messages, {
8701
+ ...options,
8702
+ routingProfile
8703
+ })).response;
8704
+ }
5271
8705
  validateMaxTokens(options?.maxTokens);
5272
- const body = {
5273
- model,
5274
- messages,
5275
- max_tokens: options?.maxTokens || DEFAULT_MAX_TOKENS2
8706
+ const buildBody = (candidate) => {
8707
+ const body = {
8708
+ model: candidate,
8709
+ messages,
8710
+ max_tokens: options?.maxTokens || DEFAULT_MAX_TOKENS2
8711
+ };
8712
+ if (options?.temperature !== void 0) body.temperature = options.temperature;
8713
+ if (options?.topP !== void 0) body.top_p = options.topP;
8714
+ if (options?.searchParameters !== void 0) body.search_parameters = options.searchParameters;
8715
+ else if (options?.search === true) body.search_parameters = { mode: "on" };
8716
+ if (options?.tools !== void 0) body.tools = options.tools;
8717
+ if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
8718
+ if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
8719
+ if (options?.stop !== void 0) body.stop = options.stop;
8720
+ return body;
5276
8721
  };
5277
- if (options?.temperature !== void 0) body.temperature = options.temperature;
5278
- if (options?.topP !== void 0) body.top_p = options.topP;
5279
- if (options?.searchParameters !== void 0) body.search_parameters = options.searchParameters;
5280
- else if (options?.search === true) body.search_parameters = { mode: "on" };
5281
- if (options?.tools !== void 0) body.tools = options.tools;
5282
- if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
5283
- return this.requestWithPayment("/v1/chat/completions", body);
8722
+ const chain = [model, ...options?.fallbackModels ?? []];
8723
+ let lastError;
8724
+ for (let index = 0; index < chain.length; index += 1) {
8725
+ const candidate = chain[index];
8726
+ try {
8727
+ return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate));
8728
+ } catch (error) {
8729
+ lastError = error;
8730
+ if (!isTransientError(error) || index === chain.length - 1) throw error;
8731
+ console.error(`[@blockrun/llm] ${candidate} -> ${chain[index + 1]} (${errSummary(error)})`);
8732
+ }
8733
+ }
8734
+ throw lastError;
8735
+ }
8736
+ async getModelPricing() {
8737
+ if (this.modelPricingCache) return this.modelPricingCache;
8738
+ if (this.modelPricingPromise) return this.modelPricingPromise;
8739
+ this.modelPricingPromise = (async () => {
8740
+ const models = await this.listModels();
8741
+ const pricing = /* @__PURE__ */ new Map();
8742
+ for (const model of models) {
8743
+ const raw = model;
8744
+ if (model.available === false) continue;
8745
+ const num = (value) => {
8746
+ const n = Number(value ?? 0);
8747
+ return Number.isFinite(n) ? n : 0;
8748
+ };
8749
+ const inputPrice = num(model.inputPrice ?? raw.input_price ?? raw.pricing?.input);
8750
+ const outputPrice = num(model.outputPrice ?? raw.output_price ?? raw.pricing?.output);
8751
+ const flatPrice = num(model.flatPrice ?? raw.flat_price ?? raw.pricing?.flat);
8752
+ pricing.set(model.id, {
8753
+ inputPrice,
8754
+ outputPrice,
8755
+ ...(model.billingMode ?? raw.billing_mode) === "flat" && flatPrice > 0 ? { flatPrice } : {}
8756
+ });
8757
+ }
8758
+ return pricing;
8759
+ })();
8760
+ try {
8761
+ this.modelPricingCache = await this.modelPricingPromise;
8762
+ return this.modelPricingCache;
8763
+ } finally {
8764
+ this.modelPricingPromise = null;
8765
+ }
8766
+ }
8767
+ /** Inspect a Solana route without making or paying for a model call. */
8768
+ async route(prompt, options) {
8769
+ return routeWithCatalog(
8770
+ prompt,
8771
+ options?.system,
8772
+ options?.maxOutputTokens ?? options?.maxTokens ?? DEFAULT_MAX_TOKENS2,
8773
+ await this.getModelPricing(),
8774
+ {
8775
+ routingProfile: options?.routingProfile,
8776
+ requiresStructuredOutput: options?.responseFormat !== void 0,
8777
+ minimumPaymentUsd: SOLANA_MINIMUM_PAYMENT_USD
8778
+ }
8779
+ );
8780
+ }
8781
+ /** Smart one-line chat paid on Solana. */
8782
+ async smartChat(prompt, options) {
8783
+ const decision = await this.route(prompt, options);
8784
+ const response = await this.chat(decision.model, prompt, {
8785
+ ...options,
8786
+ // An explicit caller-supplied chain wins over the routed one.
8787
+ fallbackModels: options?.fallbackModels ?? decision.fallbacks
8788
+ });
8789
+ return { response, model: decision.model, routing: decision };
8790
+ }
8791
+ /** Smart full message/tool completion paid on Solana. */
8792
+ async smartChatCompletion(messages, options = {}) {
8793
+ const { prompt, systemPrompt, conversationChars, hasVision } = routingText(messages);
8794
+ const decision = routeWithCatalog(
8795
+ prompt,
8796
+ systemPrompt,
8797
+ options.maxOutputTokens ?? options.maxTokens ?? DEFAULT_MAX_TOKENS2,
8798
+ await this.getModelPricing(),
8799
+ {
8800
+ routingProfile: options.routingProfile,
8801
+ requiresStructuredOutput: options.responseFormat !== void 0,
8802
+ tools: options.tools,
8803
+ toolChoice: options.toolChoice,
8804
+ conversationChars,
8805
+ hasVision,
8806
+ minimumPaymentUsd: SOLANA_MINIMUM_PAYMENT_USD
8807
+ }
8808
+ );
8809
+ const response = await this.chatCompletion(decision.model, messages, {
8810
+ maxTokens: options.maxTokens,
8811
+ temperature: options.temperature,
8812
+ topP: options.topP,
8813
+ search: options.search,
8814
+ searchParameters: options.searchParameters,
8815
+ tools: options.tools,
8816
+ toolChoice: options.toolChoice,
8817
+ responseFormat: options.responseFormat,
8818
+ stop: options.stop,
8819
+ // An explicit caller-supplied chain wins over the routed one.
8820
+ fallbackModels: options.fallbackModels ?? decision.fallbacks
8821
+ });
8822
+ response.routing = decision;
8823
+ return { response, model: decision.model, routing: decision };
5284
8824
  }
5285
8825
  /** List available models. */
5286
8826
  async listModels() {
@@ -5345,11 +8885,11 @@ var SolanaLLMClient = class {
5345
8885
  return data;
5346
8886
  }
5347
8887
  // ── Prediction Markets (Powered by Predexon) ────────────────────────────
5348
- async pm(path5, params) {
5349
- return this.getWithPaymentRaw(`/v1/pm/${path5}`, params);
8888
+ async pm(path6, params) {
8889
+ return this.getWithPaymentRaw(`/v1/pm/${path6}`, params);
5350
8890
  }
5351
- async pmQuery(path5, query) {
5352
- return this.requestWithPaymentRaw(`/v1/pm/${path5}`, query);
8891
+ async pmQuery(path6, query) {
8892
+ return this.requestWithPaymentRaw(`/v1/pm/${path6}`, query);
5353
8893
  }
5354
8894
  // ── Exa Web Search (Powered by Exa) ──────────────────────────────────────
5355
8895
  /**
@@ -5361,8 +8901,8 @@ var SolanaLLMClient = class {
5361
8901
  * @example
5362
8902
  * const results = await client.exa("search", { query: "latest AI research", numResults: 5 });
5363
8903
  */
5364
- async exa(path5, body) {
5365
- return this.requestWithPaymentRaw(`/v1/exa/${path5}`, body);
8904
+ async exa(path6, body) {
8905
+ return this.requestWithPaymentRaw(`/v1/exa/${path6}`, body);
5366
8906
  }
5367
8907
  /**
5368
8908
  * Neural and keyword web search via Exa (Solana payment, $0.01/request).
@@ -5412,8 +8952,8 @@ var SolanaLLMClient = class {
5412
8952
  * "prices/coingecko:bitcoin,base:0x..."
5413
8953
  * @param params - Query parameters passed through to DefiLlama
5414
8954
  */
5415
- async defi(path5, params) {
5416
- return this.getWithPaymentRaw(`/v1/defillama/${path5}`, params);
8955
+ async defi(path6, params) {
8956
+ return this.getWithPaymentRaw(`/v1/defillama/${path6}`, params);
5417
8957
  }
5418
8958
  /** All DeFi protocols with TVL ($0.005/call). */
5419
8959
  async defiProtocols() {
@@ -5449,9 +8989,9 @@ var SolanaLLMClient = class {
5449
8989
  * @param params - Query params (chainId, sellToken, buyToken, sellAmount, taker, ...)
5450
8990
  * @param body - JSON body — pass to switch to POST (gasless/submit only)
5451
8991
  */
5452
- async dex(path5, params, body) {
5453
- if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path5}`, body);
5454
- return this.getWithPaymentRaw(`/v1/zerox/${path5}`, params);
8992
+ async dex(path6, params, body) {
8993
+ if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path6}`, body);
8994
+ return this.getWithPaymentRaw(`/v1/zerox/${path6}`, params);
5455
8995
  }
5456
8996
  /** Indicative Permit2 swap price — no commitment (free). */
5457
8997
  async dexPrice(params) {
@@ -5495,8 +9035,8 @@ var SolanaLLMClient = class {
5495
9035
  * "sandbox/status", "sandbox/terminate" ($0.001 each)
5496
9036
  * @param body - JSON body for the endpoint
5497
9037
  */
5498
- async modal(path5, body) {
5499
- return this.requestWithPaymentRaw(`/v1/modal/${path5}`, body ?? {});
9038
+ async modal(path6, body) {
9039
+ return this.requestWithPaymentRaw(`/v1/modal/${path6}`, body ?? {});
5500
9040
  }
5501
9041
  /** Create a sandboxed compute environment ($0.01 CPU / $0.05 GPU). */
5502
9042
  async modalSandboxCreate(body) {
@@ -5813,13 +9353,13 @@ function solanaClient(options = {}) {
5813
9353
  }
5814
9354
 
5815
9355
  // src/cache.ts
5816
- var fs4 = __toESM(require("fs"), 1);
5817
- var path4 = __toESM(require("path"), 1);
9356
+ var fs6 = __toESM(require("fs"), 1);
9357
+ var path5 = __toESM(require("path"), 1);
5818
9358
  var os4 = __toESM(require("os"), 1);
5819
9359
  var crypto2 = __toESM(require("crypto"), 1);
5820
- var CACHE_DIR = path4.join(os4.homedir(), ".blockrun", "cache");
5821
- var DATA_DIR = path4.join(os4.homedir(), ".blockrun", "data");
5822
- var COST_LOG_FILE2 = path4.join(os4.homedir(), ".blockrun", "cost_log.jsonl");
9360
+ var CACHE_DIR = path5.join(os4.homedir(), ".blockrun", "cache");
9361
+ var DATA_DIR = path5.join(os4.homedir(), ".blockrun", "data");
9362
+ var COST_LOG_FILE2 = path5.join(os4.homedir(), ".blockrun", "cost_log.jsonl");
5823
9363
  var DEFAULT_TTL = {
5824
9364
  "/v1/partner/": 3600 * 1e3,
5825
9365
  "/v1/pm/": 1800 * 1e3,
@@ -5839,19 +9379,19 @@ function cacheKey(endpoint, body) {
5839
9379
  return crypto2.createHash("sha256").update(keyData).digest("hex").slice(0, 16);
5840
9380
  }
5841
9381
  function cachePath(key) {
5842
- return path4.join(CACHE_DIR, `${key}.json`);
9382
+ return path5.join(CACHE_DIR, `${key}.json`);
5843
9383
  }
5844
9384
  function getCached(key) {
5845
9385
  const filePath = cachePath(key);
5846
- if (!fs4.existsSync(filePath)) return null;
9386
+ if (!fs6.existsSync(filePath)) return null;
5847
9387
  try {
5848
- const raw = fs4.readFileSync(filePath, "utf-8");
9388
+ const raw = fs6.readFileSync(filePath, "utf-8");
5849
9389
  const entry = JSON.parse(raw);
5850
9390
  const ttl = entry.ttlMs ?? getTtl(entry.endpoint ?? "");
5851
9391
  if (ttl <= 0) return null;
5852
9392
  if (Date.now() - entry.cachedAt > ttl) {
5853
9393
  try {
5854
- fs4.unlinkSync(filePath);
9394
+ fs6.unlinkSync(filePath);
5855
9395
  } catch {
5856
9396
  }
5857
9397
  return null;
@@ -5870,7 +9410,7 @@ function getCachedByRequest(endpoint, body) {
5870
9410
  function setCache(key, data, ttlMs) {
5871
9411
  if (ttlMs <= 0) return;
5872
9412
  try {
5873
- fs4.mkdirSync(CACHE_DIR, { recursive: true });
9413
+ fs6.mkdirSync(CACHE_DIR, { recursive: true });
5874
9414
  } catch {
5875
9415
  }
5876
9416
  const entry = {
@@ -5879,7 +9419,7 @@ function setCache(key, data, ttlMs) {
5879
9419
  ttlMs
5880
9420
  };
5881
9421
  try {
5882
- fs4.writeFileSync(cachePath(key), JSON.stringify(entry));
9422
+ fs6.writeFileSync(cachePath(key), JSON.stringify(entry));
5883
9423
  } catch {
5884
9424
  }
5885
9425
  }
@@ -5900,7 +9440,7 @@ function readableFilename(endpoint, body) {
5900
9440
  }
5901
9441
  function saveReadable(endpoint, body, response, costUsd) {
5902
9442
  try {
5903
- fs4.mkdirSync(DATA_DIR, { recursive: true });
9443
+ fs6.mkdirSync(DATA_DIR, { recursive: true });
5904
9444
  } catch {
5905
9445
  }
5906
9446
  const filename = readableFilename(endpoint, body);
@@ -5912,14 +9452,14 @@ function saveReadable(endpoint, body, response, costUsd) {
5912
9452
  response
5913
9453
  };
5914
9454
  try {
5915
- fs4.writeFileSync(path4.join(DATA_DIR, filename), JSON.stringify(entry, null, 2));
9455
+ fs6.writeFileSync(path5.join(DATA_DIR, filename), JSON.stringify(entry, null, 2));
5916
9456
  } catch {
5917
9457
  }
5918
9458
  }
5919
9459
  function appendCostLog(endpoint, costUsd) {
5920
9460
  if (costUsd <= 0) return;
5921
9461
  try {
5922
- fs4.mkdirSync(path4.dirname(COST_LOG_FILE2), { recursive: true });
9462
+ fs6.mkdirSync(path5.dirname(COST_LOG_FILE2), { recursive: true });
5923
9463
  } catch {
5924
9464
  }
5925
9465
  const entry = {
@@ -5928,7 +9468,7 @@ function appendCostLog(endpoint, costUsd) {
5928
9468
  cost_usd: costUsd
5929
9469
  };
5930
9470
  try {
5931
- fs4.appendFileSync(COST_LOG_FILE2, JSON.stringify(entry) + "\n");
9471
+ fs6.appendFileSync(COST_LOG_FILE2, JSON.stringify(entry) + "\n");
5932
9472
  } catch {
5933
9473
  }
5934
9474
  }
@@ -5936,7 +9476,7 @@ function saveToCache(endpoint, body, response, costUsd = 0) {
5936
9476
  const ttl = getTtl(endpoint);
5937
9477
  if (ttl > 0) {
5938
9478
  try {
5939
- fs4.mkdirSync(CACHE_DIR, { recursive: true });
9479
+ fs6.mkdirSync(CACHE_DIR, { recursive: true });
5940
9480
  } catch {
5941
9481
  }
5942
9482
  const key = cacheKey(endpoint, body);
@@ -5948,7 +9488,7 @@ function saveToCache(endpoint, body, response, costUsd = 0) {
5948
9488
  costUsd
5949
9489
  };
5950
9490
  try {
5951
- fs4.writeFileSync(cachePath(key), JSON.stringify(entry));
9491
+ fs6.writeFileSync(cachePath(key), JSON.stringify(entry));
5952
9492
  } catch {
5953
9493
  }
5954
9494
  }
@@ -5956,14 +9496,14 @@ function saveToCache(endpoint, body, response, costUsd = 0) {
5956
9496
  appendCostLog(endpoint, costUsd);
5957
9497
  }
5958
9498
  function clearCache() {
5959
- if (!fs4.existsSync(CACHE_DIR)) return 0;
9499
+ if (!fs6.existsSync(CACHE_DIR)) return 0;
5960
9500
  let count = 0;
5961
9501
  try {
5962
- const files = fs4.readdirSync(CACHE_DIR);
9502
+ const files = fs6.readdirSync(CACHE_DIR);
5963
9503
  for (const file of files) {
5964
9504
  if (file.endsWith(".json")) {
5965
9505
  try {
5966
- fs4.unlinkSync(path4.join(CACHE_DIR, file));
9506
+ fs6.unlinkSync(path5.join(CACHE_DIR, file));
5967
9507
  count++;
5968
9508
  } catch {
5969
9509
  }
@@ -5974,14 +9514,14 @@ function clearCache() {
5974
9514
  return count;
5975
9515
  }
5976
9516
  function getCostLogSummary() {
5977
- if (!fs4.existsSync(COST_LOG_FILE2)) {
9517
+ if (!fs6.existsSync(COST_LOG_FILE2)) {
5978
9518
  return { totalUsd: 0, calls: 0, byEndpoint: {} };
5979
9519
  }
5980
9520
  let totalUsd = 0;
5981
9521
  let calls = 0;
5982
9522
  const byEndpoint = {};
5983
9523
  try {
5984
- const content = fs4.readFileSync(COST_LOG_FILE2, "utf-8").trim();
9524
+ const content = fs6.readFileSync(COST_LOG_FILE2, "utf-8").trim();
5985
9525
  if (!content) return { totalUsd: 0, calls: 0, byEndpoint: {} };
5986
9526
  for (const line of content.split("\n")) {
5987
9527
  if (!line) continue;
@@ -6161,7 +9701,8 @@ var ChatCompletions = class {
6161
9701
  },
6162
9702
  finish_reason: choice.finish_reason || "stop"
6163
9703
  })),
6164
- usage: response.usage
9704
+ usage: response.usage,
9705
+ routing: response.routing
6165
9706
  };
6166
9707
  }
6167
9708
  };
@@ -6194,7 +9735,7 @@ var OpenAI = class {
6194
9735
  };
6195
9736
 
6196
9737
  // src/anthropic-compat.ts
6197
- var import_accounts16 = require("viem/accounts");
9738
+ var import_accounts17 = require("viem/accounts");
6198
9739
  var AnthropicClient = class {
6199
9740
  _client = null;
6200
9741
  _clientPromise = null;
@@ -6207,7 +9748,7 @@ var AnthropicClient = class {
6207
9748
  const key = options.privateKey ?? wallet.privateKey;
6208
9749
  validatePrivateKey(key);
6209
9750
  this._privateKey = key;
6210
- this._account = (0, import_accounts16.privateKeyToAccount)(this._privateKey);
9751
+ this._account = (0, import_accounts17.privateKeyToAccount)(this._privateKey);
6211
9752
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
6212
9753
  validateApiUrl(apiUrl);
6213
9754
  this._apiUrl = apiUrl.replace(/\/$/, "");