@peterwangze/claude-trigger-router 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -14059,6 +14059,8 @@ function createDefaultDeps(io = createConsoleIO()) {
14059
14059
  }
14060
14060
  function printRoutingNextSteps(io) {
14061
14061
  io.info("\u4F60\u53EF\u4EE5\u6309\u9700\u7EE7\u7EED\u914D\u7F6E\u8DEF\u7531\u80FD\u529B\uFF1A");
14062
+ io.info(' - \u5148\u8FD0\u884C ctr doctor --route-preview --route-text "\u4F60\u7684\u8BF7\u6C42"\uFF0C\u786E\u8BA4\u672C\u6B21\u4F1A\u547D\u4E2D\u54EA\u4E2A\u69FD\u4F4D\u6216 SmartRouter \u8DEF\u5F84');
14063
+ io.info(" - \u57FA\u7840\u8DEF\u7531\u987A\u5E8F\uFF1A\u663E\u5F0F\u4E0A\u6E38\u6A21\u578B -> longContext -> background -> think -> webSearch -> default");
14062
14064
  io.info(" - SmartRouter.rules\uFF1A\u9002\u5408\u9AD8\u786E\u5B9A\u6027\u4EFB\u52A1\uFF0C\u628A\u67B6\u6784\u8BBE\u8BA1\u3001\u4EE3\u7801\u5BA1\u67E5\u7B49\u8BF7\u6C42\u56FA\u5B9A\u5207\u5230\u6307\u5B9A\u6A21\u578B");
14063
14065
  io.info(" - SmartRouter candidates\uFF1A\u9002\u5408\u6A21\u7CCA\u4EFB\u52A1\uFF0C\u5728\u5019\u9009\u6A21\u578B\u4E4B\u95F4\u81EA\u52A8\u9009\u62E9\u66F4\u5408\u9002\u7684\u6A21\u578B");
14064
14066
  io.info(" - \u914D\u7F6E\u6A21\u677F\u53C2\u8003\uFF1Aconfig/trigger.advanced.yaml");
@@ -14244,6 +14246,294 @@ var init_setup2 = __esm({
14244
14246
  }
14245
14247
  });
14246
14248
 
