@blockrun/llm 3.10.0 → 3.12.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
@@ -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.10.0";
3967
+ var SDK_VERSION = "3.12.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 [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,38 @@ 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 14-dimension rule-based scoring algorithm (<1ms, 100% local)
746
- * to select the cheapest model that can handle your request.
4106
+ * Uses BlockRun's product-neutral Router Core portfolio strategy: it
4107
+ * classifies the task shape locally (no
4108
+ * extra model call), enforces capability constraints as hard filters, and
4109
+ * ranks an ordered candidate portfolio — the cheapest model that can handle
4110
+ * the request wins, and the rest become the transient-error fallback chain.
747
4111
  *
748
4112
  * @param prompt - User message
749
4113
  * @param options - Optional chat and routing parameters
@@ -753,7 +4117,8 @@ var LLMClient = class _LLMClient {
753
4117
  * ```ts
754
4118
  * const result = await client.smartChat('What is 2+2?');
755
4119
  * console.log(result.response); // '4'
756
- * console.log(result.model); // 'google/gemini-2.5-flash-lite'
4120
+ * console.log(result.model); // 'google/gemini-3.5-flash'
4121
+ * console.log(result.routing.method); // 'portfolio'
757
4122
  * console.log(result.routing.savings); // 0.78 (78% savings)
758
4123
  * ```
759
4124
  *
@@ -767,28 +4132,7 @@ var LLMClient = class _LLMClient {
767
4132
  * ```
768
4133
  */
769
4134
  async smartChat(prompt, options) {
770
- const modelPricing = await this.getModelPricing();
771
- const maxOutputTokens = options?.maxOutputTokens || options?.maxTokens || 1024;
772
- let route;
773
- let DEFAULT_ROUTING_CONFIG;
774
- let getFallbackChain;
775
- try {
776
- ({ route, DEFAULT_ROUTING_CONFIG, getFallbackChain } = await import("@blockrun/clawrouter"));
777
- } catch (err) {
778
- throw new Error(
779
- `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}`
780
- );
781
- }
782
- const decision = route(prompt, options?.system, maxOutputTokens, {
783
- config: DEFAULT_ROUTING_CONFIG,
784
- modelPricing,
785
- routingProfile: options?.routingProfile
786
- });
787
- const tierConfigs = decision.tierConfigs ?? DEFAULT_ROUTING_CONFIG.tiers;
788
- const fullChain = getFallbackChain(decision.tier, tierConfigs);
789
- const fallbacks = fullChain.filter(
790
- (id) => id !== decision.model && modelPricing.has(id)
791
- );
4135
+ const decision = await this.route(prompt, options);
792
4136
  const response = await this.chat(decision.model, prompt, {
793
4137
  system: options?.system,
794
4138
  maxTokens: options?.maxTokens,
@@ -798,14 +4142,47 @@ var LLMClient = class _LLMClient {
798
4142
  searchParameters: options?.searchParameters,
799
4143
  responseFormat: options?.responseFormat,
800
4144
  stop: options?.stop,
801
- fallbackModels: fallbacks
4145
+ // An explicit caller-supplied chain wins over the routed one.
4146
+ fallbackModels: options?.fallbackModels ?? decision.fallbacks
802
4147
  });
803
4148
  return {
804
4149
  response,
805
4150
  model: decision.model,
806
- routing: { ...decision, fallbacks }
4151
+ routing: decision
807
4152
  };
808
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
+ }
809
4186
  /**
810
4187
  * Get model pricing map (cached).
811
4188
  * Fetches from API on first call, then returns cached result.
@@ -825,31 +4202,18 @@ var LLMClient = class _LLMClient {
825
4202
  this.modelPricingPromise = null;
826
4203
  }
827
4204
  }
828
- /**
829
- * Fetch model pricing from API.
830
- *
831
- * For flat-billed models (e.g. ZAI GLM-5 family at $0.001/call) the
832
- * router still expects per-token rates, so we synthesise an equivalent
833
- * per-token price assuming ~1500 total tokens per call. Without this,
834
- * flat models would resolve to inputPrice=outputPrice=0 and the router
835
- * would treat them as free, biasing routing decisions and reporting
836
- * inflated savings %.
837
- */
4205
+ /** Fetch model pricing from the live catalog, preserving flat billing. */
838
4206
  async fetchModelPricing() {
839
4207
  const models = await this.listModels();
840
4208
  const pricing = /* @__PURE__ */ new Map();
841
4209
  for (const model of models) {
842
- if (model.billingMode === "flat" && model.flatPrice && model.flatPrice > 0) {
843
- const perDirection = model.flatPrice * 1e6 / 1500 / 2;
844
- pricing.set(model.id, {
845
- inputPrice: perDirection,
846
- outputPrice: perDirection
847
- });
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 });
848
4215
  } else {
849
- pricing.set(model.id, {
850
- inputPrice: model.inputPrice,
851
- outputPrice: model.outputPrice
852
- });
4216
+ pricing.set(model.id, { inputPrice, outputPrice });
853
4217
  }
854
4218
  }
855
4219
  return pricing;
@@ -869,6 +4233,13 @@ var LLMClient = class _LLMClient {
869
4233
  * @returns ChatResponse object with choices and usage
870
4234
  */
871
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
+ }
872
4243
  validateMaxTokens(options?.maxTokens);
873
4244
  const buildBody = (m) => {
874
4245
  const body = {
@@ -1141,9 +4512,28 @@ var LLMClient = class _LLMClient {
1141
4512
  */
1142
4513
  async chatCompletionStream(model, messages, options) {
1143
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
+ }
1144
4534
  const url = `${this.apiUrl}/v1/chat/completions`;
1145
4535
  const body = {
1146
- model,
4536
+ model: requestModel,
1147
4537
  messages,
1148
4538
  max_tokens: options?.maxTokens ?? DEFAULT_MAX_TOKENS,
1149
4539
  stream: true
@@ -1154,7 +4544,7 @@ var LLMClient = class _LLMClient {
1154
4544
  if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
1155
4545
  if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
1156
4546
  if (options?.stop !== void 0) body.stop = options.stop;
1157
- const cacheKey2 = `/v1/chat/completions:${model}`;
4547
+ const cacheKey2 = `/v1/chat/completions:${requestModel}`;
1158
4548
  const cached = this.preAuthCache.get(cacheKey2);
1159
4549
  const now = Date.now();
1160
4550
  if (cached && now - cached.cachedAt < _LLMClient.PRE_AUTH_TTL_MS) {
@@ -1764,7 +5154,13 @@ var LLMClient = class _LLMClient {
1764
5154
  */
1765
5155
  async getBalance() {
1766
5156
  const usdcContract = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
1767
- 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));
1768
5164
  const selector = "0x70a08231";
1769
5165
  const paddedAddress = this.account.address.slice(2).toLowerCase().padStart(64, "0");
1770
5166
  const data = selector + paddedAddress;
@@ -1782,7 +5178,9 @@ var LLMClient = class _LLMClient {
1782
5178
  headers: { "Content-Type": "application/json" },
1783
5179
  body: JSON.stringify(payload)
1784
5180
  });
5181
+ if (!response.ok) throw new Error(`Base RPC returned ${response.status}`);
1785
5182
  const result = await response.json();
5183
+ if (!result.result || result.error) throw new Error("Base RPC returned no balance result");
1786
5184
  const balanceRaw = parseInt(result.result || "0x0", 16);
1787
5185
  return balanceRaw / 1e6;
1788
5186
  } catch (e) {
@@ -5220,6 +8618,8 @@ var SolanaLLMClient = class {
5220
8618
  sessionTotalUsd = 0;
5221
8619
  sessionCalls = 0;
5222
8620
  addressCache = null;
8621
+ modelPricingCache = null;
8622
+ modelPricingPromise = null;
5223
8623
  constructor(options = {}) {
5224
8624
  const envKey = typeof process !== "undefined" && process.env ? process.env.SOLANA_WALLET_KEY : void 0;
5225
8625
  const privateKey = options.privateKey || envKey;
@@ -5254,25 +8654,141 @@ var SolanaLLMClient = class {
5254
8654
  temperature: options?.temperature,
5255
8655
  topP: options?.topP,
5256
8656
  search: options?.search,
5257
- searchParameters: options?.searchParameters
8657
+ searchParameters: options?.searchParameters,
8658
+ responseFormat: options?.responseFormat,
8659
+ stop: options?.stop,
8660
+ fallbackModels: options?.fallbackModels
5258
8661
  });
5259
8662
  return result.choices[0].message.content || "";
5260
8663
  }
5261
8664
  /** Full chat completion (OpenAI-compatible). */
5262
8665
  async chatCompletion(model, messages, options) {
8666
+ const routingProfile = routingProfileForModel(model);
8667
+ if (routingProfile) {
8668
+ return (await this.smartChatCompletion(messages, {
8669
+ ...options,
8670
+ routingProfile
8671
+ })).response;
8672
+ }
5263
8673
  validateMaxTokens(options?.maxTokens);
5264
- const body = {
5265
- model,
5266
- messages,
5267
- max_tokens: options?.maxTokens || DEFAULT_MAX_TOKENS2
8674
+ const buildBody = (candidate) => {
8675
+ const body = {
8676
+ model: candidate,
8677
+ messages,
8678
+ max_tokens: options?.maxTokens || DEFAULT_MAX_TOKENS2
8679
+ };
8680
+ if (options?.temperature !== void 0) body.temperature = options.temperature;
8681
+ if (options?.topP !== void 0) body.top_p = options.topP;
8682
+ if (options?.searchParameters !== void 0) body.search_parameters = options.searchParameters;
8683
+ else if (options?.search === true) body.search_parameters = { mode: "on" };
8684
+ if (options?.tools !== void 0) body.tools = options.tools;
8685
+ if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
8686
+ if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
8687
+ if (options?.stop !== void 0) body.stop = options.stop;
8688
+ return body;
5268
8689
  };
5269
- if (options?.temperature !== void 0) body.temperature = options.temperature;
5270
- if (options?.topP !== void 0) body.top_p = options.topP;
5271
- if (options?.searchParameters !== void 0) body.search_parameters = options.searchParameters;
5272
- else if (options?.search === true) body.search_parameters = { mode: "on" };
5273
- if (options?.tools !== void 0) body.tools = options.tools;
5274
- if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
5275
- return this.requestWithPayment("/v1/chat/completions", body);
8690
+ const chain = [model, ...options?.fallbackModels ?? []];
8691
+ let lastError;
8692
+ for (let index = 0; index < chain.length; index += 1) {
8693
+ const candidate = chain[index];
8694
+ try {
8695
+ return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate));
8696
+ } catch (error) {
8697
+ lastError = error;
8698
+ if (!isTransientError(error) || index === chain.length - 1) throw error;
8699
+ console.error(`[@blockrun/llm] ${candidate} -> ${chain[index + 1]} (${errSummary(error)})`);
8700
+ }
8701
+ }
8702
+ throw lastError;
8703
+ }
8704
+ async getModelPricing() {
8705
+ if (this.modelPricingCache) return this.modelPricingCache;
8706
+ if (this.modelPricingPromise) return this.modelPricingPromise;
8707
+ this.modelPricingPromise = (async () => {
8708
+ const models = await this.listModels();
8709
+ const pricing = /* @__PURE__ */ new Map();
8710
+ for (const model of models) {
8711
+ const raw = model;
8712
+ if (model.available === false) continue;
8713
+ const num = (value) => {
8714
+ const n = Number(value ?? 0);
8715
+ return Number.isFinite(n) ? n : 0;
8716
+ };
8717
+ const inputPrice = num(model.inputPrice ?? raw.input_price ?? raw.pricing?.input);
8718
+ const outputPrice = num(model.outputPrice ?? raw.output_price ?? raw.pricing?.output);
8719
+ const flatPrice = num(model.flatPrice ?? raw.flat_price ?? raw.pricing?.flat);
8720
+ pricing.set(model.id, {
8721
+ inputPrice,
8722
+ outputPrice,
8723
+ ...(model.billingMode ?? raw.billing_mode) === "flat" && flatPrice > 0 ? { flatPrice } : {}
8724
+ });
8725
+ }
8726
+ return pricing;
8727
+ })();
8728
+ try {
8729
+ this.modelPricingCache = await this.modelPricingPromise;
8730
+ return this.modelPricingCache;
8731
+ } finally {
8732
+ this.modelPricingPromise = null;
8733
+ }
8734
+ }
8735
+ /** Inspect a Solana route without making or paying for a model call. */
8736
+ async route(prompt, options) {
8737
+ return routeWithCatalog(
8738
+ prompt,
8739
+ options?.system,
8740
+ options?.maxOutputTokens ?? options?.maxTokens ?? DEFAULT_MAX_TOKENS2,
8741
+ await this.getModelPricing(),
8742
+ {
8743
+ routingProfile: options?.routingProfile,
8744
+ requiresStructuredOutput: options?.responseFormat !== void 0,
8745
+ minimumPaymentUsd: SOLANA_MINIMUM_PAYMENT_USD
8746
+ }
8747
+ );
8748
+ }
8749
+ /** Smart one-line chat paid on Solana. */
8750
+ async smartChat(prompt, options) {
8751
+ const decision = await this.route(prompt, options);
8752
+ const response = await this.chat(decision.model, prompt, {
8753
+ ...options,
8754
+ // An explicit caller-supplied chain wins over the routed one.
8755
+ fallbackModels: options?.fallbackModels ?? decision.fallbacks
8756
+ });
8757
+ return { response, model: decision.model, routing: decision };
8758
+ }
8759
+ /** Smart full message/tool completion paid on Solana. */
8760
+ async smartChatCompletion(messages, options = {}) {
8761
+ const { prompt, systemPrompt, conversationChars, hasVision } = routingText(messages);
8762
+ const decision = routeWithCatalog(
8763
+ prompt,
8764
+ systemPrompt,
8765
+ options.maxOutputTokens ?? options.maxTokens ?? DEFAULT_MAX_TOKENS2,
8766
+ await this.getModelPricing(),
8767
+ {
8768
+ routingProfile: options.routingProfile,
8769
+ requiresStructuredOutput: options.responseFormat !== void 0,
8770
+ tools: options.tools,
8771
+ toolChoice: options.toolChoice,
8772
+ conversationChars,
8773
+ hasVision,
8774
+ minimumPaymentUsd: SOLANA_MINIMUM_PAYMENT_USD
8775
+ }
8776
+ );
8777
+ const response = await this.chatCompletion(decision.model, messages, {
8778
+ maxTokens: options.maxTokens,
8779
+ temperature: options.temperature,
8780
+ topP: options.topP,
8781
+ search: options.search,
8782
+ searchParameters: options.searchParameters,
8783
+ tools: options.tools,
8784
+ toolChoice: options.toolChoice,
8785
+ responseFormat: options.responseFormat,
8786
+ stop: options.stop,
8787
+ // An explicit caller-supplied chain wins over the routed one.
8788
+ fallbackModels: options.fallbackModels ?? decision.fallbacks
8789
+ });
8790
+ response.routing = decision;
8791
+ return { response, model: decision.model, routing: decision };
5276
8792
  }
5277
8793
  /** List available models. */
5278
8794
  async listModels() {
@@ -6153,7 +9669,8 @@ var ChatCompletions = class {
6153
9669
  },
6154
9670
  finish_reason: choice.finish_reason || "stop"
6155
9671
  })),
6156
- usage: response.usage
9672
+ usage: response.usage,
9673
+ routing: response.routing
6157
9674
  };
6158
9675
  }
6159
9676
  };