@mupt-ai/dari-gateway 0.2.0 → 0.2.1
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/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 +23 -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 +81 -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 +6 -0
- package/dist/index.js +8 -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 +37 -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 +65 -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 +53 -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 +203 -0
- package/dist/interaction/types.js +5 -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 +8 -0
- package/dist/models/execution_provider.js +13 -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 +41 -0
- package/dist/models/runtime_overrides.d.ts +4 -0
- package/dist/models/runtime_overrides.js +209 -0
- package/dist/observability/errors.d.ts +8 -0
- package/dist/observability/errors.js +11 -0
- package/dist/platform/platform.d.ts +152 -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 +212 -0
- package/dist/protocol/input/chat_completions_validation.d.ts +2 -0
- package/dist/protocol/input/chat_completions_validation.js +257 -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 +319 -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 +398 -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 +292 -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 +31 -0
- package/dist/providers/subscription_limits.js +163 -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 +4 -0
- package/dist/runtime/explicit_model.js +53 -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 +167 -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/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 +171 -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 +55 -0
- package/dist/self_host/subscriptions/oauth.js +281 -0
- package/dist/self_host/subscriptions/store.d.ts +78 -0
- package/dist/self_host/subscriptions/store.js +235 -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 +94 -0
- package/dist/types.js +1 -0
- package/package.json +21 -6
- 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 +1 -1
- 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 +8 -8
- package/src/http/serve.ts +3 -3
- package/src/index.ts +6 -6
- package/src/ingress/anthropic_messages.ts +12 -12
- package/src/ingress/anthropic_models.ts +1 -1
- package/src/ingress/chat_completions.ts +9 -9
- package/src/ingress/claude_code/request_context.ts +2 -2
- package/src/ingress/codex.ts +10 -10
- package/src/ingress/codex_models.ts +4 -4
- package/src/ingress/json.ts +1 -1
- package/src/ingress/models.ts +9 -9
- 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/models/context_limits.ts +1 -1
- package/src/models/execution_provider.ts +1 -1
- package/src/models/ids.ts +1 -1
- package/src/models/provider_defaults.ts +1 -1
- package/src/models/runtime_overrides.ts +1 -1
- package/src/observability/errors.ts +2 -2
- package/src/platform/platform.ts +4 -4
- package/src/protocol/input/anthropic_messages.ts +7 -7
- package/src/protocol/input/chat_completions.ts +5 -5
- package/src/protocol/input/chat_completions_validation.ts +4 -4
- 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 +14 -14
- package/src/providers/pi_context.ts +6 -6
- package/src/providers/pi_options.ts +10 -10
- package/src/providers/pi_output.ts +5 -5
- package/src/providers/provider_attempt_runner.ts +1 -1
- package/src/providers/provider_call.ts +14 -14
- 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 +3 -3
- package/src/runtime/claude_code/auxiliary_routing.ts +5 -5
- package/src/runtime/customer_disconnect.ts +1 -1
- package/src/runtime/explicit_model.ts +4 -4
- 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 +13 -13
- 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/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 +6 -6
- 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/store.ts +1 -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
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { isReasoningEffort, isRecord } from "@mupt-ai/dari-router/policy-engine";
|
|
3
|
+
import { HttpError } from "../../http/errors.js";
|
|
4
|
+
import { parseDataImageUrl } from "../../interaction/image.js";
|
|
5
|
+
import { decodeUntrustedProviderContinuationState } from "../../interaction/provider_continuation_state.js";
|
|
6
|
+
import { CANONICAL_ROUTER_MODEL, explicitModelId, unsupportedModelMessage } from "./router_model.js";
|
|
7
|
+
import { isProtocolToolCallId, TOOL_CALL_ID_MAX_LENGTH, toolsByWireName, } from "../../interaction/tool_identity.js";
|
|
8
|
+
const UNSUPPORTED_INPUT_ITEM_TYPES = new Set([
|
|
9
|
+
"additional_tools",
|
|
10
|
+
"agent_message",
|
|
11
|
+
"image_generation_call",
|
|
12
|
+
"file_search_call",
|
|
13
|
+
"code_interpreter_call",
|
|
14
|
+
"computer_call",
|
|
15
|
+
"mcp_call",
|
|
16
|
+
"mcp_tool_call_output",
|
|
17
|
+
"local_shell_call",
|
|
18
|
+
"local_shell_call_output",
|
|
19
|
+
"tool_search_call",
|
|
20
|
+
"tool_search_output",
|
|
21
|
+
"compaction",
|
|
22
|
+
"compaction_summary",
|
|
23
|
+
"compaction_trigger",
|
|
24
|
+
"context_compaction",
|
|
25
|
+
]);
|
|
26
|
+
const CLIENT_TURN_ID_MAX_LENGTH = 200;
|
|
27
|
+
export function openAIResponsesToInteraction(payload) {
|
|
28
|
+
const model = optionalString(payload.model, "model");
|
|
29
|
+
if (model !== CANONICAL_ROUTER_MODEL && explicitModelId(model) === null) {
|
|
30
|
+
throw new HttpError(400, unsupportedModelMessage(), "unsupported_model", "model");
|
|
31
|
+
}
|
|
32
|
+
if (payload.previous_response_id !== undefined && payload.previous_response_id !== null) {
|
|
33
|
+
throw new HttpError(400, "previous_response_id is not supported by this router endpoint; send full input context.", "invalid_request_error", "previous_response_id");
|
|
34
|
+
}
|
|
35
|
+
if (payload.service_tier !== undefined && payload.service_tier !== null) {
|
|
36
|
+
throw new HttpError(400, "service_tier is not supported by this router endpoint.", "invalid_request_error", "service_tier");
|
|
37
|
+
}
|
|
38
|
+
const items = responseItems(payload.input);
|
|
39
|
+
const tools = responseTools(payload.tools);
|
|
40
|
+
toolsByWireName(tools);
|
|
41
|
+
const requestReasoning = reasoningControls(payload.reasoning);
|
|
42
|
+
const updatedEffort = latestReasoningConfiguration(items);
|
|
43
|
+
const baseEffort = requestReasoning?.effort;
|
|
44
|
+
if (updatedEffort !== undefined && baseEffort === undefined) {
|
|
45
|
+
throw new HttpError(400, "reasoning.effort is required when input contains configuration_update.", "invalid_request_error", "reasoning.effort");
|
|
46
|
+
}
|
|
47
|
+
const reasoning = updatedEffort === undefined
|
|
48
|
+
? requestReasoning
|
|
49
|
+
: {
|
|
50
|
+
effort: updatedEffort,
|
|
51
|
+
baseEffort,
|
|
52
|
+
hasConfigurationUpdate: true,
|
|
53
|
+
};
|
|
54
|
+
const responseFormat = responseFormatFromText(payload.text);
|
|
55
|
+
validateTextControls(payload.text);
|
|
56
|
+
const instructions = optionalString(payload.instructions, "instructions");
|
|
57
|
+
const stream = optionalBoolean(payload.stream, "stream") ?? false;
|
|
58
|
+
const parallelToolCalls = optionalBoolean(payload.parallel_tool_calls, "parallel_tool_calls");
|
|
59
|
+
const generation = {
|
|
60
|
+
temperature: optionalNumber(payload.temperature, "temperature"),
|
|
61
|
+
topP: optionalNumber(payload.top_p, "top_p"),
|
|
62
|
+
maxOutputTokens: optionalNumber(payload.max_output_tokens ?? payload.max_completion_tokens, "max_output_tokens"),
|
|
63
|
+
stop: stopOption(payload.stop),
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
sourceProtocol: "openai_responses",
|
|
67
|
+
model,
|
|
68
|
+
instructions,
|
|
69
|
+
items,
|
|
70
|
+
tools,
|
|
71
|
+
toolChoice: responseToolChoice(payload.tool_choice),
|
|
72
|
+
parallelToolCalls,
|
|
73
|
+
generation,
|
|
74
|
+
reasoning,
|
|
75
|
+
responseFormat,
|
|
76
|
+
promptCacheKey: optionalString(payload.prompt_cache_key, "prompt_cache_key"),
|
|
77
|
+
stream,
|
|
78
|
+
metadata: responseMetadata(payload),
|
|
79
|
+
user: optionalString(payload.user, "user"),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function responseItems(input) {
|
|
83
|
+
if (typeof input === "string") {
|
|
84
|
+
return [{ type: "message", role: "user", content: [{ type: "text", text: input }], contentShape: "string" }];
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(input)) {
|
|
87
|
+
throw new HttpError(400, "input must be a string or an array.", "invalid_request_error", "input");
|
|
88
|
+
}
|
|
89
|
+
if (input.length === 0) {
|
|
90
|
+
throw new HttpError(400, "input must include at least one item.", "invalid_request_error", "input");
|
|
91
|
+
}
|
|
92
|
+
const hostedReplayIds = hostedReplayIdsByItemIndex(input);
|
|
93
|
+
return input.map((value, itemIndex) => responseItem(value, itemIndex, hostedReplayIds.get(itemIndex)));
|
|
94
|
+
}
|
|
95
|
+
function hostedReplayIdsByItemIndex(input) {
|
|
96
|
+
const replayIds = new Map();
|
|
97
|
+
assignPositionedHostedReplayIds(input, replayIds);
|
|
98
|
+
let groupStart = 0;
|
|
99
|
+
// A reasoning continuation only describes native output items in its own
|
|
100
|
+
// contiguous provider-output group. Do not let its IDs leak across a client
|
|
101
|
+
// message or tool call/result into another response in the flattened input.
|
|
102
|
+
while (groupStart < input.length) {
|
|
103
|
+
if (!isHostedReplayItem(input[groupStart])) {
|
|
104
|
+
groupStart += 1;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let groupEnd = groupStart + 1;
|
|
108
|
+
while (groupEnd < input.length && isHostedReplayItem(input[groupEnd]))
|
|
109
|
+
groupEnd += 1;
|
|
110
|
+
assignHostedReplayGroup(input, groupStart, groupEnd, replayIds);
|
|
111
|
+
groupStart = groupEnd;
|
|
112
|
+
}
|
|
113
|
+
return replayIds;
|
|
114
|
+
}
|
|
115
|
+
function assignPositionedHostedReplayIds(input, replayIds) {
|
|
116
|
+
for (let reasoningIndex = 0; reasoningIndex < input.length; reasoningIndex += 1) {
|
|
117
|
+
const continuation = hostedReplayContinuationFromItem(input[reasoningIndex]);
|
|
118
|
+
if (!continuation?.hostedToolCallIds || !continuation.hostedToolCallOffsets)
|
|
119
|
+
continue;
|
|
120
|
+
for (let index = 0; index < continuation.hostedToolCallIds.length; index += 1) {
|
|
121
|
+
const callIndex = reasoningIndex + continuation.hostedToolCallOffsets[index];
|
|
122
|
+
const value = input[callIndex];
|
|
123
|
+
if (!isRecord(value) || value.type !== "web_search_call" || replayIds.has(callIndex))
|
|
124
|
+
continue;
|
|
125
|
+
replayIds.set(callIndex, continuation.hostedToolCallIds[index]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function isHostedReplayItem(value) {
|
|
130
|
+
return isRecord(value) && (value.type === "reasoning" || value.type === "web_search_call");
|
|
131
|
+
}
|
|
132
|
+
function assignHostedReplayGroup(input, start, end, replayIds) {
|
|
133
|
+
const explicitIdCounts = new Map();
|
|
134
|
+
const missingCallIndexes = [];
|
|
135
|
+
const carriedIds = [];
|
|
136
|
+
for (let index = start; index < end; index += 1) {
|
|
137
|
+
const value = input[index];
|
|
138
|
+
if (!isRecord(value))
|
|
139
|
+
continue;
|
|
140
|
+
if (value.type === "reasoning") {
|
|
141
|
+
carriedIds.push(...legacyHostedReplayIdsFromItem(value).map((id) => ({ id, reasoningIndex: index })));
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (value.type !== "web_search_call")
|
|
145
|
+
continue;
|
|
146
|
+
if (typeof value.id === "string" && value.id.trim()) {
|
|
147
|
+
explicitIdCounts.set(value.id, (explicitIdCounts.get(value.id) ?? 0) + 1);
|
|
148
|
+
}
|
|
149
|
+
else if (replayIds.has(index)) {
|
|
150
|
+
const replayId = replayIds.get(index);
|
|
151
|
+
if (replayId)
|
|
152
|
+
explicitIdCounts.set(replayId, (explicitIdCounts.get(replayId) ?? 0) + 1);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
missingCallIndexes.push(index);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const unclaimedIds = carriedIds.filter(({ id }) => {
|
|
159
|
+
const count = explicitIdCounts.get(id) ?? 0;
|
|
160
|
+
if (count === 0)
|
|
161
|
+
return true;
|
|
162
|
+
explicitIdCounts.set(id, count - 1);
|
|
163
|
+
return false;
|
|
164
|
+
});
|
|
165
|
+
if (unclaimedIds.length === missingCallIndexes.length) {
|
|
166
|
+
for (let index = 0; index < unclaimedIds.length; index += 1) {
|
|
167
|
+
replayIds.set(missingCallIndexes[index], unclaimedIds[index].id);
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
// Partial histories can contain more id-less calls than the continuation
|
|
172
|
+
// carries. Prefer calls following the reasoning item that carried each ID;
|
|
173
|
+
// earlier unrelated calls keep their deterministic stateless fallback.
|
|
174
|
+
const unassignedCallIndexes = [...missingCallIndexes];
|
|
175
|
+
for (const carried of unclaimedIds) {
|
|
176
|
+
const followingIndex = unassignedCallIndexes.findIndex((itemIndex) => itemIndex > carried.reasoningIndex);
|
|
177
|
+
const callIndex = unassignedCallIndexes.splice(followingIndex >= 0 ? followingIndex : 0, 1)[0];
|
|
178
|
+
if (callIndex === undefined)
|
|
179
|
+
return;
|
|
180
|
+
replayIds.set(callIndex, carried.id);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function responseItem(value, itemIndex, hostedReplayId) {
|
|
184
|
+
if (!isRecord(value)) {
|
|
185
|
+
throw new HttpError(400, "input items must be objects.", "invalid_request_error", "input");
|
|
186
|
+
}
|
|
187
|
+
const type = typeof value.type === "string" ? value.type : "";
|
|
188
|
+
if (UNSUPPORTED_INPUT_ITEM_TYPES.has(type)) {
|
|
189
|
+
throw new HttpError(400, `Responses input item type '${type}' is not supported by this router MVP.`, "unsupported_response_item", "input");
|
|
190
|
+
}
|
|
191
|
+
if (type === "reasoning")
|
|
192
|
+
return reasoningItem(value);
|
|
193
|
+
if (type === "configuration_update")
|
|
194
|
+
return reasoningConfigurationItem(value);
|
|
195
|
+
if (type === "web_search_call")
|
|
196
|
+
return hostedWebSearchCallItem(value, itemIndex, hostedReplayId);
|
|
197
|
+
if (type === "function_call" || type === "custom_tool_call")
|
|
198
|
+
return toolCallItem(value, type === "function_call" ? "function" : "custom");
|
|
199
|
+
if (type === "function_call_output" || type === "custom_tool_call_output") {
|
|
200
|
+
return toolResultItem(value, type === "function_call_output" ? "function" : "custom");
|
|
201
|
+
}
|
|
202
|
+
if (type === "message" || typeof value.role === "string")
|
|
203
|
+
return messageItem(value);
|
|
204
|
+
throw new HttpError(400, `Responses input item type '${type || "unknown"}' is not supported by this router MVP.`, "unsupported_response_item", "input");
|
|
205
|
+
}
|
|
206
|
+
function reasoningConfigurationItem(value) {
|
|
207
|
+
if (!isRecord(value.reasoning)) {
|
|
208
|
+
throw new HttpError(400, "input.configuration_update.reasoning must be an object.", "invalid_request_error", "input.configuration_update.reasoning");
|
|
209
|
+
}
|
|
210
|
+
const effort = normalizedReasoningEffort(value.reasoning.effort, "input.configuration_update.reasoning.effort");
|
|
211
|
+
if (effort === undefined) {
|
|
212
|
+
throw new HttpError(400, "input.configuration_update.reasoning.effort is required.", "invalid_request_error", "input.configuration_update.reasoning.effort");
|
|
213
|
+
}
|
|
214
|
+
const extraUpdateKeys = Object.keys(value).filter((key) => key !== "type" && key !== "reasoning");
|
|
215
|
+
const extraReasoningKeys = Object.keys(value.reasoning).filter((key) => key !== "effort");
|
|
216
|
+
if (extraUpdateKeys.length > 0 || extraReasoningKeys.length > 0) {
|
|
217
|
+
throw new HttpError(400, "configuration_update supports only reasoning.effort.", "invalid_request_error", "input.configuration_update");
|
|
218
|
+
}
|
|
219
|
+
return { type: "reasoning_configuration", effort };
|
|
220
|
+
}
|
|
221
|
+
function latestReasoningConfiguration(items) {
|
|
222
|
+
let latest;
|
|
223
|
+
let previousWasUpdate = false;
|
|
224
|
+
for (const item of items) {
|
|
225
|
+
if (item.type === "reasoning_configuration") {
|
|
226
|
+
if (previousWasUpdate) {
|
|
227
|
+
throw new HttpError(400, "Adjacent configuration_update items are not supported.", "invalid_request_error", "input");
|
|
228
|
+
}
|
|
229
|
+
latest = item.effort;
|
|
230
|
+
previousWasUpdate = true;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
previousWasUpdate = false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return latest;
|
|
237
|
+
}
|
|
238
|
+
function hostedWebSearchCallItem(value, itemIndex, hostedReplayId) {
|
|
239
|
+
const id = hostedWebSearchCallId(value, itemIndex, hostedReplayId);
|
|
240
|
+
return {
|
|
241
|
+
type: "hosted_tool_call",
|
|
242
|
+
id,
|
|
243
|
+
tool: "web_search",
|
|
244
|
+
providerType: "web_search_call",
|
|
245
|
+
status: hostedItemStatus(value.status, "input.web_search_call.status"),
|
|
246
|
+
payload: { ...value, id },
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function hostedWebSearchCallId(value, itemIndex, hostedReplayId) {
|
|
250
|
+
if (typeof value.id === "string" && value.id.trim())
|
|
251
|
+
return value.id;
|
|
252
|
+
if (value.id !== undefined && value.id !== null && typeof value.id !== "string") {
|
|
253
|
+
throw new HttpError(400, "input.web_search_call.id must be a string.", "invalid_request_error", "input.web_search_call.id");
|
|
254
|
+
}
|
|
255
|
+
if (hostedReplayId)
|
|
256
|
+
return hostedReplayId;
|
|
257
|
+
// If no client-carried continuation survives, keep the call replayable
|
|
258
|
+
// instead of dropping it from provider history.
|
|
259
|
+
const { id: _, ...itemWithoutId } = value;
|
|
260
|
+
const digest = createHash("sha256")
|
|
261
|
+
.update(JSON.stringify([itemIndex, itemWithoutId]), "utf8")
|
|
262
|
+
.digest("hex")
|
|
263
|
+
.slice(0, 32);
|
|
264
|
+
return `ws_dari_${digest}`;
|
|
265
|
+
}
|
|
266
|
+
function hostedReplayContinuationFromItem(value) {
|
|
267
|
+
if (!isRecord(value) || value.type !== "reasoning" || typeof value.encrypted_content !== "string")
|
|
268
|
+
return null;
|
|
269
|
+
const continuation = decodeUntrustedProviderContinuationState(value.encrypted_content);
|
|
270
|
+
return continuation?.kind === "openai_reasoning" ? continuation : null;
|
|
271
|
+
}
|
|
272
|
+
function legacyHostedReplayIdsFromItem(value) {
|
|
273
|
+
const continuation = hostedReplayContinuationFromItem(value);
|
|
274
|
+
return continuation && !continuation.hostedToolCallOffsets
|
|
275
|
+
? continuation.hostedToolCallIds ?? []
|
|
276
|
+
: [];
|
|
277
|
+
}
|
|
278
|
+
function messageItem(value) {
|
|
279
|
+
const role = requiredString(value.role, "input.message.role");
|
|
280
|
+
if (role !== "system" && role !== "developer" && role !== "user" && role !== "assistant") {
|
|
281
|
+
throw new HttpError(400, `Unsupported Responses message role '${role}'.`, "invalid_request_error", "input.role");
|
|
282
|
+
}
|
|
283
|
+
const phase = value.phase;
|
|
284
|
+
if (phase !== undefined && phase !== "commentary" && phase !== "final_answer") {
|
|
285
|
+
throw new HttpError(400, "input.message.phase is not supported.", "invalid_request_error", "input.message.phase");
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
type: "message",
|
|
289
|
+
id: optionalString(value.id, "input.message.id"),
|
|
290
|
+
...clientTurnId(value),
|
|
291
|
+
role,
|
|
292
|
+
content: responseContent(value.content, "input.message.content"),
|
|
293
|
+
contentShape: responseContentShape(value.content),
|
|
294
|
+
phase,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function clientTurnId(value) {
|
|
298
|
+
const metadata = value.internal_chat_message_metadata_passthrough;
|
|
299
|
+
if (!isRecord(metadata) || typeof metadata.turn_id !== "string")
|
|
300
|
+
return {};
|
|
301
|
+
const normalized = metadata.turn_id.trim();
|
|
302
|
+
return normalized && normalized.length <= CLIENT_TURN_ID_MAX_LENGTH
|
|
303
|
+
? { clientTurnId: normalized }
|
|
304
|
+
: {};
|
|
305
|
+
}
|
|
306
|
+
function reasoningItem(value) {
|
|
307
|
+
const encoded = optionalString(value.encrypted_content, "input.reasoning.encrypted_content");
|
|
308
|
+
const continuation = encoded ? decodeUntrustedProviderContinuationState(encoded) : null;
|
|
309
|
+
if (encoded && !continuation) {
|
|
310
|
+
throw new HttpError(400, "input.reasoning.encrypted_content must be a Dari provider continuation envelope; raw provider ciphertext has no recoverable provider/model provenance.", "invalid_provider_continuation_state", "input.reasoning.encrypted_content");
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
type: "reasoning",
|
|
314
|
+
id: optionalString(value.id, "input.reasoning.id"),
|
|
315
|
+
summary: textEntries(value.summary, "input.reasoning.summary", "summary_text"),
|
|
316
|
+
content: textEntries(value.content, "input.reasoning.content", "reasoning_text"),
|
|
317
|
+
...(continuation ? { continuation } : {}),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function textEntries(value, param, expectedType) {
|
|
321
|
+
if (value === undefined || value === null)
|
|
322
|
+
return [];
|
|
323
|
+
if (!Array.isArray(value))
|
|
324
|
+
throw new HttpError(400, `${param} must be an array.`, "invalid_request_error", param);
|
|
325
|
+
return value.map((entry, index) => {
|
|
326
|
+
if (!isRecord(entry) || (entry.type !== undefined && entry.type !== expectedType) || typeof entry.text !== "string") {
|
|
327
|
+
throw new HttpError(400, `${param}[${index}] is not supported.`, "invalid_request_error", `${param}.${index}`);
|
|
328
|
+
}
|
|
329
|
+
return entry.text;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function toolCallItem(value, kind) {
|
|
333
|
+
const prefix = `input.${kind === "function" ? "function_call" : "custom_tool_call"}`;
|
|
334
|
+
return {
|
|
335
|
+
type: "tool_call",
|
|
336
|
+
id: optionalString(value.id, `${prefix}.id`),
|
|
337
|
+
callId: requiredToolCallId(value.call_id, `${prefix}.call_id`),
|
|
338
|
+
kind,
|
|
339
|
+
name: requiredString(value.name, `${prefix}.name`),
|
|
340
|
+
namespace: optionalString(value.namespace, `${prefix}.namespace`),
|
|
341
|
+
input: kind === "function"
|
|
342
|
+
? requiredToolArguments(value.arguments, `${prefix}.arguments`)
|
|
343
|
+
: requiredString(value.input, `${prefix}.input`),
|
|
344
|
+
status: itemStatus(value.status, `${prefix}.status`),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function toolResultItem(value, kind) {
|
|
348
|
+
const prefix = `input.${kind === "function" ? "function_call_output" : "custom_tool_call_output"}`;
|
|
349
|
+
return {
|
|
350
|
+
type: "tool_result",
|
|
351
|
+
id: optionalString(value.id, `${prefix}.id`),
|
|
352
|
+
callId: requiredToolCallId(value.call_id, `${prefix}.call_id`),
|
|
353
|
+
kind,
|
|
354
|
+
name: optionalString(value.name, `${prefix}.name`),
|
|
355
|
+
output: responseToolOutput(value.output, `${prefix}.output`),
|
|
356
|
+
outputShape: responseContentShape(value.output),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function responseToolOutput(value, param) {
|
|
360
|
+
if (value === undefined || value === null || typeof value === "string")
|
|
361
|
+
return value ?? null;
|
|
362
|
+
return responseContent(value, param);
|
|
363
|
+
}
|
|
364
|
+
function responseContent(value, param) {
|
|
365
|
+
if (typeof value === "string")
|
|
366
|
+
return [{ type: "text", text: value }];
|
|
367
|
+
if (!Array.isArray(value)) {
|
|
368
|
+
if (value === undefined || value === null)
|
|
369
|
+
return [];
|
|
370
|
+
throw new HttpError(400, `${param} must be a string or an array.`, "invalid_request_error", param);
|
|
371
|
+
}
|
|
372
|
+
return value.map((part, index) => {
|
|
373
|
+
if (!isRecord(part))
|
|
374
|
+
throw new HttpError(400, `${param}[${index}] must be an object.`, "invalid_request_error", `${param}.${index}`);
|
|
375
|
+
if (part.type === "input_text" || part.type === "output_text" || part.type === "text") {
|
|
376
|
+
const annotations = responseAnnotations(part.annotations, `${param}.${index}.annotations`);
|
|
377
|
+
return {
|
|
378
|
+
type: "text",
|
|
379
|
+
text: requiredStringAllowEmpty(part.text, `${param}.${index}.text`),
|
|
380
|
+
...(annotations.length ? { annotations } : {}),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (part.type === "input_image") {
|
|
384
|
+
const detail = imageDetail(part.detail, `${param}.${index}.detail`);
|
|
385
|
+
return {
|
|
386
|
+
type: "image",
|
|
387
|
+
url: requiredImageUrl(part, `${param}.${index}.image_url`),
|
|
388
|
+
...(detail !== undefined ? { detail } : {}),
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
throw new HttpError(400, `Responses content part type '${String(part.type ?? "unknown")}' is not supported.`, "unsupported_response_content", `${param}.${index}.type`);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
function responseContentShape(value) {
|
|
395
|
+
if (value === undefined)
|
|
396
|
+
return "absent";
|
|
397
|
+
if (value === null)
|
|
398
|
+
return "null";
|
|
399
|
+
if (typeof value === "string")
|
|
400
|
+
return "string";
|
|
401
|
+
return "parts";
|
|
402
|
+
}
|
|
403
|
+
function responseTools(value) {
|
|
404
|
+
if (value === undefined)
|
|
405
|
+
return [];
|
|
406
|
+
if (!Array.isArray(value))
|
|
407
|
+
throw new HttpError(400, "tools must be an array.", "invalid_request_error", "tools");
|
|
408
|
+
const tools = [];
|
|
409
|
+
for (const tool of value)
|
|
410
|
+
appendResponseTool(tools, tool, undefined);
|
|
411
|
+
return tools;
|
|
412
|
+
}
|
|
413
|
+
function appendResponseTool(tools, value, namespace) {
|
|
414
|
+
if (!isRecord(value))
|
|
415
|
+
throw new HttpError(400, "tools entries must be objects.", "invalid_request_error", "tools");
|
|
416
|
+
const type = typeof value.type === "string" ? value.type : "";
|
|
417
|
+
if (type === "web_search" || type === "web_search_preview") {
|
|
418
|
+
if (namespace !== undefined) {
|
|
419
|
+
throw new HttpError(400, "Hosted web search cannot be nested in a namespace.", "unsupported_tool", "tools");
|
|
420
|
+
}
|
|
421
|
+
tools.push({ kind: "hosted", type, config: { ...value } });
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (type === "namespace") {
|
|
425
|
+
if (namespace !== undefined)
|
|
426
|
+
throw new HttpError(400, "Nested namespace tools are not supported.", "unsupported_tool", "tools");
|
|
427
|
+
const nested = value.tools;
|
|
428
|
+
if (!Array.isArray(nested))
|
|
429
|
+
throw new HttpError(400, "namespace tools must include a tools array.", "invalid_request_error", "tools");
|
|
430
|
+
const name = requiredString(value.name, "tools.namespace.name");
|
|
431
|
+
for (const tool of nested)
|
|
432
|
+
appendResponseTool(tools, tool, name);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (type !== "function" && type !== "custom") {
|
|
436
|
+
throw new HttpError(400, `Tool type '${type || "unknown"}' is not supported by this router MVP.`, "unsupported_tool", "tools");
|
|
437
|
+
}
|
|
438
|
+
const definition = isRecord(value.function) ? value.function : value;
|
|
439
|
+
const common = {
|
|
440
|
+
name: requiredString(definition.name, `tools.${type}.name`),
|
|
441
|
+
namespace,
|
|
442
|
+
description: optionalString(definition.description, `tools.${type}.description`),
|
|
443
|
+
};
|
|
444
|
+
if (type === "custom") {
|
|
445
|
+
const format = definition.format;
|
|
446
|
+
if (format !== undefined && !isRecord(format)) {
|
|
447
|
+
throw new HttpError(400, "tools.custom.format must be an object.", "invalid_request_error", "tools.custom.format");
|
|
448
|
+
}
|
|
449
|
+
tools.push({ kind: "custom", ...common, format });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const parameters = definition.parameters ?? definition.input_schema;
|
|
453
|
+
if (parameters !== undefined && !isRecord(parameters)) {
|
|
454
|
+
throw new HttpError(400, "tools.function.parameters must be an object.", "invalid_request_error", "tools.function.parameters");
|
|
455
|
+
}
|
|
456
|
+
const strict = definition.strict;
|
|
457
|
+
if (strict !== undefined && typeof strict !== "boolean") {
|
|
458
|
+
throw new HttpError(400, "tools.function.strict must be a boolean.", "invalid_request_error", "tools.function.strict");
|
|
459
|
+
}
|
|
460
|
+
tools.push({ kind: "function", ...common, parameters, strict });
|
|
461
|
+
}
|
|
462
|
+
function responseToolChoice(value) {
|
|
463
|
+
if (value === undefined)
|
|
464
|
+
return undefined;
|
|
465
|
+
if (typeof value === "string") {
|
|
466
|
+
if (value === "none" || value === "auto" || value === "required" || value === "any")
|
|
467
|
+
return value;
|
|
468
|
+
throw new HttpError(400, "tool_choice is not supported.", "invalid_request_error", "tool_choice");
|
|
469
|
+
}
|
|
470
|
+
if (isRecord(value) && (value.type === "web_search" || value.type === "web_search_preview")) {
|
|
471
|
+
return { kind: "hosted", type: value.type };
|
|
472
|
+
}
|
|
473
|
+
if (!isRecord(value) || (value.type !== "function" && value.type !== "custom")) {
|
|
474
|
+
throw new HttpError(400, "tool_choice is not supported.", "invalid_request_error", "tool_choice");
|
|
475
|
+
}
|
|
476
|
+
const definition = isRecord(value.function) ? value.function : value;
|
|
477
|
+
return {
|
|
478
|
+
kind: value.type,
|
|
479
|
+
name: requiredString(definition.name, "tool_choice.name"),
|
|
480
|
+
namespace: optionalString(definition.namespace ?? value.namespace, "tool_choice.namespace"),
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function responseAnnotations(value, param) {
|
|
484
|
+
if (value === undefined || value === null)
|
|
485
|
+
return [];
|
|
486
|
+
if (!Array.isArray(value)) {
|
|
487
|
+
throw new HttpError(400, `${param} must be an array.`, "invalid_request_error", param);
|
|
488
|
+
}
|
|
489
|
+
return value.map((annotation, index) => {
|
|
490
|
+
if (!isRecord(annotation) || typeof annotation.type !== "string") {
|
|
491
|
+
throw new HttpError(400, `${param}[${index}] must be an annotation object.`, "invalid_request_error", `${param}.${index}`);
|
|
492
|
+
}
|
|
493
|
+
return { ...annotation };
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
function reasoningControls(value) {
|
|
497
|
+
if (value === undefined || value === null)
|
|
498
|
+
return undefined;
|
|
499
|
+
if (!isRecord(value))
|
|
500
|
+
throw new HttpError(400, "reasoning must be an object.", "invalid_request_error", "reasoning");
|
|
501
|
+
const normalizedEffort = normalizedReasoningEffort(value.effort, "reasoning.effort");
|
|
502
|
+
const summary = value.summary;
|
|
503
|
+
if (summary !== undefined) {
|
|
504
|
+
throw new HttpError(400, "reasoning.summary is not supported by this router MVP.", "invalid_request_error", "reasoning.summary");
|
|
505
|
+
}
|
|
506
|
+
return normalizedEffort === undefined ? undefined : { effort: normalizedEffort };
|
|
507
|
+
}
|
|
508
|
+
function normalizedReasoningEffort(value, param) {
|
|
509
|
+
if (value === undefined)
|
|
510
|
+
return undefined;
|
|
511
|
+
if (typeof value !== "string" || value === "off") {
|
|
512
|
+
throw new HttpError(400, `${param} is not supported.`, "unsupported_reasoning_effort", param);
|
|
513
|
+
}
|
|
514
|
+
const candidate = value === "none" ? "off" : value;
|
|
515
|
+
if (!isReasoningEffort(candidate)) {
|
|
516
|
+
throw new HttpError(400, `${param} is not supported.`, "unsupported_reasoning_effort", param);
|
|
517
|
+
}
|
|
518
|
+
return candidate;
|
|
519
|
+
}
|
|
520
|
+
function validateTextControls(value) {
|
|
521
|
+
if (value === undefined || value === null)
|
|
522
|
+
return;
|
|
523
|
+
if (!isRecord(value))
|
|
524
|
+
throw new HttpError(400, "text must be an object.", "invalid_request_error", "text");
|
|
525
|
+
const verbosity = value.verbosity;
|
|
526
|
+
if (verbosity !== undefined) {
|
|
527
|
+
throw new HttpError(400, "text.verbosity is not supported by this router MVP.", "invalid_request_error", "text.verbosity");
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function responseFormatFromText(value) {
|
|
531
|
+
if (value === undefined || value === null)
|
|
532
|
+
return undefined;
|
|
533
|
+
if (!isRecord(value))
|
|
534
|
+
throw new HttpError(400, "text must be an object.", "invalid_request_error", "text");
|
|
535
|
+
const format = value.format;
|
|
536
|
+
if (format === undefined || format === null)
|
|
537
|
+
return undefined;
|
|
538
|
+
if (!isRecord(format) || typeof format.type !== "string") {
|
|
539
|
+
throw new HttpError(400, "text.format must be an object with a supported type.", "invalid_request_error", "text.format");
|
|
540
|
+
}
|
|
541
|
+
if (format.type === "text" || format.type === "json_object")
|
|
542
|
+
return { type: format.type };
|
|
543
|
+
if (format.type !== "json_schema") {
|
|
544
|
+
throw new HttpError(400, "text.format.type is not supported.", "invalid_request_error", "text.format.type");
|
|
545
|
+
}
|
|
546
|
+
if (!isRecord(format.schema)) {
|
|
547
|
+
throw new HttpError(400, "text.format.schema must be a JSON schema object.", "invalid_request_error", "text.format.schema");
|
|
548
|
+
}
|
|
549
|
+
const strict = optionalBoolean(format.strict, "text.format.strict");
|
|
550
|
+
return {
|
|
551
|
+
type: "json_schema",
|
|
552
|
+
name: requiredString(format.name, "text.format.name"),
|
|
553
|
+
schema: format.schema,
|
|
554
|
+
...(strict !== undefined ? { strict } : {}),
|
|
555
|
+
description: optionalString(format.description, "text.format.description"),
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function responseMetadata(payload) {
|
|
559
|
+
const metadata = {};
|
|
560
|
+
if (isRecord(payload.metadata))
|
|
561
|
+
Object.assign(metadata, payload.metadata);
|
|
562
|
+
if (isRecord(payload.client_metadata))
|
|
563
|
+
metadata.client_metadata = payload.client_metadata;
|
|
564
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
565
|
+
}
|
|
566
|
+
function itemStatus(value, param) {
|
|
567
|
+
if (value === undefined || value === null)
|
|
568
|
+
return undefined;
|
|
569
|
+
if (value === "in_progress" || value === "completed" || value === "incomplete")
|
|
570
|
+
return value;
|
|
571
|
+
throw new HttpError(400, `${param} is not supported.`, "invalid_request_error", param);
|
|
572
|
+
}
|
|
573
|
+
function hostedItemStatus(value, param) {
|
|
574
|
+
if (value === undefined || value === null)
|
|
575
|
+
return undefined;
|
|
576
|
+
if (value === "in_progress" ||
|
|
577
|
+
value === "searching" ||
|
|
578
|
+
value === "completed" ||
|
|
579
|
+
value === "incomplete" ||
|
|
580
|
+
value === "failed")
|
|
581
|
+
return value;
|
|
582
|
+
throw new HttpError(400, `${param} is not supported.`, "invalid_request_error", param);
|
|
583
|
+
}
|
|
584
|
+
function imageDetail(value, param) {
|
|
585
|
+
if (value === undefined || value === null)
|
|
586
|
+
return undefined;
|
|
587
|
+
if (value === "auto" || value === "low" || value === "high" || value === "original")
|
|
588
|
+
return value;
|
|
589
|
+
throw new HttpError(400, `${param} is not supported.`, "invalid_request_error", param);
|
|
590
|
+
}
|
|
591
|
+
function requiredImageUrl(value, param) {
|
|
592
|
+
let url = "";
|
|
593
|
+
if (typeof value.image_url === "string")
|
|
594
|
+
url = value.image_url;
|
|
595
|
+
else if (isRecord(value.image_url) && typeof value.image_url.url === "string")
|
|
596
|
+
url = value.image_url.url;
|
|
597
|
+
else if (typeof value.url === "string")
|
|
598
|
+
url = value.url;
|
|
599
|
+
if (!parseDataImageUrl(url)) {
|
|
600
|
+
throw new HttpError(400, `${param} must be a base64 image data URL.`, "invalid_request_error", param);
|
|
601
|
+
}
|
|
602
|
+
return url;
|
|
603
|
+
}
|
|
604
|
+
function stopOption(value) {
|
|
605
|
+
if (value === undefined)
|
|
606
|
+
return undefined;
|
|
607
|
+
if (typeof value === "string")
|
|
608
|
+
return value;
|
|
609
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string"))
|
|
610
|
+
return value;
|
|
611
|
+
throw new HttpError(400, "stop must be a string or an array of strings.", "invalid_request_error", "stop");
|
|
612
|
+
}
|
|
613
|
+
function optionalBoolean(value, param) {
|
|
614
|
+
if (value === undefined || value === null)
|
|
615
|
+
return undefined;
|
|
616
|
+
if (typeof value !== "boolean")
|
|
617
|
+
throw new HttpError(400, `${param} must be a boolean.`, "invalid_request_error", param);
|
|
618
|
+
return value;
|
|
619
|
+
}
|
|
620
|
+
function optionalNumber(value, param) {
|
|
621
|
+
if (value === undefined || value === null)
|
|
622
|
+
return undefined;
|
|
623
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
624
|
+
throw new HttpError(400, `${param} must be a number.`, "invalid_request_error", param);
|
|
625
|
+
return value;
|
|
626
|
+
}
|
|
627
|
+
function optionalString(value, param) {
|
|
628
|
+
if (value === undefined || value === null)
|
|
629
|
+
return undefined;
|
|
630
|
+
if (typeof value !== "string")
|
|
631
|
+
throw new HttpError(400, `${param} must be a string.`, "invalid_request_error", param);
|
|
632
|
+
return value;
|
|
633
|
+
}
|
|
634
|
+
function requiredString(value, param) {
|
|
635
|
+
if (typeof value === "string" && value.trim())
|
|
636
|
+
return value;
|
|
637
|
+
throw new HttpError(400, `${param} must be a non-empty string.`, "invalid_request_error", param);
|
|
638
|
+
}
|
|
639
|
+
function requiredToolCallId(value, param) {
|
|
640
|
+
if (isProtocolToolCallId(value))
|
|
641
|
+
return value;
|
|
642
|
+
throw new HttpError(400, `${param} must be a non-empty string no longer than ${TOOL_CALL_ID_MAX_LENGTH} characters.`, "invalid_request_error", param);
|
|
643
|
+
}
|
|
644
|
+
function requiredStringAllowEmpty(value, param) {
|
|
645
|
+
if (typeof value === "string")
|
|
646
|
+
return value;
|
|
647
|
+
throw new HttpError(400, `${param} must be a string.`, "invalid_request_error", param);
|
|
648
|
+
}
|
|
649
|
+
function requiredToolArguments(value, param) {
|
|
650
|
+
if (typeof value === "string") {
|
|
651
|
+
let parsed;
|
|
652
|
+
try {
|
|
653
|
+
parsed = JSON.parse(value);
|
|
654
|
+
}
|
|
655
|
+
catch {
|
|
656
|
+
throw new HttpError(400, `${param} must contain a JSON object.`, "invalid_request_error", param);
|
|
657
|
+
}
|
|
658
|
+
if (!isRecord(parsed)) {
|
|
659
|
+
throw new HttpError(400, `${param} must contain a JSON object.`, "invalid_request_error", param);
|
|
660
|
+
}
|
|
661
|
+
return value;
|
|
662
|
+
}
|
|
663
|
+
if (isRecord(value))
|
|
664
|
+
return JSON.stringify(value);
|
|
665
|
+
throw new HttpError(400, `${param} must be a string or object.`, "invalid_request_error", param);
|
|
666
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const CANONICAL_ROUTER_MODEL = "dari/routing";
|
|
2
|
+
export declare const ANTHROPIC_ROUTER_MODEL_TIERS: readonly ["mythos", "opus", "fable", "sonnet", "haiku"];
|
|
3
|
+
export declare function isAnthropicRouterModel(model: string): boolean;
|
|
4
|
+
export declare const EXPLICIT_MODEL_PREFIX = "dari/model/";
|
|
5
|
+
export declare function explicitModelId(model: string | undefined): string | null;
|
|
6
|
+
export declare function explicitModelSlug(modelId: string): string;
|
|
7
|
+
export declare function unsupportedModelMessage(): string;
|