14249
+ // src/router/route-preview.ts
14250
+ function resolveDisplayModel(config, ref) {
14251
+ if (!ref) {
14252
+ return {};
14253
+ }
14254
+ const resolved = resolveModelReference(config, ref);
14255
+ return {
14256
+ model: ref,
14257
+ resolved
14258
+ };
14259
+ }
14260
+ function extractRuleMatch(text, rules) {
14261
+ const sortedRules = rules.filter((rule) => rule.enabled !== false).sort((a, b) => (b.priority || 0) - (a.priority || 0));
14262
+ for (const rule of sortedRules) {
14263
+ for (const pattern of rule.patterns ?? []) {
14264
+ const result = patternMatcher.match(text, pattern);
14265
+ if (!result.matched) {
14266
+ continue;
14267
+ }
14268
+ const detail = result.matchedKeyword ? `keyword "${result.matchedKeyword}"` : result.regexMatch ? `regex "${pattern.pattern}"` : pattern.type;
14269
+ return { rule, detail };
14270
+ }
14271
+ }
14272
+ return void 0;
14273
+ }
14274
+ function buildSemanticCandidates(rules, config) {
14275
+ const defaultThreshold = config.semantic?.threshold;
14276
+ const prototypes = config.semantic?.prototypes ?? {};
14277
+ return rules.filter((rule) => rule.enabled !== false).map((rule) => {
14278
+ const prototype = rule.semantic_profile?.prototype ?? prototypes[rule.name] ?? rule.description;
14279
+ if (!prototype || rule.semantic_profile?.enabled === false) {
14280
+ return void 0;
14281
+ }
14282
+ return {
14283
+ intent: rule.name,
14284
+ prototype,
14285
+ threshold: rule.semantic_profile?.threshold ?? defaultThreshold,
14286
+ rule
14287
+ };
14288
+ }).filter((item) => Boolean(item));
14289
+ }
14290
+ function previewBasicRouter(config, input3) {
14291
+ const steps = [];
14292
+ const warnings = [];
14293
+ const registry = buildModelRegistry(config);
14294
+ const tokenCount = input3.tokenCount ?? 0;
14295
+ const longContextThreshold = config.Router.longContextThreshold || 6e4;
14296
+ const explicit = resolveModelReference(config, input3.model);
14297
+ if (explicit && explicit.includes(",")) {
14298
+ steps.push({
14299
+ label: "Explicit model",
14300
+ status: "matched",
14301
+ detail: `\u8BF7\u6C42\u6A21\u578B "${input3.model}" \u5DF2\u89E3\u6790\u4E3A\u4E0A\u6E38\u5F15\u7528\uFF0C\u57FA\u7840\u69FD\u4F4D\u4E0D\u4F1A\u518D\u8986\u76D6\u3002`
14302
+ });
14303
+ return {
14304
+ input: input3,
14305
+ finalModel: explicit,
14306
+ finalModelRef: input3.model,
14307
+ source: "explicit_model",
14308
+ steps,
14309
+ warnings
14310
+ };
14311
+ }
14312
+ if (tokenCount > longContextThreshold && config.Router.longContext) {
14313
+ const model = resolveDisplayModel(config, config.Router.longContext);
14314
+ steps.push({
14315
+ label: "Router.longContext",
14316
+ status: "matched",
14317
+ detail: `tokenCount ${tokenCount} > threshold ${longContextThreshold}\uFF0C\u4F18\u5148\u4F7F\u7528\u957F\u4E0A\u4E0B\u6587\u69FD\u4F4D\u3002`
14318
+ });
14319
+ return {
14320
+ input: input3,
14321
+ finalModel: model.resolved,
14322
+ finalModelRef: model.model,
14323
+ source: "basic_long_context",
14324
+ steps,
14325
+ warnings
14326
+ };
14327
+ }
14328
+ steps.push({
14329
+ label: "Router.longContext",
14330
+ status: config.Router.longContext ? "info" : "skipped",
14331
+ detail: config.Router.longContext ? `\u5F53\u524D tokenCount ${tokenCount} \u672A\u8D85\u8FC7 threshold ${longContextThreshold}\uFF1B\u771F\u5B9E\u8FD0\u884C\u8FD8\u4F1A\u5728\u6700\u7EC8\u5B9A\u6A21\u540E\u68C0\u67E5 safe_input/context_window\u3002` : "\u672A\u914D\u7F6E Router.longContext\uFF0C\u5927\u4E0A\u4E0B\u6587\u8BF7\u6C42\u4F1A\u56DE\u5230\u5F53\u524D\u9009\u4E2D\u6A21\u578B\u3002"
14332
+ });
14333
+ if (input3.model?.startsWith("claude-3-5-haiku") && config.Router.background) {
14334
+ const model = resolveDisplayModel(config, config.Router.background);
14335
+ steps.push({
14336
+ label: "Router.background",
14337
+ status: "matched",
14338
+ detail: "\u8BF7\u6C42\u6A21\u578B\u4EE5 claude-3-5-haiku \u5F00\u5934\uFF0C\u6309\u5F53\u524D\u57FA\u7840\u8DEF\u7531\u5B9E\u73B0\u8BC6\u522B\u4E3A\u540E\u53F0\u8BF7\u6C42\u3002"
14339
+ });
14340
+ return {
14341
+ input: input3,
14342
+ finalModel: model.resolved,
14343
+ finalModelRef: model.model,
14344
+ source: "basic_background",
14345
+ steps,
14346
+ warnings
14347
+ };
14348
+ }
14349
+ steps.push({
14350
+ label: "Router.background",
14351
+ status: config.Router.background ? "info" : "skipped",
14352
+ detail: config.Router.background ? "\u4EC5\u5F53\u8BF7\u6C42\u6A21\u578B\u4EE5 claude-3-5-haiku \u5F00\u5934\u65F6\u547D\u4E2D\uFF1B\u5982\u679C Claude Code \u540E\u53F0\u6A21\u578B\u6807\u8BC6\u53D8\u5316\uFF0C\u9700\u8981\u91CD\u65B0\u6821\u51C6\u3002" : "\u672A\u914D\u7F6E Router.background\u3002"
14353
+ });
14354
+ if (input3.thinking && config.Router.think) {
14355
+ const model = resolveDisplayModel(config, config.Router.think);
14356
+ steps.push({
14357
+ label: "Router.think",
14358
+ status: "matched",
14359
+ detail: "\u8BF7\u6C42\u5305\u542B thinking\uFF0C\u4F7F\u7528\u601D\u8003\u69FD\u4F4D\u3002"
14360
+ });
14361
+ return {
14362
+ input: input3,
14363
+ finalModel: model.resolved,
14364
+ finalModelRef: model.model,
14365
+ source: "basic_thinking",
14366
+ steps,
14367
+ warnings
14368
+ };
14369
+ }
14370
+ steps.push({
14371
+ label: "Router.think",
14372
+ status: config.Router.think ? "info" : "skipped",
14373
+ detail: config.Router.think ? "\u672C\u6B21\u672A\u58F0\u660E thinking\uFF1B\u5982\u679C\u540C\u65F6\u8D85\u8FC7 longContext threshold\uFF0C\u957F\u4E0A\u4E0B\u6587\u4F1A\u5148\u4E8E thinking \u547D\u4E2D\u3002" : "\u672A\u914D\u7F6E Router.think\u3002"
14374
+ });
14375
+ if (input3.webSearch && config.Router.webSearch) {
14376
+ const model = resolveDisplayModel(config, config.Router.webSearch);
14377
+ steps.push({
14378
+ label: "Router.webSearch",
14379
+ status: "matched",
14380
+ detail: "\u8BF7\u6C42\u5305\u542B web_search \u5DE5\u5177\uFF0C\u4F7F\u7528\u8054\u7F51\u641C\u7D22\u69FD\u4F4D\u3002"
14381
+ });
14382
+ return {
14383
+ input: input3,
14384
+ finalModel: model.resolved,
14385
+ finalModelRef: model.model,
14386
+ source: "basic_web_search",
14387
+ steps,
14388
+ warnings
14389
+ };
14390
+ }
14391
+ steps.push({
14392
+ label: "Router.webSearch",
14393
+ status: config.Router.webSearch ? "info" : "skipped",
14394
+ detail: config.Router.webSearch ? "\u672C\u6B21\u672A\u58F0\u660E web_search\uFF1B\u5982\u679C\u540C\u65F6\u8D85\u8FC7 longContext threshold\uFF0C\u957F\u4E0A\u4E0B\u6587\u4F1A\u5148\u4E8E webSearch \u547D\u4E2D\u3002" : "\u672A\u914D\u7F6E Router.webSearch\u3002"
14395
+ });
14396
+ const defaultModel = resolveDisplayModel(config, config.Router.default);
14397
+ if (!defaultModel.resolved || !getCompiledModelRef(config, defaultModel.resolved)) {
14398
+ warnings.push(`Router.default "${config.Router.default}" \u672A\u89E3\u6790\u5230\u53EF\u7528\u6A21\u578B\u3002`);
14399
+ }
14400
+ steps.push({
14401
+ label: "Router.default",
14402
+ status: defaultModel.resolved ? "matched" : "warning",
14403
+ detail: defaultModel.resolved ? "\u524D\u7F6E\u69FD\u4F4D\u5747\u672A\u547D\u4E2D\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u6A21\u578B\u3002" : "Router.default \u672A\u89E3\u6790\u5230\u53EF\u7528\u6A21\u578B\u3002"
14404
+ });
14405
+ if (defaultModel.model && !registry.modelMap[defaultModel.model] && !defaultModel.model.includes(",")) {
14406
+ warnings.push(`Router.default "${defaultModel.model}" \u4E0D\u5728 Models/Providers/Registration \u4E2D\u3002`);
14407
+ }
14408
+ return {
14409
+ input: input3,
14410
+ finalModel: defaultModel.resolved,
14411
+ finalModelRef: defaultModel.model,
14412
+ source: defaultModel.resolved ? "basic_default" : "unresolved",
14413
+ steps,
14414
+ warnings
14415
+ };
14416
+ }
14417
+ function previewRoute(config, input3) {
14418
+ const smartRouterConfig = deriveRuntimeSmartRouterConfig(config, config);
14419
+ const text = input3.text.trim();
14420
+ const smartRouterEnabled = Boolean(smartRouterConfig?.enabled);
14421
+ const rules = smartRouterConfig?.rules ?? [];
14422
+ if (smartRouterEnabled && smartRouterConfig && text) {
14423
+ const ruleMatch = extractRuleMatch(text, rules);
14424
+ if (ruleMatch) {
14425
+ const model = resolveDisplayModel(config, ruleMatch.rule.model);
14426
+ return {
14427
+ input: input3,
14428
+ finalModel: model.resolved,
14429
+ finalModelRef: model.model,
14430
+ source: "smart_rule",
14431
+ confidence: 1,
14432
+ ruleName: ruleMatch.rule.name,
14433
+ steps: [
14434
+ {
14435
+ label: "SmartRouter.rules",
14436
+ status: "matched",
14437
+ detail: `\u547D\u4E2D\u89C4\u5219 "${ruleMatch.rule.name}"\uFF08${ruleMatch.detail}\uFF09\uFF0C\u4F1A\u5728\u57FA\u7840 Router \u524D\u9009\u5B9A\u6A21\u578B\u3002`
14438
+ }
14439
+ ],
14440
+ warnings: []
14441
+ };
14442
+ }
14443
+ const semanticCandidates = buildSemanticCandidates(rules, smartRouterConfig);
14444
+ if (smartRouterConfig.semantic?.enabled && smartRouterConfig.semantic.mode !== "classifier" && semanticCandidates.length) {
14445
+ const result = semanticRouter.analyzeCandidates(
14446
+ text,
14447
+ semanticCandidates.map((candidate) => ({
14448
+ intent: candidate.intent,
14449
+ prototype: candidate.prototype,
14450
+ threshold: candidate.threshold
14451
+ })),
14452
+ smartRouterConfig.semantic.threshold
14453
+ );
14454
+ if (result) {
14455
+ const candidate = semanticCandidates.find((item) => item.intent === result.intent);
14456
+ const model = resolveDisplayModel(config, candidate?.rule.model);
14457
+ return {
14458
+ input: input3,
14459
+ finalModel: model.resolved,
14460
+ finalModelRef: model.model,
14461
+ source: "semantic_match",
14462
+ confidence: result.confidence,
14463
+ ruleName: result.intent,
14464
+ steps: [
14465
+ {
14466
+ label: "SmartRouter.semantic",
14467
+ status: "matched",
14468
+ detail: `\u8BED\u4E49 prototype \u547D\u4E2D "${result.intent}"\uFF0Cconfidence=${result.confidence.toFixed(3)}\u3002`
14469
+ }
14470
+ ],
14471
+ warnings: []
14472
+ };
14473
+ }
14474
+ }
14475
+ if (smartRouterConfig.router_model && (smartRouterConfig.candidates?.length ?? 0) >= 2) {
14476
+ const model = resolveDisplayModel(config, smartRouterConfig.router_model);
14477
+ return {
14478
+ input: input3,
14479
+ finalModel: void 0,
14480
+ finalModelRef: void 0,
14481
+ source: "smart_router_pending",
14482
+ steps: [
14483
+ {
14484
+ label: "SmartRouter.router_model",
14485
+ status: "info",
14486
+ detail: `\u672A\u547D\u4E2D\u786E\u5B9A\u6027\u89C4\u5219\uFF1B\u771F\u5B9E\u8BF7\u6C42\u4F1A\u5148\u8C03\u7528 router_model "${smartRouterConfig.router_model}"\uFF08${model.resolved ?? "\u672A\u89E3\u6790"}\uFF09\u5728 ${smartRouterConfig.candidates?.length ?? 0} \u4E2A\u5019\u9009\u4E2D\u9009\u62E9\uFF0C\u4F1A\u589E\u52A0\u9996\u5305\u524D\u7B49\u5F85\u3002`
14487
+ },
14488
+ {
14489
+ label: "Basic Router fallback",
14490
+ status: "info",
14491
+ detail: "\u5982\u679C SmartRouter \u8C03\u7528\u5931\u8D25\u6216\u8FD4\u56DE\u65E0\u6548\u6A21\u578B\uFF0C\u8BF7\u6C42\u4F1A\u7EE7\u7EED\u8FDB\u5165\u57FA\u7840 Router fallback\u3002"
14492
+ }
14493
+ ],
14494
+ warnings: [
14495
+ "doctor route preview \u4E0D\u4F1A\u8C03\u7528 SmartRouter LLM\uFF1B\u8FD9\u91CC\u5C55\u793A\u7684\u662F\u5F85\u51B3\u7B56\u8DEF\u5F84\uFF0C\u4E0D\u4EE3\u8868\u6700\u7EC8\u5019\u9009\u9009\u62E9\u3002"
14496
+ ]
14497
+ };
14498
+ }
14499
+ }
14500
+ const basic = previewBasicRouter(config, input3);
14501
+ if (smartRouterEnabled) {
14502
+ basic.steps.unshift({
14503
+ label: "SmartRouter",
14504
+ status: "skipped",
14505
+ detail: "SmartRouter \u5DF2\u542F\u7528\uFF0C\u4F46\u672C\u6B21\u672A\u547D\u4E2D\u89C4\u5219/\u8BED\u4E49\uFF0C\u4E14\u6CA1\u6709\u53EF\u7528 router_model+candidates \u515C\u5E95\u3002"
14506
+ });
14507
+ }
14508
+ return basic;
14509
+ }
14510
+ function formatRoutePreview(result) {
14511
+ const lines = [
14512
+ `\u8DEF\u7531\u9884\u6F14\uFF1A${result.input.text || "<empty>"}`,
14513
+ `\u9884\u8BA1\u6765\u6E90\uFF1A${result.source}${result.ruleName ? ` (${result.ruleName})` : ""}`,
14514
+ `\u9884\u8BA1\u6A21\u578B\uFF1A${result.finalModelRef ?? "-"}${result.finalModel ? ` -> ${result.finalModel}` : ""}`
14515
+ ];
14516
+ if (result.confidence !== void 0) {
14517
+ lines.push(`\u7F6E\u4FE1\u5EA6\uFF1A${result.confidence.toFixed(3)}`);
14518
+ }
14519
+ for (const step of result.steps) {
14520
+ lines.push(`- [${step.status}] ${step.label}: ${step.detail}`);
14521
+ }
14522
+ for (const warning of result.warnings) {
14523
+ lines.push(`! ${warning}`);
14524
+ }
14525
+ return lines;
14526
+ }
14527
+ var init_route_preview = __esm({
14528
+ "src/router/route-preview.ts"() {
14529
+ "use strict";
14530
+ init_compile();
14531
+ init_matcher();
14532
+ init_config();
14533
+ init_semantic_router();
14534
+ }
14535
+ });
14536
+
14247
14537
  // src/doctor/index.ts
