@warlock.js/ai 4.2.11 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -1
- package/cjs/index.cjs +10151 -4625
- package/cjs/index.cjs.map +1 -1
- package/cjs/magic-string.es-BtxW4VqG.cjs +1015 -0
- package/cjs/magic-string.es-BtxW4VqG.cjs.map +1 -0
- package/cjs/matcher-logic-SBnzYohQ.cjs +217 -0
- package/cjs/matcher-logic-SBnzYohQ.cjs.map +1 -0
- package/cjs/matchers-BBh3gyB-.cjs +13739 -0
- package/cjs/matchers-BBh3gyB-.cjs.map +1 -0
- package/esm/agent/agent-config.type.d.mts +19 -6
- package/esm/agent/agent-config.type.d.mts.map +1 -1
- package/esm/agent/agent.d.mts.map +1 -1
- package/esm/agent/agent.mjs +17 -6
- package/esm/agent/agent.mjs.map +1 -1
- package/esm/agent/index.d.mts +2 -1
- package/esm/agent/index.mjs +1 -0
- package/esm/agent/spawn-sub-agent.d.mts +87 -0
- package/esm/agent/spawn-sub-agent.d.mts.map +1 -0
- package/esm/agent/spawn-sub-agent.mjs +68 -0
- package/esm/agent/spawn-sub-agent.mjs.map +1 -0
- package/esm/ai.d.mts +58 -3
- package/esm/ai.d.mts.map +1 -1
- package/esm/ai.mjs +58 -3
- package/esm/ai.mjs.map +1 -1
- package/esm/batch/batch.d.mts +43 -0
- package/esm/batch/batch.d.mts.map +1 -0
- package/esm/batch/batch.mjs +179 -0
- package/esm/batch/batch.mjs.map +1 -0
- package/esm/batch/batch.type.d.mts +144 -0
- package/esm/batch/batch.type.d.mts.map +1 -0
- package/esm/batch/index.mjs +3 -0
- package/esm/batch/run-batch-item.mjs +100 -0
- package/esm/batch/run-batch-item.mjs.map +1 -0
- package/esm/batch/run-with-concurrency.mjs +39 -0
- package/esm/batch/run-with-concurrency.mjs.map +1 -0
- package/esm/checkpoint/index.d.mts +3 -0
- package/esm/checkpoint/memory.d.mts +21 -0
- package/esm/checkpoint/memory.d.mts.map +1 -0
- package/esm/checkpoint/memory.mjs +0 -0
- package/esm/checkpoint/memory.mjs.map +1 -0
- package/esm/checkpoint/pg.d.mts +37 -0
- package/esm/checkpoint/pg.d.mts.map +1 -0
- package/esm/checkpoint/pg.mjs +265 -0
- package/esm/checkpoint/pg.mjs.map +1 -0
- package/esm/checkpoint/redis.d.mts +39 -0
- package/esm/checkpoint/redis.d.mts.map +1 -0
- package/esm/checkpoint/redis.mjs +200 -0
- package/esm/checkpoint/redis.mjs.map +1 -0
- package/esm/config.d.mts +61 -14
- package/esm/config.d.mts.map +1 -1
- package/esm/config.mjs +25 -6
- package/esm/config.mjs.map +1 -1
- package/esm/contracts/agent/agent.contract.d.mts +43 -0
- package/esm/contracts/agent/agent.contract.d.mts.map +1 -1
- package/esm/contracts/agent/eval.type.d.mts +143 -0
- package/esm/contracts/agent/eval.type.d.mts.map +1 -0
- package/esm/contracts/agent/index.d.mts +1 -0
- package/esm/contracts/events/supervisor-events.type.d.mts +3 -3
- package/esm/contracts/fallback-model.contract.d.mts +65 -0
- package/esm/contracts/fallback-model.contract.d.mts.map +1 -0
- package/esm/contracts/index.d.mts +32 -12
- package/esm/contracts/memory/index.d.mts +4 -0
- package/esm/contracts/memory/memory-config.type.d.mts +150 -0
- package/esm/contracts/memory/memory-config.type.d.mts.map +1 -0
- package/esm/contracts/memory/memory-item.type.d.mts +64 -0
- package/esm/contracts/memory/memory-item.type.d.mts.map +1 -0
- package/esm/contracts/memory/memory.contract.d.mts +87 -0
- package/esm/contracts/memory/memory.contract.d.mts.map +1 -0
- package/esm/contracts/memory/recall-options.type.d.mts +33 -0
- package/esm/contracts/memory/recall-options.type.d.mts.map +1 -0
- package/esm/contracts/middleware/index.d.mts +2 -2
- package/esm/contracts/middleware/middleware-context.type.d.mts +42 -2
- package/esm/contracts/middleware/middleware-context.type.d.mts.map +1 -1
- package/esm/contracts/middleware/middleware.contract.d.mts +46 -2
- package/esm/contracts/middleware/middleware.contract.d.mts.map +1 -1
- package/esm/contracts/model.contract.d.mts +63 -2
- package/esm/contracts/model.contract.d.mts.map +1 -1
- package/esm/contracts/orchestrator/checkpoint-store.contract.d.mts +91 -0
- package/esm/contracts/orchestrator/checkpoint-store.contract.d.mts.map +1 -0
- package/esm/contracts/orchestrator/index.d.mts +8 -0
- package/esm/contracts/orchestrator/orchestrator-commands.type.d.mts +43 -0
- package/esm/contracts/orchestrator/orchestrator-commands.type.d.mts.map +1 -0
- package/esm/contracts/orchestrator/orchestrator-config.type.d.mts +170 -0
- package/esm/contracts/orchestrator/orchestrator-config.type.d.mts.map +1 -0
- package/esm/contracts/orchestrator/orchestrator-event.type.d.mts +118 -0
- package/esm/contracts/orchestrator/orchestrator-event.type.d.mts.map +1 -0
- package/esm/contracts/orchestrator/orchestrator-execute-options.type.d.mts +44 -0
- package/esm/contracts/orchestrator/orchestrator-execute-options.type.d.mts.map +1 -0
- package/esm/contracts/orchestrator/orchestrator.contract.d.mts +129 -0
- package/esm/contracts/orchestrator/orchestrator.contract.d.mts.map +1 -0
- package/esm/contracts/orchestrator/session.contract.d.mts +26 -0
- package/esm/contracts/orchestrator/session.contract.d.mts.map +1 -0
- package/esm/contracts/orchestrator/snapshot-store.contract.d.mts +89 -0
- package/esm/contracts/orchestrator/snapshot-store.contract.d.mts.map +1 -0
- package/esm/contracts/planner/index.d.mts +6 -0
- package/esm/contracts/planner/planner-capability.type.d.mts +39 -0
- package/esm/contracts/planner/planner-capability.type.d.mts.map +1 -0
- package/esm/contracts/planner/planner-config.type.d.mts +78 -0
- package/esm/contracts/planner/planner-config.type.d.mts.map +1 -0
- package/esm/contracts/planner/planner-execute-options.type.d.mts +43 -0
- package/esm/contracts/planner/planner-execute-options.type.d.mts.map +1 -0
- package/esm/contracts/planner/planner-plan.type.d.mts +48 -0
- package/esm/contracts/planner/planner-plan.type.d.mts.map +1 -0
- package/esm/contracts/planner/planner-result.type.d.mts +82 -0
- package/esm/contracts/planner/planner-result.type.d.mts.map +1 -0
- package/esm/contracts/planner/planner.contract.d.mts +60 -0
- package/esm/contracts/planner/planner.contract.d.mts.map +1 -0
- package/esm/contracts/result/base-report.type.d.mts +7 -2
- package/esm/contracts/result/base-report.type.d.mts.map +1 -1
- package/esm/contracts/result/base-report.type.mjs.map +1 -1
- package/esm/contracts/result/index.d.mts +2 -1
- package/esm/contracts/result/model-pricing.type.d.mts +10 -0
- package/esm/contracts/result/model-pricing.type.d.mts.map +1 -1
- package/esm/contracts/result/orchestrator-result.type.d.mts +139 -0
- package/esm/contracts/result/orchestrator-result.type.d.mts.map +1 -0
- package/esm/contracts/result/session-send-result.type.d.mts +12 -3
- package/esm/contracts/result/session-send-result.type.d.mts.map +1 -1
- package/esm/contracts/result/supervisor-result.type.d.mts +1 -1
- package/esm/contracts/result/usage.type.d.mts +24 -0
- package/esm/contracts/result/usage.type.d.mts.map +1 -1
- package/esm/contracts/sdk-adapter.contract.d.mts +1 -1
- package/esm/contracts/supervisor/dispatch-context.type.d.mts +3 -3
- package/esm/contracts/supervisor/evaluate-context.type.d.mts +1 -1
- package/esm/contracts/supervisor/index.d.mts +5 -5
- package/esm/contracts/supervisor/route-context.type.d.mts +2 -2
- package/esm/contracts/supervisor/supervisor-config.type.d.mts +55 -13
- package/esm/contracts/supervisor/supervisor-config.type.d.mts.map +1 -1
- package/esm/contracts/supervisor/supervisor-snapshot.type.d.mts +1 -1
- package/esm/contracts/supervisor/supervisor.contract.d.mts +9 -2
- package/esm/contracts/supervisor/supervisor.contract.d.mts.map +1 -1
- package/esm/contracts/workflow/index.d.mts +2 -2
- package/esm/contracts/workflow/workflow.contract.d.mts +28 -7
- package/esm/contracts/workflow/workflow.contract.d.mts.map +1 -1
- package/esm/errors/error-code.type.d.mts +1 -1
- package/esm/errors/index.d.mts +7 -0
- package/esm/errors/index.mjs +7 -0
- package/esm/errors/orchestrator-cancelled-error.d.mts +32 -0
- package/esm/errors/orchestrator-cancelled-error.d.mts.map +1 -0
- package/esm/errors/orchestrator-cancelled-error.mjs +31 -0
- package/esm/errors/orchestrator-cancelled-error.mjs.map +1 -0
- package/esm/errors/orchestrator-config-error.d.mts +26 -0
- package/esm/errors/orchestrator-config-error.d.mts.map +1 -0
- package/esm/errors/orchestrator-config-error.mjs +30 -0
- package/esm/errors/orchestrator-config-error.mjs.map +1 -0
- package/esm/errors/orchestrator-drift-error.d.mts +38 -0
- package/esm/errors/orchestrator-drift-error.d.mts.map +1 -0
- package/esm/errors/orchestrator-drift-error.mjs +37 -0
- package/esm/errors/orchestrator-drift-error.mjs.map +1 -0
- package/esm/errors/orchestrator-failed-error.d.mts +33 -0
- package/esm/errors/orchestrator-failed-error.d.mts.map +1 -0
- package/esm/errors/orchestrator-failed-error.mjs +36 -0
- package/esm/errors/orchestrator-failed-error.mjs.map +1 -0
- package/esm/errors/planner-cancelled-error.d.mts +33 -0
- package/esm/errors/planner-cancelled-error.d.mts.map +1 -0
- package/esm/errors/planner-cancelled-error.mjs +29 -0
- package/esm/errors/planner-cancelled-error.mjs.map +1 -0
- package/esm/errors/planner-failed-error.d.mts +40 -0
- package/esm/errors/planner-failed-error.d.mts.map +1 -0
- package/esm/errors/planner-failed-error.mjs +37 -0
- package/esm/errors/planner-failed-error.mjs.map +1 -0
- package/esm/errors/planner-plan-invalid-error.d.mts +21 -0
- package/esm/errors/planner-plan-invalid-error.d.mts.map +1 -0
- package/esm/errors/planner-plan-invalid-error.mjs +25 -0
- package/esm/errors/planner-plan-invalid-error.mjs.map +1 -0
- package/esm/eval/eval-runner.d.mts +17 -0
- package/esm/eval/eval-runner.d.mts.map +1 -0
- package/esm/eval/eval-runner.mjs +121 -0
- package/esm/eval/eval-runner.mjs.map +1 -0
- package/esm/eval/index.d.mts +29 -0
- package/esm/eval/index.d.mts.map +1 -0
- package/esm/eval/index.mjs +30 -0
- package/esm/eval/index.mjs.map +1 -0
- package/esm/eval/judge-scorer.d.mts +21 -0
- package/esm/eval/judge-scorer.d.mts.map +1 -0
- package/esm/eval/judge-scorer.mjs +87 -0
- package/esm/eval/judge-scorer.mjs.map +1 -0
- package/esm/eval/scorers.d.mts +50 -0
- package/esm/eval/scorers.d.mts.map +1 -0
- package/esm/eval/scorers.mjs +101 -0
- package/esm/eval/scorers.mjs.map +1 -0
- package/esm/index.d.mts +95 -30
- package/esm/index.mjs +66 -22
- package/esm/memory/derive-id.mjs +24 -0
- package/esm/memory/derive-id.mjs.map +1 -0
- package/esm/memory/episodic-memory.mjs +106 -0
- package/esm/memory/episodic-memory.mjs.map +1 -0
- package/esm/memory/index.d.mts +5 -0
- package/esm/memory/memory.d.mts +42 -0
- package/esm/memory/memory.d.mts.map +1 -0
- package/esm/memory/memory.mjs +166 -0
- package/esm/memory/memory.mjs.map +1 -0
- package/esm/memory/procedural-memory.mjs +103 -0
- package/esm/memory/procedural-memory.mjs.map +1 -0
- package/esm/memory/semantic-memory.mjs +80 -0
- package/esm/memory/semantic-memory.mjs.map +1 -0
- package/esm/memory/working-memory.mjs +62 -0
- package/esm/memory/working-memory.mjs.map +1 -0
- package/esm/middleware/builtins/budget-contract.type.d.mts +126 -0
- package/esm/middleware/builtins/budget-contract.type.d.mts.map +1 -0
- package/esm/middleware/builtins/budget.d.mts +71 -1
- package/esm/middleware/builtins/budget.d.mts.map +1 -1
- package/esm/middleware/builtins/budget.mjs +119 -4
- package/esm/middleware/builtins/budget.mjs.map +1 -1
- package/esm/middleware/builtins/semantic-cache.d.mts +1 -1
- package/esm/middleware/index.d.mts +2 -1
- package/esm/middleware/index.mjs +1 -1
- package/esm/middleware/pipeline.d.mts +9 -6
- package/esm/middleware/pipeline.d.mts.map +1 -1
- package/esm/middleware/pipeline.mjs.map +1 -1
- package/esm/mock/index.d.mts +1 -0
- package/esm/mock/index.mjs +1 -0
- package/esm/mock/mock-router.d.mts +63 -0
- package/esm/mock/mock-router.d.mts.map +1 -0
- package/esm/mock/mock-router.mjs +58 -0
- package/esm/mock/mock-router.mjs.map +1 -0
- package/esm/model/fallback-model.d.mts +45 -0
- package/esm/model/fallback-model.d.mts.map +1 -0
- package/esm/model/fallback-model.mjs +218 -0
- package/esm/model/fallback-model.mjs.map +1 -0
- package/esm/model/index.d.mts +2 -0
- package/esm/model/index.mjs +3 -0
- package/esm/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs +78 -0
- package/esm/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +1 -0
- package/esm/node_modules/@vitest/expect/dist/index.mjs +1473 -0
- package/esm/node_modules/@vitest/expect/dist/index.mjs.map +1 -0
- package/esm/node_modules/@vitest/pretty-format/dist/index.mjs +888 -0
- package/esm/node_modules/@vitest/pretty-format/dist/index.mjs.map +1 -0
- package/esm/node_modules/@vitest/runner/dist/chunk-artifact.mjs +1533 -0
- package/esm/node_modules/@vitest/runner/dist/chunk-artifact.mjs.map +1 -0
- package/esm/node_modules/@vitest/runner/dist/index.mjs +3 -0
- package/esm/node_modules/@vitest/runner/dist/utils.mjs +3 -0
- package/esm/node_modules/@vitest/snapshot/dist/index.mjs +922 -0
- package/esm/node_modules/@vitest/snapshot/dist/index.mjs.map +1 -0
- package/esm/node_modules/@vitest/spy/dist/index.mjs +386 -0
- package/esm/node_modules/@vitest/spy/dist/index.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.mjs +82 -0
- package/esm/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/diff.mjs +1357 -0
- package/esm/node_modules/@vitest/utils/dist/diff.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/display.mjs +559 -0
- package/esm/node_modules/@vitest/utils/dist/display.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/error.mjs +38 -0
- package/esm/node_modules/@vitest/utils/dist/error.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/helpers.mjs +181 -0
- package/esm/node_modules/@vitest/utils/dist/helpers.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/offset.mjs +27 -0
- package/esm/node_modules/@vitest/utils/dist/offset.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/serialize.mjs +77 -0
- package/esm/node_modules/@vitest/utils/dist/serialize.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/source-map.mjs +374 -0
- package/esm/node_modules/@vitest/utils/dist/source-map.mjs.map +1 -0
- package/esm/node_modules/@vitest/utils/dist/timers.mjs +37 -0
- package/esm/node_modules/@vitest/utils/dist/timers.mjs.map +1 -0
- package/esm/node_modules/chai/index.mjs +2973 -0
- package/esm/node_modules/chai/index.mjs.map +1 -0
- package/esm/node_modules/magic-string/dist/magic-string.es.mjs +940 -0
- package/esm/node_modules/magic-string/dist/magic-string.es.mjs.map +1 -0
- package/esm/node_modules/tinyrainbow/dist/index.mjs +87 -0
- package/esm/node_modules/tinyrainbow/dist/index.mjs.map +1 -0
- package/esm/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.mjs +6 -0
- package/esm/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.mjs.map +1 -0
- package/esm/node_modules/vitest/dist/chunks/rpc.MzXet3jl.mjs +52 -0
- package/esm/node_modules/vitest/dist/chunks/rpc.MzXet3jl.mjs.map +1 -0
- package/esm/node_modules/vitest/dist/chunks/test.DNmyFkvJ.mjs +2697 -0
- package/esm/node_modules/vitest/dist/chunks/test.DNmyFkvJ.mjs.map +1 -0
- package/esm/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.mjs +45 -0
- package/esm/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.mjs.map +1 -0
- package/esm/orchestrator/as-tool.d.mts +42 -0
- package/esm/orchestrator/as-tool.d.mts.map +1 -0
- package/esm/orchestrator/as-tool.mjs +98 -0
- package/esm/orchestrator/as-tool.mjs.map +1 -0
- package/esm/orchestrator/checkpoint.mjs +75 -0
- package/esm/orchestrator/checkpoint.mjs.map +1 -0
- package/esm/orchestrator/commands.d.mts +38 -0
- package/esm/orchestrator/commands.d.mts.map +1 -0
- package/esm/orchestrator/commands.mjs +34 -0
- package/esm/orchestrator/commands.mjs.map +1 -0
- package/esm/orchestrator/compaction.mjs +206 -0
- package/esm/orchestrator/compaction.mjs.map +1 -0
- package/esm/orchestrator/dispatch.mjs +171 -0
- package/esm/orchestrator/dispatch.mjs.map +1 -0
- package/esm/orchestrator/emitter-port.type.d.mts +31 -0
- package/esm/orchestrator/emitter-port.type.d.mts.map +1 -0
- package/esm/orchestrator/emitter.d.mts +56 -0
- package/esm/orchestrator/emitter.d.mts.map +1 -0
- package/esm/orchestrator/emitter.mjs +85 -0
- package/esm/orchestrator/emitter.mjs.map +1 -0
- package/esm/orchestrator/engine-context.type.d.mts +56 -0
- package/esm/orchestrator/engine-context.type.d.mts.map +1 -0
- package/esm/orchestrator/execution.d.mts +116 -0
- package/esm/orchestrator/execution.d.mts.map +1 -0
- package/esm/orchestrator/execution.mjs +406 -0
- package/esm/orchestrator/execution.mjs.map +1 -0
- package/esm/orchestrator/index.d.mts +8 -0
- package/esm/orchestrator/index.mjs +10 -0
- package/esm/orchestrator/load.mjs +49 -0
- package/esm/orchestrator/load.mjs.map +1 -0
- package/esm/orchestrator/lock.mjs +75 -0
- package/esm/orchestrator/lock.mjs.map +1 -0
- package/esm/orchestrator/memory.d.mts +84 -0
- package/esm/orchestrator/memory.d.mts.map +1 -0
- package/esm/orchestrator/memory.mjs +141 -0
- package/esm/orchestrator/memory.mjs.map +1 -0
- package/esm/orchestrator/orchestrator-stream.d.mts +42 -0
- package/esm/orchestrator/orchestrator-stream.d.mts.map +1 -0
- package/esm/orchestrator/orchestrator-stream.mjs +98 -0
- package/esm/orchestrator/orchestrator-stream.mjs.map +1 -0
- package/esm/orchestrator/orchestrator.d.mts +38 -0
- package/esm/orchestrator/orchestrator.d.mts.map +1 -0
- package/esm/orchestrator/orchestrator.mjs +173 -0
- package/esm/orchestrator/orchestrator.mjs.map +1 -0
- package/esm/orchestrator/resume.mjs +74 -0
- package/esm/orchestrator/resume.mjs.map +1 -0
- package/esm/orchestrator/signature.d.mts +40 -0
- package/esm/orchestrator/signature.d.mts.map +1 -0
- package/esm/orchestrator/signature.mjs +120 -0
- package/esm/orchestrator/signature.mjs.map +1 -0
- package/esm/orchestrator/window.mjs +56 -0
- package/esm/orchestrator/window.mjs.map +1 -0
- package/esm/planner/index.d.mts +5 -0
- package/esm/planner/index.mjs +6 -0
- package/esm/planner/plan-prompt.d.mts +17 -0
- package/esm/planner/plan-prompt.d.mts.map +1 -0
- package/esm/planner/plan-prompt.mjs +30 -0
- package/esm/planner/plan-prompt.mjs.map +1 -0
- package/esm/planner/plan-schema.d.mts +27 -0
- package/esm/planner/plan-schema.d.mts.map +1 -0
- package/esm/planner/plan-schema.mjs +117 -0
- package/esm/planner/plan-schema.mjs.map +1 -0
- package/esm/planner/planner-run.d.mts +23 -0
- package/esm/planner/planner-run.d.mts.map +1 -0
- package/esm/planner/planner-run.mjs +344 -0
- package/esm/planner/planner-run.mjs.map +1 -0
- package/esm/planner/planner.d.mts +37 -0
- package/esm/planner/planner.d.mts.map +1 -0
- package/esm/planner/planner.mjs +120 -0
- package/esm/planner/planner.mjs.map +1 -0
- package/esm/planner/signature.d.mts +18 -0
- package/esm/planner/signature.d.mts.map +1 -0
- package/esm/planner/signature.mjs +27 -0
- package/esm/planner/signature.mjs.map +1 -0
- package/esm/snapshot/index.d.mts +3 -0
- package/esm/snapshot/memory.d.mts +26 -0
- package/esm/snapshot/memory.d.mts.map +1 -0
- package/esm/snapshot/memory.mjs +81 -0
- package/esm/snapshot/memory.mjs.map +1 -0
- package/esm/snapshot/pg.d.mts +41 -0
- package/esm/snapshot/pg.d.mts.map +1 -0
- package/esm/snapshot/pg.mjs +146 -0
- package/esm/snapshot/pg.mjs.map +1 -0
- package/esm/snapshot/redis.d.mts +42 -0
- package/esm/snapshot/redis.d.mts.map +1 -0
- package/esm/snapshot/redis.mjs +101 -0
- package/esm/snapshot/redis.mjs.map +1 -0
- package/esm/supervisor/as-tool.d.mts +0 -6
- package/esm/supervisor/as-tool.d.mts.map +1 -1
- package/esm/supervisor/as-tool.mjs +0 -6
- package/esm/supervisor/as-tool.mjs.map +1 -1
- package/esm/supervisor/execution.d.mts +43 -8
- package/esm/supervisor/execution.d.mts.map +1 -1
- package/esm/supervisor/execution.mjs +66 -16
- package/esm/supervisor/execution.mjs.map +1 -1
- package/esm/supervisor/fan-out.d.mts +65 -0
- package/esm/supervisor/fan-out.d.mts.map +1 -0
- package/esm/supervisor/fan-out.mjs +65 -0
- package/esm/supervisor/fan-out.mjs.map +1 -0
- package/esm/supervisor/index.d.mts +5 -3
- package/esm/supervisor/index.mjs +3 -1
- package/esm/supervisor/router-factory.d.mts +110 -0
- package/esm/supervisor/router-factory.d.mts.map +1 -0
- package/esm/supervisor/router-factory.mjs +141 -0
- package/esm/supervisor/router-factory.mjs.map +1 -0
- package/esm/supervisor/router-prompt.d.mts +1 -1
- package/esm/supervisor/snapshot.d.mts +4 -10
- package/esm/supervisor/snapshot.d.mts.map +1 -1
- package/esm/supervisor/snapshot.mjs +8 -16
- package/esm/supervisor/snapshot.mjs.map +1 -1
- package/esm/supervisor/supervisor.mjs +1 -0
- package/esm/supervisor/supervisor.mjs.map +1 -1
- package/esm/system-prompt/index.mjs +6 -0
- package/esm/system-prompt/system-prompt.d.mts +51 -3
- package/esm/system-prompt/system-prompt.d.mts.map +1 -1
- package/esm/system-prompt/system-prompt.mjs +52 -6
- package/esm/system-prompt/system-prompt.mjs.map +1 -1
- package/esm/testing/matcher-logic.d.mts +76 -0
- package/esm/testing/matcher-logic.d.mts.map +1 -0
- package/esm/testing/matcher-logic.mjs +144 -0
- package/esm/testing/matcher-logic.mjs.map +1 -0
- package/esm/testing/matchers.d.mts +48 -0
- package/esm/testing/matchers.d.mts.map +1 -0
- package/esm/testing/matchers.mjs +37 -0
- package/esm/testing/matchers.mjs.map +1 -0
- package/esm/testing/register-lazy.d.mts +20 -0
- package/esm/testing/register-lazy.d.mts.map +1 -0
- package/esm/testing/register-lazy.mjs +24 -0
- package/esm/testing/register-lazy.mjs.map +1 -0
- package/esm/tool/executable-as-tool.d.mts +87 -0
- package/esm/tool/executable-as-tool.d.mts.map +1 -0
- package/esm/tool/executable-as-tool.mjs +81 -0
- package/esm/tool/executable-as-tool.mjs.map +1 -0
- package/esm/tool/index.d.mts +2 -1
- package/esm/tool/index.mjs +1 -0
- package/esm/workflow/as-tool.mjs +0 -6
- package/esm/workflow/as-tool.mjs.map +1 -1
- package/esm/workflow/engine.mjs +2 -2
- package/esm/workflow/snapshot.mjs +13 -7
- package/esm/workflow/snapshot.mjs.map +1 -1
- package/esm/workflow/step-runner.mjs +1 -1
- package/esm/workflow/workflow.mjs +1 -0
- package/esm/workflow/workflow.mjs.map +1 -1
- package/llms-full.txt +947 -42
- package/llms.txt +13 -8
- package/package.json +3 -3
- package/skills/README.md +25 -5
- package/skills/ai-basics/SKILL.md +18 -7
- package/skills/ai-dx-helpers/SKILL.md +180 -0
- package/skills/attach-ai-middleware/SKILL.md +32 -3
- package/skills/handle-ai-errors/SKILL.md +20 -6
- package/skills/manage-ai-stores/SKILL.md +127 -0
- package/skills/persist-ai-data/SKILL.md +21 -10
- package/skills/pick-ai-provider/SKILL.md +46 -12
- package/skills/run-ai-agent/SKILL.md +51 -2
- package/skills/run-orchestrator/SKILL.md +198 -0
- package/skills/run-planner/SKILL.md +68 -0
- package/skills/run-supervisor/SKILL.md +47 -2
- package/skills/use-ai-memory/SKILL.md +124 -0
- package/skills/write-system-prompt/SKILL.md +14 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { MiddlewareExecuteContext } from "../../contracts/middleware/middleware-context.type.mjs";
|
|
1
2
|
import { AgentMiddleware } from "../../contracts/middleware/middleware.contract.mjs";
|
|
3
|
+
import { BudgetContract, BudgetContractDimension, BudgetContractFallback, BudgetContractViolation, BudgetContractViolationMode } from "./budget-contract.type.mjs";
|
|
4
|
+
|
|
2
5
|
//#region ../@warlock.js/ai/src/middleware/builtins/budget.d.ts
|
|
3
6
|
/**
|
|
4
7
|
* Per-model pricing used to compute USD cost from token counts.
|
|
@@ -48,7 +51,51 @@ type BudgetOptions = {
|
|
|
48
51
|
* instances). Default `"budget"`.
|
|
49
52
|
*/
|
|
50
53
|
name?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Declarative SLO / cost contract enforced on top of (and
|
|
56
|
+
* independently of) the legacy `maxTokens` / `maxCostUSD` caps.
|
|
57
|
+
* Adds a wall-clock `maxLatencyMs` dimension and a per-contract
|
|
58
|
+
* `onViolation` reaction (`"abort"` hard-stops, `"fallback"` records
|
|
59
|
+
* a signal + fires `fallback` and lets the run continue). Omit to
|
|
60
|
+
* keep the classic budget behavior unchanged.
|
|
61
|
+
*
|
|
62
|
+
* Read a recorded fallback signal back with
|
|
63
|
+
* {@link readBudgetFallbackSignal}.
|
|
64
|
+
*/
|
|
65
|
+
contract?: BudgetContract;
|
|
51
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* Recorded contract fallback signal, stashed under the `<name>.fallback`
|
|
69
|
+
* state key when a `"fallback"` clause trips. A fallback orchestrator
|
|
70
|
+
* reads it via {@link readBudgetFallbackSignal} to decide how to degrade.
|
|
71
|
+
*/
|
|
72
|
+
type BudgetFallbackSignal = BudgetContractViolation;
|
|
73
|
+
/**
|
|
74
|
+
* Read the contract fallback signal recorded by a `budget()` middleware
|
|
75
|
+
* running under `contract.onViolation: "fallback"`. Returns `undefined`
|
|
76
|
+
* when no clause was breached.
|
|
77
|
+
*
|
|
78
|
+
* **Role.** The middleware cannot itself switch models on a soft breach,
|
|
79
|
+
* so it records a typed {@link BudgetFallbackSignal} in the shared state
|
|
80
|
+
* bag and lets the run continue. A fallback orchestrator (or the
|
|
81
|
+
* `execute.after` hook of an outer middleware) reads it back here and
|
|
82
|
+
* decides how to degrade the next run — cheaper model, cached answer,
|
|
83
|
+
* truncated context.
|
|
84
|
+
*
|
|
85
|
+
* @param state - The middleware state bag (`ctx.state`).
|
|
86
|
+
* @param name - The budget middleware's name. Default `"budget"`,
|
|
87
|
+
* matching `BudgetOptions.name`'s default.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const guard = budget({ contract: { maxCostUSD: 0.05, onViolation: "fallback" } });
|
|
91
|
+
*
|
|
92
|
+
* // In an outer middleware's execute.after, after the run:
|
|
93
|
+
* const signal = readBudgetFallbackSignal(ctx.state);
|
|
94
|
+
* if (signal?.dimension === "cost") {
|
|
95
|
+
* await rerunOnCheaperModel();
|
|
96
|
+
* }
|
|
97
|
+
*/
|
|
98
|
+
declare function readBudgetFallbackSignal(state: MiddlewareExecuteContext["state"], name?: string): BudgetFallbackSignal | undefined;
|
|
52
99
|
/**
|
|
53
100
|
* Enforced token and / or USD budget for an agent run.
|
|
54
101
|
*
|
|
@@ -80,6 +127,16 @@ type BudgetOptions = {
|
|
|
80
127
|
* measuring real-world traffic against a proposed cap before flipping
|
|
81
128
|
* to `"abort"` in production.
|
|
82
129
|
*
|
|
130
|
+
* **Contract / SLO mode.** Pass `contract` to enforce a declarative
|
|
131
|
+
* service-level objective — `maxCostUSD`, `maxLatencyMs`, `maxTokens` —
|
|
132
|
+
* on top of the legacy caps, with a single `onViolation` reaction:
|
|
133
|
+
* `"abort"` hard-stops with `BudgetExceededError`; `"fallback"` records
|
|
134
|
+
* a typed signal (read it via {@link readBudgetFallbackSignal}), fires
|
|
135
|
+
* the optional `fallback` callback, and lets the run continue so an
|
|
136
|
+
* outer layer can degrade gracefully. The contract's clauses are
|
|
137
|
+
* evaluated independently of — and after — the top-level caps; the
|
|
138
|
+
* top-level caps stay fully functional with or without a contract.
|
|
139
|
+
*
|
|
83
140
|
* @example
|
|
84
141
|
* const budgetMiddleware = budget({ maxTokens: 50_000 });
|
|
85
142
|
*
|
|
@@ -96,8 +153,21 @@ type BudgetOptions = {
|
|
|
96
153
|
* "gpt-4o": { inputPer1K: 0.005, outputPer1K: 0.015 },
|
|
97
154
|
* },
|
|
98
155
|
* });
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // SLO contract — soft-fallback on any breach
|
|
159
|
+
* const sloGuard = budget({
|
|
160
|
+
* pricing: { "gpt-4o": { inputPer1K: 0.005, outputPer1K: 0.015 } },
|
|
161
|
+
* contract: {
|
|
162
|
+
* maxCostUSD: 0.05,
|
|
163
|
+
* maxLatencyMs: 8_000,
|
|
164
|
+
* maxTokens: 40_000,
|
|
165
|
+
* onViolation: "fallback",
|
|
166
|
+
* fallback: (violation) => routeToCheaperModel(violation.dimension),
|
|
167
|
+
* },
|
|
168
|
+
* });
|
|
99
169
|
*/
|
|
100
170
|
declare function budget(options: BudgetOptions): AgentMiddleware;
|
|
101
171
|
//#endregion
|
|
102
|
-
export { BudgetOptions, BudgetPricing, budget };
|
|
172
|
+
export { BudgetFallbackSignal, BudgetOptions, BudgetPricing, budget, readBudgetFallbackSignal };
|
|
103
173
|
//# sourceMappingURL=budget.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/middleware/builtins/budget.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"budget.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/middleware/builtins/budget.ts"],"mappings":";;;;;;;;AA2BA;;;;KAAY,aAAA,GAAgB,MAAM;EAI9B,kCAAA,UAAA,UAEW;EAAX,WAAA;AAAA;;;;;KAQQ,aAAA;EAkBV;;;;;EAZA,SAAA;EAsCyB;AAAA;AAyB3B;;;EAzDE,UAAA;EAyDwD;AAoE1D;;;;EAvHE,OAAA,GAAU,aAAA;EAwHV;;;;AAEqB;AA8EvB;;EAhME,UAAA;EAgM6D;;;;;EA1L7D,IAAA;;;;;;;;;;;;EAYA,QAAA,GAAW,cAAc;AAAA;;;;;;KAyBf,oBAAA,GAAuB,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoE1C,wBAAA,CACd,KAAA,EAAO,wBAAA,WACP,IAAA,YACC,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8EP,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,eAAe"}
|
|
@@ -3,6 +3,17 @@ import "../../errors/index.mjs";
|
|
|
3
3
|
import { namespacedState } from "../utils/namespaced-state.mjs";
|
|
4
4
|
|
|
5
5
|
//#region ../@warlock.js/ai/src/middleware/builtins/budget.ts
|
|
6
|
+
/**
|
|
7
|
+
* The `BudgetUnit` to stamp on the thrown error per contract dimension.
|
|
8
|
+
* Latency has no native unit — it borrows `"requests"` as the least-wrong
|
|
9
|
+
* operational measure, while the authoritative detail rides on the
|
|
10
|
+
* error's `context.dimension`.
|
|
11
|
+
*/
|
|
12
|
+
const DIMENSION_UNIT = {
|
|
13
|
+
tokens: "tokens",
|
|
14
|
+
cost: "usd",
|
|
15
|
+
latency: "requests"
|
|
16
|
+
};
|
|
6
17
|
function breach(limit, actual, unit, name) {
|
|
7
18
|
throw new BudgetExceededError(`budget "${name}" exceeded — ${actual} ${unit} (cap: ${limit})`, {
|
|
8
19
|
limit,
|
|
@@ -10,6 +21,47 @@ function breach(limit, actual, unit, name) {
|
|
|
10
21
|
unit
|
|
11
22
|
});
|
|
12
23
|
}
|
|
24
|
+
function breachContract(name, dimension, limit, actual) {
|
|
25
|
+
throw new BudgetExceededError(`budget "${name}" contract exceeded — ${dimension} ${actual} (cap: ${limit})`, {
|
|
26
|
+
limit,
|
|
27
|
+
actual,
|
|
28
|
+
unit: DIMENSION_UNIT[dimension],
|
|
29
|
+
context: {
|
|
30
|
+
dimension,
|
|
31
|
+
limit,
|
|
32
|
+
actual,
|
|
33
|
+
source: "contract"
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read the contract fallback signal recorded by a `budget()` middleware
|
|
39
|
+
* running under `contract.onViolation: "fallback"`. Returns `undefined`
|
|
40
|
+
* when no clause was breached.
|
|
41
|
+
*
|
|
42
|
+
* **Role.** The middleware cannot itself switch models on a soft breach,
|
|
43
|
+
* so it records a typed {@link BudgetFallbackSignal} in the shared state
|
|
44
|
+
* bag and lets the run continue. A fallback orchestrator (or the
|
|
45
|
+
* `execute.after` hook of an outer middleware) reads it back here and
|
|
46
|
+
* decides how to degrade the next run — cheaper model, cached answer,
|
|
47
|
+
* truncated context.
|
|
48
|
+
*
|
|
49
|
+
* @param state - The middleware state bag (`ctx.state`).
|
|
50
|
+
* @param name - The budget middleware's name. Default `"budget"`,
|
|
51
|
+
* matching `BudgetOptions.name`'s default.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const guard = budget({ contract: { maxCostUSD: 0.05, onViolation: "fallback" } });
|
|
55
|
+
*
|
|
56
|
+
* // In an outer middleware's execute.after, after the run:
|
|
57
|
+
* const signal = readBudgetFallbackSignal(ctx.state);
|
|
58
|
+
* if (signal?.dimension === "cost") {
|
|
59
|
+
* await rerunOnCheaperModel();
|
|
60
|
+
* }
|
|
61
|
+
*/
|
|
62
|
+
function readBudgetFallbackSignal(state, name = "budget") {
|
|
63
|
+
return namespacedState({ state }, `${name}.fallback`).get();
|
|
64
|
+
}
|
|
13
65
|
/**
|
|
14
66
|
* Enforced token and / or USD budget for an agent run.
|
|
15
67
|
*
|
|
@@ -41,6 +93,16 @@ function breach(limit, actual, unit, name) {
|
|
|
41
93
|
* measuring real-world traffic against a proposed cap before flipping
|
|
42
94
|
* to `"abort"` in production.
|
|
43
95
|
*
|
|
96
|
+
* **Contract / SLO mode.** Pass `contract` to enforce a declarative
|
|
97
|
+
* service-level objective — `maxCostUSD`, `maxLatencyMs`, `maxTokens` —
|
|
98
|
+
* on top of the legacy caps, with a single `onViolation` reaction:
|
|
99
|
+
* `"abort"` hard-stops with `BudgetExceededError`; `"fallback"` records
|
|
100
|
+
* a typed signal (read it via {@link readBudgetFallbackSignal}), fires
|
|
101
|
+
* the optional `fallback` callback, and lets the run continue so an
|
|
102
|
+
* outer layer can degrade gracefully. The contract's clauses are
|
|
103
|
+
* evaluated independently of — and after — the top-level caps; the
|
|
104
|
+
* top-level caps stay fully functional with or without a contract.
|
|
105
|
+
*
|
|
44
106
|
* @example
|
|
45
107
|
* const budgetMiddleware = budget({ maxTokens: 50_000 });
|
|
46
108
|
*
|
|
@@ -57,26 +119,47 @@ function breach(limit, actual, unit, name) {
|
|
|
57
119
|
* "gpt-4o": { inputPer1K: 0.005, outputPer1K: 0.015 },
|
|
58
120
|
* },
|
|
59
121
|
* });
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* // SLO contract — soft-fallback on any breach
|
|
125
|
+
* const sloGuard = budget({
|
|
126
|
+
* pricing: { "gpt-4o": { inputPer1K: 0.005, outputPer1K: 0.015 } },
|
|
127
|
+
* contract: {
|
|
128
|
+
* maxCostUSD: 0.05,
|
|
129
|
+
* maxLatencyMs: 8_000,
|
|
130
|
+
* maxTokens: 40_000,
|
|
131
|
+
* onViolation: "fallback",
|
|
132
|
+
* fallback: (violation) => routeToCheaperModel(violation.dimension),
|
|
133
|
+
* },
|
|
134
|
+
* });
|
|
60
135
|
*/
|
|
61
136
|
function budget(options) {
|
|
62
137
|
const name = options.name ?? "budget";
|
|
63
138
|
const onExceeded = options.onExceeded ?? "abort";
|
|
64
139
|
const hasTokenCap = typeof options.maxTokens === "number";
|
|
65
140
|
const hasCostCap = typeof options.maxCostUSD === "number";
|
|
141
|
+
const contract = options.contract;
|
|
142
|
+
const contractMode = contract?.onViolation ?? "abort";
|
|
143
|
+
const hasContractTokenCap = typeof contract?.maxTokens === "number";
|
|
144
|
+
const hasContractCostCap = typeof contract?.maxCostUSD === "number";
|
|
145
|
+
const hasContractLatencyCap = typeof contract?.maxLatencyMs === "number";
|
|
146
|
+
const contractNeedsCost = hasCostCap || hasContractCostCap;
|
|
66
147
|
return {
|
|
67
148
|
name,
|
|
68
149
|
execute: { before(context) {
|
|
69
150
|
namespacedState(context, name).set({
|
|
70
151
|
tokens: 0,
|
|
71
152
|
costUSD: 0,
|
|
72
|
-
warned: false
|
|
153
|
+
warned: false,
|
|
154
|
+
startedAt: Date.now(),
|
|
155
|
+
fallbackFired: false
|
|
73
156
|
});
|
|
74
157
|
} },
|
|
75
|
-
trip: { after(context, response) {
|
|
158
|
+
trip: { async after(context, response) {
|
|
76
159
|
const counters = namespacedState(context, name).get();
|
|
77
160
|
if (!counters) return;
|
|
78
161
|
counters.tokens += response.usage.total;
|
|
79
|
-
if (
|
|
162
|
+
if (contractNeedsCost && options.pricing) {
|
|
80
163
|
const pricing = options.pricing[context.model.name];
|
|
81
164
|
if (pricing) {
|
|
82
165
|
const tripCost = response.usage.input / 1e3 * pricing.inputPer1K + response.usage.output / 1e3 * pricing.outputPer1K;
|
|
@@ -91,10 +174,42 @@ function budget(options) {
|
|
|
91
174
|
if (onExceeded === "abort") breach(options.maxCostUSD, counters.costUSD, "usd", name);
|
|
92
175
|
if (!counters.warned) counters.warned = true;
|
|
93
176
|
}
|
|
177
|
+
if (!contract) return;
|
|
178
|
+
if (hasContractTokenCap && counters.tokens > contract.maxTokens) await enforceContract(context, counters, name, contractMode, contract, "tokens", contract.maxTokens, counters.tokens);
|
|
179
|
+
if (hasContractCostCap && counters.costUSD > contract.maxCostUSD) await enforceContract(context, counters, name, contractMode, contract, "cost", contract.maxCostUSD, counters.costUSD);
|
|
180
|
+
if (hasContractLatencyCap) {
|
|
181
|
+
const elapsedMs = Date.now() - counters.startedAt;
|
|
182
|
+
if (elapsedMs > contract.maxLatencyMs) await enforceContract(context, counters, name, contractMode, contract, "latency", contract.maxLatencyMs, elapsedMs);
|
|
183
|
+
}
|
|
94
184
|
} }
|
|
95
185
|
};
|
|
96
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Apply the contract's reaction to a single breached clause. `"abort"`
|
|
189
|
+
* throws `BudgetExceededError` (stops the run); `"fallback"` records the
|
|
190
|
+
* signal once, fires the callback, and returns so the run continues.
|
|
191
|
+
*
|
|
192
|
+
* The callback is invoked at most once per run (guarded by
|
|
193
|
+
* `counters.fallbackFired`) and its rejections are swallowed — a buggy
|
|
194
|
+
* fallback hook must never crash the agent.
|
|
195
|
+
*/
|
|
196
|
+
async function enforceContract(context, counters, name, mode, contract, dimension, limit, actual) {
|
|
197
|
+
if (mode === "abort") breachContract(name, dimension, limit, actual);
|
|
198
|
+
if (counters.fallbackFired) return;
|
|
199
|
+
counters.fallbackFired = true;
|
|
200
|
+
const violation = {
|
|
201
|
+
dimension,
|
|
202
|
+
limit,
|
|
203
|
+
actual,
|
|
204
|
+
mode
|
|
205
|
+
};
|
|
206
|
+
namespacedState(context, `${name}.fallback`).set(violation);
|
|
207
|
+
if (!contract.fallback) return;
|
|
208
|
+
try {
|
|
209
|
+
await contract.fallback(violation, context);
|
|
210
|
+
} catch {}
|
|
211
|
+
}
|
|
97
212
|
|
|
98
213
|
//#endregion
|
|
99
|
-
export { budget };
|
|
214
|
+
export { budget, readBudgetFallbackSignal };
|
|
100
215
|
//# sourceMappingURL=budget.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/middleware/builtins/budget.ts"],"sourcesContent":["import type { AgentMiddleware } from \"../../contracts/middleware\";\nimport { BudgetExceededError, type BudgetUnit } from \"../../errors\";\nimport { namespacedState } from \"../utils\";\n\n/**\n * Per-model pricing used to compute USD cost from token counts.\n * Caller-supplied — no bundled table. Keys are model names (the\n * `ModelContract.name` value); values are input / output token\n * prices expressed as **USD per 1K tokens** to match every major\n * provider's published pricing sheet.\n */\nexport type BudgetPricing = Record<\n string,\n {\n /** USD per 1,000 input tokens. */\n inputPer1K: number;\n /** USD per 1,000 output tokens. */\n outputPer1K: number;\n }\n>;\n\n/**\n * Configuration for `budget()`. At least one of `maxTokens` or\n * `maxCostUSD` must be supplied — a budget with no cap is a no-op.\n */\nexport type BudgetOptions = {\n /**\n * Hard cap on cumulative total tokens (input + output, summed\n * across every trip of the run). Inclusive — exceeding triggers\n * the configured `onExceeded`.\n */\n maxTokens?: number;\n /**\n * Hard cap on cumulative USD cost. Requires `pricing` for the\n * agent's configured model — without a pricing entry the USD check\n * silently skips (tokens-only enforcement still applies).\n */\n maxCostUSD?: number;\n /**\n * Per-model pricing table used to compute USD cost. Only consulted\n * when `maxCostUSD` is set. Model names must match the running\n * agent's `ModelContract.name` exactly.\n */\n pricing?: BudgetPricing;\n /**\n * Behavior when a cap is breached. `\"abort\"` throws\n * `BudgetExceededError` — surfaces on `result.error`, stops the\n * run at the next trip boundary. `\"warn\"` logs a warning and\n * lets the run continue (useful for observability-first rollouts\n * before flipping the switch to abort). Default `\"abort\"`.\n */\n onExceeded?: \"abort\" | \"warn\";\n /**\n * Override the middleware name. Useful when two budgets coexist\n * (e.g. a per-request cap plus a session-wide cap via different\n * instances). Default `\"budget\"`.\n */\n name?: string;\n};\n\ntype BudgetCounters = {\n tokens: number;\n costUSD: number;\n warned: boolean;\n};\n\nfunction breach(\n limit: number,\n actual: number,\n unit: BudgetUnit,\n name: string,\n): never {\n throw new BudgetExceededError(\n `budget \"${name}\" exceeded — ${actual} ${unit} (cap: ${limit})`,\n { limit, actual, unit },\n );\n}\n\n/**\n * Enforced token and / or USD budget for an agent run.\n *\n * **Role.** Guards against runaway tool loops, misconfigured\n * prompts, and unexpected provider price swings by capping\n * cumulative usage across every LLM trip of a single execution.\n * Aborts the run with a typed `BudgetExceededError` the moment a cap\n * is breached, rather than letting the damage grow trip by trip.\n *\n * **Scope.** Per-execution. A fresh counter is created at\n * `execute.before` and lives in the middleware state bag until the\n * run ends. Two concurrent `agent.execute()` calls on the same\n * agent therefore enforce the cap independently.\n *\n * **Token accounting.** After each successful trip, the middleware\n * adds `response.usage.total` to its running total and checks\n * against `maxTokens`. Synthetic trips (cache hits) contribute\n * `usage.total` as returned by the cache — cache middleware is\n * expected to surface zero usage on a hit, which naturally excludes\n * those trips from the budget.\n *\n * **USD accounting.** When `maxCostUSD` + `pricing[modelName]` are\n * both present, the middleware converts per-trip input / output\n * tokens to USD and accumulates. Missing pricing silently degrades\n * to tokens-only — explicit rather than guessing.\n *\n * **Warn mode.** `onExceeded: \"warn\"` logs a single warning the first\n * time a cap is breached and lets the run continue. Useful for\n * measuring real-world traffic against a proposed cap before flipping\n * to `\"abort\"` in production.\n *\n * @example\n * const budgetMiddleware = budget({ maxTokens: 50_000 });\n *\n * const myAgent = agent({\n * model,\n * middleware: [budgetMiddleware],\n * });\n *\n * @example\n * // With USD cap and custom pricing\n * const guard = budget({\n * maxCostUSD: 0.5,\n * pricing: {\n * \"gpt-4o\": { inputPer1K: 0.005, outputPer1K: 0.015 },\n * },\n * });\n */\nexport function budget(options: BudgetOptions): AgentMiddleware {\n const name = options.name ?? \"budget\";\n const onExceeded = options.onExceeded ?? \"abort\";\n const hasTokenCap = typeof options.maxTokens === \"number\";\n const hasCostCap = typeof options.maxCostUSD === \"number\";\n\n return {\n name,\n execute: {\n before(context) {\n const counters = namespacedState<BudgetCounters>(context, name);\n counters.set({ tokens: 0, costUSD: 0, warned: false });\n },\n },\n trip: {\n after(context, response) {\n const counters = namespacedState<BudgetCounters>(context, name).get();\n\n if (!counters) {\n return;\n }\n\n counters.tokens += response.usage.total;\n\n if (hasCostCap && options.pricing) {\n const pricing = options.pricing[context.model.name];\n\n if (pricing) {\n const tripCost =\n (response.usage.input / 1000) * pricing.inputPer1K +\n (response.usage.output / 1000) * pricing.outputPer1K;\n counters.costUSD += tripCost;\n }\n }\n\n if (hasTokenCap && counters.tokens > options.maxTokens!) {\n if (onExceeded === \"abort\") {\n breach(options.maxTokens!, counters.tokens, \"tokens\", name);\n }\n\n if (!counters.warned) {\n counters.warned = true;\n }\n }\n\n if (hasCostCap && counters.costUSD > options.maxCostUSD!) {\n if (onExceeded === \"abort\") {\n breach(options.maxCostUSD!, counters.costUSD, \"usd\", name);\n }\n\n if (!counters.warned) {\n counters.warned = true;\n }\n }\n },\n },\n };\n}\n"],"mappings":";;;;;AAkEA,SAAS,OACP,OACA,QACA,MACA,MACO;CACP,MAAM,IAAI,oBACR,WAAW,KAAK,eAAe,OAAO,GAAG,KAAK,SAAS,MAAM,IAC7D;EAAE;EAAO;EAAQ;CAAK,CACxB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAgB,OAAO,SAAyC;CAC9D,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,cAAc,OAAO,QAAQ,cAAc;CACjD,MAAM,aAAa,OAAO,QAAQ,eAAe;CAEjD,OAAO;EACL;EACA,SAAS,EACP,OAAO,SAAS;GAEd,AADiB,gBAAgC,SAAS,IACnD,CAAC,CAAC,IAAI;IAAE,QAAQ;IAAG,SAAS;IAAG,QAAQ;GAAM,CAAC;EACvD,EACF;EACA,MAAM,EACJ,MAAM,SAAS,UAAU;GACvB,MAAM,WAAW,gBAAgC,SAAS,IAAI,CAAC,CAAC,IAAI;GAEpE,IAAI,CAAC,UACH;GAGF,SAAS,UAAU,SAAS,MAAM;GAElC,IAAI,cAAc,QAAQ,SAAS;IACjC,MAAM,UAAU,QAAQ,QAAQ,QAAQ,MAAM;IAE9C,IAAI,SAAS;KACX,MAAM,WACH,SAAS,MAAM,QAAQ,MAAQ,QAAQ,aACvC,SAAS,MAAM,SAAS,MAAQ,QAAQ;KAC3C,SAAS,WAAW;IACtB;GACF;GAEA,IAAI,eAAe,SAAS,SAAS,QAAQ,WAAY;IACvD,IAAI,eAAe,SACjB,OAAO,QAAQ,WAAY,SAAS,QAAQ,UAAU,IAAI;IAG5D,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;GAEtB;GAEA,IAAI,cAAc,SAAS,UAAU,QAAQ,YAAa;IACxD,IAAI,eAAe,SACjB,OAAO,QAAQ,YAAa,SAAS,SAAS,OAAO,IAAI;IAG3D,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;GAEtB;EACF,EACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"budget.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/middleware/builtins/budget.ts"],"sourcesContent":["import type {\n AgentMiddleware,\n MiddlewareExecuteContext,\n} from \"../../contracts/middleware\";\nimport { BudgetExceededError, type BudgetUnit } from \"../../errors\";\nimport { namespacedState } from \"../utils\";\nimport type {\n BudgetContract,\n BudgetContractDimension,\n BudgetContractViolation,\n} from \"./budget-contract.type\";\n\nexport type {\n BudgetContract,\n BudgetContractDimension,\n BudgetContractFallback,\n BudgetContractViolation,\n BudgetContractViolationMode,\n} from \"./budget-contract.type\";\n\n/**\n * Per-model pricing used to compute USD cost from token counts.\n * Caller-supplied — no bundled table. Keys are model names (the\n * `ModelContract.name` value); values are input / output token\n * prices expressed as **USD per 1K tokens** to match every major\n * provider's published pricing sheet.\n */\nexport type BudgetPricing = Record<\n string,\n {\n /** USD per 1,000 input tokens. */\n inputPer1K: number;\n /** USD per 1,000 output tokens. */\n outputPer1K: number;\n }\n>;\n\n/**\n * Configuration for `budget()`. At least one of `maxTokens` or\n * `maxCostUSD` must be supplied — a budget with no cap is a no-op.\n */\nexport type BudgetOptions = {\n /**\n * Hard cap on cumulative total tokens (input + output, summed\n * across every trip of the run). Inclusive — exceeding triggers\n * the configured `onExceeded`.\n */\n maxTokens?: number;\n /**\n * Hard cap on cumulative USD cost. Requires `pricing` for the\n * agent's configured model — without a pricing entry the USD check\n * silently skips (tokens-only enforcement still applies).\n */\n maxCostUSD?: number;\n /**\n * Per-model pricing table used to compute USD cost. Only consulted\n * when `maxCostUSD` is set. Model names must match the running\n * agent's `ModelContract.name` exactly.\n */\n pricing?: BudgetPricing;\n /**\n * Behavior when a cap is breached. `\"abort\"` throws\n * `BudgetExceededError` — surfaces on `result.error`, stops the\n * run at the next trip boundary. `\"warn\"` logs a warning and\n * lets the run continue (useful for observability-first rollouts\n * before flipping the switch to abort). Default `\"abort\"`.\n */\n onExceeded?: \"abort\" | \"warn\";\n /**\n * Override the middleware name. Useful when two budgets coexist\n * (e.g. a per-request cap plus a session-wide cap via different\n * instances). Default `\"budget\"`.\n */\n name?: string;\n /**\n * Declarative SLO / cost contract enforced on top of (and\n * independently of) the legacy `maxTokens` / `maxCostUSD` caps.\n * Adds a wall-clock `maxLatencyMs` dimension and a per-contract\n * `onViolation` reaction (`\"abort\"` hard-stops, `\"fallback\"` records\n * a signal + fires `fallback` and lets the run continue). Omit to\n * keep the classic budget behavior unchanged.\n *\n * Read a recorded fallback signal back with\n * {@link readBudgetFallbackSignal}.\n */\n contract?: BudgetContract;\n};\n\ntype BudgetCounters = {\n tokens: number;\n costUSD: number;\n warned: boolean;\n /**\n * Wall-clock epoch ms captured at `execute.before`. Used to derive\n * cumulative run latency for the contract's `maxLatencyMs` clause.\n */\n startedAt: number;\n /**\n * Set once a `\"fallback\"` contract clause has fired, so the signal +\n * callback are emitted at most once per run even if later trips keep\n * breaching.\n */\n fallbackFired: boolean;\n};\n\n/**\n * Recorded contract fallback signal, stashed under the `<name>.fallback`\n * state key when a `\"fallback\"` clause trips. A fallback orchestrator\n * reads it via {@link readBudgetFallbackSignal} to decide how to degrade.\n */\nexport type BudgetFallbackSignal = BudgetContractViolation;\n\n/**\n * The `BudgetUnit` to stamp on the thrown error per contract dimension.\n * Latency has no native unit — it borrows `\"requests\"` as the least-wrong\n * operational measure, while the authoritative detail rides on the\n * error's `context.dimension`.\n */\nconst DIMENSION_UNIT: Record<BudgetContractDimension, BudgetUnit> = {\n tokens: \"tokens\",\n cost: \"usd\",\n latency: \"requests\",\n};\n\nfunction breach(\n limit: number,\n actual: number,\n unit: BudgetUnit,\n name: string,\n): never {\n throw new BudgetExceededError(\n `budget \"${name}\" exceeded — ${actual} ${unit} (cap: ${limit})`,\n { limit, actual, unit },\n );\n}\n\nfunction breachContract(\n name: string,\n dimension: BudgetContractDimension,\n limit: number,\n actual: number,\n): never {\n throw new BudgetExceededError(\n `budget \"${name}\" contract exceeded — ${dimension} ${actual} (cap: ${limit})`,\n {\n limit,\n actual,\n unit: DIMENSION_UNIT[dimension],\n context: { dimension, limit, actual, source: \"contract\" },\n },\n );\n}\n\n/**\n * Read the contract fallback signal recorded by a `budget()` middleware\n * running under `contract.onViolation: \"fallback\"`. Returns `undefined`\n * when no clause was breached.\n *\n * **Role.** The middleware cannot itself switch models on a soft breach,\n * so it records a typed {@link BudgetFallbackSignal} in the shared state\n * bag and lets the run continue. A fallback orchestrator (or the\n * `execute.after` hook of an outer middleware) reads it back here and\n * decides how to degrade the next run — cheaper model, cached answer,\n * truncated context.\n *\n * @param state - The middleware state bag (`ctx.state`).\n * @param name - The budget middleware's name. Default `\"budget\"`,\n * matching `BudgetOptions.name`'s default.\n *\n * @example\n * const guard = budget({ contract: { maxCostUSD: 0.05, onViolation: \"fallback\" } });\n *\n * // In an outer middleware's execute.after, after the run:\n * const signal = readBudgetFallbackSignal(ctx.state);\n * if (signal?.dimension === \"cost\") {\n * await rerunOnCheaperModel();\n * }\n */\nexport function readBudgetFallbackSignal(\n state: MiddlewareExecuteContext[\"state\"],\n name = \"budget\",\n): BudgetFallbackSignal | undefined {\n return namespacedState<BudgetFallbackSignal>(\n { state },\n `${name}.fallback`,\n ).get();\n}\n\n/**\n * Enforced token and / or USD budget for an agent run.\n *\n * **Role.** Guards against runaway tool loops, misconfigured\n * prompts, and unexpected provider price swings by capping\n * cumulative usage across every LLM trip of a single execution.\n * Aborts the run with a typed `BudgetExceededError` the moment a cap\n * is breached, rather than letting the damage grow trip by trip.\n *\n * **Scope.** Per-execution. A fresh counter is created at\n * `execute.before` and lives in the middleware state bag until the\n * run ends. Two concurrent `agent.execute()` calls on the same\n * agent therefore enforce the cap independently.\n *\n * **Token accounting.** After each successful trip, the middleware\n * adds `response.usage.total` to its running total and checks\n * against `maxTokens`. Synthetic trips (cache hits) contribute\n * `usage.total` as returned by the cache — cache middleware is\n * expected to surface zero usage on a hit, which naturally excludes\n * those trips from the budget.\n *\n * **USD accounting.** When `maxCostUSD` + `pricing[modelName]` are\n * both present, the middleware converts per-trip input / output\n * tokens to USD and accumulates. Missing pricing silently degrades\n * to tokens-only — explicit rather than guessing.\n *\n * **Warn mode.** `onExceeded: \"warn\"` logs a single warning the first\n * time a cap is breached and lets the run continue. Useful for\n * measuring real-world traffic against a proposed cap before flipping\n * to `\"abort\"` in production.\n *\n * **Contract / SLO mode.** Pass `contract` to enforce a declarative\n * service-level objective — `maxCostUSD`, `maxLatencyMs`, `maxTokens` —\n * on top of the legacy caps, with a single `onViolation` reaction:\n * `\"abort\"` hard-stops with `BudgetExceededError`; `\"fallback\"` records\n * a typed signal (read it via {@link readBudgetFallbackSignal}), fires\n * the optional `fallback` callback, and lets the run continue so an\n * outer layer can degrade gracefully. The contract's clauses are\n * evaluated independently of — and after — the top-level caps; the\n * top-level caps stay fully functional with or without a contract.\n *\n * @example\n * const budgetMiddleware = budget({ maxTokens: 50_000 });\n *\n * const myAgent = agent({\n * model,\n * middleware: [budgetMiddleware],\n * });\n *\n * @example\n * // With USD cap and custom pricing\n * const guard = budget({\n * maxCostUSD: 0.5,\n * pricing: {\n * \"gpt-4o\": { inputPer1K: 0.005, outputPer1K: 0.015 },\n * },\n * });\n *\n * @example\n * // SLO contract — soft-fallback on any breach\n * const sloGuard = budget({\n * pricing: { \"gpt-4o\": { inputPer1K: 0.005, outputPer1K: 0.015 } },\n * contract: {\n * maxCostUSD: 0.05,\n * maxLatencyMs: 8_000,\n * maxTokens: 40_000,\n * onViolation: \"fallback\",\n * fallback: (violation) => routeToCheaperModel(violation.dimension),\n * },\n * });\n */\nexport function budget(options: BudgetOptions): AgentMiddleware {\n const name = options.name ?? \"budget\";\n const onExceeded = options.onExceeded ?? \"abort\";\n const hasTokenCap = typeof options.maxTokens === \"number\";\n const hasCostCap = typeof options.maxCostUSD === \"number\";\n\n const contract = options.contract;\n const contractMode = contract?.onViolation ?? \"abort\";\n const hasContractTokenCap = typeof contract?.maxTokens === \"number\";\n const hasContractCostCap = typeof contract?.maxCostUSD === \"number\";\n const hasContractLatencyCap = typeof contract?.maxLatencyMs === \"number\";\n const contractNeedsCost = hasCostCap || hasContractCostCap;\n\n return {\n name,\n execute: {\n before(context) {\n const counters = namespacedState<BudgetCounters>(context, name);\n counters.set({\n tokens: 0,\n costUSD: 0,\n warned: false,\n startedAt: Date.now(),\n fallbackFired: false,\n });\n },\n },\n trip: {\n async after(context, response) {\n const counters = namespacedState<BudgetCounters>(context, name).get();\n\n if (!counters) {\n return;\n }\n\n counters.tokens += response.usage.total;\n\n if (contractNeedsCost && options.pricing) {\n const pricing = options.pricing[context.model.name];\n\n if (pricing) {\n const tripCost =\n (response.usage.input / 1000) * pricing.inputPer1K +\n (response.usage.output / 1000) * pricing.outputPer1K;\n counters.costUSD += tripCost;\n }\n }\n\n if (hasTokenCap && counters.tokens > options.maxTokens!) {\n if (onExceeded === \"abort\") {\n breach(options.maxTokens!, counters.tokens, \"tokens\", name);\n }\n\n if (!counters.warned) {\n counters.warned = true;\n }\n }\n\n if (hasCostCap && counters.costUSD > options.maxCostUSD!) {\n if (onExceeded === \"abort\") {\n breach(options.maxCostUSD!, counters.costUSD, \"usd\", name);\n }\n\n if (!counters.warned) {\n counters.warned = true;\n }\n }\n\n if (!contract) {\n return;\n }\n\n if (hasContractTokenCap && counters.tokens > contract.maxTokens!) {\n await enforceContract(\n context,\n counters,\n name,\n contractMode,\n contract,\n \"tokens\",\n contract.maxTokens!,\n counters.tokens,\n );\n }\n\n if (hasContractCostCap && counters.costUSD > contract.maxCostUSD!) {\n await enforceContract(\n context,\n counters,\n name,\n contractMode,\n contract,\n \"cost\",\n contract.maxCostUSD!,\n counters.costUSD,\n );\n }\n\n if (hasContractLatencyCap) {\n const elapsedMs = Date.now() - counters.startedAt;\n\n if (elapsedMs > contract.maxLatencyMs!) {\n await enforceContract(\n context,\n counters,\n name,\n contractMode,\n contract,\n \"latency\",\n contract.maxLatencyMs!,\n elapsedMs,\n );\n }\n }\n },\n },\n };\n}\n\n/**\n * Apply the contract's reaction to a single breached clause. `\"abort\"`\n * throws `BudgetExceededError` (stops the run); `\"fallback\"` records the\n * signal once, fires the callback, and returns so the run continues.\n *\n * The callback is invoked at most once per run (guarded by\n * `counters.fallbackFired`) and its rejections are swallowed — a buggy\n * fallback hook must never crash the agent.\n */\nasync function enforceContract(\n context: MiddlewareExecuteContext,\n counters: BudgetCounters,\n name: string,\n mode: NonNullable<BudgetContract[\"onViolation\"]>,\n contract: BudgetContract,\n dimension: BudgetContractDimension,\n limit: number,\n actual: number,\n): Promise<void> {\n if (mode === \"abort\") {\n breachContract(name, dimension, limit, actual);\n }\n\n if (counters.fallbackFired) {\n return;\n }\n\n counters.fallbackFired = true;\n\n const violation: BudgetContractViolation = {\n dimension,\n limit,\n actual,\n mode,\n };\n\n namespacedState<BudgetContractViolation>(context, `${name}.fallback`).set(\n violation,\n );\n\n if (!contract.fallback) {\n return;\n }\n\n try {\n await contract.fallback(violation, context);\n } catch {\n // A fallback callback is a notification hook — its failure must\n // never crash the run. Swallow deliberately.\n }\n}\n"],"mappings":";;;;;;;;;;;AAsHA,MAAM,iBAA8D;CAClE,QAAQ;CACR,MAAM;CACN,SAAS;AACX;AAEA,SAAS,OACP,OACA,QACA,MACA,MACO;CACP,MAAM,IAAI,oBACR,WAAW,KAAK,eAAe,OAAO,GAAG,KAAK,SAAS,MAAM,IAC7D;EAAE;EAAO;EAAQ;CAAK,CACxB;AACF;AAEA,SAAS,eACP,MACA,WACA,OACA,QACO;CACP,MAAM,IAAI,oBACR,WAAW,KAAK,wBAAwB,UAAU,GAAG,OAAO,SAAS,MAAM,IAC3E;EACE;EACA;EACA,MAAM,eAAe;EACrB,SAAS;GAAE;GAAW;GAAO;GAAQ,QAAQ;EAAW;CAC1D,CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,yBACd,OACA,OAAO,UAC2B;CAClC,OAAO,gBACL,EAAE,MAAM,GACR,GAAG,KAAK,UACV,CAAC,CAAC,IAAI;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyEA,SAAgB,OAAO,SAAyC;CAC9D,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,cAAc,OAAO,QAAQ,cAAc;CACjD,MAAM,aAAa,OAAO,QAAQ,eAAe;CAEjD,MAAM,WAAW,QAAQ;CACzB,MAAM,eAAe,UAAU,eAAe;CAC9C,MAAM,sBAAsB,OAAO,UAAU,cAAc;CAC3D,MAAM,qBAAqB,OAAO,UAAU,eAAe;CAC3D,MAAM,wBAAwB,OAAO,UAAU,iBAAiB;CAChE,MAAM,oBAAoB,cAAc;CAExC,OAAO;EACL;EACA,SAAS,EACP,OAAO,SAAS;GAEd,AADiB,gBAAgC,SAAS,IACnD,CAAC,CAAC,IAAI;IACX,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,WAAW,KAAK,IAAI;IACpB,eAAe;GACjB,CAAC;EACH,EACF;EACA,MAAM,EACJ,MAAM,MAAM,SAAS,UAAU;GAC7B,MAAM,WAAW,gBAAgC,SAAS,IAAI,CAAC,CAAC,IAAI;GAEpE,IAAI,CAAC,UACH;GAGF,SAAS,UAAU,SAAS,MAAM;GAElC,IAAI,qBAAqB,QAAQ,SAAS;IACxC,MAAM,UAAU,QAAQ,QAAQ,QAAQ,MAAM;IAE9C,IAAI,SAAS;KACX,MAAM,WACH,SAAS,MAAM,QAAQ,MAAQ,QAAQ,aACvC,SAAS,MAAM,SAAS,MAAQ,QAAQ;KAC3C,SAAS,WAAW;IACtB;GACF;GAEA,IAAI,eAAe,SAAS,SAAS,QAAQ,WAAY;IACvD,IAAI,eAAe,SACjB,OAAO,QAAQ,WAAY,SAAS,QAAQ,UAAU,IAAI;IAG5D,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;GAEtB;GAEA,IAAI,cAAc,SAAS,UAAU,QAAQ,YAAa;IACxD,IAAI,eAAe,SACjB,OAAO,QAAQ,YAAa,SAAS,SAAS,OAAO,IAAI;IAG3D,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;GAEtB;GAEA,IAAI,CAAC,UACH;GAGF,IAAI,uBAAuB,SAAS,SAAS,SAAS,WACpD,MAAM,gBACJ,SACA,UACA,MACA,cACA,UACA,UACA,SAAS,WACT,SAAS,MACX;GAGF,IAAI,sBAAsB,SAAS,UAAU,SAAS,YACpD,MAAM,gBACJ,SACA,UACA,MACA,cACA,UACA,QACA,SAAS,YACT,SAAS,OACX;GAGF,IAAI,uBAAuB;IACzB,MAAM,YAAY,KAAK,IAAI,IAAI,SAAS;IAExC,IAAI,YAAY,SAAS,cACvB,MAAM,gBACJ,SACA,UACA,MACA,cACA,UACA,WACA,SAAS,cACT,SACF;GAEJ;EACF,EACF;CACF;AACF;;;;;;;;;;AAWA,eAAe,gBACb,SACA,UACA,MACA,MACA,UACA,WACA,OACA,QACe;CACf,IAAI,SAAS,SACX,eAAe,MAAM,WAAW,OAAO,MAAM;CAG/C,IAAI,SAAS,eACX;CAGF,SAAS,gBAAgB;CAEzB,MAAM,YAAqC;EACzC;EACA;EACA;EACA;CACF;CAEA,gBAAyC,SAAS,GAAG,KAAK,UAAU,CAAC,CAAC,IACpE,SACF;CAEA,IAAI,CAAC,SAAS,UACZ;CAGF,IAAI;EACF,MAAM,SAAS,SAAS,WAAW,OAAO;CAC5C,QAAQ,CAGR;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EmbedderContract } from "../../contracts/embedder.contract.mjs";
|
|
2
1
|
import { AgentMiddleware } from "../../contracts/middleware/middleware.contract.mjs";
|
|
2
|
+
import { EmbedderContract } from "../../contracts/embedder.contract.mjs";
|
|
3
3
|
import { CacheDriver } from "@warlock.js/cache";
|
|
4
4
|
|
|
5
5
|
//#region ../@warlock.js/ai/src/middleware/builtins/semantic-cache.d.ts
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BudgetContract, BudgetContractDimension, BudgetContractFallback, BudgetContractViolation, BudgetContractViolationMode } from "./builtins/budget-contract.type.mjs";
|
|
2
|
+
import { BudgetFallbackSignal, BudgetOptions, BudgetPricing, budget, readBudgetFallbackSignal } from "./builtins/budget.mjs";
|
|
2
3
|
import { GuardrailCheck, GuardrailCheckResult, GuardrailOptions, guardrail } from "./builtins/guardrail.mjs";
|
|
3
4
|
import { SemanticCacheOptions, semanticCache } from "./builtins/semantic-cache.mjs";
|
|
4
5
|
import { composeMiddleware } from "./helpers/compose.mjs";
|
package/esm/middleware/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { extractUserText } from "./utils/extract-user-text.mjs";
|
|
2
2
|
import { namespacedState } from "./utils/namespaced-state.mjs";
|
|
3
|
-
import { budget } from "./builtins/budget.mjs";
|
|
3
|
+
import { budget, readBudgetFallbackSignal } from "./builtins/budget.mjs";
|
|
4
4
|
import { guardrail } from "./builtins/guardrail.mjs";
|
|
5
5
|
import { semanticCache } from "./builtins/semantic-cache.mjs";
|
|
6
6
|
import { composeMiddleware } from "./helpers/compose.mjs";
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { MiddlewareExecuteContext, MiddlewareToolContext, MiddlewareTripContext } from "../contracts/middleware/middleware-context.type.mjs";
|
|
1
|
+
import { MiddlewareExecuteContext, MiddlewareSupervisorContext, MiddlewareToolContext, MiddlewareTripContext } from "../contracts/middleware/middleware-context.type.mjs";
|
|
2
2
|
import { AgentMiddleware } from "../contracts/middleware/middleware.contract.mjs";
|
|
3
3
|
import { Logger } from "@warlock.js/logger";
|
|
4
4
|
|
|
5
5
|
//#region ../@warlock.js/ai/src/middleware/pipeline.d.ts
|
|
6
6
|
/**
|
|
7
|
-
* The
|
|
8
|
-
* `AgentMiddleware`'s optional `execute` / `trip` / `tool`
|
|
9
|
-
* Kept as a single named union so callers can pass
|
|
10
|
-
* inline-duplicating the literals.
|
|
7
|
+
* The four levels at which middleware can hook — mirrors
|
|
8
|
+
* `AgentMiddleware`'s optional `execute` / `trip` / `tool` /
|
|
9
|
+
* `supervisor` keys. Kept as a single named union so callers can pass
|
|
10
|
+
* it around without inline-duplicating the literals. The first three
|
|
11
|
+
* fire on the agent pipeline; `supervisor` fires once around a whole
|
|
12
|
+
* `supervisor.execute()` run.
|
|
11
13
|
*/
|
|
12
|
-
type MiddlewareLevel = "execute" | "trip" | "tool";
|
|
14
|
+
type MiddlewareLevel = "execute" | "trip" | "tool" | "supervisor";
|
|
13
15
|
/**
|
|
14
16
|
* Shape of the context object for each level. The pipeline is
|
|
15
17
|
* level-parameterized on the ctx type via this mapping so callers
|
|
@@ -19,6 +21,7 @@ type MiddlewareContextByLevel = {
|
|
|
19
21
|
execute: MiddlewareExecuteContext;
|
|
20
22
|
trip: MiddlewareTripContext;
|
|
21
23
|
tool: MiddlewareToolContext;
|
|
24
|
+
supervisor: MiddlewareSupervisorContext;
|
|
22
25
|
};
|
|
23
26
|
/**
|
|
24
27
|
* Run an inner async operation through a stack of agent middlewares
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/middleware/pipeline.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"pipeline.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/middleware/pipeline.ts"],"mappings":";;;;;;;;;AAmBA;;;;KAAY,eAAA;AAOZ;;;;;AAAA,KAAY,wBAAA;EACV,OAAA,EAAS,wBAAA;EACT,IAAA,EAAM,qBAAA;EACN,IAAA,EAAM,qBAAA;EACN,UAAA,EAAY,2BAAA;AAAA;;;;;;;;AAA2B;AA+CzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMkB;iBANI,WAAA,eAA0B,eAAA,WAC9C,WAAA,EAAa,aAAA,CAAc,eAAA,GAC3B,KAAA,EAAO,KAAA,EACP,OAAA,EAAS,wBAAA,CAAyB,KAAA,GAClC,KAAA,QAAa,OAAA,CAAQ,OAAA,GACrB,MAAA,GAAS,MAAA,GACR,OAAA,CAAQ,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/middleware/pipeline.ts"],"sourcesContent":["import type { Logger } from \"@warlock.js/logger\";\nimport type {\n AgentMiddleware,\n MiddlewareExecuteContext,\n MiddlewareToolContext,\n MiddlewareTripContext,\n} from \"../contracts/middleware\";\n\nconst LOG_MODULE = \"ai.middleware\";\n\n/**\n * The
|
|
1
|
+
{"version":3,"file":"pipeline.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/middleware/pipeline.ts"],"sourcesContent":["import type { Logger } from \"@warlock.js/logger\";\nimport type {\n AgentMiddleware,\n MiddlewareExecuteContext,\n MiddlewareSupervisorContext,\n MiddlewareToolContext,\n MiddlewareTripContext,\n} from \"../contracts/middleware\";\n\nconst LOG_MODULE = \"ai.middleware\";\n\n/**\n * The four levels at which middleware can hook — mirrors\n * `AgentMiddleware`'s optional `execute` / `trip` / `tool` /\n * `supervisor` keys. Kept as a single named union so callers can pass\n * it around without inline-duplicating the literals. The first three\n * fire on the agent pipeline; `supervisor` fires once around a whole\n * `supervisor.execute()` run.\n */\nexport type MiddlewareLevel = \"execute\" | \"trip\" | \"tool\" | \"supervisor\";\n\n/**\n * Shape of the context object for each level. The pipeline is\n * level-parameterized on the ctx type via this mapping so callers\n * get compile-time narrowing when they instantiate `runPipeline`.\n */\nexport type MiddlewareContextByLevel = {\n execute: MiddlewareExecuteContext;\n trip: MiddlewareTripContext;\n tool: MiddlewareToolContext;\n supervisor: MiddlewareSupervisorContext;\n};\n\n/**\n * Run an inner async operation through a stack of agent middlewares\n * at a single level, applying the onion-model before/after/onError\n * semantics documented on `AgentMiddleware`.\n *\n * **Semantics.**\n * - `before` hooks run in registration order (top-down).\n * Returning a defined value from a `before` hook short-circuits the\n * pipeline with that value as the result, skipping `inner()` and\n * all deeper `before` / `after` hooks — but outer middleware\n * `after` hooks (registered earlier) still run on the synthetic\n * value.\n * - `after` hooks run in reverse registration order (bottom-up).\n * Returning a defined value replaces the result before it\n * propagates further out. Returning `void` / `undefined` keeps the\n * existing result.\n * - `onError` hooks also run in reverse (bottom-up) — any error\n * thrown by `inner()`, by a `before` hook, or by an `after` hook\n * unwinds through each frame's `onError` in turn. Returning a\n * defined value from `onError` recovers: the error is cleared and\n * the returned value becomes the new result (which then flows\n * through outer `after` hooks). Returning `void` propagates the\n * error to the next outer frame.\n *\n * **Implementation.** Built by folding the middleware array from the\n * end inward: each middleware produces a closure that wraps the\n * previous closure (the deeper pipeline). The outermost wrap is\n * middleware index 0 — so registration order matches onion order\n * without any reverse iteration at call time.\n *\n * **No magic.** The pipeline does not swallow, retry, or translate\n * errors. Hooks that throw propagate unchanged (subject to `onError`\n * recovery). Pipeline-level logging is debug-only and respects each\n * middleware's `log: false` kill-switch.\n *\n * @example\n * const response = await runPipeline(\n * middlewares,\n * \"trip\",\n * tripContext,\n * () => model.complete(messages, callOptions),\n * logger,\n * );\n */\nexport async function runPipeline<Level extends MiddlewareLevel, TResult>(\n middlewares: ReadonlyArray<AgentMiddleware>,\n level: Level,\n context: MiddlewareContextByLevel[Level],\n inner: () => Promise<TResult>,\n logger?: Logger,\n): Promise<TResult> {\n if (middlewares.length === 0) {\n return inner();\n }\n\n let next: () => Promise<TResult> = inner;\n\n for (let index = middlewares.length - 1; index >= 0; index--) {\n const middleware = middlewares[index];\n const hooks = middleware[level];\n\n if (!hooks) {\n continue;\n }\n\n const previous = next;\n\n next = async () => {\n const logEnabled = middleware.log !== false && logger !== undefined;\n\n if (hooks.before) {\n if (logEnabled) {\n logger!.debug(LOG_MODULE, `${level}.before`, middleware.name, {\n middleware: middleware.name,\n level,\n });\n }\n\n const shortCircuit = await (\n hooks.before as (ctx: unknown) => Promise<unknown> | unknown\n )(context);\n\n if (shortCircuit !== undefined) {\n if (logEnabled) {\n logger!.debug(\n LOG_MODULE,\n `${level}.short-circuit`,\n middleware.name,\n {\n middleware: middleware.name,\n level,\n },\n );\n }\n\n return shortCircuit as TResult;\n }\n }\n\n let result: TResult;\n\n try {\n result = await previous();\n } catch (thrown) {\n if (!hooks.onError) {\n throw thrown;\n }\n\n const recovered = await (\n hooks.onError as (\n ctx: unknown,\n error: unknown,\n ) => Promise<unknown> | unknown\n )(context, thrown);\n\n if (recovered === undefined) {\n throw thrown;\n }\n\n if (logEnabled) {\n logger!.debug(LOG_MODULE, `${level}.recovered`, middleware.name, {\n middleware: middleware.name,\n level,\n });\n }\n\n result = recovered as TResult;\n }\n\n if (hooks.after) {\n const replacement = await (\n hooks.after as (\n ctx: unknown,\n value: unknown,\n ) => Promise<unknown> | unknown\n )(context, result);\n\n if (replacement !== undefined) {\n result = replacement as TResult;\n }\n\n if (logEnabled) {\n logger!.debug(LOG_MODULE, `${level}.after`, middleware.name, {\n middleware: middleware.name,\n level,\n });\n }\n }\n\n return result;\n };\n }\n\n return next();\n}\n"],"mappings":";AASA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEnB,eAAsB,YACpB,aACA,OACA,SACA,OACA,QACkB;CAClB,IAAI,YAAY,WAAW,GACzB,OAAO,MAAM;CAGf,IAAI,OAA+B;CAEnC,KAAK,IAAI,QAAQ,YAAY,SAAS,GAAG,SAAS,GAAG,SAAS;EAC5D,MAAM,aAAa,YAAY;EAC/B,MAAM,QAAQ,WAAW;EAEzB,IAAI,CAAC,OACH;EAGF,MAAM,WAAW;EAEjB,OAAO,YAAY;GACjB,MAAM,aAAa,WAAW,QAAQ,SAAS,WAAW;GAE1D,IAAI,MAAM,QAAQ;IAChB,IAAI,YACF,OAAQ,MAAM,YAAY,GAAG,MAAM,UAAU,WAAW,MAAM;KAC5D,YAAY,WAAW;KACvB;IACF,CAAC;IAGH,MAAM,eAAe,MACnB,MAAM,OACN,OAAO;IAET,IAAI,iBAAiB,QAAW;KAC9B,IAAI,YACF,OAAQ,MACN,YACA,GAAG,MAAM,iBACT,WAAW,MACX;MACE,YAAY,WAAW;MACvB;KACF,CACF;KAGF,OAAO;IACT;GACF;GAEA,IAAI;GAEJ,IAAI;IACF,SAAS,MAAM,SAAS;GAC1B,SAAS,QAAQ;IACf,IAAI,CAAC,MAAM,SACT,MAAM;IAGR,MAAM,YAAY,MAChB,MAAM,QAIN,SAAS,MAAM;IAEjB,IAAI,cAAc,QAChB,MAAM;IAGR,IAAI,YACF,OAAQ,MAAM,YAAY,GAAG,MAAM,aAAa,WAAW,MAAM;KAC/D,YAAY,WAAW;KACvB;IACF,CAAC;IAGH,SAAS;GACX;GAEA,IAAI,MAAM,OAAO;IACf,MAAM,cAAc,MAClB,MAAM,MAIN,SAAS,MAAM;IAEjB,IAAI,gBAAgB,QAClB,SAAS;IAGX,IAAI,YACF,OAAQ,MAAM,YAAY,GAAG,MAAM,SAAS,WAAW,MAAM;KAC3D,YAAY,WAAW;KACvB;IACF,CAAC;GAEL;GAEA,OAAO;EACT;CACF;CAEA,OAAO,KAAK;AACd"}
|
package/esm/mock/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MockModelResponse, MockSDKConfig } from "./mock-config.type.mjs";
|
|
2
2
|
import { mockAgent } from "./mock-agent.mjs";
|
|
3
3
|
import { MockModel } from "./mock-model.mjs";
|
|
4
|
+
import { MockRouterDecision, MockRouterExhaustion, MockRouterOptions, mockRouter } from "./mock-router.mjs";
|
|
4
5
|
import { MockSDK } from "./mock-sdk.mjs";
|
package/esm/mock/index.mjs
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { RouteContext } from "../contracts/supervisor/route-context.type.mjs";
|
|
2
|
+
import { Next } from "../contracts/supervisor/next.type.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai/src/mock/mock-router.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* A canned routing decision for {@link mockRouter}. Either a literal
|
|
7
|
+
* {@link Next} value (intent key, fan-out array, or the `END`
|
|
8
|
+
* sentinel) or a predicate that derives the decision from the live
|
|
9
|
+
* {@link RouteContext} — the latter lets a test branch on accumulated
|
|
10
|
+
* state without scripting an exact per-iteration sequence.
|
|
11
|
+
*/
|
|
12
|
+
type MockRouterDecision<TState = Record<string, unknown>> = Next | ((context: RouteContext<TState>) => Next);
|
|
13
|
+
/**
|
|
14
|
+
* Behavior when the canned decision queue is exhausted before the
|
|
15
|
+
* supervisor terminates on its own.
|
|
16
|
+
*
|
|
17
|
+
* - `"end"` (default) — return `END`, terminating the run cleanly. The
|
|
18
|
+
* common case: script the interesting turns, let the run stop.
|
|
19
|
+
* - `"throw"` — throw, surfacing the over-run as a test failure. Use
|
|
20
|
+
* when every iteration must be accounted for.
|
|
21
|
+
* - `"repeat"` — replay the last decision for every further iteration.
|
|
22
|
+
* Useful for "keep routing to the same intent until evaluate is
|
|
23
|
+
* satisfied" scenarios.
|
|
24
|
+
*/
|
|
25
|
+
type MockRouterExhaustion = "end" | "throw" | "repeat";
|
|
26
|
+
/**
|
|
27
|
+
* Options for {@link mockRouter}.
|
|
28
|
+
*/
|
|
29
|
+
type MockRouterOptions = {
|
|
30
|
+
/** What to do once the decision queue is exhausted. Default `"end"`. */onExhausted?: MockRouterExhaustion;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Build a deterministic `route` callback that replays a canned
|
|
34
|
+
* sequence of routing decisions — one per supervisor iteration — for
|
|
35
|
+
* testing supervisors without an LLM router.
|
|
36
|
+
*
|
|
37
|
+
* Drop the returned callback into `ai.supervisor({ route: mockRouter([...]) })`
|
|
38
|
+
* in place of an LLM `router`. The Nth iteration consumes the Nth
|
|
39
|
+
* decision; a function decision is evaluated against the live
|
|
40
|
+
* `RouteContext`. When the queue runs out, behavior follows
|
|
41
|
+
* `options.onExhausted` (default: terminate with `END`).
|
|
42
|
+
*
|
|
43
|
+
* Pairs with the `toRouteTo` / `toConverge` matchers to assert the
|
|
44
|
+
* resulting report tree.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const supervisor = ai.supervisor({
|
|
48
|
+
* name: "draft-then-review",
|
|
49
|
+
* intents: { writer, critic },
|
|
50
|
+
* route: mockRouter(["writer", "critic", END]),
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Branch on accumulated state, repeat the last decision until done.
|
|
55
|
+
* route: mockRouter(
|
|
56
|
+
* ["research", (ctx) => (ctx.state.summary ? END : "research")],
|
|
57
|
+
* { onExhausted: "repeat" },
|
|
58
|
+
* );
|
|
59
|
+
*/
|
|
60
|
+
declare function mockRouter<TState = Record<string, unknown>>(decisions: MockRouterDecision<TState>[], options?: MockRouterOptions): (context: RouteContext<TState>) => Next;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { MockRouterDecision, MockRouterExhaustion, MockRouterOptions, mockRouter };
|
|
63
|
+
//# sourceMappingURL=mock-router.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-router.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/mock/mock-router.ts"],"mappings":";;;;;;AAWA;;;;;KAAY,kBAAA,UAA4B,MAAA,qBACpC,IAAA,KACE,OAAA,EAAS,YAAA,CAAa,MAAA,MAAY,IAAA;;;;;;;;;;;;;KAc5B,oBAAA;AAAZ;;;AAAA,KAKY,iBAAA;EALoB,wEAO9B,WAAA,GAAc,oBAAoB;AAAA;;;AAAA;AA+BpC;;;;;;;;;;;;;;;;;;;;;;;;AAG0C;iBAH1B,UAAA,UAAoB,MAAA,mBAClC,SAAA,EAAW,kBAAA,CAAmB,MAAA,KAC9B,OAAA,GAAS,iBAAA,IACP,OAAA,EAAS,YAAA,CAAa,MAAA,MAAY,IAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { END } from "../contracts/end.type.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai/src/mock/mock-router.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build a deterministic `route` callback that replays a canned
|
|
6
|
+
* sequence of routing decisions — one per supervisor iteration — for
|
|
7
|
+
* testing supervisors without an LLM router.
|
|
8
|
+
*
|
|
9
|
+
* Drop the returned callback into `ai.supervisor({ route: mockRouter([...]) })`
|
|
10
|
+
* in place of an LLM `router`. The Nth iteration consumes the Nth
|
|
11
|
+
* decision; a function decision is evaluated against the live
|
|
12
|
+
* `RouteContext`. When the queue runs out, behavior follows
|
|
13
|
+
* `options.onExhausted` (default: terminate with `END`).
|
|
14
|
+
*
|
|
15
|
+
* Pairs with the `toRouteTo` / `toConverge` matchers to assert the
|
|
16
|
+
* resulting report tree.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const supervisor = ai.supervisor({
|
|
20
|
+
* name: "draft-then-review",
|
|
21
|
+
* intents: { writer, critic },
|
|
22
|
+
* route: mockRouter(["writer", "critic", END]),
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Branch on accumulated state, repeat the last decision until done.
|
|
27
|
+
* route: mockRouter(
|
|
28
|
+
* ["research", (ctx) => (ctx.state.summary ? END : "research")],
|
|
29
|
+
* { onExhausted: "repeat" },
|
|
30
|
+
* );
|
|
31
|
+
*/
|
|
32
|
+
function mockRouter(decisions, options = {}) {
|
|
33
|
+
const onExhausted = options.onExhausted ?? "end";
|
|
34
|
+
let cursor = 0;
|
|
35
|
+
return (context) => {
|
|
36
|
+
if (cursor < decisions.length) {
|
|
37
|
+
const decision = decisions[cursor];
|
|
38
|
+
cursor++;
|
|
39
|
+
return resolveDecision(decision, context);
|
|
40
|
+
}
|
|
41
|
+
if (onExhausted === "throw") throw new Error(`mockRouter exhausted after ${decisions.length} decision(s) at iteration ${context.iteration}`);
|
|
42
|
+
if (onExhausted === "repeat" && decisions.length > 0) return resolveDecision(decisions[decisions.length - 1], context);
|
|
43
|
+
return END;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve a single decision entry into a concrete {@link Next} —
|
|
48
|
+
* invoking the predicate form against the live context, or returning
|
|
49
|
+
* the literal form verbatim.
|
|
50
|
+
*/
|
|
51
|
+
function resolveDecision(decision, context) {
|
|
52
|
+
if (typeof decision === "function") return decision(context);
|
|
53
|
+
return decision;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { mockRouter };
|
|
58
|
+
//# sourceMappingURL=mock-router.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-router.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/mock/mock-router.ts"],"sourcesContent":["import { END } from \"../contracts/end.type\";\nimport type { Next } from \"../contracts/supervisor/next.type\";\nimport type { RouteContext } from \"../contracts/supervisor/route-context.type\";\n\n/**\n * A canned routing decision for {@link mockRouter}. Either a literal\n * {@link Next} value (intent key, fan-out array, or the `END`\n * sentinel) or a predicate that derives the decision from the live\n * {@link RouteContext} — the latter lets a test branch on accumulated\n * state without scripting an exact per-iteration sequence.\n */\nexport type MockRouterDecision<TState = Record<string, unknown>> =\n | Next\n | ((context: RouteContext<TState>) => Next);\n\n/**\n * Behavior when the canned decision queue is exhausted before the\n * supervisor terminates on its own.\n *\n * - `\"end\"` (default) — return `END`, terminating the run cleanly. The\n * common case: script the interesting turns, let the run stop.\n * - `\"throw\"` — throw, surfacing the over-run as a test failure. Use\n * when every iteration must be accounted for.\n * - `\"repeat\"` — replay the last decision for every further iteration.\n * Useful for \"keep routing to the same intent until evaluate is\n * satisfied\" scenarios.\n */\nexport type MockRouterExhaustion = \"end\" | \"throw\" | \"repeat\";\n\n/**\n * Options for {@link mockRouter}.\n */\nexport type MockRouterOptions = {\n /** What to do once the decision queue is exhausted. Default `\"end\"`. */\n onExhausted?: MockRouterExhaustion;\n};\n\n/**\n * Build a deterministic `route` callback that replays a canned\n * sequence of routing decisions — one per supervisor iteration — for\n * testing supervisors without an LLM router.\n *\n * Drop the returned callback into `ai.supervisor({ route: mockRouter([...]) })`\n * in place of an LLM `router`. The Nth iteration consumes the Nth\n * decision; a function decision is evaluated against the live\n * `RouteContext`. When the queue runs out, behavior follows\n * `options.onExhausted` (default: terminate with `END`).\n *\n * Pairs with the `toRouteTo` / `toConverge` matchers to assert the\n * resulting report tree.\n *\n * @example\n * const supervisor = ai.supervisor({\n * name: \"draft-then-review\",\n * intents: { writer, critic },\n * route: mockRouter([\"writer\", \"critic\", END]),\n * });\n *\n * @example\n * // Branch on accumulated state, repeat the last decision until done.\n * route: mockRouter(\n * [\"research\", (ctx) => (ctx.state.summary ? END : \"research\")],\n * { onExhausted: \"repeat\" },\n * );\n */\nexport function mockRouter<TState = Record<string, unknown>>(\n decisions: MockRouterDecision<TState>[],\n options: MockRouterOptions = {},\n): (context: RouteContext<TState>) => Next {\n const onExhausted = options.onExhausted ?? \"end\";\n let cursor = 0;\n\n return (context: RouteContext<TState>): Next => {\n if (cursor < decisions.length) {\n const decision = decisions[cursor];\n cursor++;\n\n return resolveDecision(decision, context);\n }\n\n if (onExhausted === \"throw\") {\n throw new Error(\n `mockRouter exhausted after ${decisions.length} decision(s) at iteration ${context.iteration}`,\n );\n }\n\n if (onExhausted === \"repeat\" && decisions.length > 0) {\n return resolveDecision(decisions[decisions.length - 1], context);\n }\n\n return END;\n };\n}\n\n/**\n * Resolve a single decision entry into a concrete {@link Next} —\n * invoking the predicate form against the live context, or returning\n * the literal form verbatim.\n */\nfunction resolveDecision<TState>(\n decision: MockRouterDecision<TState>,\n context: RouteContext<TState>,\n): Next {\n if (typeof decision === \"function\") {\n return decision(context);\n }\n\n return decision;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEA,SAAgB,WACd,WACA,UAA6B,CAAC,GACW;CACzC,MAAM,cAAc,QAAQ,eAAe;CAC3C,IAAI,SAAS;CAEb,QAAQ,YAAwC;EAC9C,IAAI,SAAS,UAAU,QAAQ;GAC7B,MAAM,WAAW,UAAU;GAC3B;GAEA,OAAO,gBAAgB,UAAU,OAAO;EAC1C;EAEA,IAAI,gBAAgB,SAClB,MAAM,IAAI,MACR,8BAA8B,UAAU,OAAO,4BAA4B,QAAQ,WACrF;EAGF,IAAI,gBAAgB,YAAY,UAAU,SAAS,GACjD,OAAO,gBAAgB,UAAU,UAAU,SAAS,IAAI,OAAO;EAGjE,OAAO;CACT;AACF;;;;;;AAOA,SAAS,gBACP,UACA,SACM;CACN,IAAI,OAAO,aAAa,YACtB,OAAO,SAAS,OAAO;CAGzB,OAAO;AACT"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ModelContract } from "../contracts/model.contract.mjs";
|
|
2
|
+
import { FallbackModelContract, FallbackModelOptions } from "../contracts/fallback-model.contract.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai/src/model/fallback-model.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* A `ModelContract` that wraps an ordered list of models and tries each
|
|
7
|
+
* in turn, advancing to the next only when the current one fails with a
|
|
8
|
+
* matching (transient) provider error.
|
|
9
|
+
*
|
|
10
|
+
* **Role.** A drop-in `ModelContract` for resilience: hand it to any
|
|
11
|
+
* agent / workflow / supervisor in place of a single model and provider
|
|
12
|
+
* outages, rate-limits, and timeouts transparently fail over to a
|
|
13
|
+
* backup. Non-transient failures (bad key, oversized prompt, blocked
|
|
14
|
+
* content) re-throw immediately rather than wastefully retrying.
|
|
15
|
+
*
|
|
16
|
+
* **What it owns / doesn't own.** Owns the ordered model list, the
|
|
17
|
+
* retry decision, and per-call usage aggregation across attempted
|
|
18
|
+
* models. Does NOT own retry/backoff timing (it advances instantly to
|
|
19
|
+
* the next model — pair it with a backoff middleware if you want delay)
|
|
20
|
+
* nor any provider I/O of its own; every call is delegated to a wrapped
|
|
21
|
+
* model.
|
|
22
|
+
*
|
|
23
|
+
* **Streaming fall-over caveat.** `stream()` can only fail over while no
|
|
24
|
+
* chunk has been emitted yet. Once the first `delta` / `tool-call`
|
|
25
|
+
* reaches the consumer, the partial output cannot be un-sent, so a
|
|
26
|
+
* mid-stream failure propagates instead of restarting on the next
|
|
27
|
+
* model.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const model = fallbackModel([
|
|
31
|
+
* ai.openai.model({ name: "gpt-4o" }),
|
|
32
|
+
* ai.anthropic.model({ name: "claude-3-5-sonnet" }),
|
|
33
|
+
* ]);
|
|
34
|
+
* const agent = ai.agent({ model });
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // custom retry predicate
|
|
38
|
+
* const model = fallbackModel([primary, backup], {
|
|
39
|
+
* retryOn: (error) => error instanceof ProviderError,
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
declare function fallbackModel(models: ModelContract[], options?: FallbackModelOptions): FallbackModelContract;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { fallbackModel };
|
|
45
|
+
//# sourceMappingURL=fallback-model.d.mts.map
|