@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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createRequestHandler, type GatewayHandlerOptions } from "./http/handler.js";
|
|
2
|
+
export type { GatewayPlatform, OperationalErrorContext, PendingLeaseWrite, PrefixStateWrite, ProviderCredentialRequest, RequestErrorContext, RequestRecord, RequestSummary, RequestTags, ResolveRouterRequest, SubscriptionHold, } from "./platform/platform.js";
|
|
3
|
+
export { DEFAULT_CATALOG_PROVIDERS, listModelCatalog, type CatalogModel, type CatalogModelPrice, type CatalogPriceTier, type ModelCatalogOptions, } from "./models/catalog.js";
|
|
4
|
+
export { createAutoRoutingSelectorBackend } from "./strategies/auto_routing_selector.js";
|
|
5
|
+
export { createPrioritySelectorBackend } from "./strategies/priority_selector.js";
|
|
6
|
+
export { createLlmSelectorBackend, type SelectorBackend } from "./strategies/routing.js";
|
|
7
|
+
export type { ProviderCredential, ResolvedRouter, SubscriptionKind } from "./types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Public entry points. Every module is also importable by path
|
|
2
|
+
// (`@mupt-ai/dari-gateway/<dir>/<module>`) for deployments that compose the
|
|
3
|
+
// gateway with their own platform. This entry needs no Bun APIs; the Bun
|
|
4
|
+
// server and the SQLite-backed self-hosted gateway live in `./bun`.
|
|
5
|
+
export { createRequestHandler } from "./http/handler.js";
|
|
6
|
+
export { DEFAULT_CATALOG_PROVIDERS, listModelCatalog, } from "./models/catalog.js";
|
|
7
|
+
export { createAutoRoutingSelectorBackend } from "./strategies/auto_routing_selector.js";
|
|
8
|
+
export { createPrioritySelectorBackend } from "./strategies/priority_selector.js";
|
|
9
|
+
export { createLlmSelectorBackend } from "./strategies/routing.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SelectorBackend } from "../strategies/routing.js";
|
|
2
|
+
export declare function handleAnthropicMessages(request: Request, routerId: string, selectorBackend: SelectorBackend): Promise<Response>;
|
|
3
|
+
export declare function anthropicErrorResponse(error: unknown): Response;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { isRecord } from "@mupt-ai/dari-router/policy-engine";
|
|
2
|
+
import { anthropicMessagesResponse } from "../protocol/output/anthropic_messages.js";
|
|
3
|
+
import { anthropicMessagesSseStream } from "../protocol/output/anthropic_messages_stream.js";
|
|
4
|
+
import { anthropicToken } from "../http/auth.js";
|
|
5
|
+
import { errorResponseHeaders, normalizeError } from "../http/errors.js";
|
|
6
|
+
import { captureRequestError } from "../observability/errors.js";
|
|
7
|
+
import { anthropicMessagesToInteraction } from "../protocol/input/anthropic_messages.js";
|
|
8
|
+
import { handleRoutedCompletion } from "../runtime/routed_completion.js";
|
|
9
|
+
import { claudeCodeRequestContext } from "./claude_code/request_context.js";
|
|
10
|
+
import { claudeCodeConversationIdentity, } from "../runtime/harness_identity/index.js";
|
|
11
|
+
import { parseJsonRequest } from "./json.js";
|
|
12
|
+
const LEGACY_CLAUDE_CODE_BETA = "claude-code-20250219";
|
|
13
|
+
const SAFEGUARDS_BETA_PREFIX = "dangerous-tool-use-";
|
|
14
|
+
export async function handleAnthropicMessages(request, routerId, selectorBackend) {
|
|
15
|
+
try {
|
|
16
|
+
const apiKey = anthropicToken(request.headers);
|
|
17
|
+
const rawPayload = await parseJsonRequest(request);
|
|
18
|
+
const interaction = anthropicMessagesToInteraction(rawPayload);
|
|
19
|
+
const isClaudeCode = isClaudeCodeRequest(request.headers);
|
|
20
|
+
const anthropicSafeguards = safeguardsFrom(rawPayload, request.headers);
|
|
21
|
+
const forwarded = anthropicSafeguards === undefined
|
|
22
|
+
? interaction
|
|
23
|
+
: { ...interaction, anthropicSafeguards };
|
|
24
|
+
const payload = isClaudeCode
|
|
25
|
+
? {
|
|
26
|
+
...forwarded,
|
|
27
|
+
agentContext: claudeCodeRequestContext(request.headers),
|
|
28
|
+
// Native tool results may be followed by harness-injected text.
|
|
29
|
+
// Preserve their structural continuation semantics, not prompt inference.
|
|
30
|
+
...(latestMessageHasToolResult(rawPayload.messages)
|
|
31
|
+
? { requestPurpose: "tool_continuation" } : {}),
|
|
32
|
+
claudeCodeUserAgent: request.headers.get("user-agent").trim(),
|
|
33
|
+
}
|
|
34
|
+
: forwarded;
|
|
35
|
+
return await handleRoutedCompletion({
|
|
36
|
+
request,
|
|
37
|
+
routerId,
|
|
38
|
+
apiKey,
|
|
39
|
+
payload,
|
|
40
|
+
responseBody: anthropicMessagesResponse,
|
|
41
|
+
streamBody: anthropicMessagesSseStream,
|
|
42
|
+
errorResponse: anthropicErrorResponse,
|
|
43
|
+
selectorBackend,
|
|
44
|
+
clientHarness: isClaudeCode ? "claude_code" : undefined,
|
|
45
|
+
harnessConversationIdentity: isClaudeCode
|
|
46
|
+
? claudeCodeConversationIdentity(request.headers, payload.promptCacheKey)
|
|
47
|
+
: undefined,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const response = anthropicErrorResponse(error);
|
|
52
|
+
captureRequestError(error, {
|
|
53
|
+
request,
|
|
54
|
+
routerId,
|
|
55
|
+
sourceProtocol: "anthropic_messages",
|
|
56
|
+
status: response.status,
|
|
57
|
+
});
|
|
58
|
+
return response;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function latestMessageHasToolResult(messages) {
|
|
62
|
+
if (!Array.isArray(messages))
|
|
63
|
+
return false;
|
|
64
|
+
const message = messages.at(-1);
|
|
65
|
+
return isRecord(message) && message.role === "user" && Array.isArray(message.content)
|
|
66
|
+
&& message.content.some((block) => isRecord(block) && block.type === "tool_result");
|
|
67
|
+
}
|
|
68
|
+
// A safeguards body field only works with its paired beta, so forward the two
|
|
69
|
+
// together or not at all.
|
|
70
|
+
function safeguardsFrom(payload, headers) {
|
|
71
|
+
if (!Array.isArray(payload.safeguards) || payload.safeguards.length === 0) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const betas = [...anthropicBetas(headers)].filter((beta) => beta.startsWith(SAFEGUARDS_BETA_PREFIX));
|
|
75
|
+
return betas.length === 0 ? undefined : { safeguards: payload.safeguards, betas };
|
|
76
|
+
}
|
|
77
|
+
function anthropicBetas(headers) {
|
|
78
|
+
return new Set((headers.get("anthropic-beta") ?? "")
|
|
79
|
+
.split(",")
|
|
80
|
+
.map((value) => value.trim().toLowerCase())
|
|
81
|
+
.filter(Boolean));
|
|
82
|
+
}
|
|
83
|
+
function isClaudeCodeRequest(headers) {
|
|
84
|
+
const userAgent = headers.get("user-agent")?.trim().toLowerCase() ?? "";
|
|
85
|
+
const app = headers.get("x-app")?.trim().toLowerCase();
|
|
86
|
+
const betas = anthropicBetas(headers);
|
|
87
|
+
return (userAgent.startsWith("claude-cli/") &&
|
|
88
|
+
(app === "cli" || app === "cli-bg") &&
|
|
89
|
+
(Boolean(headers.get("x-claude-code-session-id")?.trim()) ||
|
|
90
|
+
betas.has(LEGACY_CLAUDE_CODE_BETA)));
|
|
91
|
+
}
|
|
92
|
+
// Dari-specific codes clients act on; every other code stays internal.
|
|
93
|
+
const CLIENT_VISIBLE_ERROR_CODES = new Set([
|
|
94
|
+
"personal_subscription_unavailable",
|
|
95
|
+
"subscription_switched",
|
|
96
|
+
"subscription_usage_limit",
|
|
97
|
+
]);
|
|
98
|
+
export function anthropicErrorResponse(error) {
|
|
99
|
+
const { status, message, code } = normalizeError(error);
|
|
100
|
+
return Response.json({
|
|
101
|
+
type: "error",
|
|
102
|
+
error: {
|
|
103
|
+
type: anthropicErrorType(status),
|
|
104
|
+
message,
|
|
105
|
+
...(CLIENT_VISIBLE_ERROR_CODES.has(code) ? { code } : {}),
|
|
106
|
+
},
|
|
107
|
+
}, { status, headers: errorResponseHeaders(error) });
|
|
108
|
+
}
|
|
109
|
+
function anthropicErrorType(status) {
|
|
110
|
+
switch (status) {
|
|
111
|
+
case 401:
|
|
112
|
+
return "authentication_error";
|
|
113
|
+
case 403:
|
|
114
|
+
return "permission_error";
|
|
115
|
+
case 404:
|
|
116
|
+
return "not_found_error";
|
|
117
|
+
case 429:
|
|
118
|
+
return "rate_limit_error";
|
|
119
|
+
default:
|
|
120
|
+
return status >= 500 ? "api_error" : "invalid_request_error";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function anthropicModelsResponse(): Record<string, unknown>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* Anthropic-compatible model discovery. Claude Desktop, Cowork, and other
|
|
2
|
+
Anthropic-SDK gateway clients list models before first use and parse the
|
|
3
|
+
Anthropic list shape ({data: [{type: "model", ...}]}), not the Codex
|
|
4
|
+
catalog shape. Discovery advertises the tier synonyms from router_model.ts —
|
|
5
|
+
the only ids such clients accept as usable (verified in-app: `dari/routing`
|
|
6
|
+
is rejected, bare tier names classify). Fixed created_at keeps the response
|
|
7
|
+
byte-stable. */
|
|
8
|
+
import { ANTHROPIC_ROUTER_MODEL_TIERS } from "../protocol/input/router_model.js";
|
|
9
|
+
const ROUTER_MODEL_CREATED_AT = "2026-01-01T00:00:00Z";
|
|
10
|
+
const tierLabel = (tier) => `Dari Routing (${tier[0].toUpperCase()}${tier.slice(1)} tier)`;
|
|
11
|
+
export function anthropicModelsResponse() {
|
|
12
|
+
return {
|
|
13
|
+
data: ANTHROPIC_ROUTER_MODEL_TIERS.map((tier) => ({
|
|
14
|
+
type: "model",
|
|
15
|
+
id: tier,
|
|
16
|
+
display_name: tierLabel(tier),
|
|
17
|
+
created_at: ROUTER_MODEL_CREATED_AT,
|
|
18
|
+
})),
|
|
19
|
+
first_id: ANTHROPIC_ROUTER_MODEL_TIERS[0],
|
|
20
|
+
last_id: ANTHROPIC_ROUTER_MODEL_TIERS[ANTHROPIC_ROUTER_MODEL_TIERS.length - 1],
|
|
21
|
+
has_more: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { bearerToken } from "../http/auth.js";
|
|
2
|
+
import { errorResponse } from "../http/errors.js";
|
|
3
|
+
import { captureRequestError } from "../observability/errors.js";
|
|
4
|
+
import { chatCompletionResponse, chatCompletionsSseStream } from "../protocol/output/chat_completions.js";
|
|
5
|
+
import { chatCompletionsToInteraction } from "../protocol/input/chat_completions.js";
|
|
6
|
+
import { handleRoutedCompletion } from "../runtime/routed_completion.js";
|
|
7
|
+
import { chatCompletionsConversationIdentity, openCodeRequestContext, } from "../runtime/harness_identity/index.js";
|
|
8
|
+
import { parseJsonRequest } from "./json.js";
|
|
9
|
+
import { museCodeClientId } from "./muse_code.js";
|
|
10
|
+
export async function handleChatCompletions(request, routerId, selectorBackend) {
|
|
11
|
+
try {
|
|
12
|
+
const apiKey = bearerToken(request.headers);
|
|
13
|
+
const payload = await parseJsonRequest(request);
|
|
14
|
+
const interaction = chatCompletionsToInteraction({ ...payload, source_protocol: "openai_chat" });
|
|
15
|
+
const agentContext = openCodeRequestContext(request.headers);
|
|
16
|
+
const museClientId = museCodeClientId(request.headers);
|
|
17
|
+
return await handleRoutedCompletion({
|
|
18
|
+
request,
|
|
19
|
+
routerId,
|
|
20
|
+
apiKey,
|
|
21
|
+
payload: {
|
|
22
|
+
...interaction,
|
|
23
|
+
...(agentContext === undefined ? {} : { agentContext }),
|
|
24
|
+
...(museClientId === undefined ? {} : { museCodeClientId: museClientId }),
|
|
25
|
+
},
|
|
26
|
+
responseBody: chatCompletionResponse,
|
|
27
|
+
streamBody: chatCompletionsSseStream,
|
|
28
|
+
errorResponse,
|
|
29
|
+
selectorBackend,
|
|
30
|
+
clientHarness: museClientId === undefined ? undefined : "muse_code",
|
|
31
|
+
harnessConversationIdentity: chatCompletionsConversationIdentity(request.headers, payload.prompt_cache_key),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const response = errorResponse(error);
|
|
36
|
+
captureRequestError(error, {
|
|
37
|
+
request,
|
|
38
|
+
routerId,
|
|
39
|
+
sourceProtocol: "openai_chat",
|
|
40
|
+
status: response.status,
|
|
41
|
+
});
|
|
42
|
+
return response;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { HttpError } from "../../http/errors.js";
|
|
2
|
+
const REQUEST_CLASSES = new Set([
|
|
3
|
+
"main", "subagent", "workflow", "auxiliary", "compaction", "side_reply",
|
|
4
|
+
]);
|
|
5
|
+
// These are client-supplied routing hints, not authentication claims. Never
|
|
6
|
+
// infer them from prompt text: a user can legitimately paste any such prompt.
|
|
7
|
+
export function claudeCodeRequestContext(headers) {
|
|
8
|
+
const value = headers.get("x-claude-code-request-class")?.trim();
|
|
9
|
+
const requestClass = REQUEST_CLASSES.has(value)
|
|
10
|
+
? value
|
|
11
|
+
: "unknown";
|
|
12
|
+
const agentId = identifier(headers, "x-claude-code-agent-id");
|
|
13
|
+
if ((requestClass === "subagent" || requestClass === "workflow") && !agentId) {
|
|
14
|
+
throw new HttpError(400, "Agent requests require x-claude-code-agent-id.", "invalid_request_error", "x-claude-code-agent-id");
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
requestClass,
|
|
18
|
+
...(agentId === undefined ? {} : { agentId }),
|
|
19
|
+
parentAgentId: identifier(headers, "x-claude-code-parent-agent-id"),
|
|
20
|
+
agentType: identifier(headers, "x-claude-code-agent-type"),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function identifier(headers, name) {
|
|
24
|
+
const value = headers.get(name)?.trim();
|
|
25
|
+
if (!value)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (value.length > 200) {
|
|
28
|
+
throw new HttpError(400, `${name} must not exceed 200 characters.`, "invalid_request_error", name);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { bearerToken } from "../http/auth.js";
|
|
2
|
+
import { errorResponse } from "../http/errors.js";
|
|
3
|
+
import { captureRequestError } from "../observability/errors.js";
|
|
4
|
+
import { openAIResponsesResponse } from "../protocol/output/openai_responses.js";
|
|
5
|
+
import { openAIResponsesSseStream } from "../protocol/output/openai_responses_stream.js";
|
|
6
|
+
import { openAIResponsesToInteraction } from "../protocol/input/openai_responses.js";
|
|
7
|
+
import { parseJsonRequest } from "./json.js";
|
|
8
|
+
import { museCodeClientId } from "./muse_code.js";
|
|
9
|
+
import { handleRoutedCompletion } from "../runtime/routed_completion.js";
|
|
10
|
+
import { codexConversationIdentity, codexRequestPurpose, codexRequestContext, } from "../runtime/harness_identity/index.js";
|
|
11
|
+
export async function handleResponses(request, routerId, selectorBackend) {
|
|
12
|
+
try {
|
|
13
|
+
const apiKey = bearerToken(request.headers);
|
|
14
|
+
const rawPayload = await parseJsonRequest(request);
|
|
15
|
+
const interaction = openAIResponsesToInteraction(rawPayload);
|
|
16
|
+
const requestPurpose = codexRequestPurpose(request.headers, rawPayload);
|
|
17
|
+
const agentContext = codexRequestContext(request.headers, rawPayload);
|
|
18
|
+
const museClientId = museCodeClientId(request.headers);
|
|
19
|
+
const payload = {
|
|
20
|
+
...interaction,
|
|
21
|
+
...(requestPurpose === undefined ? {} : { requestPurpose }),
|
|
22
|
+
...(agentContext === undefined ? {} : { agentContext }),
|
|
23
|
+
...(museClientId === undefined ? {} : { museCodeClientId: museClientId }),
|
|
24
|
+
};
|
|
25
|
+
const harnessConversationIdentity = codexConversationIdentity(request.headers, rawPayload);
|
|
26
|
+
// Codex applies its global model_reasoning_effort setting to custom model
|
|
27
|
+
// providers. That preference describes a direct model, not a heterogeneous
|
|
28
|
+
// router, so let Dari select the reasoning effort for verified Codex turns.
|
|
29
|
+
// Parsing above still validates malformed Responses reasoning controls.
|
|
30
|
+
if (harnessConversationIdentity !== undefined)
|
|
31
|
+
payload.reasoning = undefined;
|
|
32
|
+
return await handleRoutedCompletion({
|
|
33
|
+
request,
|
|
34
|
+
routerId,
|
|
35
|
+
apiKey,
|
|
36
|
+
payload,
|
|
37
|
+
responseBody: openAIResponsesResponse,
|
|
38
|
+
streamBody: openAIResponsesSseStream,
|
|
39
|
+
errorResponse,
|
|
40
|
+
selectorBackend,
|
|
41
|
+
clientHarness: museClientId === undefined ? undefined : "muse_code",
|
|
42
|
+
harnessConversationIdentity,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const response = errorResponse(error);
|
|
47
|
+
captureRequestError(error, {
|
|
48
|
+
request,
|
|
49
|
+
routerId,
|
|
50
|
+
sourceProtocol: "openai_responses",
|
|
51
|
+
status: response.status,
|
|
52
|
+
});
|
|
53
|
+
return response;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function handleResponsesWebSocketFallback() {
|
|
57
|
+
return Response.json({
|
|
58
|
+
error: {
|
|
59
|
+
message: "Responses WebSocket is not supported by this router endpoint; use HTTP Responses.",
|
|
60
|
+
type: "unsupported_transport",
|
|
61
|
+
code: "unsupported_transport",
|
|
62
|
+
},
|
|
63
|
+
}, {
|
|
64
|
+
status: 426,
|
|
65
|
+
headers: {
|
|
66
|
+
"Cache-Control": "no-store",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CANONICAL_ROUTER_MODEL, explicitModelSlug } from "../protocol/input/router_model.js";
|
|
2
|
+
import { effectiveContextWindow } from "../models/context_limits.js";
|
|
3
|
+
import { modelSupportsHostedWebSearch } from "../providers/model_capabilities.js";
|
|
4
|
+
const DEFAULT_CODEX_CONTEXT_WINDOW = 128_000;
|
|
5
|
+
function codexRouterContextWindow(router) {
|
|
6
|
+
const windows = router.enabled_models.map((model) => {
|
|
7
|
+
const configured = effectiveContextWindow(router.model_configs?.[model], router.allow_long_context === true);
|
|
8
|
+
return typeof configured === "number" && Number.isFinite(configured) && configured > 0
|
|
9
|
+
? Math.floor(configured)
|
|
10
|
+
: DEFAULT_CODEX_CONTEXT_WINDOW;
|
|
11
|
+
});
|
|
12
|
+
return windows.length > 0 ? Math.max(...windows) : DEFAULT_CODEX_CONTEXT_WINDOW;
|
|
13
|
+
}
|
|
14
|
+
function codexRouterSupportsHostedWebSearch(router) {
|
|
15
|
+
return router.enabled_models.some((model) => {
|
|
16
|
+
const config = router.model_configs?.[model];
|
|
17
|
+
return config ? modelSupportsHostedWebSearch({ model: config }) : false;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function codexCatalogEntry(args) {
|
|
21
|
+
return {
|
|
22
|
+
slug: args.slug,
|
|
23
|
+
display_name: args.displayName,
|
|
24
|
+
description: args.description,
|
|
25
|
+
default_reasoning_level: null,
|
|
26
|
+
supported_reasoning_levels: [],
|
|
27
|
+
shell_type: "shell_command",
|
|
28
|
+
visibility: "list",
|
|
29
|
+
supported_in_api: true,
|
|
30
|
+
priority: args.priority,
|
|
31
|
+
additional_speed_tiers: [],
|
|
32
|
+
service_tiers: [],
|
|
33
|
+
default_service_tier: null,
|
|
34
|
+
availability_nux: null,
|
|
35
|
+
upgrade: null,
|
|
36
|
+
base_instructions: "You are Codex, a coding agent. You and the user share one workspace, and your job is to collaborate with them until their goal is genuinely handled.",
|
|
37
|
+
model_messages: null,
|
|
38
|
+
include_skills_usage_instructions: false,
|
|
39
|
+
// Codex 0.144 requires the legacy field; current main reads the renamed field below.
|
|
40
|
+
supports_reasoning_summaries: false,
|
|
41
|
+
supports_reasoning_summary_parameter: false,
|
|
42
|
+
default_reasoning_summary: "none",
|
|
43
|
+
support_verbosity: false,
|
|
44
|
+
default_verbosity: null,
|
|
45
|
+
apply_patch_tool_type: "freeform",
|
|
46
|
+
web_search_tool_type: args.hostedWebSearch ? "text" : null,
|
|
47
|
+
truncation_policy: { mode: "tokens", limit: 10_000 },
|
|
48
|
+
supports_parallel_tool_calls: true,
|
|
49
|
+
supports_image_detail_original: false,
|
|
50
|
+
context_window: args.contextWindow,
|
|
51
|
+
max_context_window: args.contextWindow,
|
|
52
|
+
auto_compact_token_limit: null,
|
|
53
|
+
comp_hash: null,
|
|
54
|
+
effective_context_window_percent: 95,
|
|
55
|
+
experimental_supported_tools: [],
|
|
56
|
+
input_modalities: args.imageInput ? ["text", "image"] : ["text"],
|
|
57
|
+
supports_search_tool: false,
|
|
58
|
+
use_responses_lite: false,
|
|
59
|
+
auto_review_model_override: args.slug,
|
|
60
|
+
tool_mode: null,
|
|
61
|
+
multi_agent_version: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/* `dari/routing` stays first (the Codex launcher patches it); each enabled
|
|
65
|
+
model follows as `dari/model/<id>` so Codex's /model menu can switch to it. */
|
|
66
|
+
export function codexModelsResponse(router, imageInputFor) {
|
|
67
|
+
return {
|
|
68
|
+
models: [
|
|
69
|
+
codexCatalogEntry({
|
|
70
|
+
slug: CANONICAL_ROUTER_MODEL,
|
|
71
|
+
displayName: "Dari Routing",
|
|
72
|
+
description: "Routes Codex HTTP Responses requests through the configured Dari model router.",
|
|
73
|
+
priority: 0,
|
|
74
|
+
contextWindow: codexRouterContextWindow(router),
|
|
75
|
+
hostedWebSearch: codexRouterSupportsHostedWebSearch(router),
|
|
76
|
+
imageInput: router.enabled_models.some(imageInputFor),
|
|
77
|
+
}),
|
|
78
|
+
...router.enabled_models.map((model, index) => codexCatalogEntry({
|
|
79
|
+
slug: explicitModelSlug(model),
|
|
80
|
+
displayName: model,
|
|
81
|
+
description: `Serves ${model} directly, without routing.`,
|
|
82
|
+
priority: index + 1,
|
|
83
|
+
contextWindow: codexRouterContextWindow({ ...router, enabled_models: [model] }),
|
|
84
|
+
hostedWebSearch: codexRouterSupportsHostedWebSearch({ ...router, enabled_models: [model] }),
|
|
85
|
+
imageInput: imageInputFor(model),
|
|
86
|
+
})),
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseJsonRequest<T extends Record<string, unknown>>(request: Request): Promise<T>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { createBrotliDecompress, createGunzip, createInflate, createZstdDecompress, } from "node:zlib";
|
|
3
|
+
import { HttpError } from "../http/errors.js";
|
|
4
|
+
const MAX_ENCODED_JSON_REQUEST_BYTES = 64 * 1024 * 1024;
|
|
5
|
+
const MAX_DECODED_JSON_REQUEST_BYTES = 64 * 1024 * 1024;
|
|
6
|
+
const MAX_CONTENT_ENCODING_LAYERS = 3;
|
|
7
|
+
export async function parseJsonRequest(request) {
|
|
8
|
+
let payload;
|
|
9
|
+
try {
|
|
10
|
+
payload = JSON.parse(await requestText(request));
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error instanceof HttpError)
|
|
14
|
+
throw error;
|
|
15
|
+
throw new HttpError(400, "Request body must be valid JSON.", "invalid_request_error");
|
|
16
|
+
}
|
|
17
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
18
|
+
throw new HttpError(400, "Request body must be a JSON object.", "invalid_request_error");
|
|
19
|
+
}
|
|
20
|
+
return payload;
|
|
21
|
+
}
|
|
22
|
+
async function requestText(request) {
|
|
23
|
+
const contentLength = request.headers.get("content-length");
|
|
24
|
+
if (contentLength !== null) {
|
|
25
|
+
const parsed = Number(contentLength);
|
|
26
|
+
if (Number.isFinite(parsed) && parsed > MAX_ENCODED_JSON_REQUEST_BYTES) {
|
|
27
|
+
throw requestTooLarge();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const encodings = contentEncodings(request.headers.get("content-encoding"));
|
|
31
|
+
if (encodings.length > MAX_CONTENT_ENCODING_LAYERS) {
|
|
32
|
+
throw new HttpError(415, "Too many Content-Encoding layers.", "unsupported_content_encoding", "content-encoding");
|
|
33
|
+
}
|
|
34
|
+
let stream = boundedChunks(request.body ?? emptyByteStream(), MAX_ENCODED_JSON_REQUEST_BYTES);
|
|
35
|
+
for (const encoding of encodings.reverse()) {
|
|
36
|
+
stream = boundedChunks(decodedChunks(stream, encoding), MAX_DECODED_JSON_REQUEST_BYTES);
|
|
37
|
+
}
|
|
38
|
+
return await readText(stream);
|
|
39
|
+
}
|
|
40
|
+
function contentEncodings(header) {
|
|
41
|
+
if (!header)
|
|
42
|
+
return [];
|
|
43
|
+
return header
|
|
44
|
+
.split(",")
|
|
45
|
+
.map((value) => value.trim().toLowerCase())
|
|
46
|
+
.filter((value) => value && value !== "identity");
|
|
47
|
+
}
|
|
48
|
+
async function* boundedChunks(stream, maxBytes) {
|
|
49
|
+
let bytesRead = 0;
|
|
50
|
+
for await (const chunk of stream) {
|
|
51
|
+
bytesRead += chunk.byteLength;
|
|
52
|
+
if (bytesRead > maxBytes)
|
|
53
|
+
throw requestTooLarge();
|
|
54
|
+
yield chunk;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function* decodedChunks(stream, encoding) {
|
|
58
|
+
const decoded = Readable.from(stream).pipe(decompressor(encoding));
|
|
59
|
+
try {
|
|
60
|
+
for await (const chunk of decoded) {
|
|
61
|
+
if (!(chunk instanceof Uint8Array))
|
|
62
|
+
throw new Error("Decompressor emitted a non-byte chunk.");
|
|
63
|
+
yield chunk;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
decoded.destroy();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function decompressor(encoding) {
|
|
71
|
+
if (encoding === "zstd")
|
|
72
|
+
return createZstdDecompress();
|
|
73
|
+
if (encoding === "gzip" || encoding === "x-gzip")
|
|
74
|
+
return createGunzip();
|
|
75
|
+
if (encoding === "deflate")
|
|
76
|
+
return createInflate();
|
|
77
|
+
if (encoding === "br")
|
|
78
|
+
return createBrotliDecompress();
|
|
79
|
+
throw new HttpError(415, `Unsupported Content-Encoding '${encoding}'.`, "unsupported_content_encoding", "content-encoding");
|
|
80
|
+
}
|
|
81
|
+
async function readText(stream) {
|
|
82
|
+
const decoder = new TextDecoder();
|
|
83
|
+
const parts = [];
|
|
84
|
+
for await (const chunk of stream) {
|
|
85
|
+
parts.push(decoder.decode(chunk, { stream: true }));
|
|
86
|
+
}
|
|
87
|
+
parts.push(decoder.decode());
|
|
88
|
+
return parts.join("");
|
|
89
|
+
}
|
|
90
|
+
async function* emptyByteStream() {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
function requestTooLarge() {
|
|
94
|
+
return new HttpError(413, "Request body is too large.", "request_entity_too_large");
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleModels(request: Request, routerId: string): Promise<Response>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* Shared GET /models discovery route. One URL serves two client protocols:
|
|
2
|
+
Codex reads the Codex catalog; Anthropic-SDK clients (Claude Desktop /
|
|
3
|
+
Cowork gateway probes, Claude Code) read the Anthropic list shape. The
|
|
4
|
+
handler detects the caller, authenticates in that protocol's auth shape,
|
|
5
|
+
and selects the matching catalog adapter. `?detail=1` instead returns the
|
|
6
|
+
model catalog (models/catalog.ts), optionally narrowed with
|
|
7
|
+
`provider=<id>[,<id>...]`. */
|
|
8
|
+
import { anthropicToken, bearerToken } from "../http/auth.js";
|
|
9
|
+
import { errorResponse } from "../http/errors.js";
|
|
10
|
+
import { captureRequestError } from "../observability/errors.js";
|
|
11
|
+
import { anthropicErrorResponse } from "./anthropic_messages.js";
|
|
12
|
+
import { listModelCatalog } from "../models/catalog.js";
|
|
13
|
+
import { modelSupportsImageInput } from "../providers/model_capabilities.js";
|
|
14
|
+
import { modelMetadataLookup } from "../providers/pi.js";
|
|
15
|
+
import { resolveRouter } from "../strategies/routing.js";
|
|
16
|
+
import { anthropicModelsResponse } from "./anthropic_models.js";
|
|
17
|
+
import { codexModelsResponse } from "./codex_models.js";
|
|
18
|
+
export async function handleModels(request, routerId) {
|
|
19
|
+
// Anthropic SDK clients always send anthropic-version, and API-key probes
|
|
20
|
+
// authenticate via x-api-key instead of a bearer Authorization header.
|
|
21
|
+
// Codex sends neither.
|
|
22
|
+
const anthropicClient = request.headers.has("anthropic-version") ||
|
|
23
|
+
(!request.headers.get("authorization") && request.headers.has("x-api-key"));
|
|
24
|
+
try {
|
|
25
|
+
const apiKey = anthropicClient ? anthropicToken(request.headers) : bearerToken(request.headers);
|
|
26
|
+
// Resolution authenticates the key and confirms the router exists even
|
|
27
|
+
// when the response body (Anthropic shape) does not depend on it.
|
|
28
|
+
const router = await resolveRouter({ routerId, apiKey });
|
|
29
|
+
const query = new URL(request.url).searchParams;
|
|
30
|
+
if (query.get("detail") === "1") {
|
|
31
|
+
const providers = query.get("provider")?.split(",").map((id) => id.trim()).filter(Boolean);
|
|
32
|
+
const data = await listModelCatalog({
|
|
33
|
+
...(providers?.length ? { providers } : {}),
|
|
34
|
+
signal: request.signal,
|
|
35
|
+
});
|
|
36
|
+
return Response.json({ object: "list", data }, { headers: { "Cache-Control": "no-store" } });
|
|
37
|
+
}
|
|
38
|
+
if (anthropicClient) {
|
|
39
|
+
return Response.json(anthropicModelsResponse(), {
|
|
40
|
+
headers: {
|
|
41
|
+
"Cache-Control": "no-store",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const metadataLookup = modelMetadataLookup(router.model_configs);
|
|
46
|
+
const imageInputFor = (model) => modelSupportsImageInput(metadataLookup(model));
|
|
47
|
+
return Response.json(codexModelsResponse(router, imageInputFor), {
|
|
48
|
+
headers: {
|
|
49
|
+
"Cache-Control": "no-store",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Each protocol's clients parse their own error envelope: Anthropic's
|
|
55
|
+
// {type:"error",error:{type,message}} vs the OpenAI {error:{...}} shape.
|
|
56
|
+
const response = anthropicClient ? anthropicErrorResponse(error) : errorResponse(error);
|
|
57
|
+
captureRequestError(error, {
|
|
58
|
+
request,
|
|
59
|
+
routerId,
|
|
60
|
+
sourceProtocol: anthropicClient ? "anthropic_models" : "openai_models",
|
|
61
|
+
status: response.status,
|
|
62
|
+
});
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Muse Code names its surface (TUI, `muse exec`, desktop, `muse serve`) in
|
|
2
|
+
// x-client-id on every model request.
|
|
3
|
+
const MUSE_CODE_CLIENT_IDS = new Set(["tbh:tui", "tbh:exec", "tbh:desktop", "tbh:serve"]);
|
|
4
|
+
/** Muse Code's x-client-id when the request comes from Muse Code. */
|
|
5
|
+
export function museCodeClientId(headers) {
|
|
6
|
+
const clientId = headers.get("x-client-id")?.trim().toLowerCase();
|
|
7
|
+
return clientId !== undefined && MUSE_CODE_CLIENT_IDS.has(clientId) ? clientId : undefined;
|
|
8
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ResolvedRouter } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Muse Code fetches its model list from its base URL's origin
|
|
4
|
+
* (`/muse-code/models`) before its first request. A list of only the router's
|
|
5
|
+
* models lets Muse Code pick from them and start on the first; `dari/routing`
|
|
6
|
+
* leads it only on the Autorouting plan.
|
|
7
|
+
*/
|
|
8
|
+
export declare function museCodeModelsResponse(router: Pick<ResolvedRouter, "enabled_models" | "autorouting_enabled">): Record<string, unknown>;
|
|
9
|
+
export declare function handleMuseCodeModels(request: Request, routerId: string): Promise<Response>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { bearerToken } from "../http/auth.js";
|
|
2
|
+
import { errorResponse } from "../http/errors.js";
|
|
3
|
+
import { resolveRouter } from "../strategies/routing.js";
|
|
4
|
+
/**
|
|
5
|
+
* Muse Code fetches its model list from its base URL's origin
|
|
6
|
+
* (`/muse-code/models`) before its first request. A list of only the router's
|
|
7
|
+
* models lets Muse Code pick from them and start on the first; `dari/routing`
|
|
8
|
+
* leads it only on the Autorouting plan.
|
|
9
|
+
*/
|
|
10
|
+
export function museCodeModelsResponse(router) {
|
|
11
|
+
const ids = [
|
|
12
|
+
...(router.autorouting_enabled === false ? [] : ["dari/routing"]),
|
|
13
|
+
...router.enabled_models.map((model) => `dari/model/${model}`),
|
|
14
|
+
];
|
|
15
|
+
return {
|
|
16
|
+
data: ids.map((id, index) => ({
|
|
17
|
+
id,
|
|
18
|
+
display_label: id === "dari/routing" ? "Dari Routing" : id.slice("dari/model/".length),
|
|
19
|
+
is_default: index === 0,
|
|
20
|
+
})),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export async function handleMuseCodeModels(request, routerId) {
|
|
24
|
+
try {
|
|
25
|
+
// Only Muse Code fetches this list, so its plans apply even without a Meta key.
|
|
26
|
+
const router = await resolveRouter({ routerId, apiKey: bearerToken(request.headers), clientHarness: "muse_code" });
|
|
27
|
+
return Response.json(museCodeModelsResponse(router), { headers: { "Cache-Control": "no-store" } });
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return errorResponse(error);
|
|
31
|
+
}
|
|
32
|
+
}
|