@mupt-ai/dari-gateway 0.1.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/README.md +8 -1
- 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 +22 -5
- package/src/bun.ts +4 -0
- 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 +17 -17
- 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 +10 -9
- 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 +10 -10
- 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 +3 -0
- 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 +36 -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 +115 -41
- package/src/providers/pi_context.ts +6 -6
- package/src/providers/pi_options.ts +283 -453
- package/src/providers/pi_output.ts +6 -5
- package/src/providers/provider_attempt_runner.ts +1 -1
- package/src/providers/provider_call.ts +37 -18
- package/src/providers/provider_reported_cost.ts +138 -0
- package/src/providers/reasoning.ts +2 -2
- package/src/providers/stream_finalization.ts +10 -10
- 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 +31 -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 +46 -10
- package/src/runtime/provider_stream_lifecycle.ts +1 -1
- package/src/runtime/provider_stream_outcome.ts +2 -2
- package/src/runtime/request_tags.ts +34 -0
- package/src/runtime/routed_completion.ts +13 -11
- package/src/runtime/routed_protocol.ts +2 -2
- package/src/runtime/routed_runtime.ts +38 -20
- package/src/runtime/routing_lease.ts +7 -7
- package/src/runtime/runtime_reporter.ts +3 -3
- package/src/self_host/main.ts +19 -14
- package/src/self_host/platform.ts +5 -5
- package/src/self_host/prefix_store.ts +2 -2
- package/src/self_host/resolve.ts +11 -11
- 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 +11 -11
- 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 +30 -25
- 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 +9 -5
- package/src/types.ts +15 -0
|
@@ -1,30 +1,45 @@
|
|
|
1
1
|
import type { BedrockOptions } from "@mupt-ai/pi-ai/api/bedrock-converse-stream";
|
|
2
2
|
import { isReasoningEffort, isRecord, type ReasoningEffort } from "@mupt-ai/dari-router/policy-engine";
|
|
3
|
-
import type { InteractionRequest, InteractionResponseFormat, InteractionToolChoice } from "../interaction/types";
|
|
4
|
-
import { requiredCapabilitiesFor } from "../interaction/capabilities";
|
|
5
|
-
import { toolWireName, toolsByWireName } from "../interaction/tool_identity";
|
|
6
|
-
import { gatewayFor, payloadWithGatewayFields } from "../models/gateways";
|
|
7
|
-
import { offRequiresWireNone } from "../models/runtime_overrides";
|
|
8
|
-
import { effectiveReasoningForModel, validateReasoningEffortForModel } from "./reasoning";
|
|
9
|
-
import { isBedrockApi, isOpenAIResponsesApi } from "./model_capabilities";
|
|
10
|
-
import type { ResolvedProviderAuth } from "../types";
|
|
11
|
-
import type { ProviderFetch } from "./response_capture";
|
|
12
|
-
import { forwardsAnthropicSafeguards, payloadWithSafeguards } from "./anthropic_safeguards";
|
|
3
|
+
import type { InteractionRequest, InteractionResponseFormat, InteractionToolChoice } from "../interaction/types.js";
|
|
4
|
+
import { requiredCapabilitiesFor } from "../interaction/capabilities.js";
|
|
5
|
+
import { toolWireName, toolsByWireName } from "../interaction/tool_identity.js";
|
|
6
|
+
import { gatewayFor, payloadWithGatewayFields } from "../models/gateways.js";
|
|
7
|
+
import { offRequiresWireNone } from "../models/runtime_overrides.js";
|
|
8
|
+
import { effectiveReasoningForModel, validateReasoningEffortForModel } from "./reasoning.js";
|
|
9
|
+
import { isBedrockApi, isOpenAIResponsesApi } from "./model_capabilities.js";
|
|
10
|
+
import type { ResolvedProviderAuth } from "../types.js";
|
|
11
|
+
import type { ProviderFetch } from "./response_capture.js";
|
|
12
|
+
import { forwardsAnthropicSafeguards, payloadWithSafeguards } from "./anthropic_safeguards.js";
|
|
13
13
|
|
|
14
14
|
type PiOptionsSupport = {
|
|
15
15
|
getSupportedThinkingLevels?: (model: Record<string, unknown>) => string[];
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
type Payload = Record<string, unknown>;
|
|
19
|
+
// A patch returns the next payload, or undefined to leave it unchanged.
|
|
20
|
+
type PayloadPatch = (payload: Payload) => unknown;
|
|
21
|
+
type Model = Record<string, unknown>;
|
|
22
|
+
type ImageDetail = "auto" | "low" | "high" | "original";
|
|
23
|
+
|
|
18
24
|
const ANTHROPIC_FAST_MODE_BETA = "fast-mode-2026-02-01";
|
|
25
|
+
const ANTHROPIC_BINDING_CONTROLS_BETA = "thinking-binding-controls-2026-08-01";
|
|
19
26
|
// Anthropic rejects newer models from older Claude Code versions, so forward
|
|
20
27
|
// the client's own user agent and keep this fallback current for other clients.
|
|
21
28
|
const CLAUDE_CODE_USER_AGENT = "claude-cli/2.1.280";
|
|
29
|
+
const IMAGE_DETAILS: readonly unknown[] = ["auto", "low", "high", "original"];
|
|
30
|
+
|
|
31
|
+
const isAnthropic = (api: unknown) => api === "anthropic-messages";
|
|
32
|
+
const isCompletions = (api: unknown) => api === "openai-completions";
|
|
33
|
+
// Responses payloads Pi builds from the public OpenAI/Azure APIs (not Codex).
|
|
34
|
+
const isResponses = (api: unknown) => api === "openai-responses" || api === "azure-openai-responses";
|
|
35
|
+
const isOpenAI = (api: unknown) => isResponses(api) || isCompletions(api);
|
|
36
|
+
const isGoogle = (api: unknown) => api === "google-generative-ai" || api === "google-vertex";
|
|
22
37
|
|
|
23
38
|
export function buildOptions(
|
|
24
39
|
request: InteractionRequest,
|
|
25
40
|
auth: ResolvedProviderAuth | string,
|
|
26
41
|
selectedModel: string,
|
|
27
|
-
model:
|
|
42
|
+
model: Model,
|
|
28
43
|
pi: PiOptionsSupport,
|
|
29
44
|
sessionId?: string,
|
|
30
45
|
selectedReasoningEffort?: ReasoningEffort,
|
|
@@ -32,218 +47,153 @@ export function buildOptions(
|
|
|
32
47
|
signal?: AbortSignal,
|
|
33
48
|
fetch?: ProviderFetch,
|
|
34
49
|
): Record<string, unknown> {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
const api = model.api;
|
|
51
|
+
const providerAuth: ResolvedProviderAuth = typeof auth === "string" ? { type: "bearer", apiKey: auth } : auth;
|
|
52
|
+
const { generation, toolChoice, parallelToolCalls, responseFormat } = request;
|
|
53
|
+
const headers: Record<string, string> = {};
|
|
39
54
|
const options: Record<string, unknown> = {
|
|
40
|
-
...
|
|
41
|
-
...(isClaudeCodeOAuth(providerAuth, model) ? { headers: { "user-agent": request.claudeCodeUserAgent ?? CLAUDE_CODE_USER_AGENT } } : {}),
|
|
55
|
+
...authenticationOptions(providerAuth, api),
|
|
42
56
|
maxRetries: 0,
|
|
43
57
|
timeoutMs,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
metadata: {
|
|
47
|
-
...(request.metadata ?? {}),
|
|
48
|
-
...(request.user ? { user: request.user } : {}),
|
|
49
|
-
},
|
|
58
|
+
signal,
|
|
59
|
+
fetch,
|
|
60
|
+
metadata: { ...request.metadata, ...(request.user ? { user: request.user } : {}) },
|
|
50
61
|
// Feeds OpenAI prompt_cache_key + session-affinity headers inside pi-ai;
|
|
51
62
|
// ignored by providers without session-based caching.
|
|
52
|
-
|
|
63
|
+
sessionId: sessionId || undefined,
|
|
53
64
|
};
|
|
65
|
+
const patches: PayloadPatch[] = [];
|
|
66
|
+
|
|
67
|
+
if (isClaudeCodeOAuth(providerAuth, api)) {
|
|
68
|
+
headers["user-agent"] = request.claudeCodeUserAgent ?? CLAUDE_CODE_USER_AGENT;
|
|
69
|
+
}
|
|
54
70
|
// Pi's Anthropic transport honors compat.supportsTemperature; the OpenAI
|
|
55
71
|
// transports forward temperature regardless, so drop it here for models
|
|
56
72
|
// that reject it instead of failing the whole request.
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
&& !(isRecord(model.compat) && model.compat.supportsTemperature === false)
|
|
60
|
-
) {
|
|
61
|
-
options.temperature = request.generation.temperature;
|
|
62
|
-
}
|
|
63
|
-
if (request.generation.maxOutputTokens !== undefined) options.maxTokens = request.generation.maxOutputTokens;
|
|
64
|
-
const payloadPatches: Array<(payload: Record<string, unknown>) => unknown> = [];
|
|
65
|
-
if (request.generation.topP !== undefined || request.generation.stop !== undefined) {
|
|
66
|
-
payloadPatches.push((payload) => payloadWithSamplingControls(payload, request, model));
|
|
73
|
+
if (!(isRecord(model.compat) && model.compat.supportsTemperature === false)) {
|
|
74
|
+
options.temperature = generation.temperature;
|
|
67
75
|
}
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
...payload,
|
|
72
|
-
thinking: { type: "disabled" },
|
|
73
|
-
}));
|
|
74
|
-
} else if (requestedReasoningBudget !== undefined && model.api === "anthropic-messages") {
|
|
75
|
-
payloadPatches.push((payload) => ({
|
|
76
|
-
...payload,
|
|
77
|
-
thinking: {
|
|
78
|
-
...(isRecord(payload.thinking) ? payload.thinking : {}),
|
|
79
|
-
type: "enabled",
|
|
80
|
-
budget_tokens: requestedReasoningBudget,
|
|
81
|
-
},
|
|
82
|
-
}));
|
|
76
|
+
options.maxTokens = generation.maxOutputTokens;
|
|
77
|
+
if (generation.topP !== undefined || generation.stop !== undefined) {
|
|
78
|
+
patches.push((payload) => payloadWithSamplingControls(payload, request, api));
|
|
83
79
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const thinking = payload.thinking;
|
|
97
|
-
if (!isRecord(thinking) || !isRecord(thinking.block_binding)) return undefined;
|
|
98
|
-
const { block_binding: _dropped, ...thinkingRest } = thinking;
|
|
99
|
-
const betas = Array.isArray(payload.betas)
|
|
100
|
-
? (payload.betas as string[]).filter((beta) => beta !== "thinking-binding-controls-2026-08-01")
|
|
101
|
-
: payload.betas;
|
|
102
|
-
return { ...payload, thinking: thinkingRest, betas };
|
|
103
|
-
});
|
|
80
|
+
|
|
81
|
+
if (isAnthropic(api)) {
|
|
82
|
+
const budgetTokens = request.reasoning?.budgetTokens;
|
|
83
|
+
if (request.reasoning?.enabled === false) {
|
|
84
|
+
patches.push((payload) => ({ ...payload, thinking: { type: "disabled" } }));
|
|
85
|
+
} else if (budgetTokens !== undefined) {
|
|
86
|
+
patches.push((payload) => ({
|
|
87
|
+
...payload,
|
|
88
|
+
thinking: { ...asRecord(payload.thinking), type: "enabled", budget_tokens: budgetTokens },
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
patches.push(payloadWithoutThinkingBlockBinding);
|
|
104
92
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
});
|
|
93
|
+
|
|
94
|
+
const gatewayFields = gatewayFor(model.provider)?.requestFields({ zdr: model.zeroDataRetention === true });
|
|
108
95
|
if (gatewayFields !== undefined && Object.keys(gatewayFields).length > 0) {
|
|
109
|
-
|
|
96
|
+
patches.push((payload) => payloadWithGatewayFields(payload, gatewayFields));
|
|
110
97
|
}
|
|
111
98
|
const safeguards = request.anthropicSafeguards;
|
|
112
99
|
if (safeguards !== undefined && forwardsAnthropicSafeguards(request, model)) {
|
|
113
|
-
|
|
100
|
+
patches.push((payload) => payloadWithSafeguards(payload, safeguards));
|
|
114
101
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
isOpenAIResponsesApi(model.api)
|
|
118
|
-
) {
|
|
102
|
+
|
|
103
|
+
if (isOpenAIResponsesApi(api) && requiredCapabilitiesFor(request).includes("openai_hosted_web_search")) {
|
|
119
104
|
options.emitProviderEvents = true;
|
|
120
|
-
|
|
121
|
-
|
|
105
|
+
const hosted = request.tools.flatMap((tool) => tool.kind === "hosted" ? [{ ...tool.config }] : []);
|
|
106
|
+
if (hosted.length > 0) {
|
|
107
|
+
patches.push((payload) => ({ ...payload, tools: [...asArray(payload.tools), ...hosted] }));
|
|
122
108
|
}
|
|
123
109
|
}
|
|
124
|
-
if (
|
|
125
|
-
(
|
|
126
|
-
|
|
127
|
-
|| model.api === "azure-openai-responses"
|
|
128
|
-
|| model.api === "openai-completions"
|
|
129
|
-
)
|
|
130
|
-
&& imageDetailQueues(request).size > 0
|
|
131
|
-
) {
|
|
132
|
-
payloadPatches.push((payload) => payloadWithImageDetails(payload, request));
|
|
110
|
+
if (isOpenAI(api)) {
|
|
111
|
+
const details = imageDetailQueues(request);
|
|
112
|
+
if (details.size > 0) patches.push((payload) => payloadWithImageDetails(payload, details));
|
|
133
113
|
}
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (simpleChoice !== undefined) options.toolChoice = simpleChoice;
|
|
141
|
-
} else {
|
|
142
|
-
payloadPatches.push((payload) => payloadWithToolChoice(payload, requestedToolChoice, model));
|
|
143
|
-
}
|
|
114
|
+
|
|
115
|
+
if (toolChoice !== undefined) {
|
|
116
|
+
// Bedrock builds its Converse toolConfig from options.toolChoice before
|
|
117
|
+
// onPayload runs, so the choice must ride on options, not a payload patch.
|
|
118
|
+
if (isBedrockApi(api)) options.toolChoice = bedrockToolChoice(toolChoice);
|
|
119
|
+
else patches.push((payload) => ({ ...payload, tool_choice: providerToolChoice(toolChoice, api) }));
|
|
144
120
|
}
|
|
145
|
-
if (request.tools.some((tool) =>
|
|
146
|
-
|
|
147
|
-
)) {
|
|
148
|
-
payloadPatches.push((payload) => payloadWithStrictTools(payload, request, model));
|
|
121
|
+
if (request.tools.some((tool) => tool.kind === "function" && (tool.strict !== undefined || isAnthropic(api)))) {
|
|
122
|
+
patches.push((payload) => payloadWithStrictTools(payload, request, api));
|
|
149
123
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
options.responseFormat = responseFormat(requestedResponseFormat);
|
|
124
|
+
if (responseFormat !== undefined) {
|
|
125
|
+
options.responseFormat = piResponseFormat(responseFormat);
|
|
153
126
|
// Pi keeps responseFormat in options but does not serialize it in simple
|
|
154
127
|
// OpenAI adapters, so patch the provider payload at the boundary.
|
|
155
|
-
|
|
128
|
+
patches.push((payload) => payloadWithResponseFormat(payload, responseFormat, api));
|
|
156
129
|
}
|
|
157
|
-
const parallelToolCalls = request.parallelToolCalls;
|
|
158
130
|
if (parallelToolCalls !== undefined) {
|
|
159
|
-
|
|
131
|
+
patches.push((payload) => payloadWithParallelToolCalls(payload, parallelToolCalls, api));
|
|
160
132
|
}
|
|
133
|
+
|
|
161
134
|
if (model.fastMode === true) {
|
|
162
|
-
if (model.provider === "openai" &&
|
|
163
|
-
// Pi's simple adapter
|
|
164
|
-
// options, so patch the
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
} else if (model.provider === "anthropic" && model.api === "anthropic-messages") {
|
|
171
|
-
payloadPatches.push((payload) => ({ ...payload, speed: "fast" }));
|
|
172
|
-
options.headers = {
|
|
173
|
-
...(isRecord(options.headers) ? options.headers : {}),
|
|
174
|
-
"anthropic-beta": ANTHROPIC_FAST_MODE_BETA,
|
|
175
|
-
};
|
|
135
|
+
if (model.provider === "openai" && isOpenAI(api) && api !== "azure-openai-responses") {
|
|
136
|
+
// Pi's simple Responses adapter drops serviceTier while lowering its
|
|
137
|
+
// options, so patch the payload too; the option still feeds usage pricing.
|
|
138
|
+
if (api === "openai-responses") options.serviceTier = "priority";
|
|
139
|
+
patches.push((payload) => ({ ...payload, service_tier: "priority" }));
|
|
140
|
+
} else if (model.provider === "anthropic" && isAnthropic(api)) {
|
|
141
|
+
headers["anthropic-beta"] = ANTHROPIC_FAST_MODE_BETA;
|
|
142
|
+
patches.push((payload) => ({ ...payload, speed: "fast" }));
|
|
176
143
|
}
|
|
177
144
|
}
|
|
178
145
|
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
request,
|
|
185
|
-
selectedModel,
|
|
186
|
-
supportedLevels,
|
|
187
|
-
selectedReasoningEffort,
|
|
188
|
-
defaultReasoningEffort,
|
|
189
|
-
});
|
|
190
|
-
const providerReasoning = request.reasoning?.hasConfigurationUpdate
|
|
191
|
-
? request.reasoning.baseEffort ?? defaultReasoningEffort
|
|
192
|
-
: effectiveReasoning;
|
|
193
|
-
if (request.reasoning?.baseEffort !== undefined) {
|
|
194
|
-
validateReasoningEffortForModel(
|
|
195
|
-
request.reasoning.baseEffort,
|
|
196
|
-
selectedModel,
|
|
197
|
-
supportedLevels,
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
if (providerReasoning !== undefined && providerReasoning !== "off") {
|
|
201
|
-
options.reasoning = providerReasoning;
|
|
202
|
-
} else if (
|
|
203
|
-
providerReasoning === "off" &&
|
|
204
|
-
offRequiresWireNone(selectedModel) &&
|
|
205
|
-
model.api === "openai-completions"
|
|
206
|
-
) {
|
|
207
|
-
// These models need explicit "none" rather than the provider default.
|
|
208
|
-
// Gateways use nested reasoning; direct providers use reasoning_effort.
|
|
209
|
-
payloadPatches.push((payload) => {
|
|
210
|
-
if (!gatewayFor(model.provider)) return { ...payload, reasoning_effort: "none" };
|
|
211
|
-
const { reasoning_effort: _nativeEffort, ...rest } = payload;
|
|
212
|
-
return {
|
|
213
|
-
...rest,
|
|
214
|
-
reasoning: { ...(isRecord(payload.reasoning) ? payload.reasoning : {}), effort: "none" },
|
|
215
|
-
};
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
if (
|
|
219
|
-
(requestedToolChoice !== undefined || parallelToolCalls !== undefined) &&
|
|
220
|
-
!isBedrockApi(model.api)
|
|
221
|
-
) {
|
|
222
|
-
// Some clients send the default tool_choice even after omitting tools for
|
|
223
|
-
// a turn. OpenAI rejects every tool_choice value when the lowered payload
|
|
224
|
-
// has no tools, so enforce that provider invariant after all tool patches.
|
|
225
|
-
payloadPatches.push(payloadWithoutOrphanedToolChoice);
|
|
146
|
+
const reasoning = providerReasoning(request, selectedModel, model, pi, selectedReasoningEffort);
|
|
147
|
+
if (reasoning !== undefined && reasoning !== "off") {
|
|
148
|
+
options.reasoning = reasoning;
|
|
149
|
+
} else if (reasoning === "off" && isCompletions(api) && offRequiresWireNone(selectedModel)) {
|
|
150
|
+
patches.push((payload) => payloadWithReasoningNone(payload, model));
|
|
226
151
|
}
|
|
152
|
+
if (isOpenAIResponsesApi(api)) patches.push(payloadWithDetailedReasoningSummary);
|
|
227
153
|
|
|
228
|
-
|
|
229
|
-
|
|
154
|
+
// Some clients send the default tool_choice even after omitting tools for
|
|
155
|
+
// a turn. OpenAI rejects every tool_choice value when the lowered payload
|
|
156
|
+
// has no tools, so enforce that provider invariant after all tool patches.
|
|
157
|
+
if ((toolChoice !== undefined || parallelToolCalls !== undefined) && !isBedrockApi(api)) {
|
|
158
|
+
patches.push(payloadWithoutOrphanedToolChoice);
|
|
230
159
|
}
|
|
231
|
-
|
|
160
|
+
|
|
161
|
+
if (Object.keys(headers).length > 0) options.headers = headers;
|
|
162
|
+
if (patches.length > 0) options.onPayload = (payload: unknown) => applyPayloadPatches(payload, patches);
|
|
163
|
+
return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined));
|
|
232
164
|
}
|
|
233
165
|
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
166
|
+
export function supportedThinkingLevels(model: Model, pi: PiOptionsSupport): string[] {
|
|
167
|
+
const levels = Array.isArray(model.supportedThinkingLevels)
|
|
168
|
+
? model.supportedThinkingLevels
|
|
169
|
+
: pi.getSupportedThinkingLevels?.(model);
|
|
170
|
+
return Array.isArray(levels) ? levels.filter((level) => typeof level === "string") : ["off"];
|
|
237
171
|
}
|
|
238
172
|
|
|
239
|
-
function
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
173
|
+
function providerReasoning(
|
|
174
|
+
request: InteractionRequest,
|
|
175
|
+
selectedModel: string,
|
|
176
|
+
model: Model,
|
|
177
|
+
pi: PiOptionsSupport,
|
|
178
|
+
selectedReasoningEffort: ReasoningEffort | undefined,
|
|
179
|
+
): ReasoningEffort | undefined {
|
|
180
|
+
const supportedLevels = supportedThinkingLevels(model, pi);
|
|
181
|
+
const defaultReasoningEffort = isReasoningEffort(model.defaultThinkingLevel) ? model.defaultThinkingLevel : undefined;
|
|
182
|
+
const effective = effectiveReasoningForModel({
|
|
183
|
+
request,
|
|
184
|
+
selectedModel,
|
|
185
|
+
supportedLevels,
|
|
186
|
+
selectedReasoningEffort,
|
|
187
|
+
defaultReasoningEffort,
|
|
188
|
+
});
|
|
189
|
+
const baseEffort = request.reasoning?.baseEffort;
|
|
190
|
+
if (baseEffort !== undefined) validateReasoningEffortForModel(baseEffort, selectedModel, supportedLevels);
|
|
191
|
+
return request.reasoning?.hasConfigurationUpdate ? baseEffort ?? defaultReasoningEffort : effective;
|
|
243
192
|
}
|
|
244
193
|
|
|
245
|
-
function
|
|
194
|
+
function authenticationOptions(auth: ResolvedProviderAuth, api: unknown): BedrockOptions | Record<string, unknown> {
|
|
246
195
|
if (auth.type === "bearer") return { apiKey: auth.apiKey };
|
|
196
|
+
if (!isBedrockApi(api)) throw new Error("AWS SigV4 authentication requires the Bedrock API.");
|
|
247
197
|
return {
|
|
248
198
|
env: {
|
|
249
199
|
AWS_ACCESS_KEY_ID: auth.access_key_id,
|
|
@@ -257,18 +207,13 @@ function bedrockAuthenticationOptions(auth: ResolvedProviderAuth): BedrockOption
|
|
|
257
207
|
};
|
|
258
208
|
}
|
|
259
209
|
|
|
260
|
-
function
|
|
261
|
-
|
|
262
|
-
if (typeof choice === "string") return choice;
|
|
263
|
-
return { type: "function", function: { name: toolWireName(choice.namespace, choice.name) } };
|
|
210
|
+
function isClaudeCodeOAuth(auth: ResolvedProviderAuth, api: unknown): boolean {
|
|
211
|
+
return auth.type === "bearer" && isAnthropic(api) && auth.apiKey.includes("sk-ant-oat");
|
|
264
212
|
}
|
|
265
213
|
|
|
266
|
-
// Chains
|
|
267
|
-
//
|
|
268
|
-
function applyPayloadPatches(
|
|
269
|
-
payload: unknown,
|
|
270
|
-
patches: Array<(payload: Record<string, unknown>) => unknown>
|
|
271
|
-
): unknown {
|
|
214
|
+
// Chains patches, preserving the "undefined means unchanged" contract for the
|
|
215
|
+
// combined result.
|
|
216
|
+
function applyPayloadPatches(payload: unknown, patches: PayloadPatch[]): unknown {
|
|
272
217
|
if (!isRecord(payload)) return undefined;
|
|
273
218
|
let current = payload;
|
|
274
219
|
let changed = false;
|
|
@@ -282,305 +227,190 @@ function applyPayloadPatches(
|
|
|
282
227
|
return changed ? current : undefined;
|
|
283
228
|
}
|
|
284
229
|
|
|
285
|
-
function
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
):
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
};
|
|
306
|
-
if (google) {
|
|
307
|
-
return { ...payload, config: { ...(isRecord(payload.config) ? payload.config : {}), ...controls } };
|
|
308
|
-
}
|
|
309
|
-
if (bedrock) {
|
|
310
|
-
return {
|
|
311
|
-
...payload,
|
|
312
|
-
inferenceConfig: {
|
|
313
|
-
...(isRecord(payload.inferenceConfig) ? payload.inferenceConfig : {}),
|
|
314
|
-
...controls,
|
|
315
|
-
},
|
|
316
|
-
};
|
|
317
|
-
}
|
|
230
|
+
function asRecord(value: unknown): Payload {
|
|
231
|
+
return isRecord(value) ? value : {};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function asArray(value: unknown): unknown[] {
|
|
235
|
+
return Array.isArray(value) ? value : [];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function payloadWithSamplingControls(payload: Payload, request: InteractionRequest, api: unknown): Payload {
|
|
239
|
+
const { topP, stop } = request.generation;
|
|
240
|
+
const camelCase = isGoogle(api) || isBedrockApi(api);
|
|
241
|
+
const controls: Payload = {};
|
|
242
|
+
// Anthropic rejects top_p with thinking; keep the hosted path aligned with
|
|
243
|
+
// the router's unsupported Anthropic top_p control.
|
|
244
|
+
if (topP !== undefined && !isAnthropic(api)) controls[camelCase ? "topP" : "top_p"] = topP;
|
|
245
|
+
if (stop !== undefined && !isOpenAIResponsesApi(api)) {
|
|
246
|
+
const key = camelCase ? "stopSequences" : isAnthropic(api) ? "stop_sequences" : "stop";
|
|
247
|
+
controls[key] = isCompletions(api) || typeof stop !== "string" ? stop : [stop];
|
|
248
|
+
}
|
|
249
|
+
if (isGoogle(api)) return { ...payload, config: { ...asRecord(payload.config), ...controls } };
|
|
250
|
+
if (isBedrockApi(api)) return { ...payload, inferenceConfig: { ...asRecord(payload.inferenceConfig), ...controls } };
|
|
318
251
|
return { ...payload, ...controls };
|
|
319
252
|
}
|
|
320
253
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
254
|
+
// Pi sends `thinking.block_binding: {prefix_mismatch_behavior: "drop_block"}`
|
|
255
|
+
// plus the binding-controls beta on mid-conversation-effort models (Fable 5.1,
|
|
256
|
+
// Opus 5). That opts every request into the preserved-thinking prefix check:
|
|
257
|
+
// when a replayed thinking block's recorded prefix no longer matches (client
|
|
258
|
+
// re-render, retry, or provider refusal in the loop), the API silently drops
|
|
259
|
+
// the block, which mutates the prompt bytes from that block onward and
|
|
260
|
+
// re-writes the rest of the conversation at cache-write prices. On
|
|
261
|
+
// organizations not yet enforced (created before 2026-08-31), opting out keeps
|
|
262
|
+
// the prefix stable and the cache warm; the API records the mismatch without
|
|
263
|
+
// acting on it.
|
|
264
|
+
function payloadWithoutThinkingBlockBinding(payload: Payload): Payload | undefined {
|
|
265
|
+
const thinking = payload.thinking;
|
|
266
|
+
if (!isRecord(thinking) || !isRecord(thinking.block_binding)) return undefined;
|
|
267
|
+
const { block_binding: _, ...rest } = thinking;
|
|
268
|
+
const betas = Array.isArray(payload.betas)
|
|
269
|
+
? payload.betas.filter((beta) => beta !== ANTHROPIC_BINDING_CONTROLS_BETA)
|
|
270
|
+
: payload.betas;
|
|
271
|
+
return { ...payload, thinking: rest, betas };
|
|
328
272
|
}
|
|
329
273
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
model
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const providerFormat = providerResponseFormat(format, model);
|
|
337
|
-
if (providerFormat === null) return undefined;
|
|
338
|
-
if (model.api === "openai-responses" || model.api === "azure-openai-responses") {
|
|
339
|
-
return {
|
|
340
|
-
...payload,
|
|
341
|
-
text: {
|
|
342
|
-
...(isRecord(payload.text) ? payload.text : {}),
|
|
343
|
-
format: providerFormat,
|
|
344
|
-
},
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
if (model.api === "openai-completions") {
|
|
348
|
-
return { ...payload, response_format: providerFormat };
|
|
349
|
-
}
|
|
350
|
-
if (model.api === "anthropic-messages") {
|
|
351
|
-
return {
|
|
352
|
-
...payload,
|
|
353
|
-
output_config: {
|
|
354
|
-
...(isRecord(payload.output_config) ? payload.output_config : {}),
|
|
355
|
-
format: providerFormat,
|
|
356
|
-
},
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
return undefined;
|
|
274
|
+
// These models need explicit "none" rather than the provider default.
|
|
275
|
+
// Gateways use nested reasoning; direct providers use reasoning_effort.
|
|
276
|
+
function payloadWithReasoningNone(payload: Payload, model: Model): Payload {
|
|
277
|
+
if (!gatewayFor(model.provider)) return { ...payload, reasoning_effort: "none" };
|
|
278
|
+
const { reasoning_effort: _, ...rest } = payload;
|
|
279
|
+
return { ...rest, reasoning: { ...asRecord(payload.reasoning), effort: "none" } };
|
|
360
280
|
}
|
|
361
281
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
):
|
|
366
|
-
if (
|
|
367
|
-
|
|
368
|
-
return {
|
|
369
|
-
type: "json_schema",
|
|
370
|
-
schema: format.type === "json_schema" ? format.schema : { type: "object" },
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
if (format.type === "json_object") return format;
|
|
374
|
-
const { type: _, ...jsonSchema } = format;
|
|
375
|
-
if (model.api === "openai-responses" || model.api === "azure-openai-responses") {
|
|
376
|
-
return { ...jsonSchema, type: "json_schema" };
|
|
377
|
-
}
|
|
378
|
-
return { type: "json_schema", json_schema: jsonSchema };
|
|
282
|
+
// Pi's default "auto" summary leaves many reasoning items empty (93% on the
|
|
283
|
+
// ChatGPT Codex backend). Readable summaries are the only reasoning a later
|
|
284
|
+
// turn on another provider can replay, so ask for detailed ones.
|
|
285
|
+
function payloadWithDetailedReasoningSummary(payload: Payload): Payload | undefined {
|
|
286
|
+
if (!isRecord(payload.reasoning) || payload.reasoning.summary === undefined) return undefined;
|
|
287
|
+
return { ...payload, reasoning: { ...payload.reasoning, summary: "detailed" } };
|
|
379
288
|
}
|
|
380
289
|
|
|
381
|
-
function
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
): Record<string, unknown> {
|
|
386
|
-
if (typeof choice === "object" && choice.kind === "hosted") {
|
|
387
|
-
return { ...payload, tool_choice: { type: choice.type } };
|
|
388
|
-
}
|
|
389
|
-
if (model.api === "anthropic-messages") {
|
|
390
|
-
const toolChoice = typeof choice === "string"
|
|
391
|
-
? anthropicToolChoice(choice)
|
|
392
|
-
: { type: "tool", name: toolWireName(choice.namespace, choice.name) };
|
|
393
|
-
return { ...payload, tool_choice: toolChoice };
|
|
394
|
-
}
|
|
395
|
-
if (model.api === "openai-responses" || model.api === "azure-openai-responses") {
|
|
396
|
-
const toolChoice = typeof choice === "string"
|
|
397
|
-
? openAiToolChoice(choice)
|
|
398
|
-
// Pi currently declares both interaction tool kinds as JSON function tools.
|
|
399
|
-
: { type: "function", name: toolWireName(choice.namespace, choice.name) };
|
|
400
|
-
return { ...payload, tool_choice: toolChoice };
|
|
401
|
-
}
|
|
402
|
-
const toolChoice = typeof choice === "string"
|
|
403
|
-
? openAiToolChoice(choice)
|
|
404
|
-
: { type: "function", function: { name: toolWireName(choice.namespace, choice.name) } };
|
|
405
|
-
return { ...payload, tool_choice: toolChoice };
|
|
290
|
+
function piResponseFormat(format: InteractionResponseFormat): Payload {
|
|
291
|
+
if (format.type !== "json_schema") return format;
|
|
292
|
+
const { type: _, ...jsonSchema } = format;
|
|
293
|
+
return { type: "json_schema", jsonSchema };
|
|
406
294
|
}
|
|
407
295
|
|
|
408
|
-
function
|
|
409
|
-
|
|
410
|
-
)
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
) {
|
|
296
|
+
function payloadWithResponseFormat(payload: Payload, format: InteractionResponseFormat, api: unknown): unknown {
|
|
297
|
+
if (format.type === "text") return undefined;
|
|
298
|
+
if (isAnthropic(api)) {
|
|
299
|
+
const schema = format.type === "json_schema" ? format.schema : { type: "object" };
|
|
300
|
+
return { ...payload, output_config: { ...asRecord(payload.output_config), format: { type: "json_schema", schema } } };
|
|
301
|
+
}
|
|
302
|
+
if (format.type === "json_object") {
|
|
303
|
+
if (isResponses(api)) return { ...payload, text: { ...asRecord(payload.text), format } };
|
|
304
|
+
if (isCompletions(api)) return { ...payload, response_format: format };
|
|
415
305
|
return undefined;
|
|
416
306
|
}
|
|
417
|
-
const {
|
|
418
|
-
return
|
|
307
|
+
const { type: _, ...jsonSchema } = format;
|
|
308
|
+
if (isResponses(api)) return { ...payload, text: { ...asRecord(payload.text), format: { ...jsonSchema, type: "json_schema" } } };
|
|
309
|
+
if (isCompletions(api)) return { ...payload, response_format: { type: "json_schema", json_schema: jsonSchema } };
|
|
310
|
+
return undefined;
|
|
419
311
|
}
|
|
420
312
|
|
|
421
|
-
function
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
return { type: "any" };
|
|
426
|
-
default:
|
|
427
|
-
return { type: choice };
|
|
428
|
-
}
|
|
313
|
+
function bedrockToolChoice(choice: InteractionToolChoice): unknown {
|
|
314
|
+
if (typeof choice === "string") return choice;
|
|
315
|
+
if (choice.kind === "hosted") return undefined;
|
|
316
|
+
return { type: "function", function: { name: toolWireName(choice.namespace, choice.name) } };
|
|
429
317
|
}
|
|
430
318
|
|
|
431
|
-
function
|
|
432
|
-
|
|
319
|
+
function providerToolChoice(choice: InteractionToolChoice, api: unknown): unknown {
|
|
320
|
+
if (typeof choice === "string") {
|
|
321
|
+
if (isAnthropic(api)) return { type: choice === "required" ? "any" : choice };
|
|
322
|
+
return choice === "any" ? "required" : choice;
|
|
323
|
+
}
|
|
324
|
+
if (choice.kind === "hosted") return { type: choice.type };
|
|
325
|
+
const name = toolWireName(choice.namespace, choice.name);
|
|
326
|
+
if (isAnthropic(api)) return { type: "tool", name };
|
|
327
|
+
// Pi currently declares both interaction tool kinds as JSON function tools.
|
|
328
|
+
if (isResponses(api)) return { type: "function", name };
|
|
329
|
+
return { type: "function", function: { name } };
|
|
433
330
|
}
|
|
434
331
|
|
|
435
|
-
function
|
|
436
|
-
payload
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const hosted = request.tools.flatMap((tool) =>
|
|
440
|
-
tool.kind === "hosted" ? [{ ...tool.config }] : []
|
|
441
|
-
);
|
|
442
|
-
if (hosted.length === 0) return payload;
|
|
443
|
-
return {
|
|
444
|
-
...payload,
|
|
445
|
-
tools: [...(Array.isArray(payload.tools) ? payload.tools : []), ...hosted],
|
|
446
|
-
};
|
|
332
|
+
function payloadWithoutOrphanedToolChoice(payload: Payload): Payload | undefined {
|
|
333
|
+
if (!("tool_choice" in payload) || asArray(payload.tools).length > 0) return undefined;
|
|
334
|
+
const { tool_choice: _, ...rest } = payload;
|
|
335
|
+
return rest;
|
|
447
336
|
}
|
|
448
337
|
|
|
449
|
-
function payloadWithStrictTools(
|
|
450
|
-
payload
|
|
451
|
-
request: Pick<InteractionRequest, "tools">,
|
|
452
|
-
model: Record<string, unknown>
|
|
453
|
-
): unknown {
|
|
454
|
-
if (!Array.isArray(payload.tools)) return undefined;
|
|
455
|
-
if (
|
|
456
|
-
model.api !== "openai-responses" &&
|
|
457
|
-
model.api !== "azure-openai-responses" &&
|
|
458
|
-
model.api !== "openai-completions" &&
|
|
459
|
-
model.api !== "anthropic-messages"
|
|
460
|
-
) {
|
|
461
|
-
return undefined;
|
|
462
|
-
}
|
|
338
|
+
function payloadWithStrictTools(payload: Payload, request: Pick<InteractionRequest, "tools">, api: unknown): unknown {
|
|
339
|
+
if (!Array.isArray(payload.tools) || !(isOpenAI(api) || isAnthropic(api))) return undefined;
|
|
463
340
|
const definitions = toolsByWireName(request.tools);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (!isRecord(entry)) return entry;
|
|
468
|
-
if (model.api === "anthropic-messages") {
|
|
469
|
-
const tool = typeof entry.name === "string" ? definitions.get(entry.name) : undefined;
|
|
470
|
-
if (!tool || tool.kind !== "function") return entry;
|
|
471
|
-
// defer_loading is intentionally not restored from the definition:
|
|
472
|
-
// Pi decides placement (deferred batch vs force-loaded immediate), and
|
|
473
|
-
// re-adding it here would recreate the cache_control/defer_loading
|
|
474
|
-
// combination Anthropic rejects. The entry spread preserves Pi's choice.
|
|
475
|
-
return {
|
|
476
|
-
...entry,
|
|
477
|
-
...(tool.parameters !== undefined ? { input_schema: tool.parameters } : {}),
|
|
478
|
-
...(tool.strict !== undefined ? { strict: tool.strict } : {}),
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
const definition = model.api === "openai-completions" && isRecord(entry.function) ? entry.function : entry;
|
|
482
|
-
const tool = typeof definition.name === "string" ? definitions.get(definition.name) : undefined;
|
|
483
|
-
if (!tool || tool.kind !== "function" || tool.strict === undefined) return entry;
|
|
484
|
-
if (model.api === "openai-completions") {
|
|
485
|
-
return { ...entry, function: { ...definition, strict: tool.strict } };
|
|
486
|
-
}
|
|
487
|
-
return { ...entry, strict: tool.strict };
|
|
488
|
-
}),
|
|
341
|
+
const functionTool = (name: unknown) => {
|
|
342
|
+
const tool = typeof name === "string" ? definitions.get(name) : undefined;
|
|
343
|
+
return tool?.kind === "function" ? tool : undefined;
|
|
489
344
|
};
|
|
345
|
+
const tools = payload.tools.map((entry) => {
|
|
346
|
+
if (!isRecord(entry)) return entry;
|
|
347
|
+
if (isAnthropic(api)) {
|
|
348
|
+
const tool = functionTool(entry.name);
|
|
349
|
+
if (!tool) return entry;
|
|
350
|
+
// defer_loading is intentionally not restored from the definition: Pi
|
|
351
|
+
// decides placement (deferred batch vs force-loaded immediate), and
|
|
352
|
+
// re-adding it here would recreate the cache_control/defer_loading
|
|
353
|
+
// combination Anthropic rejects. The entry spread preserves Pi's choice.
|
|
354
|
+
return {
|
|
355
|
+
...entry,
|
|
356
|
+
...(tool.parameters !== undefined ? { input_schema: tool.parameters } : {}),
|
|
357
|
+
...(tool.strict !== undefined ? { strict: tool.strict } : {}),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (isCompletions(api) && isRecord(entry.function)) {
|
|
361
|
+
const strict = functionTool(entry.function.name)?.strict;
|
|
362
|
+
return strict === undefined ? entry : { ...entry, function: { ...entry.function, strict } };
|
|
363
|
+
}
|
|
364
|
+
const strict = functionTool(entry.name)?.strict;
|
|
365
|
+
return strict === undefined ? entry : { ...entry, strict };
|
|
366
|
+
});
|
|
367
|
+
return { ...payload, tools };
|
|
490
368
|
}
|
|
491
369
|
|
|
492
|
-
function payloadWithParallelToolCalls(
|
|
493
|
-
payload:
|
|
494
|
-
enabled
|
|
495
|
-
|
|
496
|
-
)
|
|
497
|
-
|
|
498
|
-
return { ...payload, parallel_tool_calls: enabled };
|
|
499
|
-
}
|
|
500
|
-
if (model.api === "anthropic-messages" && !enabled) {
|
|
501
|
-
const current = isRecord(payload.tool_choice) ? payload.tool_choice : {};
|
|
502
|
-
if (current.type === "none") return undefined;
|
|
503
|
-
return {
|
|
504
|
-
...payload,
|
|
505
|
-
tool_choice: {
|
|
506
|
-
type: typeof current.type === "string" ? current.type : "auto",
|
|
507
|
-
...current,
|
|
508
|
-
disable_parallel_tool_use: true,
|
|
509
|
-
},
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
return undefined;
|
|
370
|
+
function payloadWithParallelToolCalls(payload: Payload, enabled: boolean, api: unknown): unknown {
|
|
371
|
+
if (isOpenAI(api)) return { ...payload, parallel_tool_calls: enabled };
|
|
372
|
+
if (!isAnthropic(api) || enabled) return undefined;
|
|
373
|
+
const current = asRecord(payload.tool_choice);
|
|
374
|
+
if (current.type === "none") return undefined;
|
|
375
|
+
return { ...payload, tool_choice: { type: "auto", ...current, disable_parallel_tool_use: true } };
|
|
513
376
|
}
|
|
514
377
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
)
|
|
519
|
-
const details = imageDetailQueues(request);
|
|
520
|
-
if (details.size === 0) return undefined;
|
|
378
|
+
// Fills `detail` on Responses (`input_image`) and Chat (`image_url`) parts,
|
|
379
|
+
// matching request images to payload images by URL in order.
|
|
380
|
+
function payloadWithImageDetails(payload: Payload, details: Map<string, ImageDetail[]>): unknown {
|
|
381
|
+
const queues = new Map([...details].map(([url, queue]) => [url, [...queue]]));
|
|
521
382
|
let changed = false;
|
|
522
|
-
|
|
523
383
|
const patch = (value: unknown): unknown => {
|
|
524
384
|
if (Array.isArray(value)) return value.map(patch);
|
|
525
385
|
if (!isRecord(value)) return value;
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const queue = details.get(url);
|
|
541
|
-
const detail = queue?.shift();
|
|
542
|
-
if (detail === undefined) return mapped;
|
|
543
|
-
changed = true;
|
|
544
|
-
if (responseUrl) return { ...mapped, detail };
|
|
545
|
-
return { ...mapped, image_url: { ...chatImage, detail } };
|
|
386
|
+
const node = Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, patch(entry)]));
|
|
387
|
+
if (node.type === "input_image" && typeof node.image_url === "string") {
|
|
388
|
+
const detail = queues.get(node.image_url)?.shift();
|
|
389
|
+
if (detail === undefined) return node;
|
|
390
|
+
changed = true;
|
|
391
|
+
return { ...node, detail };
|
|
392
|
+
}
|
|
393
|
+
if (node.type === "image_url" && isRecord(node.image_url) && typeof node.image_url.url === "string") {
|
|
394
|
+
const detail = queues.get(node.image_url.url)?.shift();
|
|
395
|
+
if (detail === undefined) return node;
|
|
396
|
+
changed = true;
|
|
397
|
+
return { ...node, image_url: { ...node.image_url, detail } };
|
|
398
|
+
}
|
|
399
|
+
return node;
|
|
546
400
|
};
|
|
547
|
-
|
|
548
401
|
const patched = patch(payload);
|
|
549
|
-
return changed
|
|
402
|
+
return changed ? patched : undefined;
|
|
550
403
|
}
|
|
551
404
|
|
|
552
|
-
function imageDetailQueues(
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if (!Array.isArray(content)) return;
|
|
558
|
-
for (const part of content) {
|
|
405
|
+
function imageDetailQueues(request: InteractionRequest): Map<string, ImageDetail[]> {
|
|
406
|
+
const details = new Map<string, ImageDetail[]>();
|
|
407
|
+
for (const item of request.items) {
|
|
408
|
+
const content = item.type === "message" ? item.content : item.type === "tool_result" ? item.output : undefined;
|
|
409
|
+
for (const part of asArray(content)) {
|
|
559
410
|
if (!isRecord(part) || part.type !== "image" || typeof part.url !== "string") continue;
|
|
560
|
-
if (
|
|
561
|
-
|
|
562
|
-
queue.push(part.detail);
|
|
563
|
-
details.set(part.url, queue);
|
|
411
|
+
if (!IMAGE_DETAILS.includes(part.detail)) continue;
|
|
412
|
+
details.set(part.url, [...(details.get(part.url) ?? []), part.detail as ImageDetail]);
|
|
564
413
|
}
|
|
565
|
-
};
|
|
566
|
-
for (const item of request.items) {
|
|
567
|
-
if (item.type === "message") add(item.content);
|
|
568
|
-
if (item.type === "tool_result") add(item.output);
|
|
569
414
|
}
|
|
570
415
|
return details;
|
|
571
416
|
}
|
|
572
|
-
|
|
573
|
-
export function supportedThinkingLevels(
|
|
574
|
-
model: Record<string, unknown>,
|
|
575
|
-
pi: PiOptionsSupport
|
|
576
|
-
): string[] {
|
|
577
|
-
const explicitLevels = model.supportedThinkingLevels;
|
|
578
|
-
if (Array.isArray(explicitLevels)) {
|
|
579
|
-
return explicitLevels.filter((level) => typeof level === "string");
|
|
580
|
-
}
|
|
581
|
-
const levels = pi.getSupportedThinkingLevels?.(model);
|
|
582
|
-
if (Array.isArray(levels)) {
|
|
583
|
-
return levels.filter((level) => typeof level === "string");
|
|
584
|
-
}
|
|
585
|
-
return ["off"];
|
|
586
|
-
}
|