pi-smart-router 0.2.0 → 0.4.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.
Files changed (61) hide show
  1. package/.pi/extensions/smart-router/index.ts +9 -0
  2. package/.pi/extensions/smart-router/pi-model-scope.ts +127 -20
  3. package/.pi/extensions/smart-router/planning-delegate.ts +318 -0
  4. package/.pi/extensions/smart-router/route-and-delegate.ts +21 -1
  5. package/.pi/extensions/smart-router/types.ts +3 -0
  6. package/README.md +51 -0
  7. package/config/benchmark-profiles.json +145 -0
  8. package/config/models.yaml.example +5 -0
  9. package/config/routing-calibration.json.example +14 -2
  10. package/dist/config/defaults.d.ts +2 -2
  11. package/dist/config/defaults.d.ts.map +1 -1
  12. package/dist/config/defaults.js +5 -3
  13. package/dist/config/defaults.js.map +1 -1
  14. package/dist/config/pi-model-mapper.d.ts +12 -2
  15. package/dist/config/pi-model-mapper.d.ts.map +1 -1
  16. package/dist/config/pi-model-mapper.js +91 -6
  17. package/dist/config/pi-model-mapper.js.map +1 -1
  18. package/dist/domain/matching/hydra-input.d.ts +6 -5
  19. package/dist/domain/matching/hydra-input.d.ts.map +1 -1
  20. package/dist/domain/matching/hydra-input.js +73 -6
  21. package/dist/domain/matching/hydra-input.js.map +1 -1
  22. package/dist/domain/pipeline/router-pipeline.d.ts +22 -1
  23. package/dist/domain/pipeline/router-pipeline.d.ts.map +1 -1
  24. package/dist/domain/pipeline/router-pipeline.js +135 -22
  25. package/dist/domain/pipeline/router-pipeline.js.map +1 -1
  26. package/dist/domain/routing/isotonic-calibrator.d.ts +56 -0
  27. package/dist/domain/routing/isotonic-calibrator.d.ts.map +1 -0
  28. package/dist/domain/routing/isotonic-calibrator.js +187 -0
  29. package/dist/domain/routing/isotonic-calibrator.js.map +1 -0
  30. package/dist/domain/routing/p-success-classifier.d.ts +53 -7
  31. package/dist/domain/routing/p-success-classifier.d.ts.map +1 -1
  32. package/dist/domain/routing/p-success-classifier.js +205 -21
  33. package/dist/domain/routing/p-success-classifier.js.map +1 -1
  34. package/dist/domain/types/entities.d.ts +54 -0
  35. package/dist/domain/types/entities.d.ts.map +1 -1
  36. package/dist/domain/types/index.d.ts +1 -1
  37. package/dist/domain/types/index.d.ts.map +1 -1
  38. package/dist/domain/types/schemas.d.ts +29 -0
  39. package/dist/domain/types/schemas.d.ts.map +1 -1
  40. package/dist/domain/types/schemas.js +57 -0
  41. package/dist/domain/types/schemas.js.map +1 -1
  42. package/dist/infrastructure/persistence/sqlite-store.d.ts.map +1 -1
  43. package/dist/infrastructure/persistence/sqlite-store.js +2 -1
  44. package/dist/infrastructure/persistence/sqlite-store.js.map +1 -1
  45. package/dist/infrastructure/telemetry/routing-telemetry.d.ts +20 -1
  46. package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -1
  47. package/dist/infrastructure/telemetry/routing-telemetry.js +82 -2
  48. package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -1
  49. package/package.json +6 -3
  50. package/specs/001-build-smart-router/contracts/telemetry-contrib.schema.json +29 -1
  51. package/src/config/defaults.ts +6 -2
  52. package/src/config/pi-model-mapper.ts +110 -6
  53. package/src/domain/matching/hydra-input.ts +86 -7
  54. package/src/domain/pipeline/router-pipeline.ts +195 -29
  55. package/src/domain/routing/isotonic-calibrator.ts +255 -0
  56. package/src/domain/routing/p-success-classifier.ts +299 -26
  57. package/src/domain/types/entities.ts +58 -0
  58. package/src/domain/types/index.ts +4 -0
  59. package/src/domain/types/schemas.ts +73 -0
  60. package/src/infrastructure/persistence/sqlite-store.ts +2 -0
  61. package/src/infrastructure/telemetry/routing-telemetry.ts +127 -7
