pi-smart-router 0.3.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.
- package/.pi/extensions/smart-router/index.ts +9 -0
- package/.pi/extensions/smart-router/pi-model-scope.ts +127 -20
- package/.pi/extensions/smart-router/planning-delegate.ts +318 -0
- package/.pi/extensions/smart-router/route-and-delegate.ts +21 -1
- package/.pi/extensions/smart-router/types.ts +3 -0
- package/README.md +32 -0
- package/dist/config/defaults.d.ts +2 -2
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +5 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/domain/pipeline/router-pipeline.d.ts +13 -1
- package/dist/domain/pipeline/router-pipeline.d.ts.map +1 -1
- package/dist/domain/pipeline/router-pipeline.js +96 -18
- package/dist/domain/pipeline/router-pipeline.js.map +1 -1
- package/dist/domain/types/entities.d.ts +48 -0
- package/dist/domain/types/entities.d.ts.map +1 -1
- package/dist/domain/types/index.d.ts +1 -1
- package/dist/domain/types/index.d.ts.map +1 -1
- package/dist/domain/types/schemas.d.ts +29 -0
- package/dist/domain/types/schemas.d.ts.map +1 -1
- package/dist/domain/types/schemas.js +57 -0
- package/dist/domain/types/schemas.js.map +1 -1
- package/dist/infrastructure/persistence/sqlite-store.d.ts.map +1 -1
- package/dist/infrastructure/persistence/sqlite-store.js +2 -1
- package/dist/infrastructure/persistence/sqlite-store.js.map +1 -1
- package/dist/infrastructure/telemetry/routing-telemetry.d.ts +20 -1
- package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -1
- package/dist/infrastructure/telemetry/routing-telemetry.js +78 -2
- package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -1
- package/package.json +1 -1
- package/src/config/defaults.ts +6 -2
- package/src/domain/pipeline/router-pipeline.ts +141 -26
- package/src/domain/types/entities.ts +52 -0
- package/src/domain/types/index.ts +4 -0
- package/src/domain/types/schemas.ts +73 -0
- package/src/infrastructure/persistence/sqlite-store.ts +2 -0
- package/src/infrastructure/telemetry/routing-telemetry.ts +123 -7
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
13
|
ModelProfile,
|
|
14
|
+
PlanningDelegateConfig,
|
|
15
|
+
PlanningDelegateObservability,
|
|
14
16
|
PriceCatalog,
|
|
15
17
|
RoutingDecision,
|
|
16
18
|
RoutingFeatureSidecar,
|
|
@@ -49,6 +51,10 @@ import {
|
|
|
49
51
|
estimateRoutingCost,
|
|
50
52
|
enrichRoutingDecisionWithContextFit,
|
|
51
53
|
enrichRoutingDecisionWithTierSelection,
|
|
54
|
+
createPlanningDelegateObservability,
|
|
55
|
+
PLANNING_DELEGATE,
|
|
56
|
+
PLANNING_DELEGATE_DISABLED,
|
|
57
|
+
PLANNING_DIRECT_FRONTIER,
|
|
52
58
|
} from '../../infrastructure/telemetry/routing-telemetry.js';
|
|
53
59
|
import type { HydraMatcher as HydraMatcherType, MatchResult } from '../matching/hydra-matcher.js';
|
|
54
60
|
import type { ClusterMatcher, ClusterMatchResult } from '../matching/cluster-matcher.js';
|
|
@@ -178,6 +184,8 @@ export interface PipelineOptions {
|
|
|
178
184
|
readonly routingCalibrationPath?: string;
|
|
179
185
|
/** SAAR pin policy (SP-123). Must match sessionPinner.saarConfig when enabled. */
|
|
180
186
|
readonly saarConfig?: SaarConfig;
|
|
187
|
+
/** Planning delegate operator knobs (SP-143, #71). Defaults to operator config. */
|
|
188
|
+
readonly planningDelegateConfig?: PlanningDelegateConfig;
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
// ─── Orchestrator ────────────────────────────────────────────────────────────
|
|
@@ -217,6 +225,8 @@ export class RouterPipeline {
|
|
|
217
225
|
private contextOverflowTriggered = false;
|
|
218
226
|
/** Internal breakeven gate reason for SP-126 explain wiring. */
|
|
219
227
|
private currentBreakevenReason: string | null = null;
|
|
228
|
+
/** Planning delegate observability for SP-143 explain/telemetry wiring. */
|
|
229
|
+
private currentPlanningDelegate: PlanningDelegateObservability | null = null;
|
|
220
230
|
|
|
221
231
|
constructor(fleet: readonly ModelProfile[], options?: PipelineOptions) {
|
|
222
232
|
this.fleet = fleet;
|
|
@@ -265,6 +275,7 @@ export class RouterPipeline {
|
|
|
265
275
|
this.contextOverflowPreferredProvider = null;
|
|
266
276
|
this.contextOverflowTriggered = false;
|
|
267
277
|
this.currentBreakevenReason = null;
|
|
278
|
+
this.currentPlanningDelegate = null;
|
|
268
279
|
|
|
269
280
|
let currentStage: NamedPipelineStage | undefined;
|
|
270
281
|
|
|
@@ -359,6 +370,9 @@ export class RouterPipeline {
|
|
|
359
370
|
p_success_calibrated: this.currentPSuccessCalibrated,
|
|
360
371
|
p_success_alpha: this.currentPSuccessAlpha,
|
|
361
372
|
local_eligible_reason: this.currentLocalEligibleReason,
|
|
373
|
+
...(this.currentPlanningDelegate
|
|
374
|
+
? { planning_delegate: this.currentPlanningDelegate }
|
|
375
|
+
: {}),
|
|
362
376
|
};
|
|
363
377
|
|
|
364
378
|
const withBaseFeatures = { ...decision, features };
|
|
@@ -961,54 +975,155 @@ export class RouterPipeline {
|
|
|
961
975
|
const tierCandidates = this.activeFleet.filter(
|
|
962
976
|
(m) => m.tier === targetTier && m.healthy !== false,
|
|
963
977
|
);
|
|
964
|
-
const
|
|
965
|
-
if (!
|
|
978
|
+
const targetModel = selectLowestCostModel(tierCandidates);
|
|
979
|
+
if (!targetModel) {
|
|
966
980
|
return { decided: false, stage: 'turn_envelope' };
|
|
967
981
|
}
|
|
968
982
|
|
|
969
983
|
const pinner = this.options.sessionPinner;
|
|
970
984
|
const pin = pinner?.getPin(request.session_id) ?? null;
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
985
|
+
const pinnedModel =
|
|
986
|
+
pin !== null
|
|
987
|
+
? this.activeFleet.find(
|
|
988
|
+
(m) => m.id === pin.pinned_model_id && m.healthy !== false,
|
|
989
|
+
)
|
|
990
|
+
: undefined;
|
|
991
|
+
|
|
992
|
+
// SP-143: cache-preserving planning delegate when warm economical pin would switch to frontier.
|
|
993
|
+
if (
|
|
994
|
+
turnType === 'planning' &&
|
|
995
|
+
pinnedModel &&
|
|
996
|
+
pinnedModel.tier === 'economical-cloud' &&
|
|
997
|
+
pinnedModel.id !== targetModel.id
|
|
998
|
+
) {
|
|
999
|
+
const delegateDecision = this.tryPlanningDelegateDecision(
|
|
1000
|
+
request,
|
|
1001
|
+
pinnedModel,
|
|
1002
|
+
targetModel,
|
|
974
1003
|
);
|
|
975
|
-
if (
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1004
|
+
if (delegateDecision) {
|
|
1005
|
+
return delegateDecision;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if (pinnedModel && pinnedModel.id !== targetModel.id) {
|
|
1010
|
+
const skipBreakeven =
|
|
1011
|
+
turnType === 'planning' && this.isSaarPlanningBufferActive(request);
|
|
1012
|
+
if (!skipBreakeven) {
|
|
1013
|
+
const tokenEstimate =
|
|
1014
|
+
request.estimated_input_tokens ?? request.prompt_text.length;
|
|
1015
|
+
const breakeven = evaluateModelSwitchBreakeven(
|
|
1016
|
+
pinnedModel,
|
|
1017
|
+
targetModel,
|
|
1018
|
+
tokenEstimate,
|
|
1019
|
+
tokenEstimate,
|
|
1020
|
+
this.options.saarConfig,
|
|
1021
|
+
);
|
|
1022
|
+
if (!breakeven.shouldSwitch) {
|
|
1023
|
+
this.currentBreakevenReason = 'breakeven_blocked';
|
|
1024
|
+
return { decided: false, stage: 'turn_envelope' };
|
|
993
1025
|
}
|
|
1026
|
+
this.currentBreakevenReason = 'breakeven_pass';
|
|
994
1027
|
}
|
|
995
1028
|
}
|
|
996
1029
|
|
|
1030
|
+
const directReasonCode = `turn_${turnType}`;
|
|
1031
|
+
let planningDirectFallback: string | null = null;
|
|
1032
|
+
|
|
1033
|
+
if (
|
|
1034
|
+
turnType === 'planning' &&
|
|
1035
|
+
pinnedModel &&
|
|
1036
|
+
pinnedModel.tier === 'economical-cloud' &&
|
|
1037
|
+
pinnedModel.id !== targetModel.id
|
|
1038
|
+
) {
|
|
1039
|
+
planningDirectFallback = this.resolvePlanningDirectFallbackReason();
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (planningDirectFallback) {
|
|
1043
|
+
this.setPlanningDelegateDirectFallback(targetModel.id, planningDirectFallback);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
997
1046
|
return {
|
|
998
1047
|
decided: true,
|
|
999
1048
|
stage: 'turn_envelope',
|
|
1000
|
-
decision: this.withEstimatedCost(request,
|
|
1049
|
+
decision: this.withEstimatedCost(request, targetModel, {
|
|
1001
1050
|
request_id: request.request_id,
|
|
1002
|
-
selected_model_id:
|
|
1051
|
+
selected_model_id: targetModel.id,
|
|
1003
1052
|
tier: targetTier,
|
|
1004
1053
|
stage: 'turn_envelope',
|
|
1005
|
-
reason_code:
|
|
1054
|
+
reason_code: planningDirectFallback
|
|
1055
|
+
? PLANNING_DIRECT_FRONTIER
|
|
1056
|
+
: directReasonCode,
|
|
1006
1057
|
routing_latency_ms: 0,
|
|
1007
1058
|
pin_reason: null,
|
|
1008
1059
|
}),
|
|
1009
1060
|
};
|
|
1010
1061
|
}
|
|
1011
1062
|
|
|
1063
|
+
private resolvePlanningDirectFallbackReason(): string | null {
|
|
1064
|
+
const config = this.resolvePlanningDelegateConfig();
|
|
1065
|
+
if (!config.enabled) {
|
|
1066
|
+
return PLANNING_DELEGATE_DISABLED;
|
|
1067
|
+
}
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
private resolvePlanningDelegateConfig(): PlanningDelegateConfig {
|
|
1072
|
+
return (
|
|
1073
|
+
this.options.planningDelegateConfig ??
|
|
1074
|
+
DEFAULT_OPERATOR_CONFIG.planning_delegate
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* SP-143: emit planning_delegate when enabled; otherwise record direct fallback
|
|
1080
|
+
* observability and return null so breakeven-gated direct frontier can proceed.
|
|
1081
|
+
*/
|
|
1082
|
+
private tryPlanningDelegateDecision(
|
|
1083
|
+
request: RoutingRequest,
|
|
1084
|
+
pinnedModel: ModelProfile,
|
|
1085
|
+
frontierModel: ModelProfile,
|
|
1086
|
+
): StageResult | null {
|
|
1087
|
+
const config = this.resolvePlanningDelegateConfig();
|
|
1088
|
+
if (!config.enabled) {
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
this.currentPlanningDelegate = createPlanningDelegateObservability({
|
|
1093
|
+
path: 'delegate',
|
|
1094
|
+
primary_model_id: pinnedModel.id,
|
|
1095
|
+
delegate_model_id: frontierModel.id,
|
|
1096
|
+
compressed_context: config.compressed_context,
|
|
1097
|
+
planning_delegate_reason_code: PLANNING_DELEGATE,
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
return {
|
|
1101
|
+
decided: true,
|
|
1102
|
+
stage: 'turn_envelope',
|
|
1103
|
+
decision: this.withEstimatedCost(request, pinnedModel, {
|
|
1104
|
+
request_id: request.request_id,
|
|
1105
|
+
selected_model_id: pinnedModel.id,
|
|
1106
|
+
tier: pinnedModel.tier,
|
|
1107
|
+
stage: 'turn_envelope',
|
|
1108
|
+
reason_code: PLANNING_DELEGATE,
|
|
1109
|
+
routing_latency_ms: 0,
|
|
1110
|
+
pin_reason: null,
|
|
1111
|
+
}),
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
private setPlanningDelegateDirectFallback(
|
|
1116
|
+
delegateModelId: string | null,
|
|
1117
|
+
fallbackReason: string,
|
|
1118
|
+
): void {
|
|
1119
|
+
this.currentPlanningDelegate = createPlanningDelegateObservability({
|
|
1120
|
+
path: 'direct',
|
|
1121
|
+
delegate_model_id: delegateModelId,
|
|
1122
|
+
planning_delegate_reason_code: PLANNING_DIRECT_FRONTIER,
|
|
1123
|
+
fallback_reason: fallbackReason,
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1012
1127
|
// ─── Low-intensity tier gate (SP-103, #58) ───────────────────────────────
|
|
1013
1128
|
|
|
1014
1129
|
/**
|
|
@@ -208,6 +208,48 @@ export interface BreakevenObservability {
|
|
|
208
208
|
readonly breakeven_reason_code: string | null;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
/** Planning delegate routing path (SP-142, #71). */
|
|
212
|
+
export type PlanningDelegatePath = 'delegate' | 'direct' | 'none';
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Compressed context limits for ephemeral frontier sub-calls (SP-142).
|
|
216
|
+
* Excludes full execution history per #71; enforced by pi extension in SP-144.
|
|
217
|
+
*/
|
|
218
|
+
export interface CompressedContextSpec {
|
|
219
|
+
/** Max user/assistant messages retained in the delegate sub-call. */
|
|
220
|
+
readonly max_messages: number;
|
|
221
|
+
/** Max estimated tokens for delegate context window. */
|
|
222
|
+
readonly max_tokens: number;
|
|
223
|
+
/** When true, tool execution traces and verbose history are excluded. */
|
|
224
|
+
readonly exclude_execution_history: boolean;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Operator knobs for cache-preserving planning delegate (SP-142, #71). */
|
|
228
|
+
export interface PlanningDelegateConfig {
|
|
229
|
+
/** Prefer ephemeral frontier sub-call over primary model switch on planning turns. */
|
|
230
|
+
readonly enabled: boolean;
|
|
231
|
+
readonly compressed_context: CompressedContextSpec;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Planning delegate observability for explain and telemetry (SP-142, #71). */
|
|
235
|
+
export interface PlanningDelegateObservability {
|
|
236
|
+
/** Active routing path for this planning turn. */
|
|
237
|
+
readonly path: PlanningDelegatePath;
|
|
238
|
+
/** Primary model kept on pinned session when path is delegate. */
|
|
239
|
+
readonly primary_model_id: string | null;
|
|
240
|
+
/** Frontier model selected for delegate sub-call when path is delegate. */
|
|
241
|
+
readonly delegate_model_id: string | null;
|
|
242
|
+
/** Compressed context limits applied to delegate sub-call. */
|
|
243
|
+
readonly compressed_context: CompressedContextSpec | null;
|
|
244
|
+
/**
|
|
245
|
+
* Machine-readable path reason, e.g. `planning_delegate`, `planning_direct_frontier`,
|
|
246
|
+
* `planning_delegate_disabled`.
|
|
247
|
+
*/
|
|
248
|
+
readonly planning_delegate_reason_code: string | null;
|
|
249
|
+
/** Operator-visible fallback explanation when path is not delegate. */
|
|
250
|
+
readonly fallback_reason: string | null;
|
|
251
|
+
}
|
|
252
|
+
|
|
211
253
|
/** SAAR pin policy observability (SP-126, #72). */
|
|
212
254
|
export interface SaarObservability {
|
|
213
255
|
readonly buffer_active: boolean;
|
|
@@ -275,6 +317,8 @@ export interface RoutingFeatureSidecar {
|
|
|
275
317
|
readonly breakeven?: BreakevenObservability;
|
|
276
318
|
/** SAAR pin state summary (SP-126, #72). */
|
|
277
319
|
readonly saar?: SaarObservability;
|
|
320
|
+
/** Planning delegate path summary (SP-142, #71). */
|
|
321
|
+
readonly planning_delegate?: PlanningDelegateObservability;
|
|
278
322
|
/** Why local_zero eligibility passed (SP-111, #59). */
|
|
279
323
|
readonly local_eligible_reason: string | null;
|
|
280
324
|
}
|
|
@@ -441,4 +485,12 @@ export interface RoutingTelemetry {
|
|
|
441
485
|
readonly saar_hard_lock: boolean;
|
|
442
486
|
readonly turn_index_in_session: number | null;
|
|
443
487
|
readonly saar_reason_code: string | null;
|
|
488
|
+
readonly planning_delegate_path: PlanningDelegatePath | null;
|
|
489
|
+
readonly planning_delegate_primary_model_id: string | null;
|
|
490
|
+
readonly planning_delegate_model_id: string | null;
|
|
491
|
+
readonly planning_delegate_reason_code: string | null;
|
|
492
|
+
readonly planning_delegate_fallback_reason: string | null;
|
|
493
|
+
readonly planning_delegate_max_messages: number | null;
|
|
494
|
+
readonly planning_delegate_max_tokens: number | null;
|
|
495
|
+
readonly planning_delegate_exclude_execution_history: boolean | null;
|
|
444
496
|
}
|
|
@@ -2,12 +2,16 @@ export type {
|
|
|
2
2
|
BreakevenObservability,
|
|
3
3
|
CandidateScore,
|
|
4
4
|
ClusterMatchTableEntry,
|
|
5
|
+
CompressedContextSpec,
|
|
5
6
|
ContextFitObservability,
|
|
6
7
|
ContextFitRejectedEntry,
|
|
7
8
|
LoadedRoutingCluster,
|
|
8
9
|
LowIntensityBreakdown,
|
|
9
10
|
Message,
|
|
10
11
|
ModelCapabilities,
|
|
12
|
+
PlanningDelegateConfig,
|
|
13
|
+
PlanningDelegateObservability,
|
|
14
|
+
PlanningDelegatePath,
|
|
11
15
|
ModelLimits,
|
|
12
16
|
ModelPerformance,
|
|
13
17
|
ModelPricing,
|
|
@@ -251,6 +251,23 @@ export const SaarConfigSchema = z.object({
|
|
|
251
251
|
|
|
252
252
|
export type SaarConfig = z.infer<typeof SaarConfigSchema>;
|
|
253
253
|
|
|
254
|
+
/** Compressed context limits for planning delegate sub-calls (SP-142, #71). */
|
|
255
|
+
export const CompressedContextSpecSchema = z.object({
|
|
256
|
+
max_messages: z.number().int().positive(),
|
|
257
|
+
max_tokens: z.number().int().positive(),
|
|
258
|
+
exclude_execution_history: z.boolean(),
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
export type CompressedContextSpec = z.infer<typeof CompressedContextSpecSchema>;
|
|
262
|
+
|
|
263
|
+
/** Planning delegate operator knobs (SP-142, #71). */
|
|
264
|
+
export const PlanningDelegateConfigSchema = z.object({
|
|
265
|
+
enabled: z.boolean(),
|
|
266
|
+
compressed_context: CompressedContextSpecSchema,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
export type PlanningDelegateConfig = z.infer<typeof PlanningDelegateConfigSchema>;
|
|
270
|
+
|
|
254
271
|
/** SAAR defaults per routing-roadmap.md §2 P0 (SP-121). */
|
|
255
272
|
export const DEFAULT_SAAR_CONFIG: Readonly<SaarConfig> = {
|
|
256
273
|
planning_turn_buffer: 2,
|
|
@@ -259,6 +276,61 @@ export const DEFAULT_SAAR_CONFIG: Readonly<SaarConfig> = {
|
|
|
259
276
|
switch_threshold: 0.5,
|
|
260
277
|
} as const;
|
|
261
278
|
|
|
279
|
+
/** Planning delegate defaults per routing-roadmap.md §2 P0 / #71 (SP-142). */
|
|
280
|
+
export const DEFAULT_PLANNING_DELEGATE_CONFIG: Readonly<PlanningDelegateConfig> = {
|
|
281
|
+
enabled: true,
|
|
282
|
+
compressed_context: {
|
|
283
|
+
max_messages: 12,
|
|
284
|
+
max_tokens: 16_384,
|
|
285
|
+
exclude_execution_history: true,
|
|
286
|
+
},
|
|
287
|
+
} as const;
|
|
288
|
+
|
|
289
|
+
/** Env: SMART_ROUTER_PLANNING_DELEGATE_ENABLED — enable delegate path (default true). */
|
|
290
|
+
const ENV_PLANNING_DELEGATE_ENABLED = 'SMART_ROUTER_PLANNING_DELEGATE_ENABLED';
|
|
291
|
+
/** Env: SMART_ROUTER_PLANNING_DELEGATE_MAX_MESSAGES — compressed context message cap (default 12). */
|
|
292
|
+
const ENV_PLANNING_DELEGATE_MAX_MESSAGES = 'SMART_ROUTER_PLANNING_DELEGATE_MAX_MESSAGES';
|
|
293
|
+
/** Env: SMART_ROUTER_PLANNING_DELEGATE_MAX_TOKENS — compressed context token cap (default 16384). */
|
|
294
|
+
const ENV_PLANNING_DELEGATE_MAX_TOKENS = 'SMART_ROUTER_PLANNING_DELEGATE_MAX_TOKENS';
|
|
295
|
+
/** Env: SMART_ROUTER_PLANNING_DELEGATE_EXCLUDE_EXECUTION_HISTORY — exclude tool traces (default true). */
|
|
296
|
+
const ENV_PLANNING_DELEGATE_EXCLUDE_EXECUTION_HISTORY =
|
|
297
|
+
'SMART_ROUTER_PLANNING_DELEGATE_EXCLUDE_EXECUTION_HISTORY';
|
|
298
|
+
|
|
299
|
+
function readBooleanEnv(name: string): boolean | undefined {
|
|
300
|
+
const raw = process.env[name];
|
|
301
|
+
if (raw === undefined || raw === '') {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
const normalized = raw.trim().toLowerCase();
|
|
305
|
+
if (normalized === '1' || normalized === 'true' || normalized === 'yes') {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
if (normalized === '0' || normalized === 'false' || normalized === 'no') {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Merge planning delegate env overrides onto defaults (invalid env values are ignored). */
|
|
315
|
+
export function resolvePlanningDelegateConfigFromEnv(
|
|
316
|
+
base: PlanningDelegateConfig = DEFAULT_PLANNING_DELEGATE_CONFIG,
|
|
317
|
+
): PlanningDelegateConfig {
|
|
318
|
+
return {
|
|
319
|
+
enabled: readBooleanEnv(ENV_PLANNING_DELEGATE_ENABLED) ?? base.enabled,
|
|
320
|
+
compressed_context: {
|
|
321
|
+
max_messages:
|
|
322
|
+
readPositiveIntEnv(ENV_PLANNING_DELEGATE_MAX_MESSAGES) ??
|
|
323
|
+
base.compressed_context.max_messages,
|
|
324
|
+
max_tokens:
|
|
325
|
+
readPositiveIntEnv(ENV_PLANNING_DELEGATE_MAX_TOKENS) ??
|
|
326
|
+
base.compressed_context.max_tokens,
|
|
327
|
+
exclude_execution_history:
|
|
328
|
+
readBooleanEnv(ENV_PLANNING_DELEGATE_EXCLUDE_EXECUTION_HISTORY) ??
|
|
329
|
+
base.compressed_context.exclude_execution_history,
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
262
334
|
/** Env: SMART_ROUTER_PLANNING_TURN_BUFFER — SAAR planning buffer turns (default 2). */
|
|
263
335
|
const ENV_PLANNING_TURN_BUFFER = 'SMART_ROUTER_PLANNING_TURN_BUFFER';
|
|
264
336
|
/** Env: SMART_ROUTER_PREFIX_CACHE_WEIGHT — SAAR prefix cache weight 0–1 (default 0.20). */
|
|
@@ -349,6 +421,7 @@ export const OperatorConfigSchema = z.object({
|
|
|
349
421
|
hydra: HydraConfigSchema,
|
|
350
422
|
low_intensity: LowIntensityConfigSchema,
|
|
351
423
|
saar: SaarConfigSchema,
|
|
424
|
+
planning_delegate: PlanningDelegateConfigSchema,
|
|
352
425
|
routing_clusters: RoutingClustersConfigSchema.optional(),
|
|
353
426
|
});
|
|
354
427
|
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from '../telemetry/telemetry-limits.js';
|
|
25
25
|
import {
|
|
26
26
|
DEFAULT_BREAKEVEN_TELEMETRY_FIELDS,
|
|
27
|
+
DEFAULT_PLANNING_DELEGATE_TELEMETRY_FIELDS,
|
|
27
28
|
DEFAULT_SAAR_TELEMETRY_FIELDS,
|
|
28
29
|
} from '../telemetry/routing-telemetry.js';
|
|
29
30
|
import {
|
|
@@ -833,6 +834,7 @@ function telemetryRowToEntity(row: TelemetryRow): RoutingTelemetry {
|
|
|
833
834
|
tier_selection_reason_code: null,
|
|
834
835
|
...DEFAULT_BREAKEVEN_TELEMETRY_FIELDS,
|
|
835
836
|
...DEFAULT_SAAR_TELEMETRY_FIELDS,
|
|
837
|
+
...DEFAULT_PLANNING_DELEGATE_TELEMETRY_FIELDS,
|
|
836
838
|
};
|
|
837
839
|
}
|
|
838
840
|
|
|
@@ -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',
|
|
@@ -538,6 +545,51 @@ function resolveSaarReasonCode(decision: RoutingDecision): string | null {
|
|
|
538
545
|
return null;
|
|
539
546
|
}
|
|
540
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
|
+
|
|
541
593
|
/** Build privacy-safe SAAR pin state for explain and telemetry (SP-126). */
|
|
542
594
|
export function buildSaarObservability(
|
|
543
595
|
input: PinEconomicsObservabilityInput,
|
|
@@ -658,6 +710,53 @@ export const DEFAULT_BREAKEVEN_TELEMETRY_FIELDS = defaultBreakevenTelemetry();
|
|
|
658
710
|
/** Default SAAR telemetry scalars for tests and legacy store reads. */
|
|
659
711
|
export const DEFAULT_SAAR_TELEMETRY_FIELDS = defaultSaarTelemetry();
|
|
660
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
|
+
|
|
661
760
|
function pinEconomicsTelemetryFromInput(
|
|
662
761
|
input: PinEconomicsObservabilityInput,
|
|
663
762
|
): ReturnType<typeof defaultBreakevenTelemetry> &
|
|
@@ -786,7 +885,9 @@ export async function enrichRoutingDecisionForExplain(
|
|
|
786
885
|
|
|
787
886
|
return enrichRoutingDecisionWithPinEconomics(
|
|
788
887
|
request,
|
|
789
|
-
|
|
888
|
+
enrichRoutingDecisionWithPlanningDelegate(
|
|
889
|
+
enrichRoutingDecisionWithTierSelection(withContextFit, clusterMatchTable),
|
|
890
|
+
),
|
|
790
891
|
pinEconomicsOptionsFromExplain(options),
|
|
791
892
|
);
|
|
792
893
|
}
|
|
@@ -847,12 +948,14 @@ export function buildRoutingDecisionLogPayload(
|
|
|
847
948
|
): Record<string, unknown> {
|
|
848
949
|
const enriched = enrichRoutingDecisionWithPinEconomics(
|
|
849
950
|
request,
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
951
|
+
enrichRoutingDecisionWithPlanningDelegate(
|
|
952
|
+
enrichRoutingDecisionWithTierSelection(
|
|
953
|
+
enrichRoutingDecisionWithContextFit(
|
|
954
|
+
request,
|
|
955
|
+
decision,
|
|
956
|
+
fleet,
|
|
957
|
+
contextFitConfig,
|
|
958
|
+
),
|
|
856
959
|
),
|
|
857
960
|
),
|
|
858
961
|
{
|
|
@@ -869,6 +972,7 @@ export function buildRoutingDecisionLogPayload(
|
|
|
869
972
|
const tierSelection = enriched.features?.tier_selection;
|
|
870
973
|
const breakeven = enriched.features?.breakeven;
|
|
871
974
|
const saar = enriched.features?.saar;
|
|
975
|
+
const planningDelegate = enriched.features?.planning_delegate;
|
|
872
976
|
|
|
873
977
|
return {
|
|
874
978
|
request_id: enriched.request_id,
|
|
@@ -908,6 +1012,16 @@ export function buildRoutingDecisionLogPayload(
|
|
|
908
1012
|
saar_reason_code: saar.saar_reason_code,
|
|
909
1013
|
}
|
|
910
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,
|
|
911
1025
|
delegate,
|
|
912
1026
|
};
|
|
913
1027
|
}
|
|
@@ -1093,6 +1207,7 @@ export class RoutingTelemetryEmitter {
|
|
|
1093
1207
|
: {}),
|
|
1094
1208
|
...(this.saarConfig !== undefined ? { saarConfig: this.saarConfig } : {}),
|
|
1095
1209
|
});
|
|
1210
|
+
const planningDelegateFields = planningDelegateTelemetryFromDecision(decision);
|
|
1096
1211
|
|
|
1097
1212
|
const record: RoutingTelemetry = {
|
|
1098
1213
|
timestamp: this.clock(),
|
|
@@ -1128,6 +1243,7 @@ export class RoutingTelemetryEmitter {
|
|
|
1128
1243
|
saar_hard_lock: pinEconomicsFields.saar_hard_lock,
|
|
1129
1244
|
turn_index_in_session: pinEconomicsFields.turn_index_in_session,
|
|
1130
1245
|
saar_reason_code: pinEconomicsFields.saar_reason_code,
|
|
1246
|
+
...planningDelegateFields,
|
|
1131
1247
|
};
|
|
1132
1248
|
|
|
1133
1249
|
this.entries.push(record);
|