@fractaal/pi-ai 0.86.9 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -78
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +380 -144
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +9 -11
- package/dist/api/azure-openai-responses.js.map +1 -1
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.js +184 -121
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/bedrock-converse-stream.lazy.d.ts +7 -0
- package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.lazy.js +18 -5
- package/dist/api/bedrock-converse-stream.lazy.js.map +1 -1
- package/dist/api/classifier-shared.d.ts +21 -0
- package/dist/api/classifier-shared.d.ts.map +1 -0
- package/dist/api/classifier-shared.js +99 -0
- package/dist/api/classifier-shared.js.map +1 -0
- package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
- package/dist/api/cloudflare-workers-ai-system-one.js +10 -1
- package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
- package/dist/api/constrained-sampling.d.ts +15 -1
- package/dist/api/constrained-sampling.d.ts.map +1 -1
- package/dist/api/constrained-sampling.js +51 -18
- package/dist/api/constrained-sampling.js.map +1 -1
- package/dist/api/google-generative-ai.d.ts.map +1 -1
- package/dist/api/google-generative-ai.js +4 -10
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-shared.d.ts +2 -1
- package/dist/api/google-shared.d.ts.map +1 -1
- package/dist/api/google-shared.js +60 -3
- package/dist/api/google-shared.js.map +1 -1
- package/dist/api/google-vertex.d.ts.map +1 -1
- package/dist/api/google-vertex.js +9 -15
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/lazy.d.ts +7 -2
- package/dist/api/lazy.d.ts.map +1 -1
- package/dist/api/lazy.js +33 -12
- package/dist/api/lazy.js.map +1 -1
- package/dist/api/llama-cpp-classify.d.ts.map +1 -1
- package/dist/api/llama-cpp-classify.js +4 -1
- package/dist/api/llama-cpp-classify.js.map +1 -1
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +50 -60
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +30 -17
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-completions.d.ts +2 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +93 -90
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-decisions.d.ts +4 -0
- package/dist/api/openai-decisions.d.ts.map +1 -0
- package/dist/api/openai-decisions.js +162 -0
- package/dist/api/openai-decisions.js.map +1 -0
- package/dist/api/openai-decisions.lazy.d.ts +3 -0
- package/dist/api/openai-decisions.lazy.d.ts.map +1 -0
- package/dist/api/openai-decisions.lazy.js +4 -0
- package/dist/api/openai-decisions.lazy.js.map +1 -0
- package/dist/api/openai-responses-shared.d.ts +26 -0
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +132 -66
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +42 -21
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/openrouter-images.d.ts.map +1 -1
- package/dist/api/openrouter-images.js +5 -3
- package/dist/api/openrouter-images.js.map +1 -1
- package/dist/api/pi-messages.d.ts +2 -0
- package/dist/api/pi-messages.d.ts.map +1 -1
- package/dist/api/pi-messages.js +27 -12
- package/dist/api/pi-messages.js.map +1 -1
- package/dist/api/simple-options.d.ts +1 -0
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +18 -3
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/system-one-shared.d.ts +29 -3
- package/dist/api/system-one-shared.d.ts.map +1 -1
- package/dist/api/system-one-shared.js +54 -86
- package/dist/api/system-one-shared.js.map +1 -1
- package/dist/api/transform-messages.d.ts +16 -1
- package/dist/api/transform-messages.d.ts.map +1 -1
- package/dist/api/transform-messages.js +28 -15
- package/dist/api/transform-messages.js.map +1 -1
- package/dist/api/typesafe-system-one.d.ts.map +1 -1
- package/dist/api/typesafe-system-one.js +12 -1
- package/dist/api/typesafe-system-one.js.map +1 -1
- package/dist/auth/oauth/anthropic.d.ts.map +1 -1
- package/dist/auth/oauth/anthropic.js +14 -6
- package/dist/auth/oauth/anthropic.js.map +1 -1
- package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
- package/dist/auth/oauth/github-copilot.js +4 -11
- package/dist/auth/oauth/github-copilot.js.map +1 -1
- package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
- package/dist/auth/oauth/kimi-coding.js +4 -22
- package/dist/auth/oauth/kimi-coding.js.map +1 -1
- package/dist/auth/oauth/load.d.ts.map +1 -1
- package/dist/auth/oauth/load.js +5 -2
- package/dist/auth/oauth/load.js.map +1 -1
- package/dist/auth/oauth/meta.d.ts.map +1 -1
- package/dist/auth/oauth/meta.js +1 -14
- package/dist/auth/oauth/meta.js.map +1 -1
- package/dist/auth/oauth/openai-chatgpt.js +1 -1
- package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
- package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
- package/dist/auth/oauth/openai-codex.js +4 -4
- package/dist/auth/oauth/openai-codex.js.map +1 -1
- package/dist/auth/oauth/radius.d.ts.map +1 -1
- package/dist/auth/oauth/radius.js +9 -3
- package/dist/auth/oauth/radius.js.map +1 -1
- package/dist/auth/oauth/trusted-url.d.ts +7 -0
- package/dist/auth/oauth/trusted-url.d.ts.map +1 -0
- package/dist/auth/oauth/trusted-url.js +12 -0
- package/dist/auth/oauth/trusted-url.js.map +1 -0
- package/dist/auth/types.d.ts +9 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/compat/extension-oauth-types.d.ts +4 -0
- package/dist/compat/extension-oauth-types.d.ts.map +1 -1
- package/dist/compat/extension-oauth-types.js.map +1 -1
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +1 -0
- package/dist/env-api-keys.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/lazy-bedrock-provider.d.ts +10 -0
- package/dist/lazy-bedrock-provider.d.ts.map +1 -0
- package/dist/lazy-bedrock-provider.js +13 -0
- package/dist/lazy-bedrock-provider.js.map +1 -0
- package/dist/lazy-oauth.d.ts +10 -0
- package/dist/lazy-oauth.d.ts.map +1 -0
- package/dist/lazy-oauth.js +23 -0
- package/dist/lazy-oauth.js.map +1 -0
- package/dist/models.d.ts +9 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +4 -0
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +5 -1
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +28 -12
- package/dist/models.js.map +1 -1
- package/dist/providers/all.d.ts.map +1 -1
- package/dist/providers/all.js +2 -0
- package/dist/providers/all.js.map +1 -1
- package/dist/providers/amazon-bedrock.models.js +1 -1
- package/dist/providers/amazon-bedrock.models.js.map +1 -1
- package/dist/providers/ant-ling.models.js +1 -1
- package/dist/providers/ant-ling.models.js.map +1 -1
- package/dist/providers/anthropic.models.js +1 -1
- package/dist/providers/anthropic.models.js.map +1 -1
- package/dist/providers/azure.models.js +1 -1
- package/dist/providers/azure.models.js.map +1 -1
- package/dist/providers/baseten.models.js +1 -1
- package/dist/providers/baseten.models.js.map +1 -1
- package/dist/providers/cerebras.models.js +1 -1
- package/dist/providers/cerebras.models.js.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.js +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
- package/dist/providers/cloudflare-workers-ai.models.js +1 -1
- package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
- package/dist/providers/compat-schema.d.ts +494 -0
- package/dist/providers/compat-schema.d.ts.map +1 -0
- package/dist/providers/compat-schema.js +447 -0
- package/dist/providers/compat-schema.js.map +1 -0
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/anthropic.json +1 -1
- package/dist/providers/data/azure.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/cerebras.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/google-vertex.json +1 -1
- package/dist/providers/data/google.json +1 -1
- package/dist/providers/data/groq.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/kimi-coding.json +1 -1
- package/dist/providers/data/meta.json +1 -1
- package/dist/providers/data/minimax-cn.json +1 -1
- package/dist/providers/data/minimax.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/moonshotai-cn.json +1 -1
- package/dist/providers/data/moonshotai.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/openai-codex.json +1 -1
- package/dist/providers/data/openai.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/qwen-token-plan-individual-cn.json +1 -0
- package/dist/providers/data/qwen-token-plan-individual.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/typesafe.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/data/xai.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
- package/dist/providers/data/xiaomi.json +1 -1
- package/dist/providers/data/zai-coding-cn.json +1 -1
- package/dist/providers/data/zai.json +1 -1
- package/dist/providers/deepseek.models.js +1 -1
- package/dist/providers/deepseek.models.js.map +1 -1
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +31 -9
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/fireworks.models.js +1 -1
- package/dist/providers/fireworks.models.js.map +1 -1
- package/dist/providers/github-copilot.d.ts +3 -1
- package/dist/providers/github-copilot.d.ts.map +1 -1
- package/dist/providers/github-copilot.js +13 -2
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/github-copilot.models.js +1 -1
- package/dist/providers/github-copilot.models.js.map +1 -1
- package/dist/providers/google-vertex.models.js +1 -1
- package/dist/providers/google-vertex.models.js.map +1 -1
- package/dist/providers/google.models.js +1 -1
- package/dist/providers/google.models.js.map +1 -1
- package/dist/providers/groq.models.js +1 -1
- package/dist/providers/groq.models.js.map +1 -1
- package/dist/providers/huggingface.models.js +1 -1
- package/dist/providers/huggingface.models.js.map +1 -1
- package/dist/providers/kimi-coding.d.ts +1 -1
- package/dist/providers/kimi-coding.js +3 -3
- package/dist/providers/kimi-coding.js.map +1 -1
- package/dist/providers/kimi-coding.models.js +1 -1
- package/dist/providers/kimi-coding.models.js.map +1 -1
- package/dist/providers/meta.models.js +1 -1
- package/dist/providers/meta.models.js.map +1 -1
- package/dist/providers/minimax-cn.models.js +1 -1
- package/dist/providers/minimax-cn.models.js.map +1 -1
- package/dist/providers/minimax.models.js +1 -1
- package/dist/providers/minimax.models.js.map +1 -1
- package/dist/providers/mistral.models.js +1 -1
- package/dist/providers/mistral.models.js.map +1 -1
- package/dist/providers/model-schema.d.ts +91 -0
- package/dist/providers/model-schema.d.ts.map +1 -0
- package/dist/providers/model-schema.js +68 -0
- package/dist/providers/model-schema.js.map +1 -0
- package/dist/providers/moonshotai-cn.models.js +1 -1
- package/dist/providers/moonshotai-cn.models.js.map +1 -1
- package/dist/providers/moonshotai.models.js +1 -1
- package/dist/providers/moonshotai.models.js.map +1 -1
- package/dist/providers/nvidia.models.js +1 -1
- package/dist/providers/nvidia.models.js.map +1 -1
- package/dist/providers/openai-codex-catalog.d.ts.map +1 -1
- package/dist/providers/openai-codex-catalog.js +44 -44
- package/dist/providers/openai-codex-catalog.js.map +1 -1
- package/dist/providers/openai-codex.models.js +1 -1
- package/dist/providers/openai-codex.models.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +12 -3
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openai.models.js +1 -1
- package/dist/providers/openai.models.js.map +1 -1
- package/dist/providers/opencode-go.d.ts +3 -1
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +2 -0
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/opencode-go.models.js +1 -1
- package/dist/providers/opencode-go.models.js.map +1 -1
- package/dist/providers/opencode.js +1 -1
- package/dist/providers/opencode.js.map +1 -1
- package/dist/providers/opencode.models.js +1 -1
- package/dist/providers/opencode.models.js.map +1 -1
- package/dist/providers/openrouter-api.d.ts +40 -0
- package/dist/providers/openrouter-api.d.ts.map +1 -0
- package/dist/providers/openrouter-api.js +41 -0
- package/dist/providers/openrouter-api.js.map +1 -0
- package/dist/providers/openrouter.d.ts +11 -1
- package/dist/providers/openrouter.d.ts.map +1 -1
- package/dist/providers/openrouter.js +52 -1
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/openrouter.models.js +1 -1
- package/dist/providers/openrouter.models.js.map +1 -1
- package/dist/providers/qwen-token-plan-cn.models.js +1 -1
- package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
- package/dist/providers/qwen-token-plan-individual-cn.d.ts +3 -0
- package/dist/providers/qwen-token-plan-individual-cn.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan-individual-cn.js +15 -0
- package/dist/providers/qwen-token-plan-individual-cn.js.map +1 -0
- package/dist/providers/qwen-token-plan-individual-cn.models.d.ts +6 -0
- package/dist/providers/qwen-token-plan-individual-cn.models.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan-individual-cn.models.js +8 -0
- package/dist/providers/qwen-token-plan-individual-cn.models.js.map +1 -0
- package/dist/providers/qwen-token-plan-individual.models.js +1 -1
- package/dist/providers/qwen-token-plan-individual.models.js.map +1 -1
- package/dist/providers/qwen-token-plan.models.js +1 -1
- package/dist/providers/qwen-token-plan.models.js.map +1 -1
- package/dist/providers/radius-config.d.ts.map +1 -1
- package/dist/providers/radius-config.js +2 -1
- package/dist/providers/radius-config.js.map +1 -1
- package/dist/providers/radius.d.ts.map +1 -1
- package/dist/providers/radius.js +4 -12
- package/dist/providers/radius.js.map +1 -1
- package/dist/providers/radius.models.js +1 -1
- package/dist/providers/radius.models.js.map +1 -1
- package/dist/providers/schema-builders.d.ts +10 -0
- package/dist/providers/schema-builders.d.ts.map +1 -0
- package/dist/providers/schema-builders.js +18 -0
- package/dist/providers/schema-builders.js.map +1 -0
- package/dist/providers/together.models.js +1 -1
- package/dist/providers/together.models.js.map +1 -1
- package/dist/providers/typesafe.models.js +1 -1
- package/dist/providers/typesafe.models.js.map +1 -1
- package/dist/providers/vercel-ai-gateway.models.js +1 -1
- package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/dist/providers/xai.models.js +1 -1
- package/dist/providers/xai.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.js +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.js +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.js +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
- package/dist/providers/xiaomi.models.js +1 -1
- package/dist/providers/xiaomi.models.js.map +1 -1
- package/dist/providers/zai-coding-cn.models.js +1 -1
- package/dist/providers/zai-coding-cn.models.js.map +1 -1
- package/dist/providers/zai.models.js +1 -1
- package/dist/providers/zai.models.js.map +1 -1
- package/dist/telemetry.d.ts +288 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +192 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +57 -310
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/error-body.d.ts +8 -0
- package/dist/utils/error-body.d.ts.map +1 -1
- package/dist/utils/error-body.js +30 -2
- package/dist/utils/error-body.js.map +1 -1
- package/dist/utils/estimate.js +1 -1
- package/dist/utils/estimate.js.map +1 -1
- package/dist/utils/event-stream.d.ts +10 -1
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +25 -0
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/identity.d.ts +65 -0
- package/dist/utils/identity.d.ts.map +1 -0
- package/dist/utils/identity.js +141 -0
- package/dist/utils/identity.js.map +1 -0
- package/dist/utils/json-parse.d.ts +10 -0
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +26 -1
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/model-operations.d.ts +3 -1
- package/dist/utils/model-operations.d.ts.map +1 -1
- package/dist/utils/model-operations.js +6 -0
- package/dist/utils/model-operations.js.map +1 -1
- package/dist/utils/openrouter-cost.d.ts +24 -0
- package/dist/utils/openrouter-cost.d.ts.map +1 -0
- package/dist/utils/openrouter-cost.js +34 -0
- package/dist/utils/openrouter-cost.js.map +1 -0
- package/dist/utils/overflow.d.ts +1 -0
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +8 -1
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/provider-retry.d.ts +2 -0
- package/dist/utils/provider-retry.d.ts.map +1 -1
- package/dist/utils/provider-retry.js +2 -0
- package/dist/utils/provider-retry.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +29 -10
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/streaming-json.d.ts +84 -0
- package/dist/utils/streaming-json.d.ts.map +1 -0
- package/dist/utils/streaming-json.js +513 -0
- package/dist/utils/streaming-json.js.map +1 -0
- package/dist/utils/{typebox-helpers.d.ts → string-enum.d.ts} +5 -5
- package/dist/utils/string-enum.d.ts.map +1 -0
- package/dist/utils/{typebox-helpers.js → string-enum.js} +5 -6
- package/dist/utils/string-enum.js.map +1 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.d.ts.map +1 -1
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/transcript.d.ts +23 -9
- package/dist/utils/transcript.d.ts.map +1 -1
- package/dist/utils/transcript.js +30 -19
- package/dist/utils/transcript.js.map +1 -1
- package/dist/utils/validation.d.ts +5 -3
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +27 -445
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +3 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +19 -10
- package/dist/utils/assistant-message-frame.d.ts +0 -77
- package/dist/utils/assistant-message-frame.d.ts.map +0 -1
- package/dist/utils/assistant-message-frame.js +0 -425
- package/dist/utils/assistant-message-frame.js.map +0 -1
- package/dist/utils/pi-user-agent.d.ts +0 -2
- package/dist/utils/pi-user-agent.d.ts.map +0 -1
- package/dist/utils/pi-user-agent.js +0 -12
- package/dist/utils/pi-user-agent.js.map +0 -1
- package/dist/utils/typebox-helpers.d.ts.map +0 -1
- package/dist/utils/typebox-helpers.js.map +0 -1
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import Anthropic, {} from "@anthropic-ai/sdk";
|
|
2
2
|
import { ANTHROPIC_FEDERATION_RULE_ID_ENV, ANTHROPIC_IDENTITY_TOKEN_FILE_ENV, ANTHROPIC_ORGANIZATION_ID_ENV, ANTHROPIC_SERVICE_ACCOUNT_ID_ENV, ANTHROPIC_WORKSPACE_ID_ENV, } from "../env-api-keys.js";
|
|
3
3
|
import { calculateCost } from "../models.js";
|
|
4
|
+
import { registerSessionResourceCleanup } from "../session-resources.js";
|
|
4
5
|
import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
|
|
6
|
+
import { withCauseDetail } from "../utils/error-body.js";
|
|
5
7
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
8
|
+
import { shortHash } from "../utils/hash.js";
|
|
6
9
|
import { headersToRecord } from "../utils/headers.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
10
|
+
import { getIdentityHeaders } from "../utils/identity.js";
|
|
11
|
+
import { parseJsonWithRepair } from "../utils/json-parse.js";
|
|
12
|
+
import { applyOpenRouterReportedCost, isOpenRouterModel, } from "../utils/openrouter-cost.js";
|
|
9
13
|
import { getProviderEnvValue } from "../utils/provider-env.js";
|
|
10
14
|
import { retryProviderRequest } from "../utils/provider-retry.js";
|
|
11
15
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
16
|
+
import { ToolCallArgumentsStream } from "../utils/streaming-json.js";
|
|
12
17
|
import { getSystemMessageText, renderSystemMessageUpdate } from "../utils/text.js";
|
|
13
18
|
import { getCurrentTools, getInitialSystemMessage, resolveTranscript, } from "../utils/transcript.js";
|
|
14
|
-
import { getJsonSchemaToolParameters, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
|
|
19
|
+
import { getJsonSchemaToolParameters, isStrictToolLimitErrorMessage, prefersJsonSchemaStrictSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
|
|
15
20
|
import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
|
|
16
21
|
import { adjustMaxTokensForThinking, buildBaseOptions, clampMaxTokensToContext } from "./simple-options.js";
|
|
17
|
-
import { transformMessages } from "./transform-messages.js";
|
|
22
|
+
import { splitErrorToolResultImages, transformMessages } from "./transform-messages.js";
|
|
18
23
|
/**
|
|
19
24
|
* Resolve cache retention preference.
|
|
20
25
|
* Defaults to "short" and uses PI_CACHE_RETENTION for backward compatibility.
|
|
@@ -75,6 +80,16 @@ const fromClaudeCodeName = (name, tools) => {
|
|
|
75
80
|
}
|
|
76
81
|
return name;
|
|
77
82
|
};
|
|
83
|
+
function convertImageBlock(image) {
|
|
84
|
+
return {
|
|
85
|
+
type: "image",
|
|
86
|
+
source: {
|
|
87
|
+
type: "base64",
|
|
88
|
+
media_type: image.mimeType,
|
|
89
|
+
data: image.data,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
78
93
|
/**
|
|
79
94
|
* Convert content blocks to Anthropic API format
|
|
80
95
|
*/
|
|
@@ -92,14 +107,7 @@ function convertContentBlocks(content) {
|
|
|
92
107
|
text: sanitizeSurrogates(block.text),
|
|
93
108
|
};
|
|
94
109
|
}
|
|
95
|
-
return
|
|
96
|
-
type: "image",
|
|
97
|
-
source: {
|
|
98
|
-
type: "base64",
|
|
99
|
-
media_type: block.mimeType,
|
|
100
|
-
data: block.data,
|
|
101
|
-
},
|
|
102
|
-
};
|
|
110
|
+
return convertImageBlock(block);
|
|
103
111
|
});
|
|
104
112
|
// If only images (no text), add placeholder text block
|
|
105
113
|
const hasText = blocks.some((b) => b.type === "text");
|
|
@@ -117,6 +125,7 @@ const SERVER_SIDE_FALLBACK_BETA = "server-side-fallback-2026-07-01";
|
|
|
117
125
|
const MID_CONVERSATION_OUTPUT_CONFIG_BETA = "mid-conversation-output-config-2026-07-01";
|
|
118
126
|
const THINKING_BINDING_CONTROLS_BETA = "thinking-binding-controls-2026-08-01";
|
|
119
127
|
const INLINE_TOOLS_BETA = "inline-tools-2026-09-15";
|
|
128
|
+
const MESSAGE_THREADS_BETA = "message-threads-2026-08-12";
|
|
120
129
|
/**
|
|
121
130
|
* Stable deferred tool declared whenever native tool changes are in use. Anthropic adds
|
|
122
131
|
* hidden prompt scaffolding for mid-conversation tool changes; declaring this placeholder
|
|
@@ -146,8 +155,178 @@ function getAnthropicCompat(model) {
|
|
|
146
155
|
supportsStrictTools: model.compat?.supportsStrictTools ?? false,
|
|
147
156
|
supportsMidConvoSystemMessages: model.compat?.supportsMidConvoSystemMessages ?? false,
|
|
148
157
|
supportsMidConvoToolChanges: model.compat?.supportsMidConvoToolChanges ?? false,
|
|
158
|
+
// Gateways that drop unknown request fields would silently turn a `continue` into a request
|
|
159
|
+
// without the earlier conversation, so the default is limited to Anthropic's own endpoint.
|
|
160
|
+
// Widen or remove this restriction once proxies generally forward the `thread` field.
|
|
161
|
+
supportsMessageThreads: model.compat?.supportsMessageThreads ??
|
|
162
|
+
(model.provider === "anthropic" && model.baseUrl.includes("api.anthropic.com")),
|
|
149
163
|
};
|
|
150
164
|
}
|
|
165
|
+
/** Per session: where the next request can continue, or "off" after the endpoint rejected threads. */
|
|
166
|
+
const threadState = new Map();
|
|
167
|
+
const MAX_THREAD_STATE_ENTRIES = 10_000;
|
|
168
|
+
/** Rejections of a `continue` that a new thread fixes: expired, raced, or a fixed setting changed. */
|
|
169
|
+
const NEW_THREAD_ERRORS = new Set([
|
|
170
|
+
"thread_not_found",
|
|
171
|
+
"thread_already_continued",
|
|
172
|
+
"thread_continue_in_flight",
|
|
173
|
+
"thread_fingerprint_mismatch",
|
|
174
|
+
]);
|
|
175
|
+
/**
|
|
176
|
+
* Sets a session's thread state, keeping at most `MAX_THREAD_STATE_ENTRIES` sessions.
|
|
177
|
+
*
|
|
178
|
+
* State is normally freed by `cleanupSessionResources(sessionId)` when a session ends, as pi's
|
|
179
|
+
* coding agent does. The cap bounds memory for library users that never call it, for example a
|
|
180
|
+
* server passing a new `sessionId` per conversation. Dropping the least recently updated session
|
|
181
|
+
* is cheap: its thread has most likely expired on the server already (after a few minutes idle),
|
|
182
|
+
* and otherwise its next request just starts a new thread.
|
|
183
|
+
*/
|
|
184
|
+
function setThreadState(sessionId, value) {
|
|
185
|
+
// Re-insert so Map order stays oldest-first for the cap.
|
|
186
|
+
threadState.delete(sessionId);
|
|
187
|
+
threadState.set(sessionId, value);
|
|
188
|
+
if (threadState.size > MAX_THREAD_STATE_ENTRIES) {
|
|
189
|
+
const oldest = threadState.keys().next().value;
|
|
190
|
+
if (oldest !== undefined)
|
|
191
|
+
threadState.delete(oldest);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
registerSessionResourceCleanup((sessionId) => {
|
|
195
|
+
if (sessionId)
|
|
196
|
+
threadState.delete(sessionId);
|
|
197
|
+
else
|
|
198
|
+
threadState.clear();
|
|
199
|
+
});
|
|
200
|
+
/**
|
|
201
|
+
* Hash of everything a `continue` builds on: the settings that must stay the same within a thread
|
|
202
|
+
* and the messages the server already holds.
|
|
203
|
+
*
|
|
204
|
+
* - Settings, as sent: the endpoint (a thread exists on one endpoint only), and the fixed request
|
|
205
|
+
* fields. A `continue` omits `system` and `tools` and the server keeps the thread's own, so only
|
|
206
|
+
* this check can notice a change to them, including their cache markers. The server rejects
|
|
207
|
+
* changes to the other fields with `thread_fingerprint_mismatch`; checking them here saves that
|
|
208
|
+
* rejected request. A fixed field missing here costs only that rejection, which also starts a
|
|
209
|
+
* new thread.
|
|
210
|
+
* - Messages, normalized so messages that mean the same to the API hash the same.
|
|
211
|
+
*/
|
|
212
|
+
function hashThreadPrefix(model, params, messages) {
|
|
213
|
+
const { system, tools, thinking, output_config, tool_choice, betas, fallbacks } = params;
|
|
214
|
+
const settings = JSON.stringify([
|
|
215
|
+
model.provider,
|
|
216
|
+
model.baseUrl,
|
|
217
|
+
params.model,
|
|
218
|
+
system,
|
|
219
|
+
tools,
|
|
220
|
+
thinking,
|
|
221
|
+
output_config,
|
|
222
|
+
tool_choice,
|
|
223
|
+
betas,
|
|
224
|
+
fallbacks,
|
|
225
|
+
]);
|
|
226
|
+
const conversation = JSON.stringify(messages, (key, value) => {
|
|
227
|
+
// Cache markers don't change what a message means, but they move: pi puts one on the newest
|
|
228
|
+
// message, so the message that had it in the last request has none in this one. Extensions
|
|
229
|
+
// may add markers anywhere too.
|
|
230
|
+
if (key === "cache_control")
|
|
231
|
+
return undefined;
|
|
232
|
+
// To attach that marker, the newest user message's string content is sent as one text block;
|
|
233
|
+
// the next request sends the same message as a string: "hi" vs [{ type: "text", text: "hi" }].
|
|
234
|
+
if (key === "content" && typeof value === "string")
|
|
235
|
+
return [{ type: "text", text: value }];
|
|
236
|
+
return value;
|
|
237
|
+
});
|
|
238
|
+
return shortHash(settings + conversation);
|
|
239
|
+
}
|
|
240
|
+
function createThreadRequest(sessionId, params) {
|
|
241
|
+
return { sessionId, body: { ...params, thread: { type: "create" } } };
|
|
242
|
+
}
|
|
243
|
+
function prepareThreadRequest(model, sessionId, params) {
|
|
244
|
+
const state = threadState.get(sessionId);
|
|
245
|
+
if (state === "off")
|
|
246
|
+
return undefined;
|
|
247
|
+
if (state !== undefined &&
|
|
248
|
+
params.messages.length > state.prefixCount &&
|
|
249
|
+
hashThreadPrefix(model, params, params.messages.slice(0, state.prefixCount)) === state.prefixHash) {
|
|
250
|
+
// Each reply can be continued once; deleting also keeps a concurrent request from trying.
|
|
251
|
+
threadState.delete(sessionId);
|
|
252
|
+
const { system: _system, tools: _tools, ...rest } = params;
|
|
253
|
+
return {
|
|
254
|
+
body: {
|
|
255
|
+
...rest,
|
|
256
|
+
messages: params.messages.slice(state.prefixCount),
|
|
257
|
+
thread: { type: "continue", previous_message_id: state.replyId },
|
|
258
|
+
},
|
|
259
|
+
sessionId,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return createThreadRequest(sessionId, params);
|
|
263
|
+
}
|
|
264
|
+
/** The `error_code` Anthropic puts in `error.details` of thread rejections. */
|
|
265
|
+
function getThreadErrorCode(error) {
|
|
266
|
+
const body = error.error;
|
|
267
|
+
const code = body?.error?.details?.error_code;
|
|
268
|
+
return typeof code === "string" ? code : undefined;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Sends a thread request. A rejection about the thread (never a started stream) is resent once in
|
|
272
|
+
* full: as a new thread when the old one can't be continued, or without `thread` when the endpoint
|
|
273
|
+
* can't use threads, which also turns threads off for the session. Other errors are passed on.
|
|
274
|
+
* Returns the response and the thread request that produced it, if any.
|
|
275
|
+
*/
|
|
276
|
+
async function sendThreadRequest(thread, params, send, output, signal) {
|
|
277
|
+
try {
|
|
278
|
+
return { response: await send(thread.body), thread };
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
if (signal?.aborted || !(error instanceof Anthropic.APIError))
|
|
282
|
+
throw error;
|
|
283
|
+
const errorCode = getThreadErrorCode(error);
|
|
284
|
+
const startNewThread = errorCode !== undefined && NEW_THREAD_ERRORS.has(errorCode);
|
|
285
|
+
// The endpoint can't use threads: it can't thread this request, or it doesn't know the
|
|
286
|
+
// `thread` field and rejects it without a code ("thread: Extra inputs are not permitted"),
|
|
287
|
+
// e.g. with an `anthropic-beta` header that lacks the threads beta.
|
|
288
|
+
const threadsUnsupported = errorCode === "thread_unsupported_request" ||
|
|
289
|
+
(errorCode === undefined && error.status === 400 && error.message.includes("thread"));
|
|
290
|
+
// Any other error (prompt too long, unknown model, strict tool limits, ...) is not about the
|
|
291
|
+
// thread, and resending would only fail again.
|
|
292
|
+
if (!startNewThread && !threadsUnsupported)
|
|
293
|
+
throw error;
|
|
294
|
+
// Recorded before the resend, so a failed resend keeps it.
|
|
295
|
+
appendAssistantMessageDiagnostic(output, {
|
|
296
|
+
type: "anthropic_thread_fallback",
|
|
297
|
+
timestamp: Date.now(),
|
|
298
|
+
error: { message: error.message },
|
|
299
|
+
details: { reason: errorCode ?? String(error.status) },
|
|
300
|
+
});
|
|
301
|
+
if (startNewThread) {
|
|
302
|
+
const created = createThreadRequest(thread.sessionId, params);
|
|
303
|
+
return { response: await send(created.body), thread: created };
|
|
304
|
+
}
|
|
305
|
+
// With the threads beta, a request without `thread` is a normal request.
|
|
306
|
+
const response = await send(params);
|
|
307
|
+
setThreadState(thread.sessionId, "off");
|
|
308
|
+
return { response, thread: undefined };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Records where the session's next request can continue: after `output`, the clean reply to the
|
|
313
|
+
* thread request `thread`, whose full request was `params`.
|
|
314
|
+
*/
|
|
315
|
+
function recordThreadReply(model, thread, params, output, isOAuth, allowEmptySignature) {
|
|
316
|
+
// `responseModel` is set only when a fallback model answered; the server does not store those
|
|
317
|
+
// replies, so they cannot be continued.
|
|
318
|
+
if (output.responseId === undefined || output.responseModel !== undefined)
|
|
319
|
+
return;
|
|
320
|
+
// Convert the reply like the next request will, keeping only the assistant message
|
|
321
|
+
// (transformMessages adds placeholder results for trailing tool calls).
|
|
322
|
+
const reply = convertMessages(transformMessages([output], model, normalizeToolCallId), isOAuth, undefined, allowEmptySignature).messages.filter((message) => message.role === "assistant");
|
|
323
|
+
const prefix = [...params.messages, ...reply];
|
|
324
|
+
setThreadState(thread.sessionId, {
|
|
325
|
+
prefixCount: prefix.length,
|
|
326
|
+
prefixHash: hashThreadPrefix(model, params, prefix),
|
|
327
|
+
replyId: output.responseId,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
151
330
|
function mergeHeaders(...headerSources) {
|
|
152
331
|
const merged = {};
|
|
153
332
|
for (const headers of headerSources) {
|
|
@@ -157,9 +336,6 @@ function mergeHeaders(...headerSources) {
|
|
|
157
336
|
}
|
|
158
337
|
return merged;
|
|
159
338
|
}
|
|
160
|
-
function mergeClientHeaders(...headerSources) {
|
|
161
|
-
return mergeHeaders({ "User-Agent": getPiUserAgent() }, ...headerSources);
|
|
162
|
-
}
|
|
163
339
|
function hasHeader(headers, name) {
|
|
164
340
|
if (!headers)
|
|
165
341
|
return false;
|
|
@@ -375,17 +551,16 @@ async function* iterateAnthropicEvents(response, signal) {
|
|
|
375
551
|
}
|
|
376
552
|
export const stream = (model, context, options) => {
|
|
377
553
|
const stream = new AssistantMessageEventStream();
|
|
378
|
-
const
|
|
554
|
+
const compat = getAnthropicCompat(model);
|
|
555
|
+
const normalizedContext = resolveTranscript(context, compat.supportsMidConvoSystemMessages);
|
|
379
556
|
const currentTools = getCurrentTools(normalizedContext.messages);
|
|
380
557
|
(async () => {
|
|
381
|
-
const providerThinkingLevel = model.compat?.supportsMidConvoEffort ? (options?.effort ?? "high") : undefined;
|
|
382
558
|
const output = {
|
|
383
559
|
role: "assistant",
|
|
384
560
|
content: [],
|
|
385
561
|
api: model.api,
|
|
386
562
|
provider: model.provider,
|
|
387
563
|
model: model.id,
|
|
388
|
-
...(providerThinkingLevel === undefined ? {} : { providerThinkingLevel }),
|
|
389
564
|
usage: {
|
|
390
565
|
input: 0,
|
|
391
566
|
output: 0,
|
|
@@ -402,6 +577,12 @@ export const stream = (model, context, options) => {
|
|
|
402
577
|
let isOAuth;
|
|
403
578
|
let usageModel = model;
|
|
404
579
|
let inputTransformations;
|
|
580
|
+
const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
|
|
581
|
+
const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
|
|
582
|
+
// Session whose requests may use threads; undefined when threads don't apply. Sessions where
|
|
583
|
+
// the endpoint rejected threads ("off") still send the threads beta, which the resend that
|
|
584
|
+
// turned them off was accepted with; `prepareThreadRequest()` just sends no `thread`.
|
|
585
|
+
const threadSessionId = compat.supportsMessageThreads ? cacheSessionId : undefined;
|
|
405
586
|
if (options?.client) {
|
|
406
587
|
client = options.client;
|
|
407
588
|
isOAuth = false;
|
|
@@ -419,30 +600,77 @@ export const stream = (model, context, options) => {
|
|
|
419
600
|
hasImages,
|
|
420
601
|
});
|
|
421
602
|
}
|
|
422
|
-
const
|
|
423
|
-
const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
|
|
424
|
-
const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation);
|
|
603
|
+
const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation, options?.identity);
|
|
425
604
|
client = created.client;
|
|
426
605
|
isOAuth = created.isOAuthToken;
|
|
427
606
|
}
|
|
428
|
-
let params = buildParams(model, normalizedContext, isOAuth, options);
|
|
429
|
-
const nextParams = await options?.onPayload?.(params, model);
|
|
430
|
-
if (nextParams !== undefined) {
|
|
431
|
-
params = { ...nextParams, stream: true };
|
|
432
|
-
}
|
|
433
607
|
const requestOptions = {
|
|
434
608
|
...(options?.signal ? { signal: options.signal } : {}),
|
|
435
609
|
...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
|
|
436
610
|
maxRetries: 0,
|
|
437
611
|
};
|
|
438
|
-
const
|
|
612
|
+
const send = (body) => retryProviderRequest(() => client.beta.messages.create(body, requestOptions).asResponse(), {
|
|
439
613
|
maxRetries: options?.maxRetries,
|
|
440
614
|
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
441
615
|
signal: options?.signal,
|
|
442
616
|
});
|
|
617
|
+
let disablePreferredStrictTools = false;
|
|
618
|
+
let response;
|
|
619
|
+
let params;
|
|
620
|
+
// The thread request that produced the response; undefined when no `thread` was sent.
|
|
621
|
+
let thread;
|
|
622
|
+
for (;;) {
|
|
623
|
+
options?.signal?.throwIfAborted();
|
|
624
|
+
const built = buildParams(model, normalizedContext, isOAuth, options, {
|
|
625
|
+
disablePreferredStrictTools,
|
|
626
|
+
messageThreads: threadSessionId !== undefined,
|
|
627
|
+
});
|
|
628
|
+
if (built.providerThinkingLevel)
|
|
629
|
+
output.providerThinkingLevel = built.providerThinkingLevel;
|
|
630
|
+
params = built.params;
|
|
631
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
632
|
+
if (nextParams !== undefined) {
|
|
633
|
+
params = { ...nextParams, stream: true };
|
|
634
|
+
}
|
|
635
|
+
const prepared = threadSessionId !== undefined && options?.disableContinuation !== true
|
|
636
|
+
? prepareThreadRequest(model, threadSessionId, params)
|
|
637
|
+
: undefined;
|
|
638
|
+
try {
|
|
639
|
+
const sent = prepared
|
|
640
|
+
? await sendThreadRequest(prepared, params, send, output, options?.signal)
|
|
641
|
+
: { response: await send(params), thread: undefined };
|
|
642
|
+
response = sent.response;
|
|
643
|
+
thread = sent.thread;
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
// Only downgrade a rejected request, never an already-started stream.
|
|
648
|
+
if (options?.signal?.aborted ||
|
|
649
|
+
disablePreferredStrictTools ||
|
|
650
|
+
!built.hasPreferredStrictTools ||
|
|
651
|
+
!(error instanceof Anthropic.APIError) ||
|
|
652
|
+
error.status !== 400 ||
|
|
653
|
+
error.type !== "invalid_request_error" ||
|
|
654
|
+
!isStrictToolLimitErrorMessage(error.message)) {
|
|
655
|
+
throw error;
|
|
656
|
+
}
|
|
657
|
+
disablePreferredStrictTools = true;
|
|
658
|
+
appendAssistantMessageDiagnostic(output, {
|
|
659
|
+
type: "anthropic_strict_tools_fallback",
|
|
660
|
+
timestamp: Date.now(),
|
|
661
|
+
error: { message: error.message },
|
|
662
|
+
details: { reason: "strict_tool_limits_exceeded" },
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
443
666
|
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
444
667
|
stream.push({ type: "start", partial: output });
|
|
445
|
-
|
|
668
|
+
// Stream state per Anthropic block index, kept off the content blocks so it never reaches partials.
|
|
669
|
+
const openBlocks = new Map();
|
|
670
|
+
const openBlock = (anthropicIndex) => {
|
|
671
|
+
const open = openBlocks.get(anthropicIndex);
|
|
672
|
+
return open === undefined ? undefined : { ...open, block: output.content[open.contentIndex] };
|
|
673
|
+
};
|
|
446
674
|
for await (const event of iterateAnthropicEvents(response, options?.signal)) {
|
|
447
675
|
await options?.onProviderStreamEvent?.(event, model);
|
|
448
676
|
if (event.type === "message_start") {
|
|
@@ -477,105 +705,92 @@ export const stream = (model, context, options) => {
|
|
|
477
705
|
continue;
|
|
478
706
|
}
|
|
479
707
|
if (event.content_block.type === "text") {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
text: event.content_block.text ?? "",
|
|
483
|
-
index: event.index,
|
|
484
|
-
};
|
|
485
|
-
output.content.push(block);
|
|
708
|
+
output.content.push({ type: "text", text: event.content_block.text ?? "" });
|
|
709
|
+
openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
|
|
486
710
|
stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
|
|
487
711
|
}
|
|
488
712
|
else if (event.content_block.type === "thinking") {
|
|
489
|
-
|
|
713
|
+
output.content.push({
|
|
490
714
|
type: "thinking",
|
|
491
715
|
thinking: event.content_block.thinking ?? "",
|
|
492
716
|
thinkingSignature: event.content_block.signature ?? "",
|
|
493
|
-
|
|
494
|
-
};
|
|
495
|
-
output.content.push(block);
|
|
717
|
+
});
|
|
718
|
+
openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
|
|
496
719
|
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
497
720
|
}
|
|
498
721
|
else if (event.content_block.type === "redacted_thinking") {
|
|
499
|
-
|
|
722
|
+
output.content.push({
|
|
500
723
|
type: "thinking",
|
|
501
724
|
thinking: "[Reasoning redacted]",
|
|
502
725
|
thinkingSignature: event.content_block.data,
|
|
503
726
|
redacted: true,
|
|
504
|
-
|
|
505
|
-
};
|
|
506
|
-
output.content.push(block);
|
|
727
|
+
});
|
|
728
|
+
openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
|
|
507
729
|
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
508
730
|
}
|
|
509
731
|
else if (event.content_block.type === "tool_use") {
|
|
510
|
-
|
|
732
|
+
output.content.push({
|
|
511
733
|
type: "toolCall",
|
|
512
734
|
id: event.content_block.id,
|
|
513
735
|
name: isOAuth
|
|
514
736
|
? fromClaudeCodeName(event.content_block.name, currentTools)
|
|
515
737
|
: event.content_block.name,
|
|
516
738
|
arguments: event.content_block.input ?? {},
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
739
|
+
});
|
|
740
|
+
openBlocks.set(event.index, {
|
|
741
|
+
contentIndex: output.content.length - 1,
|
|
742
|
+
arguments: new ToolCallArgumentsStream(),
|
|
743
|
+
});
|
|
521
744
|
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
|
|
522
745
|
}
|
|
523
746
|
}
|
|
524
747
|
else if (event.type === "content_block_delta") {
|
|
748
|
+
const open = openBlock(event.index);
|
|
525
749
|
if (event.delta.type === "text_delta") {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (block && block.type === "text") {
|
|
529
|
-
block.text += event.delta.text;
|
|
750
|
+
if (open?.block.type === "text") {
|
|
751
|
+
open.block.text += event.delta.text;
|
|
530
752
|
stream.push({
|
|
531
753
|
type: "text_delta",
|
|
532
|
-
contentIndex:
|
|
754
|
+
contentIndex: open.contentIndex,
|
|
533
755
|
delta: event.delta.text,
|
|
534
756
|
partial: output,
|
|
535
757
|
});
|
|
536
758
|
}
|
|
537
759
|
}
|
|
538
760
|
else if (event.delta.type === "thinking_delta") {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (block && block.type === "thinking") {
|
|
542
|
-
block.thinking += event.delta.thinking;
|
|
761
|
+
if (open?.block.type === "thinking") {
|
|
762
|
+
open.block.thinking += event.delta.thinking;
|
|
543
763
|
stream.push({
|
|
544
764
|
type: "thinking_delta",
|
|
545
|
-
contentIndex:
|
|
765
|
+
contentIndex: open.contentIndex,
|
|
546
766
|
delta: event.delta.thinking,
|
|
547
767
|
partial: output,
|
|
548
768
|
});
|
|
549
769
|
}
|
|
550
770
|
}
|
|
551
771
|
else if (event.delta.type === "input_json_delta") {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if (block && block.type === "toolCall") {
|
|
555
|
-
block.partialJson += event.delta.partial_json;
|
|
556
|
-
block.arguments = parseStreamingJson(block.partialJson);
|
|
772
|
+
if (open?.block.type === "toolCall" && open.arguments !== undefined) {
|
|
773
|
+
open.block.arguments = open.arguments.append(event.delta.partial_json);
|
|
557
774
|
stream.push({
|
|
558
775
|
type: "toolcall_delta",
|
|
559
|
-
contentIndex:
|
|
776
|
+
contentIndex: open.contentIndex,
|
|
560
777
|
delta: event.delta.partial_json,
|
|
561
778
|
partial: output,
|
|
562
779
|
});
|
|
563
780
|
}
|
|
564
781
|
}
|
|
565
782
|
else if (event.delta.type === "signature_delta") {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
block.thinkingSignature = block.thinkingSignature || "";
|
|
570
|
-
block.thinkingSignature += event.delta.signature;
|
|
783
|
+
if (open?.block.type === "thinking") {
|
|
784
|
+
open.block.thinkingSignature = open.block.thinkingSignature || "";
|
|
785
|
+
open.block.thinkingSignature += event.delta.signature;
|
|
571
786
|
}
|
|
572
787
|
}
|
|
573
788
|
}
|
|
574
789
|
else if (event.type === "content_block_stop") {
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
if (
|
|
578
|
-
|
|
790
|
+
const open = openBlock(event.index);
|
|
791
|
+
openBlocks.delete(event.index);
|
|
792
|
+
if (open) {
|
|
793
|
+
const { block, contentIndex: index } = open;
|
|
579
794
|
if (block.type === "text") {
|
|
580
795
|
stream.push({
|
|
581
796
|
type: "text_end",
|
|
@@ -592,11 +807,8 @@ export const stream = (model, context, options) => {
|
|
|
592
807
|
partial: output,
|
|
593
808
|
});
|
|
594
809
|
}
|
|
595
|
-
else if (block.type === "toolCall") {
|
|
596
|
-
block.arguments
|
|
597
|
-
// Finalize in-place and strip the scratch buffer so replay only
|
|
598
|
-
// carries parsed arguments.
|
|
599
|
-
delete block.partialJson;
|
|
810
|
+
else if (block.type === "toolCall" && open.arguments !== undefined) {
|
|
811
|
+
Object.assign(block, open.arguments.finish());
|
|
600
812
|
stream.push({
|
|
601
813
|
type: "toolcall_end",
|
|
602
814
|
contentIndex: index,
|
|
@@ -648,6 +860,11 @@ export const stream = (model, context, options) => {
|
|
|
648
860
|
output.usage.totalTokens =
|
|
649
861
|
output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
|
|
650
862
|
calculateCost(usageModel, output.usage);
|
|
863
|
+
if (isOpenRouterModel(model) && event.usage) {
|
|
864
|
+
// OpenRouter adds billing fields to streamed message_delta usage at runtime,
|
|
865
|
+
// although its OpenAPI schema and the Anthropic SDK do not type those fields.
|
|
866
|
+
applyOpenRouterReportedCost(output.usage, event.usage);
|
|
867
|
+
}
|
|
651
868
|
}
|
|
652
869
|
}
|
|
653
870
|
if (options?.signal?.aborted) {
|
|
@@ -659,6 +876,8 @@ export const stream = (model, context, options) => {
|
|
|
659
876
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
660
877
|
throw new Error(output.errorMessage || "An unknown error occurred");
|
|
661
878
|
}
|
|
879
|
+
if (thread)
|
|
880
|
+
recordThreadReply(model, thread, params, output, isOAuth, compat.allowEmptySignature);
|
|
662
881
|
if (inputTransformations && inputTransformations.length > 0) {
|
|
663
882
|
appendAssistantMessageDiagnostic(output, {
|
|
664
883
|
type: "anthropic_input_transformations",
|
|
@@ -676,13 +895,8 @@ export const stream = (model, context, options) => {
|
|
|
676
895
|
stream.end();
|
|
677
896
|
}
|
|
678
897
|
catch (error) {
|
|
679
|
-
for (const block of output.content) {
|
|
680
|
-
delete block.index;
|
|
681
|
-
// partialJson is only a streaming scratch buffer; never persist it.
|
|
682
|
-
delete block.partialJson;
|
|
683
|
-
}
|
|
684
898
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
685
|
-
output.errorMessage = error instanceof Error ? error
|
|
899
|
+
output.errorMessage = error instanceof Error ? withCauseDetail(error) : JSON.stringify(error);
|
|
686
900
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
687
901
|
stream.end();
|
|
688
902
|
}
|
|
@@ -748,7 +962,7 @@ export const streamSimple = (model, context, options) => {
|
|
|
748
962
|
function isOAuthToken(apiKey) {
|
|
749
963
|
return apiKey.includes("sk-ant-oat");
|
|
750
964
|
}
|
|
751
|
-
function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sessionId, federation) {
|
|
965
|
+
function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sessionId, federation, identity) {
|
|
752
966
|
// Copilot: Bearer auth.
|
|
753
967
|
if (model.provider === "github-copilot") {
|
|
754
968
|
const client = new PiAnthropic({
|
|
@@ -757,14 +971,14 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
|
|
|
757
971
|
baseURL: model.baseUrl,
|
|
758
972
|
dangerouslyAllowBrowser: true,
|
|
759
973
|
fetch,
|
|
760
|
-
defaultHeaders:
|
|
974
|
+
defaultHeaders: mergeHeaders(getIdentityHeaders(model, identity), {
|
|
761
975
|
accept: "application/json",
|
|
762
976
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
763
977
|
}, model.headers, dynamicHeaders, optionsHeaders),
|
|
764
978
|
});
|
|
765
979
|
return { client, isOAuthToken: false };
|
|
766
980
|
}
|
|
767
|
-
// OAuth: Bearer auth, Claude Code identity headers
|
|
981
|
+
// OAuth: Bearer auth, Claude Code identity headers instead of the client identity.
|
|
768
982
|
if (apiKey && isOAuthToken(apiKey)) {
|
|
769
983
|
const client = new PiAnthropic({
|
|
770
984
|
apiKey: null,
|
|
@@ -772,7 +986,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
|
|
|
772
986
|
baseURL: model.baseUrl,
|
|
773
987
|
dangerouslyAllowBrowser: true,
|
|
774
988
|
fetch,
|
|
775
|
-
defaultHeaders:
|
|
989
|
+
defaultHeaders: mergeHeaders({
|
|
776
990
|
accept: "application/json",
|
|
777
991
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
778
992
|
"user-agent": `claude-cli/${claudeCodeVersion}`,
|
|
@@ -788,7 +1002,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
|
|
|
788
1002
|
const header = compat.sessionAffinityFormat === "openrouter" ? "x-session-id" : "x-session-affinity";
|
|
789
1003
|
sessionAffinityHeaders[header] = sessionId;
|
|
790
1004
|
}
|
|
791
|
-
const defaultHeaders =
|
|
1005
|
+
const defaultHeaders = mergeHeaders(getIdentityHeaders(model, identity), {
|
|
792
1006
|
accept: "application/json",
|
|
793
1007
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
794
1008
|
}, sessionAffinityHeaders, model.headers, optionsHeaders);
|
|
@@ -817,7 +1031,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
|
|
|
817
1031
|
});
|
|
818
1032
|
return { client, isOAuthToken: false };
|
|
819
1033
|
}
|
|
820
|
-
function getBetaFeatures(model, context, isOAuthToken, nativeToolChanges,
|
|
1034
|
+
function getBetaFeatures(model, context, options, { isOAuthToken, nativeToolChanges, messageThreads }) {
|
|
821
1035
|
let configuredFeatures;
|
|
822
1036
|
for (const headers of [model.headers, options?.headers]) {
|
|
823
1037
|
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
@@ -853,9 +1067,11 @@ function getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, option
|
|
|
853
1067
|
}
|
|
854
1068
|
if (nativeToolChanges)
|
|
855
1069
|
features.push(INLINE_TOOLS_BETA);
|
|
1070
|
+
if (messageThreads)
|
|
1071
|
+
features.push(MESSAGE_THREADS_BETA);
|
|
856
1072
|
return [...new Set(features)];
|
|
857
1073
|
}
|
|
858
|
-
function buildParams(model, context, isOAuthToken, options) {
|
|
1074
|
+
function buildParams(model, context, isOAuthToken, options, flags) {
|
|
859
1075
|
const { cacheControl } = getCacheControl(model, options?.cacheRetention, options?.env);
|
|
860
1076
|
const compat = getAnthropicCompat(model);
|
|
861
1077
|
const initialSystemMessage = getInitialSystemMessage(context.messages);
|
|
@@ -866,18 +1082,29 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
866
1082
|
// by value in a `tool_addition` block, which also expresses same-name redefinitions.
|
|
867
1083
|
// Anthropic rejects a tool list where every tool is deferred, so there must be an initial
|
|
868
1084
|
// active tool to anchor the placeholder. Otherwise the current tool list is sent.
|
|
1085
|
+
let hasPreferredStrictTools = false;
|
|
1086
|
+
const convertToolDefinitions = (tools, toolCacheControl) => {
|
|
1087
|
+
const definitions = convertTools(tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl, flags.disablePreferredStrictTools);
|
|
1088
|
+
if (definitions.some((definition, index) => definition.strict === true && prefersJsonSchemaStrictSampling(tools[index]))) {
|
|
1089
|
+
hasPreferredStrictTools = true;
|
|
1090
|
+
}
|
|
1091
|
+
return definitions;
|
|
1092
|
+
};
|
|
869
1093
|
const initialTools = initialSystemMessage?.toolsAdded ?? [];
|
|
870
1094
|
const nativeToolChanges = compat.supportsMidConvoSystemMessages && compat.supportsMidConvoToolChanges && initialTools.length > 0;
|
|
871
|
-
const
|
|
872
|
-
|
|
873
|
-
|
|
1095
|
+
const thinkingDisabled = model.reasoning && options?.thinkingEnabled === false && model.thinkingLevelMap?.off !== null;
|
|
1096
|
+
// Effort cannot change between messages while thinking is disabled, so "off" sends no effort markers.
|
|
1097
|
+
const effortMessages = model.compat?.supportsMidConvoEffort === true && !thinkingDisabled;
|
|
1098
|
+
const converted = convertMessages(conversationMessages, isOAuthToken, cacheControl, compat.allowEmptySignature, effortMessages ? model.provider : undefined, nativeToolChanges ? convertToolDefinitions : undefined);
|
|
874
1099
|
const activeEffort = options?.effort ?? "high";
|
|
875
|
-
const betaFeatures = getBetaFeatures(model, context,
|
|
1100
|
+
const betaFeatures = getBetaFeatures(model, context, options, {
|
|
1101
|
+
isOAuthToken,
|
|
1102
|
+
nativeToolChanges,
|
|
1103
|
+
messageThreads: flags.messageThreads,
|
|
1104
|
+
});
|
|
876
1105
|
const params = {
|
|
877
1106
|
model: model.id,
|
|
878
|
-
messages:
|
|
879
|
-
? insertThinkingLevelMessages(converted, activeEffort)
|
|
880
|
-
: converted.messages,
|
|
1107
|
+
messages: effortMessages ? insertThinkingLevelMessages(converted, activeEffort) : converted.messages,
|
|
881
1108
|
max_tokens: options?.maxTokens ?? model.maxTokens,
|
|
882
1109
|
stream: true,
|
|
883
1110
|
...(betaFeatures.length > 0 ? { betas: betaFeatures } : {}),
|
|
@@ -922,50 +1149,50 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
922
1149
|
// placeholder. The list never changes afterwards: later tools are defined by value in
|
|
923
1150
|
// `tool_addition` blocks and withdrawn by `tool_removal`, so the cached prefix survives
|
|
924
1151
|
// every tool change.
|
|
925
|
-
params.tools = [
|
|
926
|
-
...convertTools(initialTools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl),
|
|
927
|
-
DEFERRED_TOOL_PLACEHOLDER,
|
|
928
|
-
];
|
|
1152
|
+
params.tools = [...convertToolDefinitions(initialTools, toolCacheControl), DEFERRED_TOOL_PLACEHOLDER];
|
|
929
1153
|
}
|
|
930
1154
|
else {
|
|
931
1155
|
const tools = getCurrentTools(context.messages);
|
|
932
1156
|
if (tools.length > 0) {
|
|
933
|
-
params.tools =
|
|
1157
|
+
params.tools = convertToolDefinitions(tools, toolCacheControl);
|
|
934
1158
|
}
|
|
935
1159
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1160
|
+
if (thinkingDisabled) {
|
|
1161
|
+
// No effort is sent, so the model default applies. Anthropic rejects `block_binding`
|
|
1162
|
+
// with disabled thinking.
|
|
1163
|
+
params.thinking = { type: "disabled" };
|
|
1164
|
+
}
|
|
1165
|
+
else if (effortMessages) {
|
|
1166
|
+
// Managed effort models use adaptive thinking so prefix mismatches can
|
|
1167
|
+
// be dropped instead of surfacing as persistent 400 responses.
|
|
939
1168
|
params.thinking = {
|
|
940
1169
|
type: "adaptive",
|
|
941
1170
|
display: options?.thinkingDisplay ?? "summarized",
|
|
942
1171
|
block_binding: { prefix_mismatch_behavior: "drop_block" },
|
|
943
1172
|
};
|
|
944
|
-
|
|
1173
|
+
// Effort markers set the level for every reply. The top-level effort is part of the cached
|
|
1174
|
+
// prompt, so it stays fixed. It is "low" because organizations can cap effort and reject
|
|
1175
|
+
// requests containing any level above the cap.
|
|
1176
|
+
params.output_config = { effort: "low" };
|
|
945
1177
|
}
|
|
946
|
-
else if (model.reasoning) {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
params.output_config = { effort: options.effort };
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
else {
|
|
959
|
-
// Budget-based thinking for older models
|
|
960
|
-
params.thinking = {
|
|
961
|
-
type: "enabled",
|
|
962
|
-
budget_tokens: options.thinkingBudgetTokens || 1024,
|
|
963
|
-
display,
|
|
964
|
-
};
|
|
1178
|
+
else if (model.reasoning && options?.thinkingEnabled) {
|
|
1179
|
+
// Default to "summarized" so Opus 4.7 and Mythos Preview behave like
|
|
1180
|
+
// older Claude 4 models (whose API default is also "summarized").
|
|
1181
|
+
const display = options.thinkingDisplay ?? "summarized";
|
|
1182
|
+
if (model.compat?.forceAdaptiveThinking === true) {
|
|
1183
|
+
// Adaptive thinking: Claude decides when and how much to think.
|
|
1184
|
+
params.thinking = { type: "adaptive", display };
|
|
1185
|
+
if (options.effort) {
|
|
1186
|
+
params.output_config = { effort: options.effort };
|
|
965
1187
|
}
|
|
966
1188
|
}
|
|
967
|
-
else
|
|
968
|
-
|
|
1189
|
+
else {
|
|
1190
|
+
// Budget-based thinking for older models
|
|
1191
|
+
params.thinking = {
|
|
1192
|
+
type: "enabled",
|
|
1193
|
+
budget_tokens: options.thinkingBudgetTokens || 1024,
|
|
1194
|
+
display,
|
|
1195
|
+
};
|
|
969
1196
|
}
|
|
970
1197
|
}
|
|
971
1198
|
if (options?.metadata) {
|
|
@@ -986,17 +1213,17 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
986
1213
|
if (allowedFallbackModels && allowedFallbackModels.length > 0) {
|
|
987
1214
|
params.fallbacks = allowedFallbackModels.map((fallback) => ({ model: fallback.model }));
|
|
988
1215
|
}
|
|
989
|
-
return params;
|
|
1216
|
+
return { params, hasPreferredStrictTools, providerThinkingLevel: effortMessages ? activeEffort : undefined };
|
|
990
1217
|
}
|
|
991
1218
|
// Normalize tool call IDs to match Anthropic's required pattern and length
|
|
992
1219
|
function normalizeToolCallId(id) {
|
|
993
1220
|
return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
994
1221
|
}
|
|
995
|
-
function convertToolResult(msg) {
|
|
1222
|
+
function convertToolResult(msg, content = msg.content) {
|
|
996
1223
|
return {
|
|
997
1224
|
type: "tool_result",
|
|
998
1225
|
tool_use_id: msg.toolCallId,
|
|
999
|
-
content: convertContentBlocks(
|
|
1226
|
+
content: convertContentBlocks(content),
|
|
1000
1227
|
is_error: msg.isError,
|
|
1001
1228
|
};
|
|
1002
1229
|
}
|
|
@@ -1006,7 +1233,9 @@ convertToolDefinitions) {
|
|
|
1006
1233
|
const params = [];
|
|
1007
1234
|
const assistantLevels = new Map();
|
|
1008
1235
|
// Later system messages are held back and emitted directly before the next assistant
|
|
1009
|
-
// message (or at the end of the transcript).
|
|
1236
|
+
// message that is sent (or at the end of the transcript). Assistant messages that convert to
|
|
1237
|
+
// no blocks are dropped, so holding past them keeps an update from landing before a user
|
|
1238
|
+
// message (#11091). Anthropic requires `tool_result` blocks to
|
|
1010
1239
|
// immediately follow their `tool_use`, so a system message between them is rejected; this
|
|
1011
1240
|
// also mirrors where the managed-effort system messages are inserted. As a result an
|
|
1012
1241
|
// update placed before a user message in the transcript lands after it on the wire.
|
|
@@ -1061,14 +1290,7 @@ convertToolDefinitions) {
|
|
|
1061
1290
|
};
|
|
1062
1291
|
}
|
|
1063
1292
|
else {
|
|
1064
|
-
return
|
|
1065
|
-
type: "image",
|
|
1066
|
-
source: {
|
|
1067
|
-
type: "base64",
|
|
1068
|
-
media_type: item.mimeType,
|
|
1069
|
-
data: item.data,
|
|
1070
|
-
},
|
|
1071
|
-
};
|
|
1293
|
+
return convertImageBlock(item);
|
|
1072
1294
|
}
|
|
1073
1295
|
});
|
|
1074
1296
|
const filteredBlocks = blocks.filter((b) => {
|
|
@@ -1086,7 +1308,6 @@ convertToolDefinitions) {
|
|
|
1086
1308
|
}
|
|
1087
1309
|
}
|
|
1088
1310
|
else if (msg.role === "assistant") {
|
|
1089
|
-
flushPendingSystemMessages();
|
|
1090
1311
|
const blocks = [];
|
|
1091
1312
|
for (const block of msg.content) {
|
|
1092
1313
|
if (block.type === "text") {
|
|
@@ -1144,6 +1365,7 @@ convertToolDefinitions) {
|
|
|
1144
1365
|
}
|
|
1145
1366
|
if (blocks.length === 0)
|
|
1146
1367
|
continue;
|
|
1368
|
+
flushPendingSystemMessages();
|
|
1147
1369
|
const messageIndex = params.length;
|
|
1148
1370
|
params.push({
|
|
1149
1371
|
role: "assistant",
|
|
@@ -1159,16 +1381,24 @@ convertToolDefinitions) {
|
|
|
1159
1381
|
else if (msg.role === "toolResult") {
|
|
1160
1382
|
// Collect all consecutive toolResult messages, needed for z.ai Anthropic endpoint.
|
|
1161
1383
|
const toolResults = [];
|
|
1384
|
+
// Anthropic rejects error tool results that contain anything but text, so their images follow
|
|
1385
|
+
// all tool results of the turn, which must come first in the user message (#10794).
|
|
1386
|
+
const errorImages = [];
|
|
1162
1387
|
let j = i;
|
|
1163
1388
|
while (j < transformedMessages.length && transformedMessages[j].role === "toolResult") {
|
|
1164
|
-
|
|
1389
|
+
const toolMsg = transformedMessages[j];
|
|
1390
|
+
const split = splitErrorToolResultImages(toolMsg);
|
|
1391
|
+
toolResults.push(convertToolResult(toolMsg, split?.content));
|
|
1392
|
+
if (split) {
|
|
1393
|
+
errorImages.push({ type: "text", text: split.caption }, ...split.images.map(convertImageBlock));
|
|
1394
|
+
}
|
|
1165
1395
|
j++;
|
|
1166
1396
|
}
|
|
1167
1397
|
// Skip the messages we've already processed.
|
|
1168
1398
|
i = j - 1;
|
|
1169
1399
|
params.push({
|
|
1170
1400
|
role: "user",
|
|
1171
|
-
content: toolResults,
|
|
1401
|
+
content: [...toolResults, ...errorImages],
|
|
1172
1402
|
});
|
|
1173
1403
|
}
|
|
1174
1404
|
}
|
|
@@ -1255,17 +1485,23 @@ const isAnthropicStrictUnsupportedKeyword = (key, value) => {
|
|
|
1255
1485
|
return typeof value !== "string" || !ANTHROPIC_STRICT_STRING_FORMATS.has(value);
|
|
1256
1486
|
return false;
|
|
1257
1487
|
};
|
|
1258
|
-
function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, supportsStrictTools, cacheControl) {
|
|
1488
|
+
function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, supportsStrictTools, cacheControl, disablePreferredStrictTools = false) {
|
|
1259
1489
|
if (!tools)
|
|
1260
1490
|
return [];
|
|
1261
1491
|
return tools.map((tool, index) => {
|
|
1262
|
-
const strict =
|
|
1492
|
+
const strict = disablePreferredStrictTools && prefersJsonSchemaStrictSampling(tool)
|
|
1493
|
+
? undefined
|
|
1494
|
+
: resolveJsonSchemaStrictSampling(tool, supportsStrictTools, isAnthropicStrictUnsupportedKeyword);
|
|
1263
1495
|
const parameters = getJsonSchemaToolParameters(tool, strict);
|
|
1264
1496
|
const schema = parameters;
|
|
1497
|
+
// Keep shared definitions so property $refs still resolve. Other root keywords stay out:
|
|
1498
|
+
// Anthropic rejects root anyOf/oneOf/allOf with a 400.
|
|
1265
1499
|
const legacyInputSchema = {
|
|
1266
1500
|
type: "object",
|
|
1267
1501
|
properties: schema.properties ?? {},
|
|
1268
1502
|
required: schema.required ?? [],
|
|
1503
|
+
...(schema.$defs === undefined ? {} : { $defs: schema.$defs }),
|
|
1504
|
+
...(schema.definitions === undefined ? {} : { definitions: schema.definitions }),
|
|
1269
1505
|
};
|
|
1270
1506
|
const inputSchema = strict === true
|
|
1271
1507
|
? {
|