@@ -26,6 +26,8 @@ import type {
26
26
  ContextFitRejectedEntry,
27
27
  LowIntensityBreakdown,
28
28
  ModelProfile,
29
+ PlanningDelegateObservability,
30
+ PlanningDelegatePath,
29
31
  PriceCatalog,
30
32
  RejectedTierEntry,
31
33
  RoutingDecision,
@@ -59,6 +61,11 @@ export const BREAKEVEN_PASS = 'breakeven_pass' as const;
59
61
  export const SAAR_BUFFER_ACTIVE = 'saar_buffer_active' as const;
60
62
  export const SAAR_HARD_LOCK = 'saar_hard_lock' as const;
61
63
 
64
+ export const PLANNING_DELEGATE = 'planning_delegate' as const;
65
+ export const PLANNING_DIRECT_FRONTIER = 'planning_direct_frontier' as const;
66
+ export const PLANNING_DELEGATE_DISABLED = 'planning_delegate_disabled' as const;
67
+ export const PLANNING_DELEGATE_UNAVAILABLE = 'planning_delegate_unavailable' as const;
68
+
62
69
  const TURN_ENVELOPE_TIER_MAP: Readonly<Record<string, Tier | null>> = {
63
70
  planning: 'frontier-cloud',
64
71
  tool_result: 'economical-cloud',
@@ -388,6 +395,8 @@ function buildLowIntensityBreakdown(
388
395
  tier_hint_reason_code: features.tier_hint_reason_code,
389
396
  tier_selection_reason_code: resolveTierSelectionReasonCode(features),
390
397
  p_success_cheap: features.p_success_cheap,
398
+ p_success_raw: features.p_success_raw,
399
+ p_success_calibrated: features.p_success_calibrated,
391
400
  p_success_alpha: features.p_success_alpha,
392
401
  rejected_tiers: extractRejectedTiers(features.candidates),
393
402
  };
@@ -536,6 +545,51 @@ function resolveSaarReasonCode(decision: RoutingDecision): string | null {
536
545
  return null;
537
546
  }
538
547
 
548
+ /** Build planning delegate observability from routing decision features (SP-142). */
549
+ export function buildPlanningDelegateObservability(
550
+ decision: RoutingDecision,
551
+ ): PlanningDelegateObservability | null {
552
+ return decision.features?.planning_delegate ?? null;
553
+ }
554
+
555
+ /** Construct planning delegate observability for pipeline and tests (SP-142). */
556
+ export function createPlanningDelegateObservability(input: {
557
+ path: PlanningDelegatePath;
558
+ primary_model_id?: string | null;
559
+ delegate_model_id?: string | null;
560
+ compressed_context?: PlanningDelegateObservability['compressed_context'];
561
+ planning_delegate_reason_code: string;
562
+ fallback_reason?: string | null;
563
+ }): PlanningDelegateObservability {
564
+ return {
565
+ path: input.path,
566
+ primary_model_id: input.primary_model_id ?? null,
567
+ delegate_model_id: input.delegate_model_id ?? null,
568
+ compressed_context: input.compressed_context ?? null,
569
+ planning_delegate_reason_code: input.planning_delegate_reason_code,
570
+ fallback_reason: input.fallback_reason ?? null,
571
+ };
572
+ }
573
+
574
+ /** Attach planning delegate observability to routing decision features (SP-142). */
575
+ export function enrichRoutingDecisionWithPlanningDelegate(
576
+ decision: RoutingDecision,
577
+ planningDelegate?: PlanningDelegateObservability | null,
578
+ ): RoutingDecision {
579
+ const observability = planningDelegate ?? buildPlanningDelegateObservability(decision);
580
+ if (!observability) {
581
+ return decision;
582
+ }
583
+
584
+ return {
585
+ ...decision,
586
+ features: {
587
+ ...(decision.features ?? emptyFeatureSidecar()),
588
+ planning_delegate: observability,
589
+ },
590
+ };
591
+ }
592
+
539
593
  /** Build privacy-safe SAAR pin state for explain and telemetry (SP-126). */
540
594
  export function buildSaarObservability(
541
595
  input: PinEconomicsObservabilityInput,
@@ -656,6 +710,53 @@ export const DEFAULT_BREAKEVEN_TELEMETRY_FIELDS = defaultBreakevenTelemetry();
656
710
  /** Default SAAR telemetry scalars for tests and legacy store reads. */
657
711
  export const DEFAULT_SAAR_TELEMETRY_FIELDS = defaultSaarTelemetry();
658
712
 
713
+ function defaultPlanningDelegateTelemetry(): Pick<
714
+ RoutingTelemetry,
715
+ | 'planning_delegate_path'
716
+ | 'planning_delegate_primary_model_id'
717
+ | 'planning_delegate_model_id'
718
+ | 'planning_delegate_reason_code'
719
+ | 'planning_delegate_fallback_reason'
720
+ | 'planning_delegate_max_messages'
721
+ | 'planning_delegate_max_tokens'
722
+ | 'planning_delegate_exclude_execution_history'
723
+ > {
724
+ return {
725
+ planning_delegate_path: null,
726
+ planning_delegate_primary_model_id: null,
727
+ planning_delegate_model_id: null,
728
+ planning_delegate_reason_code: null,
729
+ planning_delegate_fallback_reason: null,
730
+ planning_delegate_max_messages: null,
731
+ planning_delegate_max_tokens: null,
732
+ planning_delegate_exclude_execution_history: null,
733
+ };
734
+ }
735
+
736
+ /** Default planning delegate telemetry scalars for tests and legacy store reads. */
737
+ export const DEFAULT_PLANNING_DELEGATE_TELEMETRY_FIELDS = defaultPlanningDelegateTelemetry();
738
+
739
+ function planningDelegateTelemetryFromDecision(
740
+ decision: RoutingDecision,
741
+ ): ReturnType<typeof defaultPlanningDelegateTelemetry> {
742
+ const observability = buildPlanningDelegateObservability(decision);
743
+ if (!observability) {
744
+ return defaultPlanningDelegateTelemetry();
745
+ }
746
+
747
+ return {
748
+ planning_delegate_path: observability.path === 'none' ? null : observability.path,
749
+ planning_delegate_primary_model_id: observability.primary_model_id,
750
+ planning_delegate_model_id: observability.delegate_model_id,
751
+ planning_delegate_reason_code: observability.planning_delegate_reason_code,
752
+ planning_delegate_fallback_reason: observability.fallback_reason,
753
+ planning_delegate_max_messages: observability.compressed_context?.max_messages ?? null,
754
+ planning_delegate_max_tokens: observability.compressed_context?.max_tokens ?? null,
755
+ planning_delegate_exclude_execution_history:
756
+ observability.compressed_context?.exclude_execution_history ?? null,
757
+ };
758
+ }
759
+
659
760
  function pinEconomicsTelemetryFromInput(
660
761
  input: PinEconomicsObservabilityInput,
661
762
  ): ReturnType<typeof defaultBreakevenTelemetry> &
@@ -722,6 +823,8 @@ function emptyFeatureSidecar() {
722
823
  tier_hint_reason_code: null,
723
824
  low_intensity_score: null,
724
825
  p_success_cheap: null,
826
+ p_success_raw: null,
827
+ p_success_calibrated: null,
725
828
  p_success_alpha: null,
726
829
  local_eligible_reason: null,
727
830
  };
@@ -782,7 +885,9 @@ export async function enrichRoutingDecisionForExplain(
782
885
 
783
886
  return enrichRoutingDecisionWithPinEconomics(
784
887
  request,
785
- enrichRoutingDecisionWithTierSelection(withContextFit, clusterMatchTable),
888
+ enrichRoutingDecisionWithPlanningDelegate(
889
+ enrichRoutingDecisionWithTierSelection(withContextFit, clusterMatchTable),
890
+ ),
786
891
  pinEconomicsOptionsFromExplain(options),
787
892
  );
788
893
  }
@@ -843,12 +948,14 @@ export function buildRoutingDecisionLogPayload(
843
948
  ): Record<string, unknown> {
844
949
  const enriched = enrichRoutingDecisionWithPinEconomics(
845
950
  request,
846
- enrichRoutingDecisionWithTierSelection(
847
- enrichRoutingDecisionWithContextFit(
848
- request,
849
- decision,
850
- fleet,
851
- contextFitConfig,
951
+ enrichRoutingDecisionWithPlanningDelegate(
952
+ enrichRoutingDecisionWithTierSelection(
953
+ enrichRoutingDecisionWithContextFit(
954
+ request,
955
+ decision,
956
+ fleet,
957
+ contextFitConfig,
958
+ ),
852
959
  ),
853
960
  ),
854
961
  {
@@ -865,6 +972,7 @@ export function buildRoutingDecisionLogPayload(
865
972
  const tierSelection = enriched.features?.tier_selection;
866
973
  const breakeven = enriched.features?.breakeven;
867
974
  const saar = enriched.features?.saar;
975
+ const planningDelegate = enriched.features?.planning_delegate;
868
976
 
869
977
  return {
870
978
  request_id: enriched.request_id,
@@ -904,6 +1012,16 @@ export function buildRoutingDecisionLogPayload(
904
1012
  saar_reason_code: saar.saar_reason_code,
905
1013
  }
906
1014
  : null,
1015
+ planning_delegate_summary: planningDelegate
1016
+ ? {
1017
+ path: planningDelegate.path,
1018
+ primary_model_id: planningDelegate.primary_model_id,
1019
+ delegate_model_id: planningDelegate.delegate_model_id,
1020
+ compressed_context: planningDelegate.compressed_context,
1021
+ planning_delegate_reason_code: planningDelegate.planning_delegate_reason_code,
1022
+ fallback_reason: planningDelegate.fallback_reason,
1023
+ }
1024
+ : null,
907
1025
  delegate,
908
1026
  };
909
1027
  }
@@ -1089,6 +1207,7 @@ export class RoutingTelemetryEmitter {
1089
1207
  : {}),
1090
1208
  ...(this.saarConfig !== undefined ? { saarConfig: this.saarConfig } : {}),
1091
1209
  });
1210
+ const planningDelegateFields = planningDelegateTelemetryFromDecision(decision);
1092
1211
 
1093
1212
  const record: RoutingTelemetry = {
1094
1213
  timestamp: this.clock(),
@@ -1124,6 +1243,7 @@ export class RoutingTelemetryEmitter {
1124
1243
  saar_hard_lock: pinEconomicsFields.saar_hard_lock,
1125
1244
  turn_index_in_session: pinEconomicsFields.turn_index_in_session,
1126
1245
  saar_reason_code: pinEconomicsFields.saar_reason_code,
1246
+ ...planningDelegateFields,
1127
1247
  };
1128
1248
 
1129
1249
  this.entries.push(record);