| 268 | } |
| 269 | |
| 270 | // Single sink for decision telemetry: one Analytics Engine data point and |
| 271 | // one `auto_routing_decision` log line per decision. Successes are sampled |
| 272 | // per the KV-configured rate; fallbacks, errors, and real model switches |
| 273 | // always log (failures at warn). |
| 274 | function recordDecision( |
| 275 | env: Env, |
| 276 | ctx: DecisionContext, |
| 277 | durationMs: number, |
| 278 | outcome: DecisionOutcome, |
| 279 | autoRoutingMode: string, |
| 280 | decision: AutoRoutingDecision | null = null, |
| 281 | incumbent: StickyDecision | null = null |
| 282 | ): void { |
| 283 | const summary = summarizeOutcome(outcome); |
| 284 | |
| 285 | writeClassifierMetricsDataPoint(env, { |
| 286 | status: summary.status, |
| 287 | classifierModel: summary.classifierModel, |
| 288 | requestedModel: ctx.payload.input.requestedModel, |
| 289 | classification: summary.classification, |
| 290 | classifierCostCredits: summary.cost, |
| 291 | classifierDurationMs: durationMs, |
| 292 | cacheHit: summary.cacheHit, |
| 293 | }); |
| 294 | |
| 295 | const incumbentModel = incumbent?.model ?? null; |
| 296 | const decidedVariant = |
| 297 | decision !== null && decision.source === 'benchmark' ? (decision.variant ?? null) : null; |
| 298 | const switched = |
| 299 | decision !== null && |
| 300 | incumbentModel !== null && |
| 301 | (decision.model !== incumbentModel || |
| 302 | (incumbent?.variant != null && decidedVariant !== incumbent.variant)); |
| 303 | const routeKey = summary.classification ? taxonomyRouteKey(summary.classification) : null; |
| 304 | // Null when there is no incumbent route to compare against (no incumbent, |
| 305 | // a pre-routeKey cache entry, or no classification this request). |
| 306 | const routeChanged = |
| 307 | routeKey !== null && incumbent?.routeKey != null ? routeKey !== incumbent.routeKey : null; |
| 308 | |
| 309 | // Retried decisions are rare and diagnostically valuable, so they bypass |
| 310 | // sampling along with failures. Real model switches also always log: |
| 311 | // within-session switch sequences are the signal the sampled stream |
| 312 | // decimates. |
| 313 | const isFailure = summary.status !== 'classified'; |
| 314 | const alwaysLog = isFailure || summary.retried || switched; |
| 315 | if (!alwaysLog && Math.random() >= ctx.successSampleRate) { |
| 316 | return; |
| 317 | } |
| 318 | const log = isFailure ? console.warn : console.log; |
| 319 | log( |
| 320 | JSON.stringify({ |
| 321 | event: 'auto_routing_decision', |
| 322 | status: summary.status, |
| 323 | cacheHit: summary.cacheHit, |
| 324 | retried: summary.retried, |
| 325 | classifierModel: summary.classifierModel, |
| 326 | requestedModel: ctx.payload.input.requestedModel, |
| 327 | apiKind: ctx.payload.input.apiKind, |
nothing calls this directly
no test coverage detected