@bastani/atomic 0.9.21-alpha.1 → 0.9.21-alpha.2
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/CHANGELOG.md +27 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +11 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +292 -60
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +11 -0
- package/dist/builtin/workflows/builtin/adversarial-verification.js +3 -3
- package/dist/builtin/workflows/builtin/{chunk-cw86srkf.js → chunk-632fxeb8.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-w18kbz63.js → chunk-94f8zfz1.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-kspvwc24.js → chunk-9cm2ckq2.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-jaj27rqc.js → chunk-a1ar035d.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-wh19f5ee.js → chunk-d3xhj946.js} +5 -4
- package/dist/builtin/workflows/builtin/{chunk-crw7q5g1.js → chunk-gyjck9z8.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-maqv731g.js → chunk-hj8mh07f.js} +10 -6
- package/dist/builtin/workflows/builtin/{chunk-0nrgfbag.js → chunk-kb7s3gx2.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-73c5tayt.js → chunk-kgn5wz1c.js} +19 -1
- package/dist/builtin/workflows/builtin/{chunk-2hak3b7g.js → chunk-nkj4qhj6.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-bzdvx2xr.js → chunk-r97ndfvt.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-vkmpdat4.js → chunk-wf876n16.js} +1 -1
- package/dist/builtin/workflows/builtin/classify-and-act.js +3 -3
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +3 -3
- package/dist/builtin/workflows/builtin/generate-and-filter.js +3 -3
- package/dist/builtin/workflows/builtin/goal.js +4 -4
- package/dist/builtin/workflows/builtin/index.js +12 -12
- package/dist/builtin/workflows/builtin/loop-until-done.js +3 -3
- package/dist/builtin/workflows/builtin/open-claude-design.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +4 -4
- package/dist/builtin/workflows/builtin/steering-context.js +1 -1
- package/dist/builtin/workflows/builtin/tournament.js +3 -3
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +40 -11
- package/dist/builtin/workflows/src/index.js +9 -3
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +6 -1
- package/dist/cli/auth-command.js +5 -0
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/credential-print.js +2 -0
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/cli/experimental/command.js +5 -2
- package/dist/cli/experimental/command.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -4
- package/dist/config.js +4 -4
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +9 -4
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +4 -4
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts +4 -4
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +38 -15
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +14 -9
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +4 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +140 -95
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage-backends.js +2 -3
- package/dist/core/auth-storage-backends.js.map +1 -1
- package/dist/core/auth-storage.js +4 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/builtin-resource-loader.js +9 -0
- package/dist/core/builtin-resource-loader.js.map +1 -1
- package/dist/core/cache-warmer.js +8 -1
- package/dist/core/cache-warmer.js.map +1 -1
- package/dist/core/compaction/range-planner.js +6 -0
- package/dist/core/compaction/range-planner.js.map +1 -1
- package/dist/core/execution-model-router.d.ts +13 -3
- package/dist/core/execution-model-router.d.ts.map +1 -1
- package/dist/core/execution-model-router.js +227 -131
- package/dist/core/execution-model-router.js.map +1 -1
- package/dist/core/export-html/template-js/entries-navigation.js +16 -6
- package/dist/core/export-html/template-js/initialization.js +34 -11
- package/dist/core/export-html/template.css +10 -0
- package/dist/core/export-html/template.js +50 -17
- package/dist/core/extensions/context-types.d.ts +2 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/host-input.js +8 -4
- package/dist/core/extensions/host-input.js.map +1 -1
- package/dist/core/extensions/reactive-widget.js +1 -0
- package/dist/core/extensions/reactive-widget.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +1 -0
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +4 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +48 -30
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/runtime-types.d.ts +1 -0
- package/dist/core/extensions/runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/runtime-types.js.map +1 -1
- package/dist/core/extensions/workflow-activity-hub.js +9 -7
- package/dist/core/extensions/workflow-activity-hub.js.map +1 -1
- package/dist/core/footer-data-provider.js +24 -23
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/keybindings.js +1 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/mandatory-resource-loader.js +4 -1
- package/dist/core/mandatory-resource-loader.js.map +1 -1
- package/dist/core/model-config.js +2 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.js +1 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/{model-routing-task.d.ts → model-routing-bytes.d.ts} +1 -4
- package/dist/core/model-routing-bytes.d.ts.map +1 -0
- package/dist/core/model-routing-bytes.js +33 -0
- package/dist/core/model-routing-bytes.js.map +1 -0
- package/dist/core/model-routing-candidates.d.ts +42 -0
- package/dist/core/model-routing-candidates.d.ts.map +1 -0
- package/dist/core/model-routing-candidates.js +309 -0
- package/dist/core/model-routing-candidates.js.map +1 -0
- package/dist/core/model-routing-constraints.d.ts +4 -0
- package/dist/core/model-routing-constraints.d.ts.map +1 -1
- package/dist/core/model-routing-constraints.js +13 -1
- package/dist/core/model-routing-constraints.js.map +1 -1
- package/dist/core/model-routing-debug.d.ts +9 -0
- package/dist/core/model-routing-debug.d.ts.map +1 -0
- package/dist/core/model-routing-debug.js +14 -0
- package/dist/core/model-routing-debug.js.map +1 -0
- package/dist/core/model-routing-evals.d.ts +50 -0
- package/dist/core/model-routing-evals.d.ts.map +1 -0
- package/dist/core/model-routing-evals.js +190 -0
- package/dist/core/model-routing-evals.js.map +1 -0
- package/dist/core/model-routing-needs.d.ts +75 -0
- package/dist/core/model-routing-needs.d.ts.map +1 -0
- package/dist/core/model-routing-needs.js +176 -0
- package/dist/core/model-routing-needs.js.map +1 -0
- package/dist/core/model-runtime-streaming.js +3 -0
- package/dist/core/model-runtime-streaming.js.map +1 -1
- package/dist/core/model-runtime.js +26 -14
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/models-store.js +2 -3
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/output-guard.js +1 -0
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager-paths.d.ts +2 -1
- package/dist/core/package-manager-paths.d.ts.map +1 -1
- package/dist/core/package-manager-paths.js +4 -3
- package/dist/core/package-manager-paths.js.map +1 -1
- package/dist/core/package-manager.js +1 -0
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader-assets.d.ts.map +1 -1
- package/dist/core/resource-loader-assets.js +9 -7
- package/dist/core/resource-loader-assets.js.map +1 -1
- package/dist/core/resource-loader-core.js +49 -0
- package/dist/core/resource-loader-core.js.map +1 -1
- package/dist/core/runtime-credentials.js +2 -1
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/session-cwd.js +1 -0
- package/dist/core/session-cwd.js.map +1 -1
- package/dist/core/session-manager-archive.d.ts.map +1 -1
- package/dist/core/session-manager-archive.js +4 -2
- package/dist/core/session-manager-archive.js.map +1 -1
- package/dist/core/session-manager-core.js +11 -7
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-storage.d.ts +10 -4
- package/dist/core/session-manager-storage.d.ts.map +1 -1
- package/dist/core/session-manager-storage.js +12 -6
- package/dist/core/session-manager-storage.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +2 -1
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +21 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager-core.js +13 -7
- package/dist/core/settings-manager-core.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-storage.js +6 -0
- package/dist/core/settings-storage.js.map +1 -1
- package/dist/core/settings-types.d.ts +8 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/structured-output/index.d.ts.map +1 -1
- package/dist/core/structured-output/index.js +4 -1
- package/dist/core/structured-output/index.js.map +1 -1
- package/dist/core/structured-output/invalid-output.js +1 -0
- package/dist/core/structured-output/invalid-output.js.map +1 -1
- package/dist/core/tasks/agent-adapter.js +5 -3
- package/dist/core/tasks/agent-adapter.js.map +1 -1
- package/dist/core/tasks/child-command-owner.js +3 -7
- package/dist/core/tasks/child-command-owner.js.map +1 -1
- package/dist/core/tasks/completion.js +5 -1
- package/dist/core/tasks/completion.js.map +1 -1
- package/dist/core/tasks/owner-store.js +12 -8
- package/dist/core/tasks/owner-store.js.map +1 -1
- package/dist/core/tasks/supervisor.js +14 -35
- package/dist/core/tasks/supervisor.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/build-questionnaire.js +13 -4
- package/dist/core/tools/ask-user-question/state/build-questionnaire.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js +11 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/chat-row-view.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js +5 -0
- package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js +2 -0
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.js +13 -8
- package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/submit-picker.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/submit-picker.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/tab-bar.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/tab-bar.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +30 -24
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/dialog-builder.js +5 -0
- package/dist/core/tools/ask-user-question/view/dialog-builder.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js +8 -0
- package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/tab-content-strategy.js +4 -4
- package/dist/core/tools/ask-user-question/view/tab-content-strategy.js.map +1 -1
- package/dist/core/tools/bash.js +5 -8
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-batch.js +4 -0
- package/dist/core/tools/edit-batch.js.map +1 -1
- package/dist/core/tools/edit.js +2 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-coordinator.js +9 -0
- package/dist/core/tools/file-mutation-coordinator.js.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/hashline-engine/fs.js +1 -1
- package/dist/core/tools/hashline-engine/fs.js.map +1 -1
- package/dist/core/tools/hashline-engine/input.js +4 -0
- package/dist/core/tools/hashline-engine/input.js.map +1 -1
- package/dist/core/tools/hashline-engine/mismatch.js +6 -0
- package/dist/core/tools/hashline-engine/mismatch.js.map +1 -1
- package/dist/core/tools/hashline-engine/patcher.js +13 -0
- package/dist/core/tools/hashline-engine/patcher.js.map +1 -1
- package/dist/core/tools/hashline-engine/recovery.js +1 -0
- package/dist/core/tools/hashline-engine/recovery.js.map +1 -1
- package/dist/core/tools/output-accumulator.js +20 -13
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/persisted-output-file.js +9 -3
- package/dist/core/tools/persisted-output-file.js.map +1 -1
- package/dist/core/tools/resource-selectors.js +1 -0
- package/dist/core/tools/resource-selectors.js.map +1 -1
- package/dist/core/tools/write.js +1 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.js +1 -0
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/workflow-stage-admission.js +17 -10
- package/dist/core/workflow-stage-admission.js.map +1 -1
- package/dist/extensions/llama/client.js +2 -0
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/huggingface-ui.js +17 -9
- package/dist/extensions/llama/huggingface-ui.js.map +1 -1
- package/dist/extensions/llama/huggingface.js +2 -0
- package/dist/extensions/llama/huggingface.js.map +1 -1
- package/dist/extensions/llama/ui.js +11 -3
- package/dist/extensions/llama/ui.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/armin.js +10 -6
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +10 -2
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/atomic-working-status.js +10 -2
- package/dist/modes/interactive/components/atomic-working-status.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +9 -4
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +3 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +4 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +2 -1
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.js +63 -31
- package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +8 -1
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.js +24 -14
- package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
- package/dist/modes/interactive/components/compaction-boundary-message.js +2 -1
- package/dist/modes/interactive/components/compaction-boundary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector-list.js +15 -6
- package/dist/modes/interactive/components/config-selector-list.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +7 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +2 -0
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +21 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js +4 -1
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +9 -1
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js +8 -6
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +1 -0
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +7 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +8 -2
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +9 -1
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +4 -2
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.js +5 -2
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/host-input-form.js +13 -2
- package/dist/modes/interactive/components/host-input-form.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +9 -3
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +24 -12
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +12 -3
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/reserved-bottom-overlay.js +11 -2
- package/dist/modes/interactive/components/reserved-bottom-overlay.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +13 -8
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/scroll-widget.js +4 -0
- package/dist/modes/interactive/components/scroll-widget.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-header.js +11 -7
- package/dist/modes/interactive/components/session-selector-header.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-list.js +25 -12
- package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +23 -15
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-submenus.js +19 -1
- package/dist/modes/interactive/components/settings-selector-submenus.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +1 -0
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +4 -1
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/startup-identity.js +8 -3
- package/dist/modes/interactive/components/startup-identity.js.map +1 -1
- package/dist/modes/interactive/components/task-completion-message.js +2 -0
- package/dist/modes/interactive/components/task-completion-message.js.map +1 -1
- package/dist/modes/interactive/components/task-detail.js +2 -0
- package/dist/modes/interactive/components/task-detail.js.map +1 -1
- package/dist/modes/interactive/components/task-inspector.js +22 -12
- package/dist/modes/interactive/components/task-inspector.js.map +1 -1
- package/dist/modes/interactive/components/task-list.js +2 -0
- package/dist/modes/interactive/components/task-list.js.map +1 -1
- package/dist/modes/interactive/components/task-live-transcript.js +8 -3
- package/dist/modes/interactive/components/task-live-transcript.js.map +1 -1
- package/dist/modes/interactive/components/task-navigation.js +5 -2
- package/dist/modes/interactive/components/task-navigation.js.map +1 -1
- package/dist/modes/interactive/components/task-row.js +2 -0
- package/dist/modes/interactive/components/task-row.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +2 -0
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +7 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +25 -10
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/transcript-follow-indicator.js +1 -0
- package/dist/modes/interactive/components/transcript-follow-indicator.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-component.js +7 -3
- package/dist/modes/interactive/components/tree-selector-component.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-help.js +1 -0
- package/dist/modes/interactive/components/tree-selector-help.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-label-input.js +6 -2
- package/dist/modes/interactive/components/tree-selector-label-input.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-list.js +4 -0
- package/dist/modes/interactive/components/tree-selector-list.js.map +1 -1
- package/dist/modes/interactive/components/trust-selector.js +6 -0
- package/dist/modes/interactive/components/trust-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +6 -3
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.js +5 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/working-status.js +1 -0
- package/dist/modes/interactive/components/working-status.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +2 -0
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-runtime.js +1 -0
- package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +162 -120
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-helpers.js +3 -0
- package/dist/modes/interactive/interactive-mode-helpers.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +0 -6
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-resource-disclosure.js +0 -4
- package/dist/modes/interactive/interactive-resource-disclosure.js.map +1 -1
- package/dist/modes/interactive/interactive-resource-rendering.js +0 -12
- package/dist/modes/interactive/interactive-resource-rendering.js.map +1 -1
- package/dist/modes/interactive/interactive-startup-chat-container.js +1 -4
- package/dist/modes/interactive/interactive-startup-chat-container.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.js +1 -3
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -1
- package/dist/modes/interactive/theme/color-utils.d.ts +5 -0
- package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
- package/dist/modes/interactive/theme/color-utils.js +11 -0
- package/dist/modes/interactive/theme/color-utils.js.map +1 -1
- package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/global-theme.js +4 -3
- package/dist/modes/interactive/theme/global-theme.js.map +1 -1
- package/dist/modes/interactive/theme/theme-class.js +7 -0
- package/dist/modes/interactive/theme/theme-class.js.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +9 -2
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive-engine/activity-watchdog.js +8 -3
- package/dist/modes/interactive-engine/activity-watchdog.js.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.js +12 -8
- package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
- package/dist/modes/interactive-engine/engine-dialog-host.js +7 -3
- package/dist/modes/interactive-engine/engine-dialog-host.js.map +1 -1
- package/dist/modes/interactive-engine/engine-health.js +23 -13
- package/dist/modes/interactive-engine/engine-health.js.map +1 -1
- package/dist/modes/interactive-engine/engine-input-form.js +3 -2
- package/dist/modes/interactive-engine/engine-input-form.js.map +1 -1
- package/dist/modes/interactive-engine/engine-monitor.js +13 -1
- package/dist/modes/interactive-engine/engine-monitor.js.map +1 -1
- package/dist/modes/interactive-engine/engine-project-trust.js +2 -1
- package/dist/modes/interactive-engine/engine-project-trust.js.map +1 -1
- package/dist/modes/interactive-engine/engine-render-service.js +7 -4
- package/dist/modes/interactive-engine/engine-render-service.js.map +1 -1
- package/dist/modes/interactive-engine/engine-session-picker.js +3 -2
- package/dist/modes/interactive-engine/engine-session-picker.js.map +1 -1
- package/dist/modes/interactive-engine/input-form-host.js +5 -1
- package/dist/modes/interactive-engine/input-form-host.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +37 -23
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/interactive-engine/remote-command-catalog.js +4 -3
- package/dist/modes/interactive-engine/remote-command-catalog.js.map +1 -1
- package/dist/modes/interactive-engine/remote-component.js +24 -12
- package/dist/modes/interactive-engine/remote-component.js.map +1 -1
- package/dist/modes/interactive-engine/remote-frame-clamp.js +3 -3
- package/dist/modes/interactive-engine/remote-frame-clamp.js.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js +5 -4
- package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
- package/dist/modes/interactive-engine/remote-queue-pause.js +3 -1
- package/dist/modes/interactive-engine/remote-queue-pause.js.map +1 -1
- package/dist/modes/interactive-engine/remote-renderer.js +24 -13
- package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
- package/dist/modes/interactive-engine/resource-readiness-error.js +1 -0
- package/dist/modes/interactive-engine/resource-readiness-error.js.map +1 -1
- package/dist/modes/interactive-engine/session-picker-host.js +5 -1
- package/dist/modes/interactive-engine/session-picker-host.js.map +1 -1
- package/dist/modes/interactive-engine/terminal-mode-controller.js +2 -1
- package/dist/modes/interactive-engine/terminal-mode-controller.js.map +1 -1
- package/dist/modes/rpc/queued-writer.js +13 -4
- package/dist/modes/rpc/queued-writer.js.map +1 -1
- package/dist/modes/rpc/rpc-bash-request-owners.js +2 -1
- package/dist/modes/rpc/rpc-bash-request-owners.js.map +1 -1
- package/dist/modes/rpc/rpc-client-api.d.ts +8 -4
- package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client-api.js +10 -3
- package/dist/modes/rpc/rpc-client-api.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +35 -22
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +6 -6
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-event-buffer.js +3 -1
- package/dist/modes/rpc/rpc-event-buffer.js.map +1 -1
- package/dist/modes/rpc/rpc-generation-buffer.js +1 -3
- package/dist/modes/rpc/rpc-generation-buffer.js.map +1 -1
- package/dist/modes/rpc/rpc-keybindings-reload.js +4 -1
- package/dist/modes/rpc/rpc-keybindings-reload.js.map +1 -1
- package/dist/modes/rpc/rpc-output-buffer.js +3 -4
- package/dist/modes/rpc/rpc-output-buffer.js.map +1 -1
- package/dist/modes/rpc/rpc-pending-requests.js +1 -3
- package/dist/modes/rpc/rpc-pending-requests.js.map +1 -1
- package/dist/modes/rpc/rpc-provider-auth.js +3 -1
- package/dist/modes/rpc/rpc-provider-auth.js.map +1 -1
- package/dist/modes/rpc/rpc-resource-readiness.js +4 -5
- package/dist/modes/rpc/rpc-resource-readiness.js.map +1 -1
- package/dist/modes/rpc/rpc-session-binding.js +15 -0
- package/dist/modes/rpc/rpc-session-binding.js.map +1 -1
- package/dist/modes/rpc/rpc-terminal-drain.js +3 -0
- package/dist/modes/rpc/rpc-terminal-drain.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +10 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/models/evals.md +883 -39
- package/docs/models/model-selection.md +7 -8
- package/docs/rpc/examples.md +1 -1
- package/docs/rpc/protocol.md +9 -3
- package/docs/sdk/structured-decisions.md +1 -1
- package/docs/sdk.md +3 -3
- package/docs/settings.md +24 -3
- package/docs/subagents/reference.md +22 -4
- package/docs/subagents.md +2 -0
- package/docs/usage.md +3 -3
- package/docs/workflows/api-reference.md +1 -1
- package/docs/workflows/authoring.md +1 -7
- package/docs/workflows/builtins.md +1 -1
- package/docs/workflows/reliable-design.md +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/rpc-client.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/npm-shrinkwrap.json +70 -33
- package/package.json +6 -7
- package/dist/core/model-routing-task.d.ts.map +0 -1
- package/dist/core/model-routing-task.js +0 -120
- package/dist/core/model-routing-task.js.map +0 -1
|
@@ -3,8 +3,12 @@ import { join } from "node:path";
|
|
|
3
3
|
import { containsKnownEnvCredential, getSupportedThinkingLevels, isModelType, } from "@bastani/pi-ai";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
5
|
import { getDocsPath } from "../config.js";
|
|
6
|
+
import { ROUTING_REQUEST_BYTES } from "./model-routing-bytes.js";
|
|
7
|
+
import { describeOption, distinctTop, rankCandidates, } from "./model-routing-candidates.js";
|
|
6
8
|
import { eligiblePair, parseModelConstraints, } from "./model-routing-constraints.js";
|
|
7
|
-
import {
|
|
9
|
+
import { reportModelRoutingDebug } from "./model-routing-debug.js";
|
|
10
|
+
import { parseEvalsCatalog } from "./model-routing-evals.js";
|
|
11
|
+
import { effortForDifficulty, missingNeedsQuestions, parseTaskNeeds, resolveTaskNeeds, } from "./model-routing-needs.js";
|
|
8
12
|
import { resolveRouterModel, routeModel } from "./structured-output/index.js";
|
|
9
13
|
/**
|
|
10
14
|
* Total auto-routing inference failure: Jev and the chat structured-output
|
|
@@ -12,6 +16,12 @@ import { resolveRouterModel, routeModel } from "./structured-output/index.js";
|
|
|
12
16
|
* eligibility, and credential-screening errors are never marked.
|
|
13
17
|
*/
|
|
14
18
|
export class AutoRoutingInferenceError extends Error {
|
|
19
|
+
/**
|
|
20
|
+
* Route pinned to the current chat model, present only when that model is
|
|
21
|
+
* available and satisfies every routing constraint. Consumers may degrade
|
|
22
|
+
* to it; when it is absent the failure stays fatal.
|
|
23
|
+
*/
|
|
24
|
+
currentModelRoute;
|
|
15
25
|
constructor(message, currentModelRoute) {
|
|
16
26
|
super(message);
|
|
17
27
|
this.name = "AutoRoutingInferenceError";
|
|
@@ -30,56 +40,23 @@ const CURRENT_MODEL_EFFORT_PREFERENCE = [
|
|
|
30
40
|
"max",
|
|
31
41
|
"off",
|
|
32
42
|
];
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
| Stage role | Default thinking level | Model cost tier | Why |
|
|
48
|
-
| --- | --- | --- | --- |
|
|
49
|
-
| Codebase exploration: locating files, reading code, tracing call sites | \`minimal\` or \`low\` | Cheap, fast | Tool-driven lookups need speed, not deliberation; escalate to mapping or analysis only when the question becomes a design judgement. |
|
|
50
|
-
| Coding, implementation, routine fixes | \`low\` or \`medium\` | Cheap or mid-priced | Runs many times per task and is validated by tools and review afterwards. |
|
|
51
|
-
| Code review, test design, failure analysis, security, identity, adversarial challenge, final approval | \`high\` or \`xhigh\` | Frontier | A missed defect is the expensive outcome; spend the strongest model and reasoning here. |
|
|
52
|
-
| Codebase mapping, lifecycle analysis, compatibility, planning, synthesis, triage | \`high\` | Frontier or mid-priced | Resolve ambiguity before downstream work depends on it. |
|
|
53
|
-
| Orchestration, delegation, and multi-stage coordination | \`medium\` or \`high\` | Mid-priced | Judge scope, sequence work, and integrate results without re-deriving what delegated stages already verified. |
|
|
54
|
-
| User-impact review and final reporting | \`medium\` | Mid-priced | Preserve evidence and communicate clearly without unnecessary reasoning. |
|
|
55
|
-
| Deterministic checks | No model call | — | Run tests, typechecks, probes, and scripts directly. |
|
|
56
|
-
|
|
57
|
-
An explicit user request wins over these defaults, but the requested level must exist for the selected catalog entry. Do not invent unsupported suffixes. If \`xhigh\` is unavailable, use \`high\` rather than automatically promoting to \`max\`; choose another catalog model or leave the stage unpinned if neither fits.
|
|
58
|
-
`;
|
|
59
|
-
// Keeps the shipped evals snapshot small enough to reach Jev intact alongside the
|
|
60
|
-
// guide, a full task excerpt, and a small candidate question. Larger candidate
|
|
61
|
-
// questions truncate the routing copy instead; see fitRoutingState.
|
|
62
|
-
export const MODEL_SELECTION_EVALS_JSON_BYTES = 14_200;
|
|
63
|
-
const EVALS_BUDGET_ERROR = `Auto routing requires a nonempty evals.md document within ${MODEL_SELECTION_EVALS_JSON_BYTES.toLocaleString("en-US")} JSON-encoded bytes. Repair the Atomic installation or select a concrete execution model.`;
|
|
64
|
-
// Decision policy, key names and JSON framing the classifier transport adds.
|
|
65
|
-
const ROUTING_WIRE_OVERHEAD_BYTES = 1_000;
|
|
66
|
-
const PAIR_QUESTION = "Which eligible model and reasoning effort best suit this task and agent role, considering the model_selection_guide role tiers, evals, and candidate capabilities and prices? Prefer cheaper candidates for exploration and routine implementation and stronger ones for review and verification. Candidate cost is USD per million tokens, not benchmark task cost.";
|
|
67
|
-
/**
|
|
68
|
-
* Truncate the routing copy of the task, then evals, so state plus the candidate
|
|
69
|
-
* question fits ROUTING_REQUEST_BYTES. Candidates, agent and guide stay intact.
|
|
70
|
-
*/
|
|
71
|
-
function fitRoutingState(state, criteria) {
|
|
72
|
-
const fixed = Buffer.byteLength(JSON.stringify({ ...state, task: "", evals: "", instructions, question: PAIR_QUESTION, criteria }), "utf8") + ROUTING_WIRE_OVERHEAD_BYTES;
|
|
73
|
-
const room = Math.max(0, ROUTING_REQUEST_BYTES - fixed);
|
|
74
|
-
const evalsMarkerBytes = jsonBytes(TRUNCATED_MARKER);
|
|
75
|
-
const task = modelRoutingTask(state.task, Math.max(0, Math.min(MODEL_ROUTING_TASK_BYTES, room - evalsMarkerBytes)));
|
|
76
|
-
const evals = truncateToBytes(state.evals, Math.max(evalsMarkerBytes, room - jsonBytes(task)));
|
|
77
|
-
return { ...state, task, evals };
|
|
78
|
-
}
|
|
43
|
+
/** Most options the router compares when it builds the shortlist itself. */
|
|
44
|
+
const SHORTLIST_SIZE = 6;
|
|
45
|
+
/** Conservative bytes per token when checking that a task fits a reader's context window. */
|
|
46
|
+
const BYTES_PER_TOKEN = 3;
|
|
47
|
+
/** Room in the reader's window for the questions, instructions and its answer. */
|
|
48
|
+
const READER_OVERHEAD_TOKENS = 4_000;
|
|
49
|
+
/** Jev accepts at most this many options in one Choice question. */
|
|
50
|
+
const MAX_CHOICE_OPTIONS = 255;
|
|
51
|
+
/** A task that must hold this much in context prefers models whose window is at least this large. */
|
|
52
|
+
const LONG_CONTEXT_TOKENS = 400_000;
|
|
53
|
+
const NEEDS_INSTRUCTIONS = "Read `task`, which `agent` will perform, and answer each field: what kind of work it is, how hard it is, how costly a mistake would be, whether it needs screenshots or images, whether it must hold a very large amount of material in context, and whether speed matters more than extra reasoning. Each field's description lists its options. Judge the task as written; `caller_says` lists answers the caller already gave.";
|
|
54
|
+
const CHOICE_INSTRUCTIONS = "Pick the model that should do the task `agent` will perform. The task's needs are in `needs`; the task itself is not shown. Each option lists the model's release date, price tier and prices, whether it reads images, and its results for this kind of work and overall; standings compare it with every eligible model. For demanding or high-stakes tasks prefer the best-proven model; for easy, low-stakes tasks prefer an adequate cheaper one. Prefer newer models over older ones with similar results. Missing results are unknown, not weak.";
|
|
55
|
+
const EVALS_BUDGET_ERROR = "Auto routing requires a nonempty evals.md document. Repair the Atomic installation or select a concrete execution model.";
|
|
79
56
|
async function readModelSelectionEvals(signal) {
|
|
80
57
|
try {
|
|
81
58
|
const evals = await readFile(join(getDocsPath(), "models", "evals.md"), { encoding: "utf8", signal });
|
|
82
|
-
if (!evals.trim()
|
|
59
|
+
if (!evals.trim())
|
|
83
60
|
throw new Error(EVALS_BUDGET_ERROR);
|
|
84
61
|
return evals;
|
|
85
62
|
}
|
|
@@ -94,9 +71,16 @@ export async function routeExecutionModel(input) {
|
|
|
94
71
|
const { ctx, signal } = input;
|
|
95
72
|
signal?.throwIfAborted();
|
|
96
73
|
const constraints = structuredClone((input.constraints ?? []).map((c) => parseModelConstraints(c)));
|
|
74
|
+
const statedNeeds = parseTaskNeeds(input.taskNeeds);
|
|
75
|
+
// settings.json provider lists are defaults that only the call itself may
|
|
76
|
+
// replace; every constraint's own provider lists are enforced by eligiblePair.
|
|
77
|
+
const { allowedProviders = [], excludedProviders = [] } = input.overrideProviderSettings
|
|
78
|
+
? {}
|
|
79
|
+
: (ctx.getModelRouting?.() ?? {});
|
|
80
|
+
const providerPermitted = (provider) => (allowedProviders.length === 0 || allowedProviders.includes(provider)) && !excludedProviders.includes(provider);
|
|
97
81
|
const catalog = () => ctx.modelRegistry
|
|
98
82
|
.getAvailable()
|
|
99
|
-
.filter((model) => isModelType(model, "chat"))
|
|
83
|
+
.filter((model) => isModelType(model, "chat") && providerPermitted(model.provider))
|
|
100
84
|
.map((model) => ({
|
|
101
85
|
model,
|
|
102
86
|
pairs: (model.reasoning ? getSupportedThinkingLevels(model) : [null])
|
|
@@ -107,27 +91,19 @@ export async function routeExecutionModel(input) {
|
|
|
107
91
|
const available = catalog();
|
|
108
92
|
const pairs = available.flatMap((entry) => entry.pairs);
|
|
109
93
|
if (!pairs.length)
|
|
110
|
-
throw new Error(
|
|
94
|
+
throw new Error(allowedProviders.length || excludedProviders.length
|
|
95
|
+
? "Auto routing has no eligible model/effort pairs. Check configured providers, modelConstraints, and the modelRouting allowedProviders/excludedProviders settings."
|
|
96
|
+
: "Auto routing has no eligible model/effort pairs. Check configured providers and modelConstraints.");
|
|
111
97
|
let selection = input.selection;
|
|
112
98
|
if (selection === undefined) {
|
|
113
99
|
const settings = { getRouterModel: () => ctx.getRouterModel() };
|
|
114
|
-
resolveRouterModel({ settings, currentModel: ctx.model, modelRegistry: ctx.modelRegistry });
|
|
115
|
-
|
|
116
|
-
...pair,
|
|
117
|
-
input: model.input,
|
|
118
|
-
contextWindow: model.contextWindow,
|
|
119
|
-
cost: { ...model.cost, tiers: (model.cost.tiers ?? []).map((tier) => ({ ...tier })) },
|
|
120
|
-
})));
|
|
121
|
-
const allCriteria = Object.fromEntries(candidates.map((candidate, index) => [`pair_${index}`, JSON.stringify(candidate)]));
|
|
122
|
-
const state = {
|
|
123
|
-
task: input.task,
|
|
124
|
-
agent: { name: input.agent.name, description: input.agent.description },
|
|
125
|
-
evals: await readModelSelectionEvals(signal),
|
|
126
|
-
model_selection_guide: MODEL_SELECTION_GUIDE,
|
|
127
|
-
};
|
|
128
|
-
if (!state.task.trim())
|
|
100
|
+
const router = resolveRouterModel({ settings, currentModel: ctx.model, modelRegistry: ctx.modelRegistry });
|
|
101
|
+
if (!input.task.trim())
|
|
129
102
|
throw new Error("Auto routing requires task instructions.");
|
|
130
|
-
const
|
|
103
|
+
const stated = statedNeeds;
|
|
104
|
+
const agent = { name: input.agent.name, description: input.agent.description };
|
|
105
|
+
// Screen the task before any model reads it.
|
|
106
|
+
const serialized = JSON.stringify({ task: input.task, agent, stated, constraints });
|
|
131
107
|
let configuredCredential;
|
|
132
108
|
try {
|
|
133
109
|
configuredCredential = await ctx.modelRegistry.containsConfiguredCredential(serialized);
|
|
@@ -139,10 +115,7 @@ export async function routeExecutionModel(input) {
|
|
|
139
115
|
configuredCredential ||
|
|
140
116
|
/\bBearer\s+[A-Za-z0-9._~+/-]{8,}|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----|\b(?:sk|ghp|github_pat)[-_][A-Za-z0-9_-]{16,}/i.test(serialized))
|
|
141
117
|
throw new Error("Auto routing context contains credential material. Remove secrets before retrying.");
|
|
142
|
-
|
|
143
|
-
// Truncation only affects the routing request, never the execution prompt,
|
|
144
|
-
// so it is not surfaced to the user.
|
|
145
|
-
const ranked = [];
|
|
118
|
+
const task = input.task;
|
|
146
119
|
// Degrade only to a current chat model that is available and eligible under
|
|
147
120
|
// the same constraints, restored through the normal selection path (#3206).
|
|
148
121
|
const currentModelRoute = async () => {
|
|
@@ -153,74 +126,197 @@ export async function routeExecutionModel(input) {
|
|
|
153
126
|
return undefined;
|
|
154
127
|
return routeExecutionModel({ ...input, selection: { model: pair.model, effort: pair.effort } });
|
|
155
128
|
};
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
// Jev and its chat structured-output fallback both failed (#3206). A failed
|
|
160
|
-
// optional fallback-ranking pass keeps the models already ranked.
|
|
161
|
-
while (ranked.length < Math.min(3, available.length)) {
|
|
162
|
-
const remaining = pairs.filter((pair) => !ranked.some((selected) => selected.model === pair.model));
|
|
163
|
-
if (!remaining.length)
|
|
164
|
-
break;
|
|
165
|
-
const criteria = Object.fromEntries(remaining.map((pair) => {
|
|
166
|
-
const key = `pair_${pairs.indexOf(pair)}`;
|
|
167
|
-
return [key, allCriteria[key]];
|
|
168
|
-
}));
|
|
169
|
-
// Strict Responses providers reject object unions, and Anthropic rejects an
|
|
170
|
-
// enum under a type array. Enumerate scalar values on the wire with one
|
|
171
|
-
// declared type per enum, then verify the exact model/effort relation.
|
|
172
|
-
const efforts = [...new Set(remaining.map((pair) => pair.effort))];
|
|
173
|
-
const stringEfforts = efforts.filter((effort) => effort !== null);
|
|
174
|
-
const stringEffortSchema = { type: "string", enum: stringEfforts };
|
|
175
|
-
const reasoningEffort = !efforts.includes(null)
|
|
176
|
-
? stringEffortSchema
|
|
177
|
-
: stringEfforts.length
|
|
178
|
-
? { anyOf: [stringEffortSchema, { type: "null" }] }
|
|
179
|
-
: { type: "null" };
|
|
180
|
-
const schema = Type.Unsafe({
|
|
181
|
-
type: "object",
|
|
182
|
-
properties: {
|
|
183
|
-
modelId: Type.String({ enum: [...new Set(remaining.map((pair) => pair.model))] }),
|
|
184
|
-
reasoningEffort,
|
|
185
|
-
},
|
|
186
|
-
required: ["modelId", "reasoningEffort"],
|
|
187
|
-
additionalProperties: false,
|
|
188
|
-
});
|
|
189
|
-
let result;
|
|
129
|
+
// Any failure before a model is chosen is a total inference failure: the
|
|
130
|
+
// router and its chat structured-output fallback both failed (#3206).
|
|
131
|
+
const infer = async (decide) => {
|
|
190
132
|
try {
|
|
191
|
-
|
|
192
|
-
settings,
|
|
193
|
-
modelRegistry: ctx.modelRegistry,
|
|
194
|
-
currentModel: ctx.model,
|
|
195
|
-
state: fitRoutingState(state, criteria),
|
|
196
|
-
instructions,
|
|
197
|
-
schema,
|
|
198
|
-
classifier: {
|
|
199
|
-
questions: {
|
|
200
|
-
pair: {
|
|
201
|
-
instructions: PAIR_QUESTION,
|
|
202
|
-
criteria,
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
decode: (choices) => {
|
|
206
|
-
const pair = pairs[Number(choices.pair?.replace(/^pair_/, ""))];
|
|
207
|
-
if (!pair || choices.pair !== `pair_${pairs.indexOf(pair)}`)
|
|
208
|
-
throw new Error("Invalid execution model Choice.");
|
|
209
|
-
return { modelId: pair.model, reasoningEffort: pair.effort };
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
signal,
|
|
213
|
-
}, (value) => remaining.some((pair) => pair.model === value.modelId && pair.effort === value.reasoningEffort));
|
|
133
|
+
return await decide();
|
|
214
134
|
}
|
|
215
135
|
catch (error) {
|
|
216
136
|
signal?.throwIfAborted();
|
|
217
|
-
if (ranked.length > 0)
|
|
218
|
-
break;
|
|
219
137
|
throw new AutoRoutingInferenceError(error instanceof Error ? error.message : String(error), await currentModelRoute().catch(() => undefined));
|
|
220
138
|
}
|
|
221
|
-
|
|
139
|
+
};
|
|
140
|
+
// Where code cannot pick a model either, the current chat model takes over
|
|
141
|
+
// the same way.
|
|
142
|
+
const fallBackToCurrentModel = async (message) => {
|
|
143
|
+
throw new AutoRoutingInferenceError(message, await currentModelRoute().catch(() => undefined));
|
|
144
|
+
};
|
|
145
|
+
const catalogEvals = await readModelSelectionEvals(signal)
|
|
146
|
+
.then(parseEvalsCatalog)
|
|
147
|
+
.catch((error) => {
|
|
148
|
+
signal?.throwIfAborted();
|
|
149
|
+
return fallBackToCurrentModel(error instanceof Error ? error.message : String(error));
|
|
150
|
+
});
|
|
151
|
+
// Step 1: a chat model reads the task and answers only what the caller did
|
|
152
|
+
// not state. A classifier router such as Jev never receives the task.
|
|
153
|
+
const questions = missingNeedsQuestions(stated);
|
|
154
|
+
let answers = {};
|
|
155
|
+
if (questions.length) {
|
|
156
|
+
const current = ctx.model;
|
|
157
|
+
const preferred = router.kind === "chat"
|
|
158
|
+
? router.model
|
|
159
|
+
: current && current.id !== "auto" && isModelType(current, "chat")
|
|
160
|
+
? current
|
|
161
|
+
: undefined;
|
|
162
|
+
// The reader gets the complete task. When the task is larger than the
|
|
163
|
+
// preferred reader's window, the cheapest eligible model that holds it
|
|
164
|
+
// reads instead; eligible models are the ones allowed to receive the task.
|
|
165
|
+
const taskTokens = Math.ceil(Buffer.byteLength(task, "utf8") / BYTES_PER_TOKEN) + READER_OVERHEAD_TOKENS;
|
|
166
|
+
const holdsTask = (model) => model.contextWindow >= taskTokens;
|
|
167
|
+
const readerModel = preferred && holdsTask(preferred)
|
|
168
|
+
? preferred
|
|
169
|
+
: (available
|
|
170
|
+
.map((entry) => entry.model)
|
|
171
|
+
.filter(holdsTask)
|
|
172
|
+
.sort((a, b) => a.cost.input - b.cost.input ||
|
|
173
|
+
`${a.provider}/${a.id}`.localeCompare(`${b.provider}/${b.id}`))[0] ?? preferred);
|
|
174
|
+
const reader = readerModel ? `${readerModel.provider}/${readerModel.id}` : undefined;
|
|
175
|
+
if (reader === undefined)
|
|
176
|
+
await fallBackToCurrentModel("Auto routing needs a chat model to read the task. Select a chat model or state every taskNeeds field.");
|
|
177
|
+
const schema = Type.Object(Object.fromEntries(questions.map((question) => [
|
|
178
|
+
question.id,
|
|
179
|
+
Type.String({
|
|
180
|
+
enum: Object.keys(question.criteria),
|
|
181
|
+
description: `${question.instructions} ${Object.entries(question.criteria)
|
|
182
|
+
.map(([key, meaning]) => `${key}: ${meaning}`)
|
|
183
|
+
.join("; ")}.`,
|
|
184
|
+
}),
|
|
185
|
+
])), { additionalProperties: false });
|
|
186
|
+
const callerSays = stated
|
|
187
|
+
? {
|
|
188
|
+
...(stated.work ? { work: stated.work } : {}),
|
|
189
|
+
...(stated.difficulty ? { difficulty: stated.difficulty } : {}),
|
|
190
|
+
...(stated.mistakeCost ? { mistake_cost: stated.mistakeCost } : {}),
|
|
191
|
+
...(stated.needsImages !== undefined ? { needs_images: stated.needsImages ? "yes" : "no" } : {}),
|
|
192
|
+
...(stated.longContext !== undefined ? { long_context: stated.longContext ? "yes" : "no" } : {}),
|
|
193
|
+
...(stated.latencySensitive !== undefined
|
|
194
|
+
? { latency_sensitive: stated.latencySensitive ? "yes" : "no" }
|
|
195
|
+
: {}),
|
|
196
|
+
}
|
|
197
|
+
: undefined;
|
|
198
|
+
const result = await infer(() => routeModel({
|
|
199
|
+
settings: { getRouterModel: () => reader },
|
|
200
|
+
modelRegistry: ctx.modelRegistry,
|
|
201
|
+
currentModel: ctx.model,
|
|
202
|
+
state: {
|
|
203
|
+
task,
|
|
204
|
+
agent,
|
|
205
|
+
...(callerSays && Object.keys(callerSays).length ? { caller_says: callerSays } : {}),
|
|
206
|
+
},
|
|
207
|
+
instructions: NEEDS_INSTRUCTIONS,
|
|
208
|
+
schema,
|
|
209
|
+
// Unused: the reader always resolves to a chat model.
|
|
210
|
+
classifier: {
|
|
211
|
+
questions: Object.fromEntries(questions.map((question) => [
|
|
212
|
+
question.id,
|
|
213
|
+
{ instructions: question.instructions, criteria: question.criteria },
|
|
214
|
+
])),
|
|
215
|
+
decode: (choices) => Object.fromEntries(questions.map((question) => [question.id, choices[question.id]])),
|
|
216
|
+
},
|
|
217
|
+
signal,
|
|
218
|
+
}, (value) => questions.every((question) => Object.hasOwn(question.criteria, String(value[question.id])))));
|
|
219
|
+
answers = result.value;
|
|
220
|
+
}
|
|
221
|
+
const needs = resolveTaskNeeds(stated, answers);
|
|
222
|
+
// Step 2: narrow in code. A task that needs images only goes to models that read them.
|
|
223
|
+
const seeing = needs.needsImages ? available.filter((entry) => entry.model.input.includes("image")) : available;
|
|
224
|
+
if (seeing.length === 0)
|
|
225
|
+
await fallBackToCurrentModel("Auto routing: this task needs a model that can read images, and no eligible model can. Allow an image-capable model or select a concrete execution model.");
|
|
226
|
+
// A large context window is preferred, not required: context size is a matter of degree.
|
|
227
|
+
const roomy = needs.longContext
|
|
228
|
+
? seeing.filter((entry) => entry.model.contextWindow >= LONG_CONTEXT_TOKENS)
|
|
229
|
+
: seeing;
|
|
230
|
+
const usable = roomy.length ? roomy : seeing;
|
|
231
|
+
const pairsFor = new Map(usable.map((entry) => [`${entry.model.provider}/${entry.model.id}`, entry.pairs]));
|
|
232
|
+
const toCandidate = (model) => ({
|
|
233
|
+
model: `${model.provider}/${model.id}`,
|
|
234
|
+
name: model.name,
|
|
235
|
+
cost: model.cost,
|
|
236
|
+
input: model.input,
|
|
237
|
+
...(model.fastRoute ? { fastRouteOf: `${model.provider}/${model.fastRoute.baseModelId}` } : {}),
|
|
238
|
+
});
|
|
239
|
+
// A caller that lists models (`allowedModels`) chooses the contenders itself,
|
|
240
|
+
// but standings still compare them with every model the user could route to.
|
|
241
|
+
const callerListed = constraints.some((constraint) => (constraint.allowedModels?.length ?? 0) > 0);
|
|
242
|
+
const reference = callerListed
|
|
243
|
+
? ctx.modelRegistry
|
|
244
|
+
.getAvailable()
|
|
245
|
+
.filter((model) => isModelType(model, "chat") &&
|
|
246
|
+
providerPermitted(model.provider) &&
|
|
247
|
+
(!needs.needsImages || model.input.includes("image")))
|
|
248
|
+
: usable.map((entry) => entry.model);
|
|
249
|
+
const standings = rankCandidates(catalogEvals, reference.map(toCandidate), needs);
|
|
250
|
+
const ranked = standings.filter((candidate) => pairsFor.has(candidate.model));
|
|
251
|
+
const choiceState = {
|
|
252
|
+
agent,
|
|
253
|
+
needs: {
|
|
254
|
+
work: needs.work,
|
|
255
|
+
difficulty: needs.difficulty,
|
|
256
|
+
mistake_cost: needs.mistakeCost,
|
|
257
|
+
needs_images: needs.needsImages,
|
|
258
|
+
long_context: needs.longContext,
|
|
259
|
+
latency_sensitive: needs.latencySensitive,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
const describeAll = (options) => Object.fromEntries(options.map((option, index) => [`m${index}`, describeOption(option, needs, standings)]));
|
|
263
|
+
const fitsOneChoice = (options) => options.length <= MAX_CHOICE_OPTIONS &&
|
|
264
|
+
Buffer.byteLength(JSON.stringify({ state: choiceState, instructions: CHOICE_INSTRUCTIONS, criteria: describeAll(options) }), "utf8") <= ROUTING_REQUEST_BYTES;
|
|
265
|
+
// A caller's list is offered whole when it fits one choice request; otherwise
|
|
266
|
+
// duplicate routes of one model share a slot, and a list that still does not
|
|
267
|
+
// fit falls back rather than silently dropping a contender.
|
|
268
|
+
const callerChoices = !callerListed || fitsOneChoice(ranked) ? ranked : distinctTop(ranked, ranked.length);
|
|
269
|
+
if (callerListed && !fitsOneChoice(callerChoices))
|
|
270
|
+
await fallBackToCurrentModel(`Auto routing cannot compare ${callerChoices.length} different models from modelConstraints.allowedModels in one routing request. List fewer models.`);
|
|
271
|
+
const shortlist = callerListed ? callerChoices : distinctTop(ranked, SHORTLIST_SIZE);
|
|
272
|
+
// Step 3: the router picks one option; each carries its own evidence.
|
|
273
|
+
let chosen = shortlist[0];
|
|
274
|
+
if (shortlist.length > 1) {
|
|
275
|
+
const keys = shortlist.map((_, index) => `m${index}`);
|
|
276
|
+
const schema = Type.Object({ modelId: Type.String({ enum: shortlist.map((option) => option.model) }) }, { additionalProperties: false });
|
|
277
|
+
const result = await infer(() => routeModel({
|
|
278
|
+
settings,
|
|
279
|
+
modelRegistry: ctx.modelRegistry,
|
|
280
|
+
currentModel: ctx.model,
|
|
281
|
+
state: {
|
|
282
|
+
...choiceState,
|
|
283
|
+
},
|
|
284
|
+
instructions: CHOICE_INSTRUCTIONS,
|
|
285
|
+
schema,
|
|
286
|
+
classifier: {
|
|
287
|
+
questions: {
|
|
288
|
+
model: {
|
|
289
|
+
instructions: "Which model should do this task?",
|
|
290
|
+
criteria: describeAll(shortlist),
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
decode: (choices) => {
|
|
294
|
+
const option = shortlist[keys.indexOf(choices.model ?? "")];
|
|
295
|
+
if (!option)
|
|
296
|
+
throw new Error("Invalid execution model Choice.");
|
|
297
|
+
return { modelId: option.model };
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
signal,
|
|
301
|
+
}, (value) => shortlist.some((option) => option.model === value.modelId)));
|
|
302
|
+
chosen = shortlist.find((option) => option.model === result.value.modelId);
|
|
222
303
|
}
|
|
223
|
-
|
|
304
|
+
// Step 4: effort from difficulty; fallbacks are the next distinct models in code's ranking.
|
|
305
|
+
const effortOf = (model) => effortForDifficulty((pairsFor.get(model) ?? []).map((pair) => pair.effort), needs.difficulty, needs.latencySensitive);
|
|
306
|
+
const fallbackModels = distinctTop(ranked.filter((candidate) => candidate.baseKey !== chosen.baseKey), 2);
|
|
307
|
+
reportModelRoutingDebug(`Auto routing: ${needs.work}, ${needs.difficulty}, mistake cost ${needs.mistakeCost}, images ${needs.needsImages ? "yes" : "no"}, long context ${needs.longContext ? "yes" : "no"}, latency ${needs.latencySensitive ? "sensitive" : "tolerant"}; chose ${chosen.model} from ${shortlist.map((option) => option.model).join(", ")}.`);
|
|
308
|
+
selection = {
|
|
309
|
+
model: chosen.model,
|
|
310
|
+
effort: effortOf(chosen.model),
|
|
311
|
+
...(fallbackModels.length
|
|
312
|
+
? {
|
|
313
|
+
fallbacks: fallbackModels.map((candidate) => ({
|
|
314
|
+
model: candidate.model,
|
|
315
|
+
effort: effortOf(candidate.model),
|
|
316
|
+
})),
|
|
317
|
+
}
|
|
318
|
+
: {}),
|
|
319
|
+
};
|
|
224
320
|
}
|
|
225
321
|
const fallbacks = selection.fallbacks?.map((pair) => Object.freeze({ model: pair.model, effort: pair.effort }));
|
|
226
322
|
if (fallbacks &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-model-router.js","sourceRoot":"","sources":["../../src/core/execution-model-router.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n\ttype Api,\n\tcontainsKnownEnvCredential,\n\tgetSupportedThinkingLevels,\n\tisModelType,\n\ttype Model,\n} from \"@bastani/pi-ai\";\nimport { Type } from \"typebox\";\nimport { getDocsPath } from \"../config.js\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport {\n\teligiblePair,\n\ttype ModelConstraints,\n\ttype ModelRouterOutput,\n\tparseModelConstraints,\n} from \"./model-routing-constraints.js\";\nimport {\n\tjsonBytes,\n\tMODEL_ROUTING_TASK_BYTES,\n\tmodelRoutingTask,\n\tROUTING_REQUEST_BYTES,\n\tTRUNCATED_MARKER,\n\ttruncateToBytes,\n} from \"./model-routing-task.js\";\nimport { resolveRouterModel, routeModel } from \"./structured-output/index.js\";\n\nexport interface ModelRoutingContext {\n\treadonly modelRegistry: Pick<\n\t\tModelRegistry,\n\t\t\"getAll\" | \"getAvailable\" | \"streamSimple\" | \"containsConfiguredCredential\"\n\t> &\n\t\tPartial<Pick<ModelRegistry, \"getProviderAuthStatus\" | \"getProviderAuth\" | \"getClassifierModel\" | \"classify\">>;\n\treadonly model?: Model<Api>;\n\tgetRouterModel(): string;\n}\nexport interface ModelRoute {\n\treadonly routerSelection: ModelRouterOutput;\n\treadonly modelOverride: string;\n\treadonly fallbackModels?: readonly string[];\n\tassertCurrent(): void;\n\tallowsModel(model: Model<Api>, effort?: string): boolean;\n}\n\ninterface RouterWireDecision {\n\tmodelId: string;\n\treasoningEffort: string | null;\n}\n\n/**\n * Total auto-routing inference failure: Jev and the chat structured-output\n * fallback both failed before any model was selected (#3206). Validation,\n * eligibility, and credential-screening errors are never marked.\n */\nexport class AutoRoutingInferenceError extends Error {\n\t/**\n\t * Route pinned to the current chat model, present only when that model is\n\t * available and satisfies every routing constraint. Consumers may degrade\n\t * to it; when it is absent the failure stays fatal.\n\t */\n\treadonly currentModelRoute?: ModelRoute;\n\n\tconstructor(message: string, currentModelRoute?: ModelRoute) {\n\t\tsuper(message);\n\t\tthis.name = \"AutoRoutingInferenceError\";\n\t\tif (currentModelRoute !== undefined) this.currentModelRoute = currentModelRoute;\n\t}\n}\n\n/** Degraded routes keep a balanced effort when the constraints leave a choice. */\nconst CURRENT_MODEL_EFFORT_PREFERENCE: readonly (string | null)[] = [\n\tnull,\n\t\"medium\",\n\t\"high\",\n\t\"low\",\n\t\"xhigh\",\n\t\"minimal\",\n\t\"max\",\n\t\"off\",\n];\nconst instructions =\n\t\"Select one eligible model/effort pair for `task` and `agent` from the supplied Choice criteria, using `evals` as evidence and `model_selection_guide` as policy. Match the agent role to the guide's model cost tier and thinking level first, then consider task fit, measured effort, dates, caveats and cost. Evals cannot add candidates or bypass constraints. Return exactly modelId and reasoningEffort; null reasoningEffort means no configurable reasoning.\";\n\n/** Static selection policy sent with every auto-routing request alongside the dated `evals` evidence. */\nexport const MODEL_SELECTION_GUIDE = `## Benchmarks are evidence, not policy\n\nBenchmark results are measurements under named harnesses, dates, models, efforts, agents, tools, prompts, prices, and scoring rules. Treat a bracketed effort level as the measurement configuration for that row, not a command to run every task at that effort. Compare only records whose measured setup resembles the decision at hand, and keep unmeasured work under ordinary validation rather than inheriting a score.\n\nMissing evidence is unknown, not zero. A rounded lead is not proof of significance. A result for one provider, model version, effort, agent, fallback setting, or benchmark harness does not transfer to another identity.\n\n## Role-based thinking effort\n\nUse these starting defaults unless the user requests a level. Higher effort can improve hard reasoning, but it also costs more and can be slower. \\`max\\` is an exception, not a default.\n\nPrice is per task. Candidate cost is USD per million tokens, and roles differ in token volume and in what a mistake costs. High-volume, tool-checked roles such as exploration and routine implementation default to cheaper, faster models; roles where a missed defect is expensive, such as review, verification, and final approval, justify frontier models at high effort. Pick the tier first, then the effort within it; do not compensate for a cheap model with \\`max\\` or for an expensive one with \\`minimal\\`.\n\n| Stage role | Default thinking level | Model cost tier | Why |\n| --- | --- | --- | --- |\n| Codebase exploration: locating files, reading code, tracing call sites | \\`minimal\\` or \\`low\\` | Cheap, fast | Tool-driven lookups need speed, not deliberation; escalate to mapping or analysis only when the question becomes a design judgement. |\n| Coding, implementation, routine fixes | \\`low\\` or \\`medium\\` | Cheap or mid-priced | Runs many times per task and is validated by tools and review afterwards. |\n| Code review, test design, failure analysis, security, identity, adversarial challenge, final approval | \\`high\\` or \\`xhigh\\` | Frontier | A missed defect is the expensive outcome; spend the strongest model and reasoning here. |\n| Codebase mapping, lifecycle analysis, compatibility, planning, synthesis, triage | \\`high\\` | Frontier or mid-priced | Resolve ambiguity before downstream work depends on it. |\n| Orchestration, delegation, and multi-stage coordination | \\`medium\\` or \\`high\\` | Mid-priced | Judge scope, sequence work, and integrate results without re-deriving what delegated stages already verified. |\n| User-impact review and final reporting | \\`medium\\` | Mid-priced | Preserve evidence and communicate clearly without unnecessary reasoning. |\n| Deterministic checks | No model call | — | Run tests, typechecks, probes, and scripts directly. |\n\nAn explicit user request wins over these defaults, but the requested level must exist for the selected catalog entry. Do not invent unsupported suffixes. If \\`xhigh\\` is unavailable, use \\`high\\` rather than automatically promoting to \\`max\\`; choose another catalog model or leave the stage unpinned if neither fits.\n`;\n\n// Keeps the shipped evals snapshot small enough to reach Jev intact alongside the\n// guide, a full task excerpt, and a small candidate question. Larger candidate\n// questions truncate the routing copy instead; see fitRoutingState.\nexport const MODEL_SELECTION_EVALS_JSON_BYTES = 14_200;\nconst EVALS_BUDGET_ERROR = `Auto routing requires a nonempty evals.md document within ${MODEL_SELECTION_EVALS_JSON_BYTES.toLocaleString(\"en-US\")} JSON-encoded bytes. Repair the Atomic installation or select a concrete execution model.`;\n// Decision policy, key names and JSON framing the classifier transport adds.\nconst ROUTING_WIRE_OVERHEAD_BYTES = 1_000;\nconst PAIR_QUESTION =\n\t\"Which eligible model and reasoning effort best suit this task and agent role, considering the model_selection_guide role tiers, evals, and candidate capabilities and prices? Prefer cheaper candidates for exploration and routine implementation and stronger ones for review and verification. Candidate cost is USD per million tokens, not benchmark task cost.\";\n\ntype RoutingState = {\n\ttask: string;\n\tagent: { name: string; description: string };\n\tevals: string;\n\tmodel_selection_guide: string;\n};\n\n/**\n * Truncate the routing copy of the task, then evals, so state plus the candidate\n * question fits ROUTING_REQUEST_BYTES. Candidates, agent and guide stay intact.\n */\nfunction fitRoutingState(state: RoutingState, criteria: Record<string, string>): RoutingState {\n\tconst fixed =\n\t\tBuffer.byteLength(\n\t\t\tJSON.stringify({ ...state, task: \"\", evals: \"\", instructions, question: PAIR_QUESTION, criteria }),\n\t\t\t\"utf8\",\n\t\t) + ROUTING_WIRE_OVERHEAD_BYTES;\n\tconst room = Math.max(0, ROUTING_REQUEST_BYTES - fixed);\n\tconst evalsMarkerBytes = jsonBytes(TRUNCATED_MARKER);\n\tconst task = modelRoutingTask(state.task, Math.max(0, Math.min(MODEL_ROUTING_TASK_BYTES, room - evalsMarkerBytes)));\n\tconst evals = truncateToBytes(state.evals, Math.max(evalsMarkerBytes, room - jsonBytes(task)));\n\treturn { ...state, task, evals };\n}\n\nasync function readModelSelectionEvals(signal?: AbortSignal): Promise<string> {\n\ttry {\n\t\tconst evals = await readFile(join(getDocsPath(), \"models\", \"evals.md\"), { encoding: \"utf8\", signal });\n\t\tif (!evals.trim() || jsonBytes(evals) > MODEL_SELECTION_EVALS_JSON_BYTES) throw new Error(EVALS_BUDGET_ERROR);\n\t\treturn evals;\n\t} catch (error) {\n\t\tsignal?.throwIfAborted();\n\t\tif (error instanceof Error && error.message === EVALS_BUDGET_ERROR) throw error;\n\t\tthrow new Error(EVALS_BUDGET_ERROR);\n\t}\n}\n\nexport async function routeExecutionModel(input: {\n\tctx: ModelRoutingContext;\n\ttask: string;\n\tagent: { name: string; description: string };\n\tconstraints?: readonly ModelConstraints[];\n\tsignal?: AbortSignal;\n\t/** Restore a recorded decision without another inference call. */\n\tselection?: ModelRouterOutput;\n}): Promise<ModelRoute> {\n\tconst { ctx, signal } = input;\n\tsignal?.throwIfAborted();\n\tconst constraints = structuredClone((input.constraints ?? []).map((c) => parseModelConstraints(c)!));\n\tconst catalog = () =>\n\t\tctx.modelRegistry\n\t\t\t.getAvailable()\n\t\t\t.filter((model) => isModelType(model, \"chat\"))\n\t\t\t.map((model) => ({\n\t\t\t\tmodel,\n\t\t\t\tpairs: (model.reasoning ? getSupportedThinkingLevels(model) : [null])\n\t\t\t\t\t.map((effort) => ({ model: `${model.provider}/${model.id}`, effort }))\n\t\t\t\t\t.filter((pair) => eligiblePair(model, pair, constraints)),\n\t\t\t}))\n\t\t\t.filter((entry) => entry.pairs.length > 0);\n\tconst available = catalog();\n\tconst pairs = available.flatMap((entry) => entry.pairs);\n\tif (!pairs.length)\n\t\tthrow new Error(\n\t\t\t\"Auto routing has no eligible model/effort pairs. Check configured providers and modelConstraints.\",\n\t\t);\n\tlet selection = input.selection;\n\tif (selection === undefined) {\n\t\tconst settings = { getRouterModel: () => ctx.getRouterModel() };\n\t\tresolveRouterModel({ settings, currentModel: ctx.model, modelRegistry: ctx.modelRegistry });\n\t\tconst candidates = available.flatMap(({ model, pairs }) =>\n\t\t\tpairs.map((pair) => ({\n\t\t\t\t...pair,\n\t\t\t\tinput: model.input,\n\t\t\t\tcontextWindow: model.contextWindow,\n\t\t\t\tcost: { ...model.cost, tiers: (model.cost.tiers ?? []).map((tier) => ({ ...tier })) },\n\t\t\t})),\n\t\t);\n\t\tconst allCriteria = Object.fromEntries(\n\t\t\tcandidates.map((candidate, index) => [`pair_${index}`, JSON.stringify(candidate)]),\n\t\t);\n\t\tconst state = {\n\t\t\ttask: input.task,\n\t\t\tagent: { name: input.agent.name, description: input.agent.description },\n\t\t\tevals: await readModelSelectionEvals(signal),\n\t\t\tmodel_selection_guide: MODEL_SELECTION_GUIDE,\n\t\t};\n\t\tif (!state.task.trim()) throw new Error(\"Auto routing requires task instructions.\");\n\t\tconst serialized = JSON.stringify({ state, criteria: allCriteria, constraints });\n\t\tlet configuredCredential: boolean;\n\t\ttry {\n\t\t\tconfiguredCredential = await ctx.modelRegistry.containsConfiguredCredential(serialized);\n\t\t} catch {\n\t\t\tthrow new Error(\"Auto routing could not screen configured credentials. No inference was performed.\");\n\t\t}\n\t\tif (\n\t\t\tcontainsKnownEnvCredential(serialized) ||\n\t\t\tconfiguredCredential ||\n\t\t\t/\\bBearer\\s+[A-Za-z0-9._~+/-]{8,}|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----|\\b(?:sk|ghp|github_pat)[-_][A-Za-z0-9_-]{16,}/i.test(\n\t\t\t\tserialized,\n\t\t\t)\n\t\t)\n\t\t\tthrow new Error(\"Auto routing context contains credential material. Remove secrets before retrying.\");\n\t\t// Screen the full task first, even credentials in text the router will omit.\n\t\t// Truncation only affects the routing request, never the execution prompt,\n\t\t// so it is not surfaced to the user.\n\t\tconst ranked: ModelRouterOutput[] = [];\n\t\t// Degrade only to a current chat model that is available and eligible under\n\t\t// the same constraints, restored through the normal selection path (#3206).\n\t\tconst currentModelRoute = async (): Promise<ModelRoute | undefined> => {\n\t\t\tconst current = ctx.model;\n\t\t\tconst entry = available.find(\n\t\t\t\t(candidate) => candidate.model.provider === current?.provider && candidate.model.id === current?.id,\n\t\t\t);\n\t\t\tconst pair = CURRENT_MODEL_EFFORT_PREFERENCE.map((effort) =>\n\t\t\t\tentry?.pairs.find((candidate) => candidate.effort === effort),\n\t\t\t).find((candidate) => candidate !== undefined);\n\t\t\tif (pair === undefined) return undefined;\n\t\t\treturn routeExecutionModel({ ...input, selection: { model: pair.model, effort: pair.effort } });\n\t\t};\n\t\t// Rank by repeated bounded choices, excluding all efforts of earlier models.\n\t\t// Probabilities from separate tournament batches are not comparable.\n\t\t// Only a failure before the primary is chosen is a total inference failure:\n\t\t// Jev and its chat structured-output fallback both failed (#3206). A failed\n\t\t// optional fallback-ranking pass keeps the models already ranked.\n\t\twhile (ranked.length < Math.min(3, available.length)) {\n\t\t\tconst remaining = pairs.filter((pair) => !ranked.some((selected) => selected.model === pair.model));\n\t\t\tif (!remaining.length) break;\n\t\t\tconst criteria = Object.fromEntries(\n\t\t\t\tremaining.map((pair) => {\n\t\t\t\t\tconst key = `pair_${pairs.indexOf(pair)}`;\n\t\t\t\t\treturn [key, allCriteria[key]];\n\t\t\t\t}),\n\t\t\t);\n\t\t\t// Strict Responses providers reject object unions, and Anthropic rejects an\n\t\t\t// enum under a type array. Enumerate scalar values on the wire with one\n\t\t\t// declared type per enum, then verify the exact model/effort relation.\n\t\t\tconst efforts = [...new Set(remaining.map((pair) => pair.effort))];\n\t\t\tconst stringEfforts = efforts.filter((effort) => effort !== null);\n\t\t\tconst stringEffortSchema = { type: \"string\", enum: stringEfforts };\n\t\t\tconst reasoningEffort = !efforts.includes(null)\n\t\t\t\t? stringEffortSchema\n\t\t\t\t: stringEfforts.length\n\t\t\t\t\t? { anyOf: [stringEffortSchema, { type: \"null\" }] }\n\t\t\t\t\t: { type: \"null\" };\n\t\t\tconst schema = Type.Unsafe<RouterWireDecision>({\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tmodelId: Type.String({ enum: [...new Set(remaining.map((pair) => pair.model))] }),\n\t\t\t\t\treasoningEffort,\n\t\t\t\t},\n\t\t\t\trequired: [\"modelId\", \"reasoningEffort\"],\n\t\t\t\tadditionalProperties: false,\n\t\t\t});\n\t\t\tlet result: Awaited<ReturnType<typeof routeModel<typeof schema>>>;\n\t\t\ttry {\n\t\t\t\tresult = await routeModel(\n\t\t\t\t\t{\n\t\t\t\t\t\tsettings,\n\t\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t\t\tcurrentModel: ctx.model,\n\t\t\t\t\t\tstate: fitRoutingState(state, criteria),\n\t\t\t\t\t\tinstructions,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tclassifier: {\n\t\t\t\t\t\t\tquestions: {\n\t\t\t\t\t\t\t\tpair: {\n\t\t\t\t\t\t\t\t\tinstructions: PAIR_QUESTION,\n\t\t\t\t\t\t\t\t\tcriteria,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tdecode: (choices) => {\n\t\t\t\t\t\t\t\tconst pair = pairs[Number(choices.pair?.replace(/^pair_/, \"\"))];\n\t\t\t\t\t\t\t\tif (!pair || choices.pair !== `pair_${pairs.indexOf(pair)}`)\n\t\t\t\t\t\t\t\t\tthrow new Error(\"Invalid execution model Choice.\");\n\t\t\t\t\t\t\t\treturn { modelId: pair.model, reasoningEffort: pair.effort };\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t},\n\t\t\t\t\t(value) =>\n\t\t\t\t\t\tremaining.some((pair) => pair.model === value.modelId && pair.effort === value.reasoningEffort),\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tif (ranked.length > 0) break;\n\t\t\t\tthrow new AutoRoutingInferenceError(\n\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\tawait currentModelRoute().catch(() => undefined),\n\t\t\t\t);\n\t\t\t}\n\t\t\tranked.push({ model: result.value.modelId, effort: result.value.reasoningEffort });\n\t\t}\n\t\tselection = { ...ranked[0]!, ...(ranked.length > 1 ? { fallbacks: ranked.slice(1) } : {}) };\n\t}\n\tconst fallbacks = selection.fallbacks?.map((pair) => Object.freeze({ model: pair.model, effort: pair.effort }));\n\tif (\n\t\tfallbacks &&\n\t\t(fallbacks.length > 2 ||\n\t\t\tnew Set([selection.model, ...fallbacks.map((pair) => pair.model)]).size !== fallbacks.length + 1)\n\t)\n\t\tthrow new Error(\"Invalid ranked auto selection: expected up to three distinct models.\");\n\tconst routerSelection = Object.freeze({\n\t\tmodel: selection.model,\n\t\teffort: selection.effort,\n\t\t...(fallbacks?.length ? { fallbacks: Object.freeze(fallbacks) } : {}),\n\t});\n\tconst hasPair = (pair: ModelRouterOutput) =>\n\t\tcatalog().some((entry) => entry.pairs.some((p) => p.model === pair.model && p.effort === pair.effort));\n\tconst allowsModel = (model: Model<Api>, effort?: string): boolean => {\n\t\tconst levels = model.reasoning ? getSupportedThinkingLevels(model) : [null];\n\t\treturn levels.some((level) => {\n\t\t\tif (model.reasoning && effort !== undefined && level !== effort) return false;\n\t\t\tconst pair = { model: `${model.provider}/${model.id}`, effort: level };\n\t\t\treturn eligiblePair(model, pair, constraints) && hasPair(pair);\n\t\t});\n\t};\n\tconst assertCurrent = () => {\n\t\tsignal?.throwIfAborted();\n\t\tif (![routerSelection, ...(routerSelection.fallbacks ?? [])].every(hasPair))\n\t\t\tthrow new Error(\"Auto selection is no longer eligible. Retry explicitly with the current catalog.\");\n\t};\n\tassertCurrent();\n\treturn {\n\t\trouterSelection,\n\t\tmodelOverride: routerSelection.model + (routerSelection.effort === null ? \"\" : `:${routerSelection.effort}`),\n\t\tfallbackModels: Object.freeze(\n\t\t\t(routerSelection.fallbacks ?? []).map((pair) => pair.model + (pair.effort === null ? \"\" : `:${pair.effort}`)),\n\t\t),\n\t\tassertCurrent,\n\t\tallowsModel,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"execution-model-router.js","sourceRoot":"","sources":["../../src/core/execution-model-router.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n\ttype Api,\n\tcontainsKnownEnvCredential,\n\tgetSupportedThinkingLevels,\n\tisModelType,\n\ttype Model,\n} from \"@bastani/pi-ai\";\nimport { Type } from \"typebox\";\nimport { getDocsPath } from \"../config.js\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { ROUTING_REQUEST_BYTES } from \"./model-routing-bytes.js\";\nimport {\n\ttype CandidateModel,\n\tdescribeOption,\n\tdistinctTop,\n\ttype RankedCandidate,\n\trankCandidates,\n} from \"./model-routing-candidates.js\";\nimport {\n\teligiblePair,\n\ttype ModelConstraints,\n\ttype ModelRouterOutput,\n\tparseModelConstraints,\n} from \"./model-routing-constraints.js\";\nimport { reportModelRoutingDebug } from \"./model-routing-debug.js\";\nimport { parseEvalsCatalog } from \"./model-routing-evals.js\";\nimport {\n\teffortForDifficulty,\n\tmissingNeedsQuestions,\n\tparseTaskNeeds,\n\ttype ResolvedTaskNeeds,\n\tresolveTaskNeeds,\n\ttype TaskNeeds,\n} from \"./model-routing-needs.js\";\nimport type { ModelRoutingSettings } from \"./settings-types.ts\";\nimport { resolveRouterModel, routeModel } from \"./structured-output/index.js\";\n\nexport interface ModelRoutingContext {\n\treadonly modelRegistry: Pick<\n\t\tModelRegistry,\n\t\t\"getAll\" | \"getAvailable\" | \"streamSimple\" | \"containsConfiguredCredential\"\n\t> &\n\t\tPartial<Pick<ModelRegistry, \"getProviderAuthStatus\" | \"getProviderAuth\" | \"getClassifierModel\" | \"classify\">>;\n\treadonly model?: Model<Api>;\n\tgetRouterModel(): string;\n\t/** Provider filters from settings.json `modelRouting`; absent means every provider is a candidate. */\n\tgetModelRouting?(): ModelRoutingSettings;\n}\nexport interface ModelRoute {\n\treadonly routerSelection: ModelRouterOutput;\n\treadonly modelOverride: string;\n\treadonly fallbackModels?: readonly string[];\n\tassertCurrent(): void;\n\tallowsModel(model: Model<Api>, effort?: string): boolean;\n}\n\n/**\n * Total auto-routing inference failure: Jev and the chat structured-output\n * fallback both failed before any model was selected (#3206). Validation,\n * eligibility, and credential-screening errors are never marked.\n */\nexport class AutoRoutingInferenceError extends Error {\n\t/**\n\t * Route pinned to the current chat model, present only when that model is\n\t * available and satisfies every routing constraint. Consumers may degrade\n\t * to it; when it is absent the failure stays fatal.\n\t */\n\treadonly currentModelRoute?: ModelRoute;\n\n\tconstructor(message: string, currentModelRoute?: ModelRoute) {\n\t\tsuper(message);\n\t\tthis.name = \"AutoRoutingInferenceError\";\n\t\tif (currentModelRoute !== undefined) this.currentModelRoute = currentModelRoute;\n\t}\n}\n\n/** Degraded routes keep a balanced effort when the constraints leave a choice. */\nconst CURRENT_MODEL_EFFORT_PREFERENCE: readonly (string | null)[] = [\n\tnull,\n\t\"medium\",\n\t\"high\",\n\t\"low\",\n\t\"xhigh\",\n\t\"minimal\",\n\t\"max\",\n\t\"off\",\n];\n/** Most options the router compares when it builds the shortlist itself. */\nconst SHORTLIST_SIZE = 6;\n/** Conservative bytes per token when checking that a task fits a reader's context window. */\nconst BYTES_PER_TOKEN = 3;\n/** Room in the reader's window for the questions, instructions and its answer. */\nconst READER_OVERHEAD_TOKENS = 4_000;\n/** Jev accepts at most this many options in one Choice question. */\nconst MAX_CHOICE_OPTIONS = 255;\n\n/** A task that must hold this much in context prefers models whose window is at least this large. */\nconst LONG_CONTEXT_TOKENS = 400_000;\n\nconst NEEDS_INSTRUCTIONS =\n\t\"Read `task`, which `agent` will perform, and answer each field: what kind of work it is, how hard it is, how costly a mistake would be, whether it needs screenshots or images, whether it must hold a very large amount of material in context, and whether speed matters more than extra reasoning. Each field's description lists its options. Judge the task as written; `caller_says` lists answers the caller already gave.\";\nconst CHOICE_INSTRUCTIONS =\n\t\"Pick the model that should do the task `agent` will perform. The task's needs are in `needs`; the task itself is not shown. Each option lists the model's release date, price tier and prices, whether it reads images, and its results for this kind of work and overall; standings compare it with every eligible model. For demanding or high-stakes tasks prefer the best-proven model; for easy, low-stakes tasks prefer an adequate cheaper one. Prefer newer models over older ones with similar results. Missing results are unknown, not weak.\";\n\nconst EVALS_BUDGET_ERROR =\n\t\"Auto routing requires a nonempty evals.md document. Repair the Atomic installation or select a concrete execution model.\";\n\nasync function readModelSelectionEvals(signal?: AbortSignal): Promise<string> {\n\ttry {\n\t\tconst evals = await readFile(join(getDocsPath(), \"models\", \"evals.md\"), { encoding: \"utf8\", signal });\n\t\tif (!evals.trim()) throw new Error(EVALS_BUDGET_ERROR);\n\t\treturn evals;\n\t} catch (error) {\n\t\tsignal?.throwIfAborted();\n\t\tif (error instanceof Error && error.message === EVALS_BUDGET_ERROR) throw error;\n\t\tthrow new Error(EVALS_BUDGET_ERROR);\n\t}\n}\n\nexport async function routeExecutionModel(input: {\n\tctx: ModelRoutingContext;\n\ttask: string;\n\tagent: { name: string; description: string };\n\tconstraints?: readonly ModelConstraints[];\n\tsignal?: AbortSignal;\n\t/** Restore a recorded decision without another inference call. */\n\tselection?: ModelRouterOutput;\n\t/** What the caller already knows about the task; routing asks the router only for the rest. */\n\ttaskNeeds?: TaskNeeds;\n\t/**\n\t * True when this call's own constraints set a provider list because the user\n\t * asked. Only then are the settings.json provider lists replaced; provider\n\t * lists from agent definitions or inherited workflow constraints restrict\n\t * candidates on top of the settings and never lift a user's exclusion.\n\t */\n\toverrideProviderSettings?: boolean;\n}): Promise<ModelRoute> {\n\tconst { ctx, signal } = input;\n\tsignal?.throwIfAborted();\n\tconst constraints = structuredClone((input.constraints ?? []).map((c) => parseModelConstraints(c)!));\n\tconst statedNeeds = parseTaskNeeds(input.taskNeeds);\n\t// settings.json provider lists are defaults that only the call itself may\n\t// replace; every constraint's own provider lists are enforced by eligiblePair.\n\tconst { allowedProviders = [], excludedProviders = [] } = input.overrideProviderSettings\n\t\t? {}\n\t\t: (ctx.getModelRouting?.() ?? {});\n\tconst providerPermitted = (provider: string) =>\n\t\t(allowedProviders.length === 0 || allowedProviders.includes(provider)) && !excludedProviders.includes(provider);\n\tconst catalog = () =>\n\t\tctx.modelRegistry\n\t\t\t.getAvailable()\n\t\t\t.filter((model) => isModelType(model, \"chat\") && providerPermitted(model.provider))\n\t\t\t.map((model) => ({\n\t\t\t\tmodel,\n\t\t\t\tpairs: (model.reasoning ? getSupportedThinkingLevels(model) : [null])\n\t\t\t\t\t.map((effort) => ({ model: `${model.provider}/${model.id}`, effort }))\n\t\t\t\t\t.filter((pair) => eligiblePair(model, pair, constraints)),\n\t\t\t}))\n\t\t\t.filter((entry) => entry.pairs.length > 0);\n\tconst available = catalog();\n\tconst pairs = available.flatMap((entry) => entry.pairs);\n\tif (!pairs.length)\n\t\tthrow new Error(\n\t\t\tallowedProviders.length || excludedProviders.length\n\t\t\t\t? \"Auto routing has no eligible model/effort pairs. Check configured providers, modelConstraints, and the modelRouting allowedProviders/excludedProviders settings.\"\n\t\t\t\t: \"Auto routing has no eligible model/effort pairs. Check configured providers and modelConstraints.\",\n\t\t);\n\tlet selection = input.selection;\n\tif (selection === undefined) {\n\t\tconst settings = { getRouterModel: () => ctx.getRouterModel() };\n\t\tconst router = resolveRouterModel({ settings, currentModel: ctx.model, modelRegistry: ctx.modelRegistry });\n\t\tif (!input.task.trim()) throw new Error(\"Auto routing requires task instructions.\");\n\t\tconst stated = statedNeeds;\n\t\tconst agent = { name: input.agent.name, description: input.agent.description };\n\t\t// Screen the task before any model reads it.\n\t\tconst serialized = JSON.stringify({ task: input.task, agent, stated, constraints });\n\t\tlet configuredCredential: boolean;\n\t\ttry {\n\t\t\tconfiguredCredential = await ctx.modelRegistry.containsConfiguredCredential(serialized);\n\t\t} catch {\n\t\t\tthrow new Error(\"Auto routing could not screen configured credentials. No inference was performed.\");\n\t\t}\n\t\tif (\n\t\t\tcontainsKnownEnvCredential(serialized) ||\n\t\t\tconfiguredCredential ||\n\t\t\t/\\bBearer\\s+[A-Za-z0-9._~+/-]{8,}|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----|\\b(?:sk|ghp|github_pat)[-_][A-Za-z0-9_-]{16,}/i.test(\n\t\t\t\tserialized,\n\t\t\t)\n\t\t)\n\t\t\tthrow new Error(\"Auto routing context contains credential material. Remove secrets before retrying.\");\n\t\tconst task = input.task;\n\t\t// Degrade only to a current chat model that is available and eligible under\n\t\t// the same constraints, restored through the normal selection path (#3206).\n\t\tconst currentModelRoute = async (): Promise<ModelRoute | undefined> => {\n\t\t\tconst current = ctx.model;\n\t\t\tconst entry = available.find(\n\t\t\t\t(candidate) => candidate.model.provider === current?.provider && candidate.model.id === current?.id,\n\t\t\t);\n\t\t\tconst pair = CURRENT_MODEL_EFFORT_PREFERENCE.map((effort) =>\n\t\t\t\tentry?.pairs.find((candidate) => candidate.effort === effort),\n\t\t\t).find((candidate) => candidate !== undefined);\n\t\t\tif (pair === undefined) return undefined;\n\t\t\treturn routeExecutionModel({ ...input, selection: { model: pair.model, effort: pair.effort } });\n\t\t};\n\t\t// Any failure before a model is chosen is a total inference failure: the\n\t\t// router and its chat structured-output fallback both failed (#3206).\n\t\tconst infer = async <T>(decide: () => Promise<T>): Promise<T> => {\n\t\t\ttry {\n\t\t\t\treturn await decide();\n\t\t\t} catch (error) {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tthrow new AutoRoutingInferenceError(\n\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\tawait currentModelRoute().catch(() => undefined),\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t\t// Where code cannot pick a model either, the current chat model takes over\n\t\t// the same way.\n\t\tconst fallBackToCurrentModel = async (message: string): Promise<never> => {\n\t\t\tthrow new AutoRoutingInferenceError(message, await currentModelRoute().catch(() => undefined));\n\t\t};\n\t\tconst catalogEvals = await readModelSelectionEvals(signal)\n\t\t\t.then(parseEvalsCatalog)\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\treturn fallBackToCurrentModel(error instanceof Error ? error.message : String(error));\n\t\t\t});\n\n\t\t// Step 1: a chat model reads the task and answers only what the caller did\n\t\t// not state. A classifier router such as Jev never receives the task.\n\t\tconst questions = missingNeedsQuestions(stated);\n\t\tlet answers: Record<string, string> = {};\n\t\tif (questions.length) {\n\t\t\tconst current = ctx.model;\n\t\t\tconst preferred =\n\t\t\t\trouter.kind === \"chat\"\n\t\t\t\t\t? router.model\n\t\t\t\t\t: current && current.id !== \"auto\" && isModelType(current, \"chat\")\n\t\t\t\t\t\t? current\n\t\t\t\t\t\t: undefined;\n\t\t\t// The reader gets the complete task. When the task is larger than the\n\t\t\t// preferred reader's window, the cheapest eligible model that holds it\n\t\t\t// reads instead; eligible models are the ones allowed to receive the task.\n\t\t\tconst taskTokens = Math.ceil(Buffer.byteLength(task, \"utf8\") / BYTES_PER_TOKEN) + READER_OVERHEAD_TOKENS;\n\t\t\tconst holdsTask = (model: Model<Api>) => model.contextWindow >= taskTokens;\n\t\t\tconst readerModel =\n\t\t\t\tpreferred && holdsTask(preferred)\n\t\t\t\t\t? preferred\n\t\t\t\t\t: (available\n\t\t\t\t\t\t\t.map((entry) => entry.model)\n\t\t\t\t\t\t\t.filter(holdsTask)\n\t\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t\t(a, b) =>\n\t\t\t\t\t\t\t\t\ta.cost.input - b.cost.input ||\n\t\t\t\t\t\t\t\t\t`${a.provider}/${a.id}`.localeCompare(`${b.provider}/${b.id}`),\n\t\t\t\t\t\t\t)[0] ?? preferred);\n\t\t\tconst reader = readerModel ? `${readerModel.provider}/${readerModel.id}` : undefined;\n\t\t\tif (reader === undefined)\n\t\t\t\tawait fallBackToCurrentModel(\n\t\t\t\t\t\"Auto routing needs a chat model to read the task. Select a chat model or state every taskNeeds field.\",\n\t\t\t\t);\n\t\t\tconst schema = Type.Object(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tquestions.map((question) => [\n\t\t\t\t\t\tquestion.id,\n\t\t\t\t\t\tType.String({\n\t\t\t\t\t\t\tenum: Object.keys(question.criteria),\n\t\t\t\t\t\t\tdescription: `${question.instructions} ${Object.entries(question.criteria)\n\t\t\t\t\t\t\t\t.map(([key, meaning]) => `${key}: ${meaning}`)\n\t\t\t\t\t\t\t\t.join(\"; \")}.`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t]),\n\t\t\t\t),\n\t\t\t\t{ additionalProperties: false },\n\t\t\t);\n\t\t\tconst callerSays = stated\n\t\t\t\t? {\n\t\t\t\t\t\t...(stated.work ? { work: stated.work } : {}),\n\t\t\t\t\t\t...(stated.difficulty ? { difficulty: stated.difficulty } : {}),\n\t\t\t\t\t\t...(stated.mistakeCost ? { mistake_cost: stated.mistakeCost } : {}),\n\t\t\t\t\t\t...(stated.needsImages !== undefined ? { needs_images: stated.needsImages ? \"yes\" : \"no\" } : {}),\n\t\t\t\t\t\t...(stated.longContext !== undefined ? { long_context: stated.longContext ? \"yes\" : \"no\" } : {}),\n\t\t\t\t\t\t...(stated.latencySensitive !== undefined\n\t\t\t\t\t\t\t? { latency_sensitive: stated.latencySensitive ? \"yes\" : \"no\" }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\t\tconst result = await infer(() =>\n\t\t\t\trouteModel(\n\t\t\t\t\t{\n\t\t\t\t\t\tsettings: { getRouterModel: () => reader! },\n\t\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t\t\tcurrentModel: ctx.model,\n\t\t\t\t\t\tstate: {\n\t\t\t\t\t\t\ttask,\n\t\t\t\t\t\t\tagent,\n\t\t\t\t\t\t\t...(callerSays && Object.keys(callerSays).length ? { caller_says: callerSays } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tinstructions: NEEDS_INSTRUCTIONS,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\t// Unused: the reader always resolves to a chat model.\n\t\t\t\t\t\tclassifier: {\n\t\t\t\t\t\t\tquestions: Object.fromEntries(\n\t\t\t\t\t\t\t\tquestions.map((question) => [\n\t\t\t\t\t\t\t\t\tquestion.id,\n\t\t\t\t\t\t\t\t\t{ instructions: question.instructions, criteria: question.criteria },\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tdecode: (choices) =>\n\t\t\t\t\t\t\t\tObject.fromEntries(questions.map((question) => [question.id, choices[question.id]!])),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t},\n\t\t\t\t\t(value) => questions.every((question) => Object.hasOwn(question.criteria, String(value[question.id]))),\n\t\t\t\t),\n\t\t\t);\n\t\t\tanswers = result.value;\n\t\t}\n\t\tconst needs: ResolvedTaskNeeds = resolveTaskNeeds(stated, answers);\n\n\t\t// Step 2: narrow in code. A task that needs images only goes to models that read them.\n\t\tconst seeing = needs.needsImages ? available.filter((entry) => entry.model.input.includes(\"image\")) : available;\n\t\tif (seeing.length === 0)\n\t\t\tawait fallBackToCurrentModel(\n\t\t\t\t\"Auto routing: this task needs a model that can read images, and no eligible model can. Allow an image-capable model or select a concrete execution model.\",\n\t\t\t);\n\t\t// A large context window is preferred, not required: context size is a matter of degree.\n\t\tconst roomy = needs.longContext\n\t\t\t? seeing.filter((entry) => entry.model.contextWindow >= LONG_CONTEXT_TOKENS)\n\t\t\t: seeing;\n\t\tconst usable = roomy.length ? roomy : seeing;\n\t\tconst pairsFor = new Map(usable.map((entry) => [`${entry.model.provider}/${entry.model.id}`, entry.pairs]));\n\t\tconst toCandidate = (model: Model<Api>): CandidateModel => ({\n\t\t\tmodel: `${model.provider}/${model.id}`,\n\t\t\tname: model.name,\n\t\t\tcost: model.cost,\n\t\t\tinput: model.input,\n\t\t\t...(model.fastRoute ? { fastRouteOf: `${model.provider}/${model.fastRoute.baseModelId}` } : {}),\n\t\t});\n\t\t// A caller that lists models (`allowedModels`) chooses the contenders itself,\n\t\t// but standings still compare them with every model the user could route to.\n\t\tconst callerListed = constraints.some((constraint) => (constraint.allowedModels?.length ?? 0) > 0);\n\t\tconst reference = callerListed\n\t\t\t? ctx.modelRegistry\n\t\t\t\t\t.getAvailable()\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(model) =>\n\t\t\t\t\t\t\tisModelType(model, \"chat\") &&\n\t\t\t\t\t\t\tproviderPermitted(model.provider) &&\n\t\t\t\t\t\t\t(!needs.needsImages || model.input.includes(\"image\")),\n\t\t\t\t\t)\n\t\t\t: usable.map((entry) => entry.model);\n\t\tconst standings = rankCandidates(catalogEvals, reference.map(toCandidate), needs);\n\t\tconst ranked = standings.filter((candidate) => pairsFor.has(candidate.model));\n\t\tconst choiceState = {\n\t\t\tagent,\n\t\t\tneeds: {\n\t\t\t\twork: needs.work,\n\t\t\t\tdifficulty: needs.difficulty,\n\t\t\t\tmistake_cost: needs.mistakeCost,\n\t\t\t\tneeds_images: needs.needsImages,\n\t\t\t\tlong_context: needs.longContext,\n\t\t\t\tlatency_sensitive: needs.latencySensitive,\n\t\t\t},\n\t\t};\n\t\tconst describeAll = (options: readonly RankedCandidate[]) =>\n\t\t\tObject.fromEntries(options.map((option, index) => [`m${index}`, describeOption(option, needs, standings)]));\n\t\tconst fitsOneChoice = (options: readonly RankedCandidate[]) =>\n\t\t\toptions.length <= MAX_CHOICE_OPTIONS &&\n\t\t\tBuffer.byteLength(\n\t\t\t\tJSON.stringify({ state: choiceState, instructions: CHOICE_INSTRUCTIONS, criteria: describeAll(options) }),\n\t\t\t\t\"utf8\",\n\t\t\t) <= ROUTING_REQUEST_BYTES;\n\t\t// A caller's list is offered whole when it fits one choice request; otherwise\n\t\t// duplicate routes of one model share a slot, and a list that still does not\n\t\t// fit falls back rather than silently dropping a contender.\n\t\tconst callerChoices = !callerListed || fitsOneChoice(ranked) ? ranked : distinctTop(ranked, ranked.length);\n\t\tif (callerListed && !fitsOneChoice(callerChoices))\n\t\t\tawait fallBackToCurrentModel(\n\t\t\t\t`Auto routing cannot compare ${callerChoices.length} different models from modelConstraints.allowedModels in one routing request. List fewer models.`,\n\t\t\t);\n\t\tconst shortlist = callerListed ? callerChoices : distinctTop(ranked, SHORTLIST_SIZE);\n\n\t\t// Step 3: the router picks one option; each carries its own evidence.\n\t\tlet chosen = shortlist[0]!;\n\t\tif (shortlist.length > 1) {\n\t\t\tconst keys = shortlist.map((_, index) => `m${index}`);\n\t\t\tconst schema = Type.Object(\n\t\t\t\t{ modelId: Type.String({ enum: shortlist.map((option) => option.model) }) },\n\t\t\t\t{ additionalProperties: false },\n\t\t\t);\n\t\t\tconst result = await infer(() =>\n\t\t\t\trouteModel(\n\t\t\t\t\t{\n\t\t\t\t\t\tsettings,\n\t\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t\t\tcurrentModel: ctx.model,\n\t\t\t\t\t\tstate: {\n\t\t\t\t\t\t\t...choiceState,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tinstructions: CHOICE_INSTRUCTIONS,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tclassifier: {\n\t\t\t\t\t\t\tquestions: {\n\t\t\t\t\t\t\t\tmodel: {\n\t\t\t\t\t\t\t\t\tinstructions: \"Which model should do this task?\",\n\t\t\t\t\t\t\t\t\tcriteria: describeAll(shortlist),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tdecode: (choices) => {\n\t\t\t\t\t\t\t\tconst option = shortlist[keys.indexOf(choices.model ?? \"\")];\n\t\t\t\t\t\t\t\tif (!option) throw new Error(\"Invalid execution model Choice.\");\n\t\t\t\t\t\t\t\treturn { modelId: option.model };\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t},\n\t\t\t\t\t(value) => shortlist.some((option) => option.model === value.modelId),\n\t\t\t\t),\n\t\t\t);\n\t\t\tchosen = shortlist.find((option) => option.model === result.value.modelId)!;\n\t\t}\n\n\t\t// Step 4: effort from difficulty; fallbacks are the next distinct models in code's ranking.\n\t\tconst effortOf = (model: string) =>\n\t\t\teffortForDifficulty(\n\t\t\t\t(pairsFor.get(model) ?? []).map((pair) => pair.effort),\n\t\t\t\tneeds.difficulty,\n\t\t\t\tneeds.latencySensitive,\n\t\t\t);\n\t\tconst fallbackModels = distinctTop(\n\t\t\tranked.filter((candidate) => candidate.baseKey !== chosen.baseKey),\n\t\t\t2,\n\t\t);\n\t\treportModelRoutingDebug(\n\t\t\t`Auto routing: ${needs.work}, ${needs.difficulty}, mistake cost ${needs.mistakeCost}, images ${needs.needsImages ? \"yes\" : \"no\"}, long context ${needs.longContext ? \"yes\" : \"no\"}, latency ${needs.latencySensitive ? \"sensitive\" : \"tolerant\"}; chose ${chosen.model} from ${shortlist.map((option) => option.model).join(\", \")}.`,\n\t\t);\n\t\tselection = {\n\t\t\tmodel: chosen.model,\n\t\t\teffort: effortOf(chosen.model),\n\t\t\t...(fallbackModels.length\n\t\t\t\t? {\n\t\t\t\t\t\tfallbacks: fallbackModels.map((candidate) => ({\n\t\t\t\t\t\t\tmodel: candidate.model,\n\t\t\t\t\t\t\teffort: effortOf(candidate.model),\n\t\t\t\t\t\t})),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\tconst fallbacks = selection.fallbacks?.map((pair) => Object.freeze({ model: pair.model, effort: pair.effort }));\n\tif (\n\t\tfallbacks &&\n\t\t(fallbacks.length > 2 ||\n\t\t\tnew Set([selection.model, ...fallbacks.map((pair) => pair.model)]).size !== fallbacks.length + 1)\n\t)\n\t\tthrow new Error(\"Invalid ranked auto selection: expected up to three distinct models.\");\n\tconst routerSelection = Object.freeze({\n\t\tmodel: selection.model,\n\t\teffort: selection.effort,\n\t\t...(fallbacks?.length ? { fallbacks: Object.freeze(fallbacks) } : {}),\n\t});\n\tconst hasPair = (pair: ModelRouterOutput) =>\n\t\tcatalog().some((entry) => entry.pairs.some((p) => p.model === pair.model && p.effort === pair.effort));\n\tconst allowsModel = (model: Model<Api>, effort?: string): boolean => {\n\t\tconst levels = model.reasoning ? getSupportedThinkingLevels(model) : [null];\n\t\treturn levels.some((level) => {\n\t\t\tif (model.reasoning && effort !== undefined && level !== effort) return false;\n\t\t\tconst pair = { model: `${model.provider}/${model.id}`, effort: level };\n\t\t\treturn eligiblePair(model, pair, constraints) && hasPair(pair);\n\t\t});\n\t};\n\tconst assertCurrent = () => {\n\t\tsignal?.throwIfAborted();\n\t\tif (![routerSelection, ...(routerSelection.fallbacks ?? [])].every(hasPair))\n\t\t\tthrow new Error(\"Auto selection is no longer eligible. Retry explicitly with the current catalog.\");\n\t};\n\tassertCurrent();\n\treturn {\n\t\trouterSelection,\n\t\tmodelOverride: routerSelection.model + (routerSelection.effort === null ? \"\" : `:${routerSelection.effort}`),\n\t\tfallbackModels: Object.freeze(\n\t\t\t(routerSelection.fallbacks ?? []).map((pair) => pair.model + (pair.effort === null ? \"\" : `:${pair.effort}`)),\n\t\t),\n\t\tassertCurrent,\n\t\tallowsModel,\n\t};\n}\n"]}
|