@mupt-ai/dari-gateway 0.2.0 → 0.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/README.md +8 -4
- package/dist/bun.d.ts +2 -0
- package/dist/bun.js +4 -0
- package/dist/context/eval_catalog.d.ts +3 -0
- package/dist/context/eval_catalog.js +7 -0
- package/dist/context/interaction_fingerprint.d.ts +27 -0
- package/dist/context/interaction_fingerprint.js +519 -0
- package/dist/context/interaction_fingerprint_format.d.ts +42 -0
- package/dist/context/interaction_fingerprint_format.js +113 -0
- package/dist/context/interaction_lease_depth.d.ts +2 -0
- package/dist/context/interaction_lease_depth.js +110 -0
- package/dist/context/lease_history.d.ts +7 -0
- package/dist/context/lease_history.js +158 -0
- package/dist/context/pricing.d.ts +4 -0
- package/dist/context/pricing.js +27 -0
- package/dist/context/provider_prompt_accounting.d.ts +14 -0
- package/dist/context/provider_prompt_accounting.js +187 -0
- package/dist/context/routing_context.d.ts +40 -0
- package/dist/context/routing_context.js +211 -0
- package/dist/http/auth.d.ts +2 -0
- package/dist/http/auth.js +35 -0
- package/dist/http/errors.d.ts +19 -0
- package/dist/http/errors.js +89 -0
- package/dist/http/handler.d.ts +12 -0
- package/dist/http/handler.js +88 -0
- package/dist/http/http2_server.d.ts +6 -0
- package/dist/http/http2_server.js +103 -0
- package/dist/http/serve.d.ts +20 -0
- package/dist/http/serve.js +78 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +9 -0
- package/dist/ingress/anthropic_messages.d.ts +3 -0
- package/dist/ingress/anthropic_messages.js +122 -0
- package/dist/ingress/anthropic_models.d.ts +1 -0
- package/dist/ingress/anthropic_models.js +23 -0
- package/dist/ingress/chat_completions.d.ts +2 -0
- package/dist/ingress/chat_completions.js +44 -0
- package/dist/ingress/claude_code/request_context.d.ts +2 -0
- package/dist/ingress/claude_code/request_context.js +31 -0
- package/dist/ingress/codex.d.ts +3 -0
- package/dist/ingress/codex.js +69 -0
- package/dist/ingress/codex_models.d.ts +2 -0
- package/dist/ingress/codex_models.js +89 -0
- package/dist/ingress/json.d.ts +1 -0
- package/dist/ingress/json.js +95 -0
- package/dist/ingress/models.d.ts +1 -0
- package/dist/ingress/models.js +65 -0
- package/dist/ingress/muse_code.d.ts +2 -0
- package/dist/ingress/muse_code.js +8 -0
- package/dist/ingress/muse_models.d.ts +9 -0
- package/dist/ingress/muse_models.js +32 -0
- package/dist/interaction/capabilities.d.ts +3 -0
- package/dist/interaction/capabilities.js +24 -0
- package/dist/interaction/image.d.ts +4 -0
- package/dist/interaction/image.js +13 -0
- package/dist/interaction/portable_reasoning_state.d.ts +4 -0
- package/dist/interaction/portable_reasoning_state.js +18 -0
- package/dist/interaction/provider_continuation_state.d.ts +6 -0
- package/dist/interaction/provider_continuation_state.js +27 -0
- package/dist/interaction/tool_identity.d.ts +10 -0
- package/dist/interaction/tool_identity.js +41 -0
- package/dist/interaction/types.d.ts +204 -0
- package/dist/interaction/types.js +5 -0
- package/dist/models/catalog.d.ts +35 -0
- package/dist/models/catalog.js +232 -0
- package/dist/models/context_limits.d.ts +2 -0
- package/dist/models/context_limits.js +7 -0
- package/dist/models/execution_provider.d.ts +9 -0
- package/dist/models/execution_provider.js +17 -0
- package/dist/models/gateways.d.ts +11 -0
- package/dist/models/gateways.js +45 -0
- package/dist/models/ids.d.ts +1 -0
- package/dist/models/ids.js +15 -0
- package/dist/models/provider_defaults.d.ts +4 -0
- package/dist/models/provider_defaults.js +44 -0
- package/dist/models/runtime_overrides.d.ts +7 -0
- package/dist/models/runtime_overrides.js +226 -0
- package/dist/models/subscriptions.d.ts +8 -0
- package/dist/models/subscriptions.js +37 -0
- package/dist/observability/errors.d.ts +8 -0
- package/dist/observability/errors.js +11 -0
- package/dist/platform/platform.d.ts +156 -0
- package/dist/platform/platform.js +11 -0
- package/dist/platform/routing_contract.d.ts +2 -0
- package/dist/platform/routing_contract.js +5 -0
- package/dist/protocol/input/anthropic_messages.d.ts +2 -0
- package/dist/protocol/input/anthropic_messages.js +494 -0
- package/dist/protocol/input/chat_completions.d.ts +4 -0
- package/dist/protocol/input/chat_completions.js +214 -0
- package/dist/protocol/input/chat_completions_validation.d.ts +2 -0
- package/dist/protocol/input/chat_completions_validation.js +258 -0
- package/dist/protocol/input/openai_responses.d.ts +2 -0
- package/dist/protocol/input/openai_responses.js +666 -0
- package/dist/protocol/input/router_model.d.ts +7 -0
- package/dist/protocol/input/router_model.js +43 -0
- package/dist/protocol/output/anthropic_messages.d.ts +10 -0
- package/dist/protocol/output/anthropic_messages.js +15 -0
- package/dist/protocol/output/anthropic_messages_output.d.ts +10 -0
- package/dist/protocol/output/anthropic_messages_output.js +124 -0
- package/dist/protocol/output/anthropic_messages_stream.d.ts +16 -0
- package/dist/protocol/output/anthropic_messages_stream.js +267 -0
- package/dist/protocol/output/chat_completions.d.ts +29 -0
- package/dist/protocol/output/chat_completions.js +406 -0
- package/dist/protocol/output/chat_reasoning_detail.d.ts +2 -0
- package/dist/protocol/output/chat_reasoning_detail.js +13 -0
- package/dist/protocol/output/delivered_stream_snapshot.d.ts +10 -0
- package/dist/protocol/output/delivered_stream_snapshot.js +31 -0
- package/dist/protocol/output/openai_responses.d.ts +10 -0
- package/dist/protocol/output/openai_responses.js +14 -0
- package/dist/protocol/output/openai_responses_output.d.ts +14 -0
- package/dist/protocol/output/openai_responses_output.js +125 -0
- package/dist/protocol/output/openai_responses_stream.d.ts +18 -0
- package/dist/protocol/output/openai_responses_stream.js +546 -0
- package/dist/protocol/output/sse_stream.d.ts +31 -0
- package/dist/protocol/output/sse_stream.js +67 -0
- package/dist/providers/anthropic_safeguards.d.ts +11 -0
- package/dist/providers/anthropic_safeguards.js +81 -0
- package/dist/providers/aws_assume_role.d.ts +17 -0
- package/dist/providers/aws_assume_role.js +107 -0
- package/dist/providers/cancel_estimate.d.ts +43 -0
- package/dist/providers/cancel_estimate.js +89 -0
- package/dist/providers/execution.d.ts +72 -0
- package/dist/providers/execution.js +491 -0
- package/dist/providers/model_capabilities.d.ts +11 -0
- package/dist/providers/model_capabilities.js +29 -0
- package/dist/providers/pi.d.ts +65 -0
- package/dist/providers/pi.js +325 -0
- package/dist/providers/pi_context.d.ts +7 -0
- package/dist/providers/pi_context.js +405 -0
- package/dist/providers/pi_options.d.ts +11 -0
- package/dist/providers/pi_options.js +410 -0
- package/dist/providers/pi_output.d.ts +2 -0
- package/dist/providers/pi_output.js +453 -0
- package/dist/providers/provider_attempt_runner.d.ts +55 -0
- package/dist/providers/provider_attempt_runner.js +90 -0
- package/dist/providers/provider_call.d.ts +77 -0
- package/dist/providers/provider_call.js +296 -0
- package/dist/providers/provider_reported_cost.d.ts +10 -0
- package/dist/providers/provider_reported_cost.js +123 -0
- package/dist/providers/reasoning.d.ts +12 -0
- package/dist/providers/reasoning.js +30 -0
- package/dist/providers/response_capture.d.ts +23 -0
- package/dist/providers/response_capture.js +47 -0
- package/dist/providers/stream_finalization.d.ts +46 -0
- package/dist/providers/stream_finalization.js +165 -0
- package/dist/providers/subscription_limits.d.ts +33 -0
- package/dist/providers/subscription_limits.js +179 -0
- package/dist/providers/subscription_usage.d.ts +10 -0
- package/dist/providers/subscription_usage.js +79 -0
- package/dist/runtime/claude_code/auxiliary_routing.d.ts +3 -0
- package/dist/runtime/claude_code/auxiliary_routing.js +60 -0
- package/dist/runtime/customer_disconnect.d.ts +20 -0
- package/dist/runtime/customer_disconnect.js +49 -0
- package/dist/runtime/explicit_model.d.ts +7 -0
- package/dist/runtime/explicit_model.js +57 -0
- package/dist/runtime/harness_identity/conversation.d.ts +33 -0
- package/dist/runtime/harness_identity/conversation.js +39 -0
- package/dist/runtime/harness_identity/extract.d.ts +8 -0
- package/dist/runtime/harness_identity/extract.js +214 -0
- package/dist/runtime/harness_identity/index.d.ts +2 -0
- package/dist/runtime/harness_identity/index.js +2 -0
- package/dist/runtime/prepare_routed_completion.d.ts +33 -0
- package/dist/runtime/prepare_routed_completion.js +186 -0
- package/dist/runtime/provider_stream_lifecycle.d.ts +31 -0
- package/dist/runtime/provider_stream_lifecycle.js +88 -0
- package/dist/runtime/provider_stream_outcome.d.ts +23 -0
- package/dist/runtime/provider_stream_outcome.js +55 -0
- package/dist/runtime/request_tags.d.ts +7 -0
- package/dist/runtime/request_tags.js +33 -0
- package/dist/runtime/routed_completion.d.ts +17 -0
- package/dist/runtime/routed_completion.js +80 -0
- package/dist/runtime/routed_protocol.d.ts +30 -0
- package/dist/runtime/routed_protocol.js +1 -0
- package/dist/runtime/routed_runtime.d.ts +40 -0
- package/dist/runtime/routed_runtime.js +535 -0
- package/dist/runtime/routing_lease.d.ts +26 -0
- package/dist/runtime/routing_lease.js +210 -0
- package/dist/runtime/runtime_reporter.d.ts +71 -0
- package/dist/runtime/runtime_reporter.js +35 -0
- package/dist/runtime/strict_provider_access.d.ts +6 -0
- package/dist/runtime/strict_provider_access.js +40 -0
- package/dist/self_host/config.d.ts +46 -0
- package/dist/self_host/config.js +105 -0
- package/dist/self_host/main.d.ts +16 -0
- package/dist/self_host/main.js +95 -0
- package/dist/self_host/platform.d.ts +13 -0
- package/dist/self_host/platform.js +50 -0
- package/dist/self_host/prefix_store.d.ts +46 -0
- package/dist/self_host/prefix_store.js +261 -0
- package/dist/self_host/resolve.d.ts +28 -0
- package/dist/self_host/resolve.js +167 -0
- package/dist/self_host/subscriptions/access.d.ts +10 -0
- package/dist/self_host/subscriptions/access.js +37 -0
- package/dist/self_host/subscriptions/http.d.ts +15 -0
- package/dist/self_host/subscriptions/http.js +187 -0
- package/dist/self_host/subscriptions/index.d.ts +24 -0
- package/dist/self_host/subscriptions/index.js +24 -0
- package/dist/self_host/subscriptions/oauth.d.ts +59 -0
- package/dist/self_host/subscriptions/oauth.js +376 -0
- package/dist/self_host/subscriptions/store.d.ts +78 -0
- package/dist/self_host/subscriptions/store.js +236 -0
- package/dist/strategies/auto_routing_selector.d.ts +12 -0
- package/dist/strategies/auto_routing_selector.js +61 -0
- package/dist/strategies/eval_relevance.d.ts +17 -0
- package/dist/strategies/eval_relevance.js +32 -0
- package/dist/strategies/fallback_decision.d.ts +8 -0
- package/dist/strategies/fallback_decision.js +17 -0
- package/dist/strategies/priority_selector.d.ts +7 -0
- package/dist/strategies/priority_selector.js +37 -0
- package/dist/strategies/route_decision.d.ts +22 -0
- package/dist/strategies/route_decision.js +65 -0
- package/dist/strategies/routing.d.ts +74 -0
- package/dist/strategies/routing.js +280 -0
- package/dist/strategies/selector_projection.d.ts +15 -0
- package/dist/strategies/selector_projection.js +161 -0
- package/dist/telemetry/background.d.ts +7 -0
- package/dist/telemetry/background.js +33 -0
- package/dist/telemetry/benchmark_replay.d.ts +37 -0
- package/dist/telemetry/benchmark_replay.js +108 -0
- package/dist/telemetry/billing_finalization.d.ts +64 -0
- package/dist/telemetry/billing_finalization.js +96 -0
- package/dist/telemetry/ingest_values.d.ts +4 -0
- package/dist/telemetry/ingest_values.js +12 -0
- package/dist/telemetry/model_cost.d.ts +3 -0
- package/dist/telemetry/model_cost.js +15 -0
- package/dist/telemetry/prefix_state.d.ts +47 -0
- package/dist/telemetry/prefix_state.js +116 -0
- package/dist/telemetry/report.d.ts +88 -0
- package/dist/telemetry/report.js +621 -0
- package/dist/telemetry/requests/interaction_observability.d.ts +45 -0
- package/dist/telemetry/requests/interaction_observability.js +180 -0
- package/dist/telemetry/requests/retained_interaction_request.d.ts +8 -0
- package/dist/telemetry/requests/retained_interaction_request.js +10 -0
- package/dist/telemetry/scrub.d.ts +2 -0
- package/dist/telemetry/scrub.js +31 -0
- package/dist/telemetry/skills/claude/catalog.d.ts +6 -0
- package/dist/telemetry/skills/claude/catalog.js +99 -0
- package/dist/telemetry/skills/claude/file_reads.d.ts +4 -0
- package/dist/telemetry/skills/claude/file_reads.js +110 -0
- package/dist/telemetry/skills/claude/observations.d.ts +3 -0
- package/dist/telemetry/skills/claude/observations.js +366 -0
- package/dist/telemetry/skills/codex/catalog.d.ts +11 -0
- package/dist/telemetry/skills/codex/catalog.js +138 -0
- package/dist/telemetry/skills/codex/exec_file_reads.d.ts +9 -0
- package/dist/telemetry/skills/codex/exec_file_reads.js +36 -0
- package/dist/telemetry/skills/codex/explicit.d.ts +3 -0
- package/dist/telemetry/skills/codex/explicit.js +90 -0
- package/dist/telemetry/skills/codex/file_reads.d.ts +4 -0
- package/dist/telemetry/skills/codex/file_reads.js +37 -0
- package/dist/telemetry/skills/codex/identity.d.ts +2 -0
- package/dist/telemetry/skills/codex/identity.js +20 -0
- package/dist/telemetry/skills/observations.d.ts +21 -0
- package/dist/telemetry/skills/observations.js +41 -0
- package/dist/telemetry/skills/paths.d.ts +11 -0
- package/dist/telemetry/skills/paths.js +61 -0
- package/dist/telemetry/skills/pi.d.ts +6 -0
- package/dist/telemetry/skills/pi.js +223 -0
- package/dist/telemetry/skills/shell_file_reads.d.ts +7 -0
- package/dist/telemetry/skills/shell_file_reads.js +226 -0
- package/dist/telemetry/skills/shell_words.d.ts +13 -0
- package/dist/telemetry/skills/shell_words.js +189 -0
- package/dist/telemetry/tools/input.d.ts +2 -0
- package/dist/telemetry/tools/input.js +14 -0
- package/dist/telemetry/tools/observations.d.ts +29 -0
- package/dist/telemetry/tools/observations.js +247 -0
- package/dist/telemetry/usage.d.ts +57 -0
- package/dist/telemetry/usage.js +136 -0
- package/dist/types.d.ts +97 -0
- package/dist/types.js +1 -0
- package/package.json +22 -7
- package/src/bun.ts +2 -2
- package/src/context/eval_catalog.ts +1 -1
- package/src/context/interaction_fingerprint.ts +9 -9
- package/src/context/interaction_fingerprint_format.ts +1 -1
- package/src/context/interaction_lease_depth.ts +1 -1
- package/src/context/pricing.ts +6 -3
- package/src/context/provider_prompt_accounting.ts +5 -5
- package/src/context/routing_context.ts +16 -16
- package/src/http/auth.ts +1 -1
- package/src/http/handler.ts +15 -8
- package/src/http/serve.ts +3 -3
- package/src/index.ts +14 -6
- package/src/ingress/anthropic_messages.ts +12 -12
- package/src/ingress/anthropic_models.ts +1 -1
- package/src/ingress/chat_completions.ts +17 -10
- package/src/ingress/claude_code/request_context.ts +2 -2
- package/src/ingress/codex.ts +14 -10
- package/src/ingress/codex_models.ts +4 -4
- package/src/ingress/json.ts +1 -1
- package/src/ingress/models.ts +22 -10
- package/src/ingress/muse_code.ts +9 -0
- package/src/ingress/muse_models.ts +36 -0
- package/src/interaction/capabilities.ts +1 -1
- package/src/interaction/portable_reasoning_state.ts +1 -1
- package/src/interaction/provider_continuation_state.ts +1 -1
- package/src/interaction/tool_identity.ts +2 -2
- package/src/interaction/types.ts +2 -0
- package/src/models/catalog.ts +310 -0
- package/src/models/context_limits.ts +1 -1
- package/src/models/execution_provider.ts +5 -2
- package/src/models/ids.ts +1 -1
- package/src/models/provider_defaults.ts +4 -1
- package/src/models/runtime_overrides.ts +23 -1
- package/src/models/subscriptions.ts +54 -0
- package/src/observability/errors.ts +2 -2
- package/src/platform/platform.ts +9 -5
- package/src/protocol/input/anthropic_messages.ts +7 -7
- package/src/protocol/input/chat_completions.ts +8 -6
- package/src/protocol/input/chat_completions_validation.ts +6 -5
- package/src/protocol/input/openai_responses.ts +6 -6
- package/src/protocol/output/anthropic_messages.ts +3 -3
- package/src/protocol/output/anthropic_messages_output.ts +6 -4
- package/src/protocol/output/anthropic_messages_stream.ts +7 -7
- package/src/protocol/output/chat_completions.ts +13 -13
- package/src/protocol/output/chat_reasoning_detail.ts +1 -1
- package/src/protocol/output/openai_responses.ts +3 -3
- package/src/protocol/output/openai_responses_output.ts +4 -2
- package/src/protocol/output/openai_responses_stream.ts +6 -6
- package/src/protocol/output/sse_stream.ts +3 -3
- package/src/providers/anthropic_safeguards.ts +2 -2
- package/src/providers/aws_assume_role.ts +2 -2
- package/src/providers/cancel_estimate.ts +1 -1
- package/src/providers/execution.ts +17 -17
- package/src/providers/pi.ts +34 -19
- package/src/providers/pi_context.ts +6 -6
- package/src/providers/pi_options.ts +27 -11
- package/src/providers/pi_output.ts +5 -5
- package/src/providers/provider_attempt_runner.ts +1 -1
- package/src/providers/provider_call.ts +21 -17
- package/src/providers/provider_reported_cost.ts +3 -3
- package/src/providers/reasoning.ts +2 -2
- package/src/providers/stream_finalization.ts +9 -9
- package/src/providers/subscription_limits.ts +24 -7
- package/src/runtime/claude_code/auxiliary_routing.ts +5 -5
- package/src/runtime/customer_disconnect.ts +1 -1
- package/src/runtime/explicit_model.ts +19 -20
- package/src/runtime/harness_identity/conversation.ts +1 -1
- package/src/runtime/harness_identity/extract.ts +3 -3
- package/src/runtime/harness_identity/index.ts +2 -2
- package/src/runtime/prepare_routed_completion.ts +35 -16
- package/src/runtime/provider_stream_lifecycle.ts +1 -1
- package/src/runtime/provider_stream_outcome.ts +2 -2
- package/src/runtime/request_tags.ts +2 -2
- package/src/runtime/routed_completion.ts +11 -11
- package/src/runtime/routed_protocol.ts +2 -2
- package/src/runtime/routed_runtime.ts +20 -19
- package/src/runtime/routing_lease.ts +7 -7
- package/src/runtime/runtime_reporter.ts +3 -3
- package/src/runtime/strict_provider_access.ts +42 -0
- package/src/self_host/main.ts +12 -12
- package/src/self_host/platform.ts +5 -5
- package/src/self_host/prefix_store.ts +2 -2
- package/src/self_host/resolve.ts +17 -31
- package/src/self_host/subscriptions/access.ts +2 -2
- package/src/self_host/subscriptions/http.ts +2 -2
- package/src/self_host/subscriptions/index.ts +4 -4
- package/src/self_host/subscriptions/oauth.ts +139 -8
- package/src/self_host/subscriptions/store.ts +2 -1
- package/src/strategies/auto_routing_selector.ts +2 -2
- package/src/strategies/eval_relevance.ts +3 -3
- package/src/strategies/priority_selector.ts +1 -1
- package/src/strategies/route_decision.ts +7 -7
- package/src/strategies/routing.ts +10 -10
- package/src/strategies/selector_projection.ts +2 -2
- package/src/telemetry/billing_finalization.ts +3 -3
- package/src/telemetry/model_cost.ts +1 -1
- package/src/telemetry/prefix_state.ts +4 -4
- package/src/telemetry/report.ts +21 -21
- package/src/telemetry/requests/interaction_observability.ts +1 -1
- package/src/telemetry/requests/retained_interaction_request.ts +1 -1
- package/src/telemetry/skills/claude/catalog.ts +1 -1
- package/src/telemetry/skills/claude/file_reads.ts +6 -6
- package/src/telemetry/skills/claude/observations.ts +3 -3
- package/src/telemetry/skills/codex/catalog.ts +3 -3
- package/src/telemetry/skills/codex/exec_file_reads.ts +4 -4
- package/src/telemetry/skills/codex/explicit.ts +3 -3
- package/src/telemetry/skills/codex/file_reads.ts +5 -5
- package/src/telemetry/skills/observations.ts +7 -7
- package/src/telemetry/skills/pi.ts +3 -3
- package/src/telemetry/skills/shell_file_reads.ts +2 -2
- package/src/telemetry/skills/shell_words.ts +1 -1
- package/src/telemetry/tools/input.ts +1 -1
- package/src/telemetry/tools/observations.ts +9 -9
- package/src/telemetry/usage.ts +5 -5
- package/src/types.ts +20 -3
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ docker run -p 8080:8080 \
|
|
|
64
64
|
| `DARI_GATEWAY_DB` | SQLite database for subscriptions and conversation state. Default `dari-gateway.db`. It stores subscription tokens in plaintext; protect it like a secret. |
|
|
65
65
|
| `DARI_AUTO_ROUTING_API_KEY` | Optional. Select among a router's models with Dari Auto Routing. |
|
|
66
66
|
| `PORT` | Listen port. Default `8080`. |
|
|
67
|
-
| Provider keys | Each provider's standard variable, such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `OPENROUTER_API_KEY`. |
|
|
67
|
+
| Provider keys | Each provider's standard variable, such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `META_API_KEY`, or `OPENROUTER_API_KEY`. |
|
|
68
68
|
|
|
69
69
|
## Configure
|
|
70
70
|
|
|
@@ -88,13 +88,17 @@ providers: # endpoints outside the built-in catalog
|
|
|
88
88
|
|
|
89
89
|
Model ids are `provider/model`. Clients send `model: "dari/routing"` to let the router choose, or `model: "dari/model/<id>"` to pin one of its models. A Claude Code subscription serves Anthropic models only for Claude Code itself; a ChatGPT subscription serves OpenAI models for every client.
|
|
90
90
|
|
|
91
|
+
### Model catalog
|
|
92
|
+
|
|
93
|
+
`GET /v1/models?detail=1` (any gateway key; optionally `&provider=openrouter,meta`) and `listModelCatalog({ providers?, fetch?, signal? })` list each provider's tool-capable models as `dari/model/<id>`-ready ids with context window, max output tokens, input modalities, accepted thinking levels, and list prices in USD per million tokens (input, output, cache read, cache write, long-context tiers) with `price_as_of`. OpenRouter and Vercel AI Gateway are read live from their public models endpoints; other providers (default: OpenAI, Anthropic, Meta, xAI, Fireworks) come from the bundled Pi catalog.
|
|
94
|
+
|
|
91
95
|
## Coding agents
|
|
92
96
|
|
|
93
97
|
```bash
|
|
94
98
|
export DARI_GATEWAY_URL=http://localhost:8080
|
|
95
99
|
export DARI_GATEWAY_KEY=<one of DARI_GATEWAY_KEYS>
|
|
96
100
|
|
|
97
|
-
dari credentials subscription connect claude-code # or codex; repeat to pool plans
|
|
101
|
+
dari credentials subscription connect claude-code # or codex or muse-code; repeat to pool plans
|
|
98
102
|
dari claude
|
|
99
103
|
```
|
|
100
104
|
|
|
@@ -115,8 +119,8 @@ The root entry needs no Bun APIs, so any runtime with web-standard `fetch`, `Req
|
|
|
115
119
|
|
|
116
120
|
Platforms can also:
|
|
117
121
|
|
|
118
|
-
- implement `resolveProviderCredential({ provider, model, routerId, organizationId, apiKey })` to supply a per-request `{ apiKey, baseUrl?, headers? }`, such as a tenant's own key, in place of the router's resolved key;
|
|
119
|
-
- resolve routers with `strict: true` to serve only the requested `dari/model/<id>` at the requested thinking level, with no selector, lease, fallback, or personal subscription;
|
|
122
|
+
- implement `resolveProviderCredential({ provider, model, routerId, organizationId, apiKey, clientHarness? })` to supply a per-request `{ apiKey, baseUrl?, headers?, subscription? }`, such as a tenant's own key, in place of the router's resolved key. With `subscription` (`"openai_codex"`, `"anthropic_claude_code"`, or `"meta_muse"`), `apiKey` is that plan's access token: Codex's `auth.json` `tokens.access_token`, the token `claude setup-token` prints, or the Model API key a Meta sign-in mints. The platform keeps it fresh. Strict routers serve these supplied plans, but never subscriptions the gateway resolved itself. A plan serves only its own provider's models; a Claude Code plan serves only Claude Code clients, and a Muse Code plan only Muse Code clients (recognized by their `x-client-id`);
|
|
123
|
+
- resolve routers with `strict: true` to serve only the requested `dari/model/<id>` at the requested thinking level, with no selector, lease, fallback, or personal subscription. Set `strict_providers: ["openai"]` on the resolved router to permit any bundled-catalog model from that provider without enumerating `enabled_models`. Use `dari/model/openai/gpt-5.5`; bare IDs and unknown models remain refused. This grant is ignored on non-strict routers and does not discover uncatalogued models;
|
|
120
124
|
- read exact usage from `recordRequest`: the summary carries input, output, cache-read, and cache-write tokens and `total_cost_usd`, preferring the provider's billed cost (`cost_source: "provider"`, from OpenRouter and Vercel AI Gateway). Failed and aborted requests are recorded too;
|
|
121
125
|
- attribute cost with `X-Dari-Tags: run=r1,trial=3`, which is parsed into `RequestRecord.tags` and the `recordUsage` payload's `tags`.
|
|
122
126
|
|
package/dist/bun.d.ts
ADDED
package/dist/bun.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Convert public cards into hidden ratio-calibration inputs. Cards without a
|
|
2
|
+
// sync timestamp or scores carry no signal and are dropped.
|
|
3
|
+
export function publicCatalogSelectorEvals(catalog) {
|
|
4
|
+
if (!catalog)
|
|
5
|
+
return [];
|
|
6
|
+
return catalog.filter((card) => card.updated_at !== null && card.scores.length > 0);
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { InteractionOutput, InteractionRequest, ProviderIdentity } from "../interaction/types.js";
|
|
2
|
+
export type InteractionPrefixContext = {
|
|
3
|
+
chainByModel: ReadonlyMap<string, string[]>;
|
|
4
|
+
itemIndexByHashByModel: ReadonlyMap<string, ReadonlyMap<string, number>>;
|
|
5
|
+
looseChain: string[];
|
|
6
|
+
fingerprints: {
|
|
7
|
+
tool_choice_fp: string;
|
|
8
|
+
response_format_fp: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Hash variants sent during router resolution. The portable chain covers
|
|
13
|
+
* providers that receive readable history only. Exact continuation-source
|
|
14
|
+
* variants cover a return to the originating model. Because provider APIs
|
|
15
|
+
* are unknown before resolution, Responses tool history also contributes a
|
|
16
|
+
* composite call/item-ID variant for OpenAI-compatible Responses targets.
|
|
17
|
+
*/
|
|
18
|
+
export declare function interactionPrefixLookupHashes(routerId: string, request: InteractionRequest): string[];
|
|
19
|
+
export declare function interactionLooseChain(routerId: string, request: InteractionRequest): string[];
|
|
20
|
+
export declare function interactionConversationPrefix(request: InteractionRequest): string;
|
|
21
|
+
export declare function interactionPrefixContext(routerId: string, request: InteractionRequest, targets: ReadonlyMap<string, ProviderIdentity>): InteractionPrefixContext;
|
|
22
|
+
export declare function interactionPrefixChain(routerId: string, request: InteractionRequest, target: ProviderIdentity | null): string[];
|
|
23
|
+
export declare function extendInteractionChain(lastHash: string, output: InteractionOutput, target: ProviderIdentity, policy: {
|
|
24
|
+
includeItemIds: boolean;
|
|
25
|
+
sourceProtocol: InteractionRequest["sourceProtocol"];
|
|
26
|
+
}): string[];
|
|
27
|
+
export declare function extendInteractionLooseChain(lastHash: string, output: InteractionOutput, sourceProtocol: InteractionRequest["sourceProtocol"]): string[];
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { stableStringify } from "@mupt-ai/dari-router/policy-engine";
|
|
3
|
+
import { anthropicMessagesToInteraction } from "../protocol/input/anthropic_messages.js";
|
|
4
|
+
import { internalChatCompletionsToInteraction } from "../protocol/input/chat_completions.js";
|
|
5
|
+
import { anthropicContent } from "../protocol/output/anthropic_messages_output.js";
|
|
6
|
+
import { assistantMessagePayload } from "../protocol/output/chat_completions.js";
|
|
7
|
+
import { compatibleProviderContinuation } from "../interaction/provider_continuation_state.js";
|
|
8
|
+
import { providerToolCallId, toolWireName } from "../interaction/tool_identity.js";
|
|
9
|
+
import { ROUTING_PREFIX_HASHES_MAX_COUNT } from "../platform/routing_contract.js";
|
|
10
|
+
import { appendFingerprintContentText, canonicalInteractionFingerprintStep, interactionFingerprintContent, interactionFingerprintHeadHash, interactionOptionsFingerprints, normalizedAssistantFingerprintContent, } from "./interaction_fingerprint_format.js";
|
|
11
|
+
const MAX_CONTINUATION_TARGETS = 16;
|
|
12
|
+
// The loose tier ignores reasoning and item ids, so provider identity and
|
|
13
|
+
// tool-call-id composition cannot influence it: one chain per request.
|
|
14
|
+
const LOOSE_POLICY = {
|
|
15
|
+
includeItemIds: false,
|
|
16
|
+
includePhase: false,
|
|
17
|
+
canonicalizeFunctionArguments: true,
|
|
18
|
+
loose: true,
|
|
19
|
+
};
|
|
20
|
+
// Salts the loose chain's seed so the two hash tiers can never collide in
|
|
21
|
+
// the shared prefix-entry lookup space.
|
|
22
|
+
const LOOSE_SEED_SALT = "fp-loose-1";
|
|
23
|
+
const CONVERSATION_PREFIX_VERSION = "conversation-prefix-v1";
|
|
24
|
+
/**
|
|
25
|
+
* Hash variants sent during router resolution. The portable chain covers
|
|
26
|
+
* providers that receive readable history only. Exact continuation-source
|
|
27
|
+
* variants cover a return to the originating model. Because provider APIs
|
|
28
|
+
* are unknown before resolution, Responses tool history also contributes a
|
|
29
|
+
* composite call/item-ID variant for OpenAI-compatible Responses targets.
|
|
30
|
+
*/
|
|
31
|
+
export function interactionPrefixLookupHashes(routerId, request) {
|
|
32
|
+
const targets = [null];
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
for (let index = request.items.length - 1; index >= 0 && seen.size < MAX_CONTINUATION_TARGETS; index -= 1) {
|
|
35
|
+
const item = request.items[index];
|
|
36
|
+
if (item.type !== "reasoning" || !item.continuation)
|
|
37
|
+
continue;
|
|
38
|
+
const key = identityKey(item.continuation.source);
|
|
39
|
+
if (seen.has(key))
|
|
40
|
+
continue;
|
|
41
|
+
seen.add(key);
|
|
42
|
+
targets.push(item.continuation.source);
|
|
43
|
+
}
|
|
44
|
+
const chains = targets.map((target) => interactionPrefixChain(routerId, request, target));
|
|
45
|
+
if (request.sourceProtocol === "openai_responses") {
|
|
46
|
+
chains.push(interactionPrefixChainFor(routerId, request, null, "openai-responses"));
|
|
47
|
+
}
|
|
48
|
+
chains.push(interactionLooseChain(routerId, request));
|
|
49
|
+
return deepestDistinctHashes(chains, ROUTING_PREFIX_HASHES_MAX_COUNT);
|
|
50
|
+
}
|
|
51
|
+
// The loose (client-durable) chain: what the conversation looks like once
|
|
52
|
+
// reasoning payloads, item ids, and provider-specific canonicalization are
|
|
53
|
+
// stripped. Distinct seed keeps its hashes disjoint from the strict tier.
|
|
54
|
+
export function interactionLooseChain(routerId, request) {
|
|
55
|
+
return fingerprintChain(sha256(`${interactionFingerprintHeadHash(routerId, request)}\u0000${LOOSE_SEED_SALT}`), looseFingerprintSteps(request.sourceProtocol, request.items));
|
|
56
|
+
}
|
|
57
|
+
// Stable logical-conversation discriminator. This deliberately excludes the
|
|
58
|
+
// cache head (instructions and tools) and hashes only the first normalized
|
|
59
|
+
// transcript step. Growing history and tool changes preserve identity, while
|
|
60
|
+
// unrelated work reusing the same harness session gets a new group.
|
|
61
|
+
export function interactionConversationPrefix(request) {
|
|
62
|
+
const firstStep = conversationFingerprintSteps(looseFingerprintSteps(request.sourceProtocol, request.items)).at(0);
|
|
63
|
+
return sha256(stableStringify({
|
|
64
|
+
version: CONVERSATION_PREFIX_VERSION,
|
|
65
|
+
firstStep: firstStep === undefined
|
|
66
|
+
? null
|
|
67
|
+
: canonicalInteractionFingerprintStep(firstStep),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
export function interactionPrefixContext(routerId, request, targets) {
|
|
71
|
+
const chainByModel = new Map();
|
|
72
|
+
const itemIndexByHashByModel = new Map();
|
|
73
|
+
for (const [model, target] of targets) {
|
|
74
|
+
const steps = interactionPrefixStepsFor(routerId, request, target, target.api);
|
|
75
|
+
chainByModel.set(model, steps.map((step) => step.hash));
|
|
76
|
+
itemIndexByHashByModel.set(model, new Map(steps.map((step) => [step.hash, step.itemIndex])));
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
chainByModel,
|
|
80
|
+
itemIndexByHashByModel,
|
|
81
|
+
looseChain: interactionLooseChain(routerId, request),
|
|
82
|
+
fingerprints: interactionOptionsFingerprints(request),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Loose fingerprints share the strict grouping logic so step boundaries
|
|
86
|
+
// stay identical whether or not the client echoed reasoning; the policy just
|
|
87
|
+
// drops the volatile fields. Target-independent by construction.
|
|
88
|
+
function looseFingerprintSteps(sourceProtocol, items) {
|
|
89
|
+
return sourceProtocol === "openai_chat"
|
|
90
|
+
? fingerprintChatInputSteps(items, null, null, LOOSE_POLICY)
|
|
91
|
+
: fingerprintSteps(items, null, null, LOOSE_POLICY);
|
|
92
|
+
}
|
|
93
|
+
export function interactionPrefixChain(routerId, request, target) {
|
|
94
|
+
return interactionPrefixStepsFor(routerId, request, target, target?.api ?? null)
|
|
95
|
+
.map((step) => step.hash);
|
|
96
|
+
}
|
|
97
|
+
function interactionPrefixChainFor(routerId, request, target, toolCallApi) {
|
|
98
|
+
return interactionPrefixStepsFor(routerId, request, target, toolCallApi)
|
|
99
|
+
.map((step) => step.hash);
|
|
100
|
+
}
|
|
101
|
+
function interactionPrefixStepsFor(routerId, request, target, toolCallApi) {
|
|
102
|
+
let hash = interactionFingerprintHeadHash(routerId, request);
|
|
103
|
+
const steps = [];
|
|
104
|
+
const responses = request.sourceProtocol === "openai_responses";
|
|
105
|
+
const fingerprintStepsForRequest = conversationFingerprintSteps(request.sourceProtocol === "openai_chat"
|
|
106
|
+
? fingerprintChatInputSteps(request.items, target, toolCallApi)
|
|
107
|
+
: fingerprintSteps(request.items, target, toolCallApi, {
|
|
108
|
+
includeItemIds: true,
|
|
109
|
+
includePhase: responses,
|
|
110
|
+
canonicalizeFunctionArguments: responses,
|
|
111
|
+
}));
|
|
112
|
+
for (const step of fingerprintStepsForRequest) {
|
|
113
|
+
hash = extendFingerprintHash(hash, step);
|
|
114
|
+
steps.push({ hash, itemIndex: step.itemIndex });
|
|
115
|
+
}
|
|
116
|
+
return steps;
|
|
117
|
+
}
|
|
118
|
+
export function extendInteractionChain(lastHash, output, target, policy) {
|
|
119
|
+
let hash = lastHash;
|
|
120
|
+
const extension = [];
|
|
121
|
+
const steps = fingerprintClientEchoSteps(output, target, policy);
|
|
122
|
+
for (const step of steps) {
|
|
123
|
+
if (step.actor === "system" || step.actor === "developer")
|
|
124
|
+
continue;
|
|
125
|
+
hash = extendFingerprintHash(hash, step);
|
|
126
|
+
extension.push(hash);
|
|
127
|
+
}
|
|
128
|
+
return extension;
|
|
129
|
+
}
|
|
130
|
+
function fingerprintClientEchoSteps(output, target, policy) {
|
|
131
|
+
const items = clientEchoItems(output, policy.sourceProtocol);
|
|
132
|
+
if (policy.sourceProtocol === "openai_chat") {
|
|
133
|
+
return fingerprintChatInputSteps(items, target, target.api);
|
|
134
|
+
}
|
|
135
|
+
return fingerprintSteps(items, target, target.api, {
|
|
136
|
+
includeItemIds: policy.includeItemIds,
|
|
137
|
+
includePhase: policy.sourceProtocol === "openai_responses",
|
|
138
|
+
canonicalizeFunctionArguments: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// Prefix extensions must match the assistant output as the next client request
|
|
142
|
+
// will carry it, not necessarily the provider's canonical output items.
|
|
143
|
+
function clientEchoItems(output, sourceProtocol) {
|
|
144
|
+
if (sourceProtocol === "openai_chat") {
|
|
145
|
+
return internalChatCompletionsToInteraction({
|
|
146
|
+
model: "dari/routing",
|
|
147
|
+
messages: [assistantMessagePayload(output)],
|
|
148
|
+
}).items;
|
|
149
|
+
}
|
|
150
|
+
if (sourceProtocol === "anthropic_messages") {
|
|
151
|
+
return anthropicMessagesToInteraction({
|
|
152
|
+
model: "dari/routing",
|
|
153
|
+
max_tokens: 1,
|
|
154
|
+
messages: [{ role: "assistant", content: anthropicContent(output) }],
|
|
155
|
+
}).items;
|
|
156
|
+
}
|
|
157
|
+
return output.items;
|
|
158
|
+
}
|
|
159
|
+
// Loose-tier extension for the reply, mirroring interactionLooseChain's
|
|
160
|
+
// policy: what the next request's loose chain computes at this depth no
|
|
161
|
+
// matter which volatile fields the client's echo keeps.
|
|
162
|
+
export function extendInteractionLooseChain(lastHash, output, sourceProtocol) {
|
|
163
|
+
let hash = lastHash;
|
|
164
|
+
const extension = [];
|
|
165
|
+
const items = clientEchoItems(output, sourceProtocol);
|
|
166
|
+
for (const step of looseFingerprintSteps(sourceProtocol, items)) {
|
|
167
|
+
if (step.actor === "system" || step.actor === "developer")
|
|
168
|
+
continue;
|
|
169
|
+
hash = extendFingerprintHash(hash, step);
|
|
170
|
+
extension.push(hash);
|
|
171
|
+
}
|
|
172
|
+
return extension;
|
|
173
|
+
}
|
|
174
|
+
// Chat carries reasoning, assistant text, and tool calls in one assistant
|
|
175
|
+
// message. Its canonical interaction items intentionally separate those
|
|
176
|
+
// concerns, so prefix hashing must regroup one wire message before hashing.
|
|
177
|
+
function fingerprintChatInputSteps(items, target, toolCallApi, policy) {
|
|
178
|
+
const steps = [];
|
|
179
|
+
const toolCalls = new Map();
|
|
180
|
+
let pendingReasoning = [];
|
|
181
|
+
let pendingReasoningItemIndex = -1;
|
|
182
|
+
const flushReasoning = () => {
|
|
183
|
+
if (pendingReasoning.length === 0)
|
|
184
|
+
return;
|
|
185
|
+
steps.push({
|
|
186
|
+
actor: "assistant",
|
|
187
|
+
itemIndex: pendingReasoningItemIndex,
|
|
188
|
+
content: null,
|
|
189
|
+
calls: [],
|
|
190
|
+
resultCallId: null,
|
|
191
|
+
reasoning: pendingReasoning,
|
|
192
|
+
});
|
|
193
|
+
pendingReasoning = [];
|
|
194
|
+
pendingReasoningItemIndex = -1;
|
|
195
|
+
};
|
|
196
|
+
for (const [itemIndex, item] of items.entries()) {
|
|
197
|
+
if (item.type === "reasoning") {
|
|
198
|
+
if (policy?.loose)
|
|
199
|
+
continue;
|
|
200
|
+
pendingReasoning.push(reasoningFingerprint(item, target, false));
|
|
201
|
+
pendingReasoningItemIndex = itemIndex;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (item.type === "message") {
|
|
205
|
+
if (item.role !== "assistant")
|
|
206
|
+
flushReasoning();
|
|
207
|
+
steps.push({
|
|
208
|
+
actor: item.role,
|
|
209
|
+
itemIndex,
|
|
210
|
+
content: item.role === "assistant"
|
|
211
|
+
? normalizedAssistantFingerprintContent(item.content, item.contentShape)
|
|
212
|
+
: interactionFingerprintContent(item.content, item.contentShape),
|
|
213
|
+
calls: [],
|
|
214
|
+
resultCallId: null,
|
|
215
|
+
...(item.role === "assistant" && pendingReasoning.length > 0
|
|
216
|
+
? { reasoning: pendingReasoning }
|
|
217
|
+
: {}),
|
|
218
|
+
});
|
|
219
|
+
pendingReasoning = [];
|
|
220
|
+
pendingReasoningItemIndex = -1;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (item.type === "reasoning_configuration") {
|
|
224
|
+
// Configuration updates change execution effort without becoming part
|
|
225
|
+
// of the conversational identity, just like request-level effort.
|
|
226
|
+
flushReasoning();
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (item.type === "tool_result") {
|
|
230
|
+
flushReasoning();
|
|
231
|
+
steps.push({
|
|
232
|
+
actor: "tool",
|
|
233
|
+
itemIndex,
|
|
234
|
+
content: interactionFingerprintContent(item.output, item.outputShape),
|
|
235
|
+
calls: [],
|
|
236
|
+
resultCallId: toolCalls.get(item.callId) ?? item.callId,
|
|
237
|
+
});
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
flushReasoning();
|
|
241
|
+
let assistant = steps.at(-1);
|
|
242
|
+
if (!assistant || assistant.actor !== "assistant") {
|
|
243
|
+
assistant = {
|
|
244
|
+
actor: "assistant",
|
|
245
|
+
content: null,
|
|
246
|
+
calls: [],
|
|
247
|
+
resultCallId: null,
|
|
248
|
+
itemIndex,
|
|
249
|
+
};
|
|
250
|
+
steps.push(assistant);
|
|
251
|
+
}
|
|
252
|
+
assistant.itemIndex = itemIndex;
|
|
253
|
+
if (item.type === "tool_call") {
|
|
254
|
+
const id = appendToolCall(assistant, item, toolCallApi, false, policy?.canonicalizeFunctionArguments ?? false);
|
|
255
|
+
toolCalls.set(item.callId, id);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
appendHostedToolCall(assistant, item);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
flushReasoning();
|
|
262
|
+
return steps;
|
|
263
|
+
}
|
|
264
|
+
function fingerprintSteps(items, target, toolCallApi, policy) {
|
|
265
|
+
const steps = [];
|
|
266
|
+
const toolCalls = new Map();
|
|
267
|
+
for (const [itemIndex, item] of items.entries()) {
|
|
268
|
+
if (item.type === "reasoning") {
|
|
269
|
+
if (policy.loose)
|
|
270
|
+
continue;
|
|
271
|
+
const continuation = compatibleProviderContinuation(item.continuation, target);
|
|
272
|
+
const readable = continuationReadableText(item, continuation);
|
|
273
|
+
const itemFingerprint = reasoningFingerprint(item, target, policy.includeItemIds);
|
|
274
|
+
const previous = steps.at(-1);
|
|
275
|
+
if (previous?.actor === "assistant"
|
|
276
|
+
&& (previous.calls.length > 0 || previous.reasoning?.length)) {
|
|
277
|
+
previous.itemIndex = itemIndex;
|
|
278
|
+
previous.content = appendFingerprintContentText(previous.content, readable);
|
|
279
|
+
previous.reasoning = [...(previous.reasoning ?? []), itemFingerprint];
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
steps.push({
|
|
283
|
+
actor: "assistant",
|
|
284
|
+
itemIndex,
|
|
285
|
+
content: readable || null,
|
|
286
|
+
calls: [],
|
|
287
|
+
resultCallId: null,
|
|
288
|
+
reasoning: [itemFingerprint],
|
|
289
|
+
});
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (item.type === "reasoning_configuration") {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (item.type === "tool_result") {
|
|
296
|
+
steps.push({
|
|
297
|
+
actor: "tool",
|
|
298
|
+
itemIndex,
|
|
299
|
+
content: interactionFingerprintContent(item.output, item.outputShape),
|
|
300
|
+
calls: [],
|
|
301
|
+
resultCallId: toolCalls.get(item.callId) ?? item.callId,
|
|
302
|
+
});
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (item.type === "tool_call") {
|
|
306
|
+
let assistant = steps.at(-1);
|
|
307
|
+
if (!assistant || assistant.actor !== "assistant") {
|
|
308
|
+
assistant = {
|
|
309
|
+
actor: "assistant",
|
|
310
|
+
content: null,
|
|
311
|
+
calls: [],
|
|
312
|
+
resultCallId: null,
|
|
313
|
+
itemIndex,
|
|
314
|
+
};
|
|
315
|
+
steps.push(assistant);
|
|
316
|
+
}
|
|
317
|
+
assistant.itemIndex = itemIndex;
|
|
318
|
+
const id = appendToolCall(assistant, item, toolCallApi, policy.includeItemIds, policy.canonicalizeFunctionArguments);
|
|
319
|
+
toolCalls.set(item.callId, id);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (item.type === "hosted_tool_call") {
|
|
323
|
+
let assistant = steps.at(-1);
|
|
324
|
+
if (!assistant || assistant.actor !== "assistant") {
|
|
325
|
+
assistant = {
|
|
326
|
+
actor: "assistant",
|
|
327
|
+
content: null,
|
|
328
|
+
calls: [],
|
|
329
|
+
resultCallId: null,
|
|
330
|
+
itemIndex,
|
|
331
|
+
};
|
|
332
|
+
steps.push(assistant);
|
|
333
|
+
}
|
|
334
|
+
assistant.itemIndex = itemIndex;
|
|
335
|
+
appendHostedToolCall(assistant, item);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
steps.push({
|
|
339
|
+
actor: item.role,
|
|
340
|
+
itemIndex,
|
|
341
|
+
content: policy.loose && item.role === "assistant"
|
|
342
|
+
? normalizedAssistantFingerprintContent(item.content, item.contentShape)
|
|
343
|
+
: interactionFingerprintContent(item.content, item.contentShape),
|
|
344
|
+
calls: [],
|
|
345
|
+
resultCallId: null,
|
|
346
|
+
...(policy.includeItemIds && item.id ? { itemId: item.id } : {}),
|
|
347
|
+
...(policy.includePhase && item.phase ? { phase: item.phase } : {}),
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return steps;
|
|
351
|
+
}
|
|
352
|
+
function reasoningFingerprint(item, target, includeItemId) {
|
|
353
|
+
const continuation = compatibleProviderContinuation(item.continuation, target);
|
|
354
|
+
const readable = continuationReadableText(item, continuation);
|
|
355
|
+
return {
|
|
356
|
+
...(includeItemId && item.id ? { itemId: item.id } : {}),
|
|
357
|
+
readableSha256: sha256(readable),
|
|
358
|
+
...(continuation
|
|
359
|
+
? { continuationStateSha256: sha256(stableStringify(continuation)) }
|
|
360
|
+
: {}),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function appendToolCall(assistant, item, toolCallApi, includeItemId, canonicalizeFunctionArguments) {
|
|
364
|
+
const id = providerToolCallId(item.callId, includeItemId ? item.id : undefined, toolCallApi);
|
|
365
|
+
assistant.calls = [
|
|
366
|
+
...assistant.calls,
|
|
367
|
+
{
|
|
368
|
+
id,
|
|
369
|
+
kind: item.kind,
|
|
370
|
+
name: toolWireName(item.namespace, item.name),
|
|
371
|
+
input: toolArguments(item.kind, item.input, canonicalizeFunctionArguments),
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
return id;
|
|
375
|
+
}
|
|
376
|
+
function appendHostedToolCall(assistant, item) {
|
|
377
|
+
const payload = {
|
|
378
|
+
...item.payload,
|
|
379
|
+
type: item.providerType,
|
|
380
|
+
id: item.id,
|
|
381
|
+
};
|
|
382
|
+
if (item.status)
|
|
383
|
+
payload.status = item.status;
|
|
384
|
+
else
|
|
385
|
+
delete payload.status;
|
|
386
|
+
assistant.calls = [
|
|
387
|
+
...assistant.calls,
|
|
388
|
+
{
|
|
389
|
+
id: item.id,
|
|
390
|
+
kind: "hosted",
|
|
391
|
+
name: item.tool,
|
|
392
|
+
input: payload,
|
|
393
|
+
},
|
|
394
|
+
];
|
|
395
|
+
}
|
|
396
|
+
function continuationReadableText(item, continuation) {
|
|
397
|
+
if (continuation?.kind === "anthropic_thinking")
|
|
398
|
+
return normalizedReadable(continuation.thinking);
|
|
399
|
+
if (continuation?.kind === "anthropic_redacted_thinking")
|
|
400
|
+
return "";
|
|
401
|
+
return normalizedReadable([...item.summary, ...item.content].join("\n"));
|
|
402
|
+
}
|
|
403
|
+
// Streamed reasoning reaches clients as delta concatenations whose whitespace
|
|
404
|
+
// joins differ from the aggregated response body (part separators, trailing
|
|
405
|
+
// newlines), and the client echoes whichever form it saw. Whitespace therefore
|
|
406
|
+
// cannot participate in conversation identity.
|
|
407
|
+
function normalizedReadable(text) {
|
|
408
|
+
return text.replace(/\s+/g, " ").trim();
|
|
409
|
+
}
|
|
410
|
+
function fingerprintChain(seed, steps) {
|
|
411
|
+
let hash = seed;
|
|
412
|
+
return conversationFingerprintSteps(steps).map((step) => {
|
|
413
|
+
hash = extendFingerprintHash(hash, step);
|
|
414
|
+
return hash;
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
function conversationFingerprintSteps(steps) {
|
|
418
|
+
const positional = [];
|
|
419
|
+
let sawConversationStep = false;
|
|
420
|
+
for (const step of steps) {
|
|
421
|
+
if (step.actor !== "system" && step.actor !== "developer") {
|
|
422
|
+
sawConversationStep = true;
|
|
423
|
+
const previous = positional.at(-1);
|
|
424
|
+
if (step.actor === "user" && previous?.actor === "user") {
|
|
425
|
+
previous.content = appendPositionalFingerprintContent(previous.content, step.content);
|
|
426
|
+
previous.itemIndex = step.itemIndex;
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
positional.push({ ...step });
|
|
430
|
+
}
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (!sawConversationStep)
|
|
434
|
+
continue;
|
|
435
|
+
const text = fingerprintReadableText(step.content);
|
|
436
|
+
if (!text)
|
|
437
|
+
continue;
|
|
438
|
+
const previous = positional.at(-1);
|
|
439
|
+
if (previous?.actor === "user" || previous?.actor === "tool") {
|
|
440
|
+
previous.content = appendPositionalFingerprintText(previous.content, text);
|
|
441
|
+
previous.itemIndex = step.itemIndex;
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
positional.push({
|
|
445
|
+
actor: "user",
|
|
446
|
+
content: [{ type: "text", text }],
|
|
447
|
+
calls: [],
|
|
448
|
+
resultCallId: null,
|
|
449
|
+
itemIndex: step.itemIndex,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
return positional;
|
|
453
|
+
}
|
|
454
|
+
function fingerprintReadableText(content) {
|
|
455
|
+
if (typeof content === "string")
|
|
456
|
+
return content;
|
|
457
|
+
if (!Array.isArray(content))
|
|
458
|
+
return "";
|
|
459
|
+
return content.map((part) => part.type === "text" ? part.text : "(image omitted)").join("\n");
|
|
460
|
+
}
|
|
461
|
+
function appendPositionalFingerprintText(content, text) {
|
|
462
|
+
return appendPositionalFingerprintContent(content, [{ type: "text", text }]);
|
|
463
|
+
}
|
|
464
|
+
function appendPositionalFingerprintContent(content, appended) {
|
|
465
|
+
const contentParts = typeof content === "string"
|
|
466
|
+
? [{ type: "text", text: content }]
|
|
467
|
+
: content ?? [];
|
|
468
|
+
const appendedParts = typeof appended === "string"
|
|
469
|
+
? [{ type: "text", text: appended }]
|
|
470
|
+
: appended ?? [];
|
|
471
|
+
return [...contentParts, ...appendedParts];
|
|
472
|
+
}
|
|
473
|
+
function extendFingerprintHash(lastHash, step) {
|
|
474
|
+
return sha256(`${lastHash}\u0000${canonicalInteractionFingerprintStep(step)}`);
|
|
475
|
+
}
|
|
476
|
+
function toolArguments(kind, input, canonicalizeFunctionArguments) {
|
|
477
|
+
if (kind === "custom")
|
|
478
|
+
return JSON.stringify({ input: typeof input === "string" ? input : "" });
|
|
479
|
+
if (canonicalizeFunctionArguments && typeof input === "string") {
|
|
480
|
+
try {
|
|
481
|
+
const parsed = JSON.parse(input);
|
|
482
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
483
|
+
return parsed;
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
// Provider execution reports malformed function arguments separately;
|
|
487
|
+
// fingerprinting itself must remain best-effort.
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return input;
|
|
491
|
+
}
|
|
492
|
+
function deepestDistinctHashes(chains, max) {
|
|
493
|
+
const result = [];
|
|
494
|
+
const seen = new Set();
|
|
495
|
+
for (let depth = 1; result.length < max; depth += 1) {
|
|
496
|
+
let found = false;
|
|
497
|
+
for (const chain of chains) {
|
|
498
|
+
const hash = chain.at(-depth);
|
|
499
|
+
if (!hash)
|
|
500
|
+
continue;
|
|
501
|
+
found = true;
|
|
502
|
+
if (!seen.has(hash)) {
|
|
503
|
+
seen.add(hash);
|
|
504
|
+
result.push(hash);
|
|
505
|
+
if (result.length === max)
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (!found)
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
return result;
|
|
513
|
+
}
|
|
514
|
+
function identityKey(identity) {
|
|
515
|
+
return `${identity.provider}\u0000${identity.api}\u0000${identity.model}`;
|
|
516
|
+
}
|
|
517
|
+
function sha256(text) {
|
|
518
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
519
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { InteractionContent, InteractionRequest } from "../interaction/types.js";
|
|
2
|
+
export type FingerprintContent = string | Array<{
|
|
3
|
+
type: "text";
|
|
4
|
+
text: string;
|
|
5
|
+
} | {
|
|
6
|
+
type: "image";
|
|
7
|
+
url: string;
|
|
8
|
+
detail?: "auto" | "low" | "high" | "original";
|
|
9
|
+
} | {
|
|
10
|
+
type: "tool_reference";
|
|
11
|
+
toolName: string;
|
|
12
|
+
}> | null;
|
|
13
|
+
export type FingerprintCall = {
|
|
14
|
+
id: string;
|
|
15
|
+
kind: "function" | "custom" | "hosted";
|
|
16
|
+
name: string;
|
|
17
|
+
input: unknown;
|
|
18
|
+
};
|
|
19
|
+
export type ReasoningFingerprint = {
|
|
20
|
+
itemId?: string;
|
|
21
|
+
readableSha256: string;
|
|
22
|
+
continuationStateSha256?: string;
|
|
23
|
+
};
|
|
24
|
+
export type InteractionFingerprintStep = {
|
|
25
|
+
actor: "system" | "developer" | "user" | "assistant" | "tool";
|
|
26
|
+
content: FingerprintContent;
|
|
27
|
+
calls: FingerprintCall[];
|
|
28
|
+
resultCallId: string | null;
|
|
29
|
+
itemIndex: number;
|
|
30
|
+
itemId?: string;
|
|
31
|
+
phase?: "commentary" | "final_answer";
|
|
32
|
+
reasoning?: ReasoningFingerprint[];
|
|
33
|
+
};
|
|
34
|
+
export declare function interactionFingerprintHeadHash(routerId: string, request: InteractionRequest): string;
|
|
35
|
+
export declare function interactionOptionsFingerprints(request: InteractionRequest): {
|
|
36
|
+
tool_choice_fp: string;
|
|
37
|
+
response_format_fp: string;
|
|
38
|
+
};
|
|
39
|
+
export declare function canonicalInteractionFingerprintStep(step: InteractionFingerprintStep): string;
|
|
40
|
+
export declare function interactionFingerprintContent(content: string | InteractionContent[] | null, shape: "string" | "parts" | "null" | "absent"): FingerprintContent;
|
|
41
|
+
export declare function normalizedAssistantFingerprintContent(content: string | InteractionContent[] | null, shape: "string" | "parts" | "null" | "absent"): FingerprintContent;
|
|
42
|
+
export declare function appendFingerprintContentText(content: FingerprintContent, text: string): FingerprintContent;
|