@linxiraos/pi-ai 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +123 -30
- package/package.json +135 -136
- package/src/auth/sqlite-credential-store.ts +0 -56
- package/src/auth-retry.ts +5 -4
- package/src/auth-storage.ts +122 -112
- package/src/dialect/owned-stream.ts +3 -0
- package/src/error/auth-classify.ts +7 -6
- package/src/error/aws.ts +5 -1
- package/src/error/flags.ts +25 -1
- package/src/error/provider.ts +6 -5
- package/src/error/rate-limit.ts +71 -1
- package/src/providers/amazon-bedrock.ts +38 -0
- package/src/providers/anthropic-client.ts +6 -10
- package/src/providers/anthropic.ts +107 -259
- package/src/providers/aws-credentials.ts +222 -29
- package/src/providers/azure-openai-responses.ts +1 -0
- package/src/providers/cursor/exec-modern.ts +1 -0
- package/src/providers/cursor-pi-args.ts +22 -0
- package/src/providers/cursor.ts +81 -1
- package/src/providers/devin.ts +3 -3
- package/src/providers/google-antigravity-forced-tool.md +1 -0
- package/src/providers/google-gemini-cli.ts +83 -40
- package/src/providers/google-shared.ts +22 -8
- package/src/providers/ollama.ts +15 -3
- package/src/providers/openai-codex/request-transformer.ts +47 -21
- package/src/providers/openai-codex-responses.ts +3 -3
- package/src/providers/openai-reasoning-fallback.ts +21 -8
- package/src/providers/openai-responses.ts +77 -12
- package/src/providers/openai-shared.ts +114 -15
- package/src/providers/pi-native-server.ts +1 -0
- package/src/providers/register-builtins.ts +18 -14
- package/src/providers/vision-guard.ts +15 -3
- package/src/registry/amazon-bedrock.ts +9 -14
- package/src/registry/api-key-login.ts +26 -12
- package/src/registry/aws.ts +16 -8
- package/src/registry/cloudflare-ai-gateway.ts +10 -29
- package/src/registry/kagi.ts +11 -29
- package/src/registry/litellm.ts +11 -29
- package/src/registry/llama-cpp.ts +11 -21
- package/src/registry/lm-studio.ts +9 -20
- package/src/registry/oauth/callback-server.ts +93 -5
- package/src/registry/oauth/google-antigravity.ts +99 -44
- package/src/registry/oauth/perplexity.ts +32 -4
- package/src/registry/parallel.ts +10 -28
- package/src/registry/tavily.ts +9 -27
- package/src/registry/together.ts +7 -3
- package/src/registry/vercel-ai-gateway.ts +10 -28
- package/src/registry/vllm.ts +11 -21
- package/src/stream.ts +459 -53
- package/src/types.ts +23 -0
- package/src/usage/alibaba-token-plan.ts +8 -15
- package/src/usage/claude.ts +12 -19
- package/src/usage/cursor.ts +179 -59
- package/src/usage/gemini.ts +4 -3
- package/src/usage/github-copilot.ts +3 -2
- package/src/usage/google-antigravity.ts +10 -17
- package/src/usage/kimi.ts +35 -17
- package/src/usage/minimax-code.ts +7 -27
- package/src/usage/openai-codex-reset.ts +3 -2
- package/src/usage/openai-codex.ts +5 -5
- package/src/usage/opencode-go.ts +170 -60
- package/src/usage/shared.ts +33 -10
- package/src/usage/synthetic.ts +4 -11
- package/src/usage/umans.ts +2 -2
- package/src/usage/xai-oauth.ts +62 -34
- package/src/usage/zai.ts +4 -6
- package/src/usage.ts +0 -20
- package/src/utils/aws-profile.ts +39 -1
- package/src/utils/block-symbols.ts +18 -0
- package/src/utils/empty-completion-retry.ts +4 -1
- package/src/utils/leaked-thinking-stream.ts +3 -0
- package/src/utils/openrouter-headers.ts +4 -4
- package/src/utils/thinking-loop.ts +17 -38
- package/dist/types/api-registry.d.ts +0 -30
- package/dist/types/auth/sqlite-credential-store.d.ts +0 -117
- package/dist/types/auth-broker/client.d.ts +0 -93
- package/dist/types/auth-broker/discover.d.ts +0 -39
- package/dist/types/auth-broker/index.d.ts +0 -7
- package/dist/types/auth-broker/refresher.d.ts +0 -25
- package/dist/types/auth-broker/remote-store.d.ts +0 -136
- package/dist/types/auth-broker/server.d.ts +0 -48
- package/dist/types/auth-broker/snapshot-cache.d.ts +0 -17
- package/dist/types/auth-broker/types.d.ts +0 -152
- package/dist/types/auth-broker/wire-schema-resource.d.ts +0 -53
- package/dist/types/auth-broker/wire-schemas.d.ts +0 -5
- package/dist/types/auth-gateway/http.d.ts +0 -56
- package/dist/types/auth-gateway/index.d.ts +0 -3
- package/dist/types/auth-gateway/server.d.ts +0 -36
- package/dist/types/auth-gateway/types.d.ts +0 -127
- package/dist/types/auth-retry.d.ts +0 -150
- package/dist/types/auth-storage.d.ts +0 -1258
- package/dist/types/dialect/anthropic.d.ts +0 -15
- package/dist/types/dialect/catalog.d.ts +0 -3
- package/dist/types/dialect/coercion.d.ts +0 -23
- package/dist/types/dialect/deepseek.d.ts +0 -14
- package/dist/types/dialect/demotion.d.ts +0 -30
- package/dist/types/dialect/examples.d.ts +0 -18
- package/dist/types/dialect/factory.d.ts +0 -3
- package/dist/types/dialect/fenced-thinking.d.ts +0 -53
- package/dist/types/dialect/gemini.d.ts +0 -17
- package/dist/types/dialect/gemma.d.ts +0 -15
- package/dist/types/dialect/glm.d.ts +0 -9
- package/dist/types/dialect/harmony.d.ts +0 -8
- package/dist/types/dialect/hermes.d.ts +0 -9
- package/dist/types/dialect/history.d.ts +0 -3
- package/dist/types/dialect/index.d.ts +0 -11
- package/dist/types/dialect/inventory.d.ts +0 -9
- package/dist/types/dialect/kimi.d.ts +0 -14
- package/dist/types/dialect/minimax.d.ts +0 -3
- package/dist/types/dialect/owned-stream.d.ts +0 -4
- package/dist/types/dialect/qwen3.d.ts +0 -9
- package/dist/types/dialect/rendering.d.ts +0 -54
- package/dist/types/dialect/thinking.d.ts +0 -6
- package/dist/types/dialect/types.d.ts +0 -68
- package/dist/types/dialect/xml.d.ts +0 -9
- package/dist/types/error/abort.d.ts +0 -14
- package/dist/types/error/auth-classify.d.ts +0 -20
- package/dist/types/error/auth.d.ts +0 -27
- package/dist/types/error/aws.d.ts +0 -27
- package/dist/types/error/classes.d.ts +0 -106
- package/dist/types/error/finalize.d.ts +0 -39
- package/dist/types/error/flags.d.ts +0 -94
- package/dist/types/error/format.d.ts +0 -20
- package/dist/types/error/gateway.d.ts +0 -20
- package/dist/types/error/index.d.ts +0 -14
- package/dist/types/error/oauth.d.ts +0 -43
- package/dist/types/error/provider.d.ts +0 -42
- package/dist/types/error/rate-limit.d.ts +0 -80
- package/dist/types/error/retryable.d.ts +0 -27
- package/dist/types/error/validation.d.ts +0 -32
- package/dist/types/index.d.ts +0 -51
- package/dist/types/provider-details.d.ts +0 -24
- package/dist/types/providers/amazon-bedrock.d.ts +0 -39
- package/dist/types/providers/anthropic-client.d.ts +0 -106
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +0 -937
- package/dist/types/providers/anthropic-messages-server.d.ts +0 -17
- package/dist/types/providers/anthropic-wire.d.ts +0 -345
- package/dist/types/providers/anthropic.d.ts +0 -261
- package/dist/types/providers/aws-credentials.d.ts +0 -48
- package/dist/types/providers/aws-eventstream.d.ts +0 -39
- package/dist/types/providers/aws-sigv4.d.ts +0 -55
- package/dist/types/providers/azure-openai-responses.d.ts +0 -16
- package/dist/types/providers/bedrock-mantle.d.ts +0 -13
- package/dist/types/providers/claude-code-fingerprint.d.ts +0 -19
- package/dist/types/providers/cowork-fetch.d.ts +0 -3
- package/dist/types/providers/cursor/exec-modern.d.ts +0 -98
- package/dist/types/providers/cursor-pi-args.d.ts +0 -105
- package/dist/types/providers/cursor.d.ts +0 -221
- package/dist/types/providers/devin.d.ts +0 -12
- package/dist/types/providers/error-message.d.ts +0 -25
- package/dist/types/providers/github-copilot-headers.d.ts +0 -40
- package/dist/types/providers/gitlab-duo-workflow.d.ts +0 -254
- package/dist/types/providers/gitlab-duo.d.ts +0 -27
- package/dist/types/providers/google-auth.d.ts +0 -24
- package/dist/types/providers/google-gemini-cli.d.ts +0 -120
- package/dist/types/providers/google-shared.d.ts +0 -203
- package/dist/types/providers/google-types.d.ts +0 -155
- package/dist/types/providers/google-vertex.d.ts +0 -7
- package/dist/types/providers/google.d.ts +0 -4
- package/dist/types/providers/grammar.d.ts +0 -1
- package/dist/types/providers/kimi.d.ts +0 -27
- package/dist/types/providers/mock.d.ts +0 -179
- package/dist/types/providers/ollama.d.ts +0 -8
- package/dist/types/providers/openai-anthropic-shim.d.ts +0 -35
- package/dist/types/providers/openai-chat-server-schema.d.ts +0 -1311
- package/dist/types/providers/openai-chat-server.d.ts +0 -16
- package/dist/types/providers/openai-chat-wire.d.ts +0 -669
- package/dist/types/providers/openai-codex/request-transformer.d.ts +0 -109
- package/dist/types/providers/openai-codex/response-handler.d.ts +0 -26
- package/dist/types/providers/openai-codex-responses.d.ts +0 -238
- package/dist/types/providers/openai-completions.d.ts +0 -48
- package/dist/types/providers/openai-reasoning-fallback.d.ts +0 -25
- package/dist/types/providers/openai-responses-server-schema.d.ts +0 -1314
- package/dist/types/providers/openai-responses-server.d.ts +0 -17
- package/dist/types/providers/openai-responses-wire.d.ts +0 -6099
- package/dist/types/providers/openai-responses.d.ts +0 -131
- package/dist/types/providers/openai-shared.d.ts +0 -624
- package/dist/types/providers/pi-native-client.d.ts +0 -13
- package/dist/types/providers/pi-native-server.d.ts +0 -69
- package/dist/types/providers/register-builtins.d.ts +0 -37
- package/dist/types/providers/synthetic.d.ts +0 -26
- package/dist/types/providers/transform-messages.d.ts +0 -32
- package/dist/types/providers/vision-guard.d.ts +0 -20
- package/dist/types/registry/aiand.d.ts +0 -7
- package/dist/types/registry/aimlapi.d.ts +0 -4
- package/dist/types/registry/alibaba-coding-plan.d.ts +0 -8
- package/dist/types/registry/alibaba-token-plan.d.ts +0 -18
- package/dist/types/registry/amazon-bedrock.d.ts +0 -5
- package/dist/types/registry/anthropic.d.ts +0 -10
- package/dist/types/registry/api-key-login.d.ts +0 -42
- package/dist/types/registry/api-key-validation.d.ts +0 -43
- package/dist/types/registry/aws.d.ts +0 -13
- package/dist/types/registry/azure.d.ts +0 -4
- package/dist/types/registry/baseten.d.ts +0 -7
- package/dist/types/registry/bedrock-mantle.d.ts +0 -22
- package/dist/types/registry/cerebras.d.ts +0 -7
- package/dist/types/registry/cloudflare-ai-gateway.d.ts +0 -13
- package/dist/types/registry/coreweave.d.ts +0 -7
- package/dist/types/registry/cursor.d.ts +0 -7
- package/dist/types/registry/deepseek.d.ts +0 -8
- package/dist/types/registry/derived.d.ts +0 -5
- package/dist/types/registry/devin.d.ts +0 -8
- package/dist/types/registry/exa.d.ts +0 -8
- package/dist/types/registry/firepass.d.ts +0 -16
- package/dist/types/registry/fireworks.d.ts +0 -7
- package/dist/types/registry/github-copilot.d.ts +0 -7
- package/dist/types/registry/gitlab-duo-workflow.d.ts +0 -10
- package/dist/types/registry/gitlab-duo.d.ts +0 -9
- package/dist/types/registry/gmi-cloud.d.ts +0 -7
- package/dist/types/registry/google-antigravity.d.ts +0 -9
- package/dist/types/registry/google-gemini-cli.d.ts +0 -9
- package/dist/types/registry/google-vertex.d.ts +0 -5
- package/dist/types/registry/google.d.ts +0 -4
- package/dist/types/registry/groq.d.ts +0 -4
- package/dist/types/registry/huggingface.d.ts +0 -7
- package/dist/types/registry/index.d.ts +0 -4
- package/dist/types/registry/kagi.d.ts +0 -14
- package/dist/types/registry/kilo.d.ts +0 -7
- package/dist/types/registry/kimi-code.d.ts +0 -7
- package/dist/types/registry/litellm.d.ts +0 -13
- package/dist/types/registry/llama-cpp.d.ts +0 -8
- package/dist/types/registry/lm-studio.d.ts +0 -8
- package/dist/types/registry/meta.d.ts +0 -7
- package/dist/types/registry/minimax-code-cn.d.ts +0 -6
- package/dist/types/registry/minimax-code.d.ts +0 -6
- package/dist/types/registry/minimax.d.ts +0 -4
- package/dist/types/registry/mistral.d.ts +0 -4
- package/dist/types/registry/moonshot.d.ts +0 -7
- package/dist/types/registry/nanogpt.d.ts +0 -7
- package/dist/types/registry/novita.d.ts +0 -6
- package/dist/types/registry/nvidia.d.ts +0 -7
- package/dist/types/registry/oauth/anthropic-constants.d.ts +0 -12
- package/dist/types/registry/oauth/anthropic.d.ts +0 -24
- package/dist/types/registry/oauth/callback-server.d.ts +0 -74
- package/dist/types/registry/oauth/cursor.d.ts +0 -16
- package/dist/types/registry/oauth/device-code.d.ts +0 -25
- package/dist/types/registry/oauth/devin.d.ts +0 -5
- package/dist/types/registry/oauth/github-copilot.d.ts +0 -30
- package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +0 -6
- package/dist/types/registry/oauth/gitlab-duo.d.ts +0 -3
- package/dist/types/registry/oauth/google-antigravity.d.ts +0 -11
- package/dist/types/registry/oauth/google-gemini-cli.d.ts +0 -22
- package/dist/types/registry/oauth/google-oauth-shared.d.ts +0 -56
- package/dist/types/registry/oauth/index.d.ts +0 -45
- package/dist/types/registry/oauth/kimi.d.ts +0 -21
- package/dist/types/registry/oauth/minimax-code.d.ts +0 -27
- package/dist/types/registry/oauth/openai-codex.d.ts +0 -33
- package/dist/types/registry/oauth/opencode.d.ts +0 -18
- package/dist/types/registry/oauth/perplexity.d.ts +0 -9
- package/dist/types/registry/oauth/pkce.d.ts +0 -8
- package/dist/types/registry/oauth/types.d.ts +0 -87
- package/dist/types/registry/oauth/wafer.d.ts +0 -1
- package/dist/types/registry/oauth/xai-oauth.d.ts +0 -46
- package/dist/types/registry/oauth/xiaomi.d.ts +0 -25
- package/dist/types/registry/oauth/zai.d.ts +0 -25
- package/dist/types/registry/ollama-cloud.d.ts +0 -7
- package/dist/types/registry/ollama.d.ts +0 -12
- package/dist/types/registry/openai-codex-device.d.ts +0 -8
- package/dist/types/registry/openai-codex.d.ts +0 -9
- package/dist/types/registry/openai.d.ts +0 -4
- package/dist/types/registry/opencode-go.d.ts +0 -6
- package/dist/types/registry/opencode-zen.d.ts +0 -6
- package/dist/types/registry/openrouter.d.ts +0 -13
- package/dist/types/registry/parallel.d.ts +0 -14
- package/dist/types/registry/perplexity.d.ts +0 -7
- package/dist/types/registry/qianfan.d.ts +0 -7
- package/dist/types/registry/qwen-portal.d.ts +0 -7
- package/dist/types/registry/registry.d.ts +0 -367
- package/dist/types/registry/sakana.d.ts +0 -7
- package/dist/types/registry/siliconflow-cn.d.ts +0 -7
- package/dist/types/registry/siliconflow.d.ts +0 -7
- package/dist/types/registry/synthetic.d.ts +0 -6
- package/dist/types/registry/tavily.d.ts +0 -14
- package/dist/types/registry/together.d.ts +0 -6
- package/dist/types/registry/types.d.ts +0 -75
- package/dist/types/registry/umans.d.ts +0 -7
- package/dist/types/registry/venice.d.ts +0 -13
- package/dist/types/registry/vercel-ai-gateway.d.ts +0 -7
- package/dist/types/registry/vllm.d.ts +0 -7
- package/dist/types/registry/wafer-serverless.d.ts +0 -6
- package/dist/types/registry/xai-oauth.d.ts +0 -7
- package/dist/types/registry/xai.d.ts +0 -7
- package/dist/types/registry/xiaomi-token-plan-ams.d.ts +0 -6
- package/dist/types/registry/xiaomi-token-plan-cn.d.ts +0 -6
- package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +0 -6
- package/dist/types/registry/xiaomi.d.ts +0 -6
- package/dist/types/registry/zai.d.ts +0 -15
- package/dist/types/registry/zenmux.d.ts +0 -7
- package/dist/types/registry/zhipu-coding-plan.d.ts +0 -7
- package/dist/types/stream.d.ts +0 -46
- package/dist/types/types.d.ts +0 -1064
- package/dist/types/usage/alibaba-token-plan.d.ts +0 -3
- package/dist/types/usage/claude.d.ts +0 -4
- package/dist/types/usage/cursor.d.ts +0 -4
- package/dist/types/usage/gemini.d.ts +0 -2
- package/dist/types/usage/github-copilot.d.ts +0 -7
- package/dist/types/usage/google-antigravity.d.ts +0 -15
- package/dist/types/usage/kimi.d.ts +0 -2
- package/dist/types/usage/minimax-code.d.ts +0 -3
- package/dist/types/usage/ollama.d.ts +0 -5
- package/dist/types/usage/openai-codex-base-url.d.ts +0 -18
- package/dist/types/usage/openai-codex-reset.d.ts +0 -88
- package/dist/types/usage/openai-codex.d.ts +0 -10
- package/dist/types/usage/opencode-go.d.ts +0 -2
- package/dist/types/usage/shared.d.ts +0 -1
- package/dist/types/usage/synthetic.d.ts +0 -2
- package/dist/types/usage/umans.d.ts +0 -2
- package/dist/types/usage/xai-oauth.d.ts +0 -12
- package/dist/types/usage/zai.d.ts +0 -3
- package/dist/types/usage.d.ts +0 -527
- package/dist/types/utils/abort.d.ts +0 -25
- package/dist/types/utils/anthropic-auth.d.ts +0 -35
- package/dist/types/utils/aws-profile.d.ts +0 -17
- package/dist/types/utils/block-symbols.d.ts +0 -62
- package/dist/types/utils/deterministic-id.d.ts +0 -16
- package/dist/types/utils/empty-completion-retry.d.ts +0 -21
- package/dist/types/utils/event-stream.d.ts +0 -39
- package/dist/types/utils/foundry.d.ts +0 -1
- package/dist/types/utils/google-validation.d.ts +0 -2
- package/dist/types/utils/harmony-leak.d.ts +0 -135
- package/dist/types/utils/http-inspector.d.ts +0 -49
- package/dist/types/utils/idle-iterator.d.ts +0 -149
- package/dist/types/utils/leaked-thinking-stream.d.ts +0 -33
- package/dist/types/utils/openai-http.d.ts +0 -48
- package/dist/types/utils/openrouter-headers.d.ts +0 -1
- package/dist/types/utils/parse-bind.d.ts +0 -23
- package/dist/types/utils/provider-response.d.ts +0 -3
- package/dist/types/utils/proxy.d.ts +0 -39
- package/dist/types/utils/request-debug.d.ts +0 -29
- package/dist/types/utils/retry-after.d.ts +0 -4
- package/dist/types/utils/retry.d.ts +0 -14
- package/dist/types/utils/schema/adapt.d.ts +0 -24
- package/dist/types/utils/schema/compatibility.d.ts +0 -30
- package/dist/types/utils/schema/dereference.d.ts +0 -11
- package/dist/types/utils/schema/draft.d.ts +0 -10
- package/dist/types/utils/schema/equality.d.ts +0 -4
- package/dist/types/utils/schema/fields.d.ts +0 -54
- package/dist/types/utils/schema/index.d.ts +0 -14
- package/dist/types/utils/schema/json-schema-validator.d.ts +0 -20
- package/dist/types/utils/schema/meta-validator.d.ts +0 -2
- package/dist/types/utils/schema/normalize.d.ts +0 -153
- package/dist/types/utils/schema/spill.d.ts +0 -8
- package/dist/types/utils/schema/stamps.d.ts +0 -17
- package/dist/types/utils/schema/strict-tool-validation.d.ts +0 -16
- package/dist/types/utils/schema/types.d.ts +0 -4
- package/dist/types/utils/schema/typescript.d.ts +0 -24
- package/dist/types/utils/schema/wire.d.ts +0 -52
- package/dist/types/utils/sdk-stream-timeout.d.ts +0 -33
- package/dist/types/utils/sse-debug.d.ts +0 -5
- package/dist/types/utils/stream-markup-healing.d.ts +0 -87
- package/dist/types/utils/thinking-loop.d.ts +0 -102
- package/dist/types/utils/tool-call-loop-guard.d.ts +0 -26
- package/dist/types/utils/tool-choice.d.ts +0 -52
- package/dist/types/utils/validation.d.ts +0 -28
- package/dist/types/utils.d.ts +0 -57
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { scheduler } from "node:timers/promises";
|
|
2
2
|
import { hostMatchesUrl } from "@linxiraos/pi-catalog/hosts";
|
|
3
|
+
import { bareModelId, parseOpenAIModel, semverGte } from "@linxiraos/pi-catalog/identity";
|
|
3
4
|
import { $flag, logger, structuredCloneJSON } from "@linxiraos/pi-utils";
|
|
4
5
|
import * as AIError from "../error";
|
|
5
6
|
import { getEnvApiKey } from "../stream";
|
|
@@ -80,6 +81,7 @@ import {
|
|
|
80
81
|
createInitialResponsesAssistantMessage,
|
|
81
82
|
createOpenAIStrictToolsState,
|
|
82
83
|
disableStrictToolsForScope,
|
|
84
|
+
getJuiceValue,
|
|
83
85
|
getOpenAIPromptCacheKey,
|
|
84
86
|
getOpenAIResponsesRoutingSessionId,
|
|
85
87
|
getOpenAIStrictToolsScope,
|
|
@@ -298,14 +300,23 @@ interface OpenAIResponsesChainedParams {
|
|
|
298
300
|
*/
|
|
299
301
|
function buildOpenAIResponsesChainedParams(
|
|
300
302
|
params: OpenAIResponsesSamplingParams,
|
|
303
|
+
trailingScaffoldingItems: number,
|
|
301
304
|
chain: OpenAIResponsesChainState,
|
|
302
305
|
): OpenAIResponsesChainedParams {
|
|
306
|
+
const historyParams =
|
|
307
|
+
trailingScaffoldingItems > 0 && Array.isArray(params.input)
|
|
308
|
+
? { ...params, input: params.input.slice(0, params.input.length - trailingScaffoldingItems) }
|
|
309
|
+
: params;
|
|
303
310
|
const deltaInput = chain.canAppend
|
|
304
|
-
? buildResponsesDeltaInput(chain.lastParams, chain.lastResponseItems,
|
|
311
|
+
? buildResponsesDeltaInput(chain.lastParams, chain.lastResponseItems, historyParams)
|
|
305
312
|
: null;
|
|
306
313
|
if (deltaInput && deltaInput.length > 0 && chain.lastResponseId) {
|
|
314
|
+
const scaffolding =
|
|
315
|
+
historyParams !== params && Array.isArray(params.input)
|
|
316
|
+
? params.input.slice(params.input.length - trailingScaffoldingItems)
|
|
317
|
+
: [];
|
|
307
318
|
return {
|
|
308
|
-
params: { ...params, previous_response_id: chain.lastResponseId, input: deltaInput },
|
|
319
|
+
params: { ...params, previous_response_id: chain.lastResponseId, input: [...deltaInput, ...scaffolding] },
|
|
309
320
|
previousResponseId: chain.lastResponseId,
|
|
310
321
|
};
|
|
311
322
|
}
|
|
@@ -462,8 +473,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
462
473
|
false,
|
|
463
474
|
chainState?.canAppend ? chainState.lastParams?.input : undefined,
|
|
464
475
|
);
|
|
465
|
-
const params = builtParams
|
|
476
|
+
const { params, trailingScaffoldingItems } = builtParams;
|
|
466
477
|
let activeParams = params;
|
|
478
|
+
let activeTrailingScaffoldingItems = trailingScaffoldingItems;
|
|
467
479
|
const resolvedBaseUrl = (baseUrl ?? "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
468
480
|
const requestReasoningEffortFallbacks = new Map<string, OpenAIReasoningEffortFallback>();
|
|
469
481
|
const attemptedReasoningEffortFallbacks = new Set<string>();
|
|
@@ -490,7 +502,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
490
502
|
}
|
|
491
503
|
applyReasoningEffortFallbackForRequest(params);
|
|
492
504
|
let chained: OpenAIResponsesChainedParams =
|
|
493
|
-
chainState && !chainState.disabled
|
|
505
|
+
chainState && !chainState.disabled
|
|
506
|
+
? buildOpenAIResponsesChainedParams(params, trailingScaffoldingItems, chainState)
|
|
507
|
+
: { params };
|
|
494
508
|
sentPreviousResponseId = chained.previousResponseId;
|
|
495
509
|
const idleTimeoutMs =
|
|
496
510
|
options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(model.compat.streamIdleTimeoutMs);
|
|
@@ -586,7 +600,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
586
600
|
const reasoningEffortFallback =
|
|
587
601
|
activeReasoningEffortFallbackKey && activeRequestParams && !requestSignal.aborted
|
|
588
602
|
? resolveOpenAIReasoningEffortFallback(error, capturedErrorResponse, activeRequestParams, {
|
|
589
|
-
explicitDisable:
|
|
603
|
+
explicitDisable:
|
|
604
|
+
options?.forceReasoningOff === true ||
|
|
605
|
+
(options?.disableReasoning === true && options.reasoning === undefined),
|
|
590
606
|
})
|
|
591
607
|
: undefined;
|
|
592
608
|
if (reasoningEffortFallback !== undefined && activeReasoningEffortFallbackKey) {
|
|
@@ -632,7 +648,11 @@ const streamOpenAIResponsesOnce = (
|
|
|
632
648
|
if (chainState && !chainState.disabled) fallbackParams.store = true;
|
|
633
649
|
let fallbackChained: OpenAIResponsesChainedParams =
|
|
634
650
|
chainState && !chainState.disabled
|
|
635
|
-
? buildOpenAIResponsesChainedParams(
|
|
651
|
+
? buildOpenAIResponsesChainedParams(
|
|
652
|
+
fallbackParams,
|
|
653
|
+
fallbackBuilt.trailingScaffoldingItems,
|
|
654
|
+
chainState,
|
|
655
|
+
)
|
|
636
656
|
: { params: fallbackParams };
|
|
637
657
|
sentPreviousResponseId = fallbackChained.previousResponseId;
|
|
638
658
|
fallbackChained = {
|
|
@@ -642,7 +662,7 @@ const streamOpenAIResponsesOnce = (
|
|
|
642
662
|
chained = fallbackChained;
|
|
643
663
|
activeRawRequestDump.body = chained.params;
|
|
644
664
|
activeParams = fallbackParams;
|
|
645
|
-
|
|
665
|
+
activeTrailingScaffoldingItems = fallbackBuilt.trailingScaffoldingItems;
|
|
646
666
|
continue;
|
|
647
667
|
}
|
|
648
668
|
if (!chainState || !sentPreviousResponseId || requestSignal.aborted) {
|
|
@@ -688,6 +708,7 @@ const streamOpenAIResponsesOnce = (
|
|
|
688
708
|
chained = { params: retryParams };
|
|
689
709
|
activeRawRequestDump.body = retryParams;
|
|
690
710
|
activeParams = currentParams;
|
|
711
|
+
activeTrailingScaffoldingItems = currentBuilt.trailingScaffoldingItems;
|
|
691
712
|
activeStrictToolsApplied = currentBuilt.strictToolsApplied;
|
|
692
713
|
}
|
|
693
714
|
}
|
|
@@ -824,7 +845,17 @@ const streamOpenAIResponsesOnce = (
|
|
|
824
845
|
if (replayableResponseItems) {
|
|
825
846
|
if (providerSessionState) providerSessionState.nativeHistoryReplayWarmed = true;
|
|
826
847
|
if (chainState) {
|
|
827
|
-
chainState.lastParams = structuredCloneJSON(
|
|
848
|
+
chainState.lastParams = structuredCloneJSON(
|
|
849
|
+
activeTrailingScaffoldingItems > 0 && Array.isArray(activeParams.input)
|
|
850
|
+
? {
|
|
851
|
+
...activeParams,
|
|
852
|
+
input: activeParams.input.slice(
|
|
853
|
+
0,
|
|
854
|
+
activeParams.input.length - activeTrailingScaffoldingItems,
|
|
855
|
+
),
|
|
856
|
+
}
|
|
857
|
+
: activeParams,
|
|
858
|
+
);
|
|
828
859
|
chainState.lastPromptCacheBreakpointPolicy = promptCacheBreakpointPolicy;
|
|
829
860
|
if (output.responseId) {
|
|
830
861
|
chainState.lastResponseId = output.responseId;
|
|
@@ -843,7 +874,14 @@ const streamOpenAIResponsesOnce = (
|
|
|
843
874
|
// baseline, but `lastParams` still records the successful wire controls
|
|
844
875
|
// without re-enabling `previous_response_id` chaining.
|
|
845
876
|
chainState.canAppend = false;
|
|
846
|
-
chainState.lastParams = structuredCloneJSON(
|
|
877
|
+
chainState.lastParams = structuredCloneJSON(
|
|
878
|
+
activeTrailingScaffoldingItems > 0 && Array.isArray(activeParams.input)
|
|
879
|
+
? {
|
|
880
|
+
...activeParams,
|
|
881
|
+
input: activeParams.input.slice(0, activeParams.input.length - activeTrailingScaffoldingItems),
|
|
882
|
+
}
|
|
883
|
+
: activeParams,
|
|
884
|
+
);
|
|
847
885
|
chainState.lastPromptCacheBreakpointPolicy = promptCacheBreakpointPolicy;
|
|
848
886
|
chainState.lastResponseId = undefined;
|
|
849
887
|
chainState.lastResponseItems = undefined;
|
|
@@ -899,6 +937,17 @@ function isOfficialOpenAIResponsesEndpoint(model: Model<"openai-responses">): bo
|
|
|
899
937
|
}
|
|
900
938
|
}
|
|
901
939
|
|
|
940
|
+
/**
|
|
941
|
+
* GPT-5.6+ family check for Responses routes. The model id classifies the
|
|
942
|
+
* reasoning family regardless of the provider/host serving it — a cliproxy or
|
|
943
|
+
* other OpenAI-compatible gateway carrying `gpt-5.6-sol` gets the same
|
|
944
|
+
* scaffolding as the official endpoint.
|
|
945
|
+
*/
|
|
946
|
+
function isGpt56PlusResponsesModel(model: Model<"openai-responses">): boolean {
|
|
947
|
+
const parsed = parseOpenAIModel(bareModelId(model.requestModelId ?? model.id));
|
|
948
|
+
return parsed !== null && semverGte(parsed.version, "5.6");
|
|
949
|
+
}
|
|
950
|
+
|
|
902
951
|
function isResponsesPromptCacheableContentBlock(block: unknown): block is ResponseInputContent {
|
|
903
952
|
if (typeof block !== "object" || block === null || !("type" in block)) return false;
|
|
904
953
|
return block.type === "input_text" || block.type === "input_image" || block.type === "input_file";
|
|
@@ -1090,7 +1139,7 @@ export function buildParams(
|
|
|
1090
1139
|
strictToolsScope?: OpenAIStrictToolsScope,
|
|
1091
1140
|
disableStrictToolsOverride = false,
|
|
1092
1141
|
statefulCacheBaseline?: ResponseInput,
|
|
1093
|
-
): { params: OpenAIResponsesSamplingParams; strictToolsApplied: boolean } {
|
|
1142
|
+
): { params: OpenAIResponsesSamplingParams; trailingScaffoldingItems: number; strictToolsApplied: boolean } {
|
|
1094
1143
|
const policy = resolveOpenAICompatPolicy(model, {
|
|
1095
1144
|
endpoint: "responses",
|
|
1096
1145
|
reasoning: options?.reasoning,
|
|
@@ -1113,6 +1162,10 @@ export function buildParams(
|
|
|
1113
1162
|
filterReasoning: policy.reasoning.filterReasoningHistory,
|
|
1114
1163
|
},
|
|
1115
1164
|
includeThinkingSignatures: shouldReplayNativeHistory && !policy.reasoning.filterReasoningHistory,
|
|
1165
|
+
requiresReasoningReplayForAllTurns:
|
|
1166
|
+
policy.reasoning.enabled && policy.reasoning.requiresReasoningContentForAllAssistantTurns,
|
|
1167
|
+
requiresReasoningReplayForToolCalls:
|
|
1168
|
+
policy.reasoning.enabled && policy.reasoning.requiresReasoningContentForToolCalls,
|
|
1116
1169
|
repairOrphanOutputs: true,
|
|
1117
1170
|
});
|
|
1118
1171
|
|
|
@@ -1240,6 +1293,7 @@ export function buildParams(
|
|
|
1240
1293
|
: options?.reasoningSummary;
|
|
1241
1294
|
applyResponsesCompatPolicy(params, reasoningPolicy, {
|
|
1242
1295
|
reasoningSummary,
|
|
1296
|
+
forceReasoningOff: options?.forceReasoningOff,
|
|
1243
1297
|
mapEffort: effort =>
|
|
1244
1298
|
model.compat.reasoningEffortMap?.[effort as NonNullable<OpenAIResponsesOptions["reasoning"]>] ??
|
|
1245
1299
|
model.thinking?.effortMap?.[effort as NonNullable<OpenAIResponsesOptions["reasoning"]>] ??
|
|
@@ -1249,7 +1303,7 @@ export function buildParams(
|
|
|
1249
1303
|
// mode survives every policy branch (disabled/omitted effort included) while
|
|
1250
1304
|
// keeping whatever effort/summary the policy produced — mode and effort are
|
|
1251
1305
|
// independent wire fields.
|
|
1252
|
-
if (model.reasoningMode) {
|
|
1306
|
+
if (model.reasoningMode && !options?.forceReasoningOff) {
|
|
1253
1307
|
params.reasoning = { ...params.reasoning, mode: model.reasoningMode };
|
|
1254
1308
|
}
|
|
1255
1309
|
|
|
@@ -1262,7 +1316,18 @@ export function buildParams(
|
|
|
1262
1316
|
applyOpenAIExtraBody(params, options?.extraBody);
|
|
1263
1317
|
applyOpenAIResponsesPromptCachePolicy(params, model, options, statefulCacheBaseline);
|
|
1264
1318
|
|
|
1265
|
-
|
|
1319
|
+
let trailingScaffoldingItems = 0;
|
|
1320
|
+
if (options?.forceReasoningOff && isGpt56PlusResponsesModel(model)) {
|
|
1321
|
+
const effort = options.reasoning ?? "medium";
|
|
1322
|
+
const juice = getJuiceValue(effort);
|
|
1323
|
+
messages.push({
|
|
1324
|
+
role: "developer",
|
|
1325
|
+
content: [{ type: "input_text", text: `# Juice: ${juice} !important` }],
|
|
1326
|
+
});
|
|
1327
|
+
trailingScaffoldingItems = 1;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
return { params, trailingScaffoldingItems, strictToolsApplied };
|
|
1266
1331
|
}
|
|
1267
1332
|
|
|
1268
1333
|
/**
|
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
kStreamingLastParseLen,
|
|
78
78
|
kStreamingPartialJson,
|
|
79
79
|
} from "../utils/block-symbols";
|
|
80
|
+
import { hasVisibleAssistantContent } from "../utils/empty-completion-retry";
|
|
80
81
|
import type { AssistantMessageEventStream } from "../utils/event-stream";
|
|
81
82
|
import {
|
|
82
83
|
escapeHarmonyControlTokens,
|
|
@@ -889,19 +890,18 @@ export function resolveOpenAICompatPolicy<TApi extends Api>(
|
|
|
889
890
|
conflictDisableReason !== undefined ||
|
|
890
891
|
(modelSupported && disabledWithoutRequest) ||
|
|
891
892
|
disabledByNoneEffort;
|
|
892
|
-
if (
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
requestedEffort === undefined &&
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
893
|
+
if (disabled && compat.supportsReasoningEffort && !omitReasoningEffort) {
|
|
894
|
+
if (disableMode === "none-effort") {
|
|
895
|
+
wireEffort = "none";
|
|
896
|
+
} else if (disableReason === "caller" && requestedEffort === undefined && disableMode === "lowest-effort") {
|
|
897
|
+
const minEffort = getSupportedEfforts(model)[0];
|
|
898
|
+
if (minEffort === undefined) {
|
|
899
|
+
throw new AIError.ConfigurationError(
|
|
900
|
+
`Model ${model.provider}/${model.id} has no supported reasoning efforts`,
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
wireEffort = mapOpenAIReasoningEffort(model, compat, minEffort);
|
|
903
904
|
}
|
|
904
|
-
wireEffort = mapOpenAIReasoningEffort(model, compat, minEffort);
|
|
905
905
|
}
|
|
906
906
|
|
|
907
907
|
return {
|
|
@@ -954,6 +954,9 @@ function encodeChatCompletionsDisabledReasoning(
|
|
|
954
954
|
): void {
|
|
955
955
|
delete params.reasoning_effort;
|
|
956
956
|
switch (disableMode) {
|
|
957
|
+
case "none-effort":
|
|
958
|
+
params.reasoning_effort = "none";
|
|
959
|
+
break;
|
|
957
960
|
case "zai-thinking-disabled":
|
|
958
961
|
params.thinking = { type: "disabled" };
|
|
959
962
|
break;
|
|
@@ -1067,7 +1070,7 @@ export function applyChatCompletionsCompatPolicy(params: OpenAICompletionsParams
|
|
|
1067
1070
|
if (
|
|
1068
1071
|
reasoning.disableReason === "caller" &&
|
|
1069
1072
|
reasoning.requestedEffort === undefined &&
|
|
1070
|
-
reasoning.disableMode === "lowest-effort" &&
|
|
1073
|
+
(reasoning.disableMode === "lowest-effort" || reasoning.disableMode === "none-effort") &&
|
|
1071
1074
|
reasoning.wireEffort !== undefined
|
|
1072
1075
|
) {
|
|
1073
1076
|
params.reasoning_effort = reasoning.wireEffort as Effort;
|
|
@@ -1635,6 +1638,14 @@ export interface BuildResponsesInputOptions<TApi extends Api> {
|
|
|
1635
1638
|
repairOrphanOutputs?: boolean;
|
|
1636
1639
|
/** Preserve assistant message item IDs from text signatures during fallback replay. */
|
|
1637
1640
|
preserveAssistantMessageIds?: boolean;
|
|
1641
|
+
/**
|
|
1642
|
+
* Synthesize a reasoning item for every replayed assistant turn that carries
|
|
1643
|
+
* content but no reasoning item. Set for DeepSeek-family Responses targets
|
|
1644
|
+
* that reject a thinking-mode continuation lacking `reasoning_text`.
|
|
1645
|
+
*/
|
|
1646
|
+
requiresReasoningReplayForAllTurns?: boolean;
|
|
1647
|
+
/** As {@link requiresReasoningReplayForAllTurns}, but only for turns that contain a tool call. */
|
|
1648
|
+
requiresReasoningReplayForToolCalls?: boolean;
|
|
1638
1649
|
}
|
|
1639
1650
|
|
|
1640
1651
|
/**
|
|
@@ -1861,6 +1872,8 @@ export function buildResponsesInput<TApi extends Api>(options: BuildResponsesInp
|
|
|
1861
1872
|
supportsCustomToolCalls,
|
|
1862
1873
|
customToolWireNameMap,
|
|
1863
1874
|
computerCallIds,
|
|
1875
|
+
options.requiresReasoningReplayForAllTurns ?? false,
|
|
1876
|
+
options.requiresReasoningReplayForToolCalls ?? false,
|
|
1864
1877
|
);
|
|
1865
1878
|
const outputItems = suppressHiddenEmptyFallback
|
|
1866
1879
|
? sanitizeOpenAIResponsesAssistantFallbackItemsForReplay(convertedOutputItems)
|
|
@@ -1914,6 +1927,8 @@ export function convertResponsesAssistantMessage<TApi extends Api>(
|
|
|
1914
1927
|
supportsCustomToolCalls = true,
|
|
1915
1928
|
customToolWireNameMap?: ReadonlyMap<string, string>,
|
|
1916
1929
|
computerCallIds?: Set<string>,
|
|
1930
|
+
requiresReasoningReplayForAllTurns = false,
|
|
1931
|
+
requiresReasoningReplayForToolCalls = false,
|
|
1917
1932
|
): ResponseInput {
|
|
1918
1933
|
const outputItems: ResponseInput = [];
|
|
1919
1934
|
let unsignedTextBlocks = 0;
|
|
@@ -1925,14 +1940,36 @@ export function convertResponsesAssistantMessage<TApi extends Api>(
|
|
|
1925
1940
|
);
|
|
1926
1941
|
const isDifferentModel =
|
|
1927
1942
|
assistantMsg.model !== model.id && assistantMsg.provider === model.provider && assistantMsg.api === model.api;
|
|
1943
|
+
// DeepSeek-family Responses targets (e.g. opencode-go) reject a thinking-mode
|
|
1944
|
+
// continuation whose replayed assistant turns carry no reasoning item: "The
|
|
1945
|
+
// reasoning_text in the thinking mode must be passed back to the API." After a
|
|
1946
|
+
// cross-model prewalk hand-off or a compaction that drops the native replay
|
|
1947
|
+
// payload, the block re-encode below demotes reasoning to text and emits no
|
|
1948
|
+
// reasoning item. Track reasoning emission so a placeholder can be synthesized,
|
|
1949
|
+
// mirroring the chat-completions `requiresReasoningContentForAllAssistantTurns`
|
|
1950
|
+
// empty-`reasoning_content` safety net.
|
|
1951
|
+
const requiresReasoningItem =
|
|
1952
|
+
assistantMsg.stopReason !== "error" &&
|
|
1953
|
+
(requiresReasoningReplayForAllTurns ||
|
|
1954
|
+
(requiresReasoningReplayForToolCalls && assistantMsg.content.some(block => block.type === "toolCall")));
|
|
1955
|
+
let reasoningItemEmitted = false;
|
|
1956
|
+
const carriedReasoningTexts: string[] = [];
|
|
1957
|
+
let synthesizedReasoningItemId: string | undefined;
|
|
1928
1958
|
|
|
1929
1959
|
for (const block of assistantMsg.content) {
|
|
1930
1960
|
if (block.type === "thinking" && assistantMsg.stopReason !== "error") {
|
|
1961
|
+
if (requiresReasoningItem) {
|
|
1962
|
+
if (block.itemId) synthesizedReasoningItemId ??= block.itemId;
|
|
1963
|
+
if (block.thinking.trim().length > 0) carriedReasoningTexts.push(block.thinking);
|
|
1964
|
+
}
|
|
1931
1965
|
if (!includeThinkingSignatures) {
|
|
1932
1966
|
continue;
|
|
1933
1967
|
}
|
|
1934
1968
|
const reasoningItem = parseResponseReasoningReplayItem(block.thinkingSignature);
|
|
1935
|
-
if (reasoningItem)
|
|
1969
|
+
if (reasoningItem) {
|
|
1970
|
+
outputItems.push(reasoningItem);
|
|
1971
|
+
reasoningItemEmitted = true;
|
|
1972
|
+
}
|
|
1936
1973
|
continue;
|
|
1937
1974
|
}
|
|
1938
1975
|
|
|
@@ -2033,6 +2070,26 @@ export function convertResponsesAssistantMessage<TApi extends Api>(
|
|
|
2033
2070
|
});
|
|
2034
2071
|
}
|
|
2035
2072
|
|
|
2073
|
+
if (requiresReasoningItem && !reasoningItemEmitted && outputItems.length > 0) {
|
|
2074
|
+
// Replay the demoted reasoning (already present in `content` as visible
|
|
2075
|
+
// text) as a structured reasoning item so the thinking-mode continuation
|
|
2076
|
+
// carries the `reasoning_text` the provider requires. The text may be empty
|
|
2077
|
+
// when the source turn was minted by another model and its reasoning is
|
|
2078
|
+
// already folded into the message text; the item's presence is what
|
|
2079
|
+
// satisfies the provider contract, mirroring the empty `reasoning_content`
|
|
2080
|
+
// placeholder used on the chat-completions path.
|
|
2081
|
+
const reasoningText = carriedReasoningTexts.join("\n");
|
|
2082
|
+
const reasoningId =
|
|
2083
|
+
synthesizedReasoningItemId ?? `rs_${Bun.hash(`${model.id}:${msgIndex}:${reasoningText}`).toString(36)}`;
|
|
2084
|
+
const reasoningItem: ResponseReasoningItem = {
|
|
2085
|
+
type: "reasoning",
|
|
2086
|
+
id: reasoningId,
|
|
2087
|
+
summary: [],
|
|
2088
|
+
content: [{ type: "reasoning_text", text: reasoningText }],
|
|
2089
|
+
};
|
|
2090
|
+
outputItems.unshift(reasoningItem);
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2036
2093
|
return outputItems;
|
|
2037
2094
|
}
|
|
2038
2095
|
|
|
@@ -2374,6 +2431,20 @@ export function finalizeMessageText(item: ResponseOutputMessage, streamedText: s
|
|
|
2374
2431
|
if (!item.content?.length) return streamedText || "";
|
|
2375
2432
|
return item.content.map(part => (part.type === "output_text" ? (part.text ?? "") : (part.refusal ?? ""))).join("");
|
|
2376
2433
|
}
|
|
2434
|
+
export const JUICE_EFFORT_MAP: Record<string, number> = {
|
|
2435
|
+
none: 0,
|
|
2436
|
+
minimal: 2,
|
|
2437
|
+
low: 4,
|
|
2438
|
+
medium: 8,
|
|
2439
|
+
high: 48,
|
|
2440
|
+
xhigh: 112,
|
|
2441
|
+
max: 960,
|
|
2442
|
+
};
|
|
2443
|
+
|
|
2444
|
+
export function getJuiceValue(effort?: string): number {
|
|
2445
|
+
if (!effort) return 8;
|
|
2446
|
+
return JUICE_EFFORT_MAP[effort] ?? 8;
|
|
2447
|
+
}
|
|
2377
2448
|
|
|
2378
2449
|
export function accumulateToolCallArgumentsDelta(
|
|
2379
2450
|
block: ResponsesToolCallBlock,
|
|
@@ -2705,6 +2776,10 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
2705
2776
|
output.content.indexOf(block);
|
|
2706
2777
|
|
|
2707
2778
|
let sawFirstToken = false;
|
|
2779
|
+
// Whether the current stream produced a completed native `web_search_call`
|
|
2780
|
+
// output item. A provider-hosted search that finishes without yield is
|
|
2781
|
+
// progress evidence: the turn should pause for continuation rather than end.
|
|
2782
|
+
let sawCompletedWebSearchCall = false;
|
|
2708
2783
|
|
|
2709
2784
|
for await (const event of openaiStream) {
|
|
2710
2785
|
const terminalEvent = getOpenAIResponsesTerminalEvent(event);
|
|
@@ -2991,6 +3066,10 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
2991
3066
|
}
|
|
2992
3067
|
closeOpenItem(event.output_index, item.id, entry, item.call_id, prefixedFunctionCallItemKey(item.call_id));
|
|
2993
3068
|
stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
|
|
3069
|
+
} else if (item.type === "web_search_call" && (item.status === undefined || item.status === "completed")) {
|
|
3070
|
+
// A completed provider-hosted web search is progress evidence even when
|
|
3071
|
+
// the model never surfaced an answer; the agent loop continues from it.
|
|
3072
|
+
sawCompletedWebSearchCall = true;
|
|
2994
3073
|
} else if (item.type === "image_generation_call" && item.status === "completed" && item.result) {
|
|
2995
3074
|
appendResponsesImageResult(output, stream, item.result);
|
|
2996
3075
|
}
|
|
@@ -3041,6 +3120,14 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
3041
3120
|
(response as { end_turn?: boolean } | undefined)?.end_turn,
|
|
3042
3121
|
shouldPromoteIncompleteToolUse,
|
|
3043
3122
|
);
|
|
3123
|
+
// A completed provider-hosted web search that yielded no visible answer
|
|
3124
|
+
// (no text, image, or client tool call) is progress, not a dead end:
|
|
3125
|
+
// pause the turn so the agent loop re-samples with the search results
|
|
3126
|
+
// instead of silently ending. Reasoning/native output items are preserved
|
|
3127
|
+
// for replay. A search followed by visible output stays a normal stop.
|
|
3128
|
+
if (sawCompletedWebSearchCall && output.stopReason === "stop" && !hasVisibleAssistantContent(output)) {
|
|
3129
|
+
output.stopDetails = { type: "pause_turn" };
|
|
3130
|
+
}
|
|
3044
3131
|
options?.onCompleted?.();
|
|
3045
3132
|
// `response.completed`/`response.incomplete`/`response.done` is the last event of a
|
|
3046
3133
|
// Responses stream. Stop pulling instead of waiting for the server to
|
|
@@ -3254,6 +3341,14 @@ type ReasoningOptions = {
|
|
|
3254
3341
|
export interface ApplyResponsesCompatPolicyOptions {
|
|
3255
3342
|
reasoningSummary?: "auto" | "detailed" | "concise" | null;
|
|
3256
3343
|
mapEffort?: (effort: string) => string;
|
|
3344
|
+
/**
|
|
3345
|
+
* Suppress native reasoning by sending `reasoning.effort: "none"` — the only
|
|
3346
|
+
* disable level the Responses API defines (`"off"` is not a wire value and
|
|
3347
|
+
* 400s everywhere). Gateways that reject `none` for a given model are
|
|
3348
|
+
* handled by the reasoning-effort fallback retry, which clamps to the
|
|
3349
|
+
* lowest level the error reports as allowed.
|
|
3350
|
+
*/
|
|
3351
|
+
forceReasoningOff?: boolean;
|
|
3257
3352
|
}
|
|
3258
3353
|
|
|
3259
3354
|
export function applyResponsesCompatPolicy<P extends ResponseCreateParamsStreaming>(
|
|
@@ -3262,6 +3357,10 @@ export function applyResponsesCompatPolicy<P extends ResponseCreateParamsStreami
|
|
|
3262
3357
|
options: ApplyResponsesCompatPolicyOptions | undefined,
|
|
3263
3358
|
): void {
|
|
3264
3359
|
const reasoning = policy.reasoning;
|
|
3360
|
+
if (options?.forceReasoningOff) {
|
|
3361
|
+
params.reasoning = { effort: "none" } as P["reasoning"];
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3265
3364
|
if (!reasoning.modelSupported) return;
|
|
3266
3365
|
if (reasoning.includeEncryptedReasoning) {
|
|
3267
3366
|
const include = params.include ?? [];
|
|
@@ -3275,7 +3374,7 @@ export function applyResponsesCompatPolicy<P extends ResponseCreateParamsStreami
|
|
|
3275
3374
|
return;
|
|
3276
3375
|
}
|
|
3277
3376
|
if (
|
|
3278
|
-
reasoning.disableMode === "lowest-effort" &&
|
|
3377
|
+
(reasoning.disableMode === "lowest-effort" || reasoning.disableMode === "none-effort") &&
|
|
3279
3378
|
reasoning.wireEffort !== undefined &&
|
|
3280
3379
|
!reasoning.omitReasoningEffort
|
|
3281
3380
|
) {
|
|
@@ -78,6 +78,7 @@ const ALLOWED_OPTION_KEYS: ReadonlySet<keyof SimpleStreamOptions> = new Set([
|
|
|
78
78
|
"preferWebsockets",
|
|
79
79
|
"openrouterVariant",
|
|
80
80
|
"loopGuard",
|
|
81
|
+
"acceptEmptyResponse",
|
|
81
82
|
] as const satisfies readonly (keyof SimpleStreamOptions)[]);
|
|
82
83
|
|
|
83
84
|
// ---------------------------------------------------------------------------
|
|
@@ -202,6 +202,11 @@ interface LazyStreamLimits {
|
|
|
202
202
|
* stream timeouts. Keep the lazy loader from racing it with generic errors.
|
|
203
203
|
*/
|
|
204
204
|
providerHandlesStreamTimeouts?: boolean;
|
|
205
|
+
/**
|
|
206
|
+
* The provider retries or fails over when no first event arrives, while the
|
|
207
|
+
* lazy wrapper continues to own steady-state idle detection.
|
|
208
|
+
*/
|
|
209
|
+
providerHandlesFirstEventTimeouts?: boolean;
|
|
205
210
|
/**
|
|
206
211
|
* Apply OpenAI-family idle timeout precedence in the lazy wrapper. Used by
|
|
207
212
|
* local backends whose users historically tune slow prompt-processing gaps
|
|
@@ -210,16 +215,13 @@ interface LazyStreamLimits {
|
|
|
210
215
|
openAIIdleEnvFloorsFirstEvent?: boolean;
|
|
211
216
|
}
|
|
212
217
|
/**
|
|
213
|
-
* Cloud Code Assist
|
|
214
|
-
*
|
|
215
|
-
* the
|
|
216
|
-
*
|
|
217
|
-
* waiting for the first event" aborts on legitimate cold reasoning starts.
|
|
218
|
-
* The steady-state idle watchdog stays on the global default since the upstream
|
|
219
|
-
* emits thinking tokens frequently once it gets going.
|
|
218
|
+
* Cloud Code Assist owns first-event detection because Antigravity can return
|
|
219
|
+
* successful headers and then never emit an SSE event. Keeping the watchdog in
|
|
220
|
+
* the provider lets it fail over before surfacing an error; the lazy wrapper
|
|
221
|
+
* still catches post-first-event stalls.
|
|
220
222
|
*/
|
|
221
223
|
const GOOGLE_GEMINI_CLI_LAZY_STREAM_LIMITS: LazyStreamLimits = {
|
|
222
|
-
|
|
224
|
+
providerHandlesFirstEventTimeouts: true,
|
|
223
225
|
};
|
|
224
226
|
|
|
225
227
|
const PROVIDER_HANDLED_STREAM_TIMEOUTS: LazyStreamLimits = {
|
|
@@ -241,6 +243,7 @@ function forwardStream<TApi extends Api>(
|
|
|
241
243
|
(async () => {
|
|
242
244
|
try {
|
|
243
245
|
const providerHandlesStreamTimeouts = limits?.providerHandlesStreamTimeouts === true;
|
|
246
|
+
const providerHandlesFirstEventTimeouts = limits?.providerHandlesFirstEventTimeouts === true;
|
|
244
247
|
// Per-model catalog compat can widen the fallback watchdog for hosts
|
|
245
248
|
// with no keepalive events (e.g. Bedrock reasoning models that go
|
|
246
249
|
// quiet for minutes mid-thinking, issue #4758). Caller options and
|
|
@@ -258,12 +261,13 @@ function forwardStream<TApi extends Api>(
|
|
|
258
261
|
(limits?.openAIIdleEnvFloorsFirstEvent
|
|
259
262
|
? getOpenAIStreamIdleTimeoutMs(idleTimeoutFallbackMs)
|
|
260
263
|
: getStreamIdleTimeoutMs(idleTimeoutFallbackMs)));
|
|
261
|
-
const firstItemTimeoutMs =
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
(
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
const firstItemTimeoutMs =
|
|
265
|
+
providerHandlesStreamTimeouts || providerHandlesFirstEventTimeouts
|
|
266
|
+
? 0
|
|
267
|
+
: (options.streamFirstEventTimeoutMs ??
|
|
268
|
+
(limits?.openAIIdleEnvFloorsFirstEvent
|
|
269
|
+
? getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs, limits.defaultFirstEventTimeoutMs)
|
|
270
|
+
: getStreamFirstEventTimeoutMs(idleTimeoutMs, limits?.defaultFirstEventTimeoutMs)));
|
|
267
271
|
// Providers with a server-driven local tool bridge (e.g. the Cursor
|
|
268
272
|
// exec channel) mark their stream busy while a local tool runs; the
|
|
269
273
|
// watchdog must not read that silence as a provider stall (#4593).
|
|
@@ -39,7 +39,11 @@ export function joinTextWithImagePlaceholder(text: string, omittedImages: boolea
|
|
|
39
39
|
* multimodal content arrays for. The compatible-mode endpoint also serves
|
|
40
40
|
* multimodal Qwen SKUs without `vl` in the id (e.g. `qwen3.7-plus`), so this
|
|
41
41
|
* guard only covers families verified to be text-only for issue #1859:
|
|
42
|
-
* `qwen*-max` and `
|
|
42
|
+
* `qwen*-coder*` and `qwen*-max` up to and including `qwen3.7-max`.
|
|
43
|
+
*
|
|
44
|
+
* Qwen-Max became multimodal at `qwen3.8-max` (image input, issue #8019), so
|
|
45
|
+
* `-max` SKUs at version 3.8 or newer are excluded — otherwise the override
|
|
46
|
+
* would strip images from a genuinely vision-capable flagship (issue #8305).
|
|
43
47
|
*
|
|
44
48
|
* Used as a defensive override in `convertMessages` so a misconfigured custom
|
|
45
49
|
* provider (issue #1859) can't drive the request into an unrecoverable 400.
|
|
@@ -48,7 +52,15 @@ export function isDashscopeCompatibleModeTextOnlyQwen(model: Model<"openai-compl
|
|
|
48
52
|
if (!isDashscopeCompatibleModeUrl(model.baseUrl)) {
|
|
49
53
|
return false;
|
|
50
54
|
}
|
|
51
|
-
const id = model.id.toLowerCase();
|
|
52
55
|
if (!isQwenModelId(model.id)) return false;
|
|
53
|
-
|
|
56
|
+
const id = model.id.toLowerCase();
|
|
57
|
+
if (/\bqwen(?:[\d.]+)?-coder\b/.test(id)) return true;
|
|
58
|
+
const maxMatch = id.match(/\bqwen(?:(\d+)(?:\.(\d+))?)?-max\b/);
|
|
59
|
+
if (!maxMatch) return false;
|
|
60
|
+
// Bare `qwen-max` (no version) is the text-only 2.5-era flagship. Compare
|
|
61
|
+
// major/minor component-wise, not as a decimal float, so `qwen3.10-max`
|
|
62
|
+
// sorts after `qwen3.8-max`: text-only through 3.7, multimodal from 3.8 on.
|
|
63
|
+
const major = maxMatch[1] ? Number.parseInt(maxMatch[1], 10) : 0;
|
|
64
|
+
const minor = maxMatch[2] ? Number.parseInt(maxMatch[2], 10) : 0;
|
|
65
|
+
return major < 3 || (major === 3 && minor < 8);
|
|
54
66
|
}
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type AwsBedrockProviderOptions, resolveAwsRegistryApiKey } from "./aws";
|
|
2
2
|
import type { ProviderDefinition } from "./types";
|
|
3
3
|
|
|
4
4
|
export const amazonBedrockProvider = {
|
|
5
5
|
id: "amazon-bedrock",
|
|
6
6
|
name: "Amazon Bedrock",
|
|
7
7
|
// Amazon Bedrock accepts bearer tokens, IAM keys, profiles, ECS/IRSA credential chains.
|
|
8
|
-
envKeys: () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
hasEcsCredentials ||
|
|
17
|
-
hasWebIdentity
|
|
18
|
-
) {
|
|
19
|
-
return "<authenticated>";
|
|
20
|
-
}
|
|
8
|
+
envKeys: () => resolveAwsRegistryApiKey({ allowSkipAuth: true }),
|
|
9
|
+
mapSimpleOptions: options => {
|
|
10
|
+
const awsOptions = options.providerOptions as AwsBedrockProviderOptions | undefined;
|
|
11
|
+
return {
|
|
12
|
+
region: awsOptions?.region,
|
|
13
|
+
profile: awsOptions?.profile,
|
|
14
|
+
bearerToken: awsOptions?.bearerToken,
|
|
15
|
+
};
|
|
21
16
|
},
|
|
22
17
|
} as const satisfies ProviderDefinition;
|
|
@@ -38,16 +38,18 @@ type ModelsEndpointValidation = {
|
|
|
38
38
|
export type ApiKeyLoginConfig = {
|
|
39
39
|
/** Display name used in error messages, e.g. "Cerebras", "NanoGPT". */
|
|
40
40
|
providerLabel: string;
|
|
41
|
-
/** URL opened in browser for the user to grab their key. */
|
|
42
|
-
authUrl
|
|
43
|
-
/** Instructions shown with the onAuth callback. */
|
|
44
|
-
instructions
|
|
41
|
+
/** URL opened in browser for the user to grab their key, or omitted to skip onAuth. */
|
|
42
|
+
authUrl?: string;
|
|
43
|
+
/** Instructions shown with the onAuth callback, or omitted to skip onAuth. */
|
|
44
|
+
instructions?: string;
|
|
45
45
|
/** Prompt message shown when asking for the key paste. */
|
|
46
46
|
promptMessage: string;
|
|
47
47
|
/** Placeholder string for the prompt (e.g. "sk-...", "csk-..."). */
|
|
48
48
|
placeholder: string;
|
|
49
49
|
/** Validation strategy, or `null` to skip validation. */
|
|
50
50
|
validation: ChatCompletionsValidation | AnthropicMessagesValidation | ModelsEndpointValidation | null;
|
|
51
|
+
/** Value returned for an empty key; also allows an empty prompt response. */
|
|
52
|
+
emptyKeyFallback?: string;
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
export function createApiKeyLogin(config: ApiKeyLoginConfig): (options: OAuthController) => Promise<string> {
|
|
@@ -56,15 +58,24 @@ export function createApiKeyLogin(config: ApiKeyLoginConfig): (options: OAuthCon
|
|
|
56
58
|
throw new AIError.OnPromptRequiredError(config.providerLabel);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
if (config.authUrl && config.instructions) {
|
|
62
|
+
options.onAuth?.({
|
|
63
|
+
url: config.authUrl,
|
|
64
|
+
instructions: config.instructions,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
63
67
|
|
|
64
|
-
const apiKey =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
const apiKey =
|
|
69
|
+
config.emptyKeyFallback === undefined
|
|
70
|
+
? await options.onPrompt({
|
|
71
|
+
message: config.promptMessage,
|
|
72
|
+
placeholder: config.placeholder,
|
|
73
|
+
})
|
|
74
|
+
: await options.onPrompt({
|
|
75
|
+
message: config.promptMessage,
|
|
76
|
+
placeholder: config.placeholder,
|
|
77
|
+
allowEmpty: true,
|
|
78
|
+
});
|
|
68
79
|
|
|
69
80
|
if (options.signal?.aborted) {
|
|
70
81
|
throw new AIError.LoginCancelledError();
|
|
@@ -72,6 +83,9 @@ export function createApiKeyLogin(config: ApiKeyLoginConfig): (options: OAuthCon
|
|
|
72
83
|
|
|
73
84
|
const trimmed = apiKey.trim();
|
|
74
85
|
if (!trimmed) {
|
|
86
|
+
if (config.emptyKeyFallback !== undefined) {
|
|
87
|
+
return config.emptyKeyFallback;
|
|
88
|
+
}
|
|
75
89
|
throw new AIError.ApiKeyRequiredError();
|
|
76
90
|
}
|
|
77
91
|
|