14248
14538
  function collectCompatibilityPreviewDiagnostics(model) {
14249
14539
  const registry = buildModelRegistry({
@@ -14311,6 +14601,18 @@ function collectCompatibilityPreviewDiagnostics(model) {
14311
14601
  function hasArg(flag) {
14312
14602
  return process.argv.slice(2).includes(flag);
14313
14603
  }
14604
+ function readArgValue(flag) {
14605
+ const args = process.argv.slice(2);
14606
+ const index = args.indexOf(flag);
14607
+ if (index < 0) {
14608
+ return void 0;
14609
+ }
14610
+ const value = args[index + 1];
14611
+ if (!value || value.startsWith("--")) {
14612
+ return void 0;
14613
+ }
14614
+ return value;
14615
+ }
14314
14616
  function createConsoleIO2() {
14315
14617
  if (process.env.CTR_DOCTOR_FORCE_SCRIPTED_INPUT === "1") {
14316
14618
  const scriptedInput = (0, import_fs10.readFileSync)(0, "utf-8");
@@ -14896,6 +15198,57 @@ function reportRouterSlotSummary(config, registry, deps) {
14896
15198
  deps.io.info("\u957F\u4E0A\u4E0B\u6587\u63D0\u793A\uFF1ARouter.longContext \u7684 context_window_tokens \u4E0D\u9AD8\u4E8E Router.default\uFF1B\u8BF7\u786E\u8BA4\u5B83\u786E\u5B9E\u662F\u957F\u4E0A\u4E0B\u6587\u6A21\u578B\u3002");
14897
15199
  }
14898
15200
  }
15201
+ function buildDoctorRoutePreviewInputs() {
15202
+ const customText = readArgValue("--route-text");
15203
+ if (customText) {
15204
+ return [{
15205
+ text: customText,
15206
+ model: readArgValue("--route-model") ?? "claude-3-5-sonnet",
15207
+ thinking: hasArg("--route-thinking"),
15208
+ webSearch: hasArg("--route-web-search"),
15209
+ tokenCount: Number(readArgValue("--route-tokens")) || void 0
15210
+ }];
15211
+ }
15212
+ return [
15213
+ {
15214
+ text: "\u65E5\u5E38\u4EE3\u7801\u4FEE\u6539\u548C\u89E3\u91CA",
15215
+ model: "claude-3-5-sonnet",
15216
+ tokenCount: 1e3
15217
+ },
15218
+ {
15219
+ text: "\u8BF7\u6DF1\u5165\u5206\u6790\u8FD9\u4E2A\u590D\u6742\u8BBE\u8BA1\u95EE\u9898",
15220
+ model: "claude-3-5-sonnet",
15221
+ thinking: true,
15222
+ tokenCount: 1e3
15223
+ },
15224
+ {
15225
+ text: "\u957F\u6587\u6863\u5168\u6587\u603B\u7ED3",
15226
+ model: "claude-3-5-sonnet",
15227
+ tokenCount: 12e4
15228
+ },
15229
+ {
15230
+ text: "\u9700\u8981\u8054\u7F51\u641C\u7D22\u8D44\u6599",
15231
+ model: "claude-3-5-sonnet",
15232
+ webSearch: true,
15233
+ tokenCount: 1e3
15234
+ },
15235
+ {
15236
+ text: "\u540E\u53F0\u8F7B\u91CF\u4EFB\u52A1",
15237
+ model: "claude-3-5-haiku",
15238
+ tokenCount: 1e3
15239
+ }
15240
+ ];
15241
+ }
15242
+ function reportRoutePreview(config, deps) {
15243
+ deps.io.info("\u8DEF\u7531\u9884\u6F14\uFF1A\u6839\u636E\u5F53\u524D\u914D\u7F6E\u9884\u4F30\u8BF7\u6C42\u4F1A\u547D\u4E2D\u54EA\u4E2A\u6A21\u578B\uFF1B\u4E0D\u4F1A\u8C03\u7528\u4E0A\u6E38\u6A21\u578B\u6216 SmartRouter LLM\u3002");
15244
+ for (const input3 of buildDoctorRoutePreviewInputs()) {
15245
+ const result = previewRoute(config, input3);
15246
+ for (const line of formatRoutePreview(result)) {
15247
+ deps.io.info(line);
15248
+ }
15249
+ }
15250
+ deps.io.info("\u8DEF\u7531\u9884\u6F14\u63D0\u793A\uFF1A\u771F\u5B9E\u8FD0\u884C\u4ECD\u4F1A\u5728\u6700\u7EC8\u5B9A\u6A21\u540E\u6267\u884C context window guard\u3001\u6A21\u578B\u6C60 fallback\u3001\u8FDC\u7A0B\u4E2D\u8F6C\u548C\u6D41\u5F0F\u6CBB\u7406\u3002");
15251
+ }
14899
15252
  function createDefaultDeps2(io = createConsoleIO2()) {
14900
15253
  return {
14901
15254
  readLegacyConfig,
@@ -14956,6 +15309,9 @@ async function runDoctorCli(customDeps) {
14956
15309
  await reportRuntimeServiceContext(normalized.config, deps);
14957
15310
  const registry = buildModelRegistry(normalized.config);
14958
15311
  reportRouterSlotSummary(normalized.config, registry, deps);
15312
+ if (hasArg("--route-preview")) {
15313
+ reportRoutePreview(normalized.config, deps);
15314
+ }
14959
15315
  for (const model of normalized.config.Models ?? []) {
14960
15316
  const compiledModel = registry.modelMap[model.id];
14961
15317
  if (!compiledModel) {
@@ -15043,6 +15399,7 @@ var init_doctor = __esm({
15043
15399
  init_service_health();
15044
15400
  init_templates();
15045
15401
  init_api_keys();
15402
+ init_route_preview();
15046
15403
  ROUTER_SLOT_DIAGNOSTICS = [
15047
15404
  {
15048
15405
  key: "default",
@@ -15193,6 +15550,7 @@ Claude Trigger Router - \u667A\u80FD\u89E6\u53D1\u8DEF\u7531\u5668
15193
15550
  \u4F7F\u7528\u793A\u4F8B\uFF1A
15194
15551
  ctr setup # \u590D\u7528\u5F53\u524D\u914D\u7F6E / \u8FC1\u79FB\u65E7\u914D\u7F6E / \u65B0\u5EFA\u6700\u5C0F\u914D\u7F6E
15195
15552
  ctr doctor # \u8BCA\u65AD\u914D\u7F6E / \u4FEE\u590D\u683C\u5F0F\u95EE\u9898 / \u6309\u9700\u63A2\u6D4B\u6A21\u578B\u53EF\u7528\u6027
15553
+ ctr doctor --route-preview --route-text "\u8BF7\u505A\u67B6\u6784\u8BBE\u8BA1" # \u9884\u6F14\u5F53\u524D\u8BF7\u6C42\u4F1A\u8D70\u54EA\u4E2A\u6A21\u578B
15196
15554
  ctr eval --tasks # \u67E5\u770B\u56FA\u5B9A\u8BC4\u6D4B\u4EFB\u52A1\u3001prompt \u548C rubric
15197
15555
  ctr eval --input results.json # \u7528\u56FA\u5B9A\u4EFB\u52A1\u96C6 rubric \u8BC4\u6D4B\u591A\u6A21\u578B\u8F93\u51FA\u7ED3\u679C
15198
15556
  ctr eval --run --models "sonnet;haiku" # \u81EA\u52A8\u8C03\u7528 CTR /v1/messages \u540E\u8BC4\u6D4B