@draht/ai 2026.10.31-1 → 2026.11.5-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +279 -86
- 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 +1 -7
- 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 +125 -103
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
- package/dist/api/cloudflare-workers-ai-system-one.js +8 -0
- 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 +0 -6
- 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 +57 -2
- 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 +0 -6
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/lazy.d.ts +1 -1
- package/dist/api/lazy.d.ts.map +1 -1
- package/dist/api/lazy.js +9 -8
- package/dist/api/lazy.js.map +1 -1
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +17 -44
- 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 +7 -8
- 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 +52 -57
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +58 -57
- 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 +4 -8
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/openrouter-images.js +1 -0
- 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 +24 -9
- 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 +16 -2
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/system-one-shared.d.ts +29 -2
- package/dist/api/system-one-shared.d.ts.map +1 -1
- package/dist/api/system-one-shared.js +55 -3
- package/dist/api/system-one-shared.js.map +1 -1
- package/dist/api/typesafe-system-one.d.ts.map +1 -1
- package/dist/api/typesafe-system-one.js +10 -0
- package/dist/api/typesafe-system-one.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/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/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 +4 -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 +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- 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 +9 -8
- 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 +447 -444
- package/dist/providers/compat-schema.d.ts.map +1 -1
- package/dist/providers/compat-schema.js +173 -112
- package/dist/providers/compat-schema.js.map +1 -1
- 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/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/huggingface.json +1 -1
- package/dist/providers/data/kimi-coding.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 +7 -2
- 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.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 +65 -65
- package/dist/providers/model-schema.d.ts.map +1 -1
- package/dist/providers/model-schema.js +41 -30
- package/dist/providers/model-schema.js.map +1 -1
- 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.models.js +1 -1
- package/dist/providers/openai-codex.models.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.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.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.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.map +1 -1
- package/dist/telemetry.js +2 -1
- package/dist/telemetry.js.map +1 -1
- package/dist/types.d.ts +24 -5
- 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/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.map +1 -1
- package/dist/utils/overflow.js +2 -0
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +3 -0
- 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 +1 -1
- package/dist/utils/transcript.js +1 -1
- 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 -277
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/version.d.ts +1 -1
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +1 -1
- package/dist/utils/version.js.map +1 -1
- package/package.json +8 -7
- package/dist/utils/typebox-helpers.d.ts.map +0 -1
- package/dist/utils/typebox-helpers.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAqBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAIb,MAAM,aAAa,CAAC;AAiKrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA6QhE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAoRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAgXzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
|
|
@@ -1,17 +1,22 @@
|
|
|
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
10
|
import { getIdentityHeaders } from "../utils/identity.js";
|
|
8
|
-
import { parseJsonWithRepair
|
|
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
22
|
import { splitErrorToolResultImages, transformMessages } from "./transform-messages.js";
|
|
@@ -120,6 +125,7 @@ const SERVER_SIDE_FALLBACK_BETA = "server-side-fallback-2026-07-01";
|
|
|
120
125
|
const MID_CONVERSATION_OUTPUT_CONFIG_BETA = "mid-conversation-output-config-2026-07-01";
|
|
121
126
|
const THINKING_BINDING_CONTROLS_BETA = "thinking-binding-controls-2026-08-01";
|
|
122
127
|
const INLINE_TOOLS_BETA = "inline-tools-2026-09-15";
|
|
128
|
+
const MESSAGE_THREADS_BETA = "message-threads-2026-08-12";
|
|
123
129
|
/**
|
|
124
130
|
* Stable deferred tool declared whenever native tool changes are in use. Anthropic adds
|
|
125
131
|
* hidden prompt scaffolding for mid-conversation tool changes; declaring this placeholder
|
|
@@ -149,8 +155,178 @@ function getAnthropicCompat(model) {
|
|
|
149
155
|
supportsStrictTools: model.compat?.supportsStrictTools ?? false,
|
|
150
156
|
supportsMidConvoSystemMessages: model.compat?.supportsMidConvoSystemMessages ?? false,
|
|
151
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")),
|
|
152
163
|
};
|
|
153
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 draht'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: draht 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
|
+
}
|
|
154
330
|
function mergeHeaders(...headerSources) {
|
|
155
331
|
const merged = {};
|
|
156
332
|
for (const headers of headerSources) {
|
|
@@ -375,7 +551,8 @@ 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
558
|
const output = {
|
|
@@ -400,6 +577,12 @@ export const stream = (model, context, options) => {
|
|
|
400
577
|
let isOAuth;
|
|
401
578
|
let usageModel = model;
|
|
402
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;
|
|
403
586
|
if (options?.client) {
|
|
404
587
|
client = options.client;
|
|
405
588
|
isOAuth = false;
|
|
@@ -417,8 +600,6 @@ export const stream = (model, context, options) => {
|
|
|
417
600
|
hasImages,
|
|
418
601
|
});
|
|
419
602
|
}
|
|
420
|
-
const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
|
|
421
|
-
const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
|
|
422
603
|
const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation, options?.identity);
|
|
423
604
|
client = created.client;
|
|
424
605
|
isOAuth = created.isOAuthToken;
|
|
@@ -428,24 +609,38 @@ export const stream = (model, context, options) => {
|
|
|
428
609
|
...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
|
|
429
610
|
maxRetries: 0,
|
|
430
611
|
};
|
|
612
|
+
const send = (body) => retryProviderRequest(() => client.beta.messages.create(body, requestOptions).asResponse(), {
|
|
613
|
+
maxRetries: options?.maxRetries,
|
|
614
|
+
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
615
|
+
signal: options?.signal,
|
|
616
|
+
});
|
|
431
617
|
let disablePreferredStrictTools = false;
|
|
432
618
|
let response;
|
|
619
|
+
let params;
|
|
620
|
+
// The thread request that produced the response; undefined when no `thread` was sent.
|
|
621
|
+
let thread;
|
|
433
622
|
for (;;) {
|
|
434
623
|
options?.signal?.throwIfAborted();
|
|
435
|
-
const built = buildParams(model, normalizedContext, isOAuth, options,
|
|
624
|
+
const built = buildParams(model, normalizedContext, isOAuth, options, {
|
|
625
|
+
disablePreferredStrictTools,
|
|
626
|
+
messageThreads: threadSessionId !== undefined,
|
|
627
|
+
});
|
|
436
628
|
if (built.providerThinkingLevel)
|
|
437
629
|
output.providerThinkingLevel = built.providerThinkingLevel;
|
|
438
|
-
|
|
630
|
+
params = built.params;
|
|
439
631
|
const nextParams = await options?.onPayload?.(params, model);
|
|
440
632
|
if (nextParams !== undefined) {
|
|
441
633
|
params = { ...nextParams, stream: true };
|
|
442
634
|
}
|
|
635
|
+
const prepared = threadSessionId !== undefined && options?.disableContinuation !== true
|
|
636
|
+
? prepareThreadRequest(model, threadSessionId, params)
|
|
637
|
+
: undefined;
|
|
443
638
|
try {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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;
|
|
449
644
|
break;
|
|
450
645
|
}
|
|
451
646
|
catch (error) {
|
|
@@ -456,11 +651,7 @@ export const stream = (model, context, options) => {
|
|
|
456
651
|
!(error instanceof Anthropic.APIError) ||
|
|
457
652
|
error.status !== 400 ||
|
|
458
653
|
error.type !== "invalid_request_error" ||
|
|
459
|
-
!
|
|
460
|
-
"The compiled grammar is too large",
|
|
461
|
-
"Too many strict tools (",
|
|
462
|
-
"Schemas contains too many parameters with union types (",
|
|
463
|
-
].some((message) => error.message.includes(message))) {
|
|
654
|
+
!isStrictToolLimitErrorMessage(error.message)) {
|
|
464
655
|
throw error;
|
|
465
656
|
}
|
|
466
657
|
disablePreferredStrictTools = true;
|
|
@@ -474,7 +665,12 @@ export const stream = (model, context, options) => {
|
|
|
474
665
|
}
|
|
475
666
|
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
476
667
|
stream.push({ type: "start", partial: output });
|
|
477
|
-
|
|
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
|
+
};
|
|
478
674
|
for await (const event of iterateAnthropicEvents(response, options?.signal)) {
|
|
479
675
|
await options?.onProviderStreamEvent?.(event, model);
|
|
480
676
|
if (event.type === "message_start") {
|
|
@@ -509,105 +705,92 @@ export const stream = (model, context, options) => {
|
|
|
509
705
|
continue;
|
|
510
706
|
}
|
|
511
707
|
if (event.content_block.type === "text") {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
text: event.content_block.text ?? "",
|
|
515
|
-
index: event.index,
|
|
516
|
-
};
|
|
517
|
-
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 });
|
|
518
710
|
stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
|
|
519
711
|
}
|
|
520
712
|
else if (event.content_block.type === "thinking") {
|
|
521
|
-
|
|
713
|
+
output.content.push({
|
|
522
714
|
type: "thinking",
|
|
523
715
|
thinking: event.content_block.thinking ?? "",
|
|
524
716
|
thinkingSignature: event.content_block.signature ?? "",
|
|
525
|
-
|
|
526
|
-
};
|
|
527
|
-
output.content.push(block);
|
|
717
|
+
});
|
|
718
|
+
openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
|
|
528
719
|
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
529
720
|
}
|
|
530
721
|
else if (event.content_block.type === "redacted_thinking") {
|
|
531
|
-
|
|
722
|
+
output.content.push({
|
|
532
723
|
type: "thinking",
|
|
533
724
|
thinking: "[Reasoning redacted]",
|
|
534
725
|
thinkingSignature: event.content_block.data,
|
|
535
726
|
redacted: true,
|
|
536
|
-
|
|
537
|
-
};
|
|
538
|
-
output.content.push(block);
|
|
727
|
+
});
|
|
728
|
+
openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
|
|
539
729
|
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
540
730
|
}
|
|
541
731
|
else if (event.content_block.type === "tool_use") {
|
|
542
|
-
|
|
732
|
+
output.content.push({
|
|
543
733
|
type: "toolCall",
|
|
544
734
|
id: event.content_block.id,
|
|
545
735
|
name: isOAuth
|
|
546
736
|
? fromClaudeCodeName(event.content_block.name, currentTools)
|
|
547
737
|
: event.content_block.name,
|
|
548
738
|
arguments: event.content_block.input ?? {},
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
739
|
+
});
|
|
740
|
+
openBlocks.set(event.index, {
|
|
741
|
+
contentIndex: output.content.length - 1,
|
|
742
|
+
arguments: new ToolCallArgumentsStream(),
|
|
743
|
+
});
|
|
553
744
|
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
|
|
554
745
|
}
|
|
555
746
|
}
|
|
556
747
|
else if (event.type === "content_block_delta") {
|
|
748
|
+
const open = openBlock(event.index);
|
|
557
749
|
if (event.delta.type === "text_delta") {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (block && block.type === "text") {
|
|
561
|
-
block.text += event.delta.text;
|
|
750
|
+
if (open?.block.type === "text") {
|
|
751
|
+
open.block.text += event.delta.text;
|
|
562
752
|
stream.push({
|
|
563
753
|
type: "text_delta",
|
|
564
|
-
contentIndex:
|
|
754
|
+
contentIndex: open.contentIndex,
|
|
565
755
|
delta: event.delta.text,
|
|
566
756
|
partial: output,
|
|
567
757
|
});
|
|
568
758
|
}
|
|
569
759
|
}
|
|
570
760
|
else if (event.delta.type === "thinking_delta") {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if (block && block.type === "thinking") {
|
|
574
|
-
block.thinking += event.delta.thinking;
|
|
761
|
+
if (open?.block.type === "thinking") {
|
|
762
|
+
open.block.thinking += event.delta.thinking;
|
|
575
763
|
stream.push({
|
|
576
764
|
type: "thinking_delta",
|
|
577
|
-
contentIndex:
|
|
765
|
+
contentIndex: open.contentIndex,
|
|
578
766
|
delta: event.delta.thinking,
|
|
579
767
|
partial: output,
|
|
580
768
|
});
|
|
581
769
|
}
|
|
582
770
|
}
|
|
583
771
|
else if (event.delta.type === "input_json_delta") {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (block && block.type === "toolCall") {
|
|
587
|
-
block.partialJson += event.delta.partial_json;
|
|
588
|
-
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);
|
|
589
774
|
stream.push({
|
|
590
775
|
type: "toolcall_delta",
|
|
591
|
-
contentIndex:
|
|
776
|
+
contentIndex: open.contentIndex,
|
|
592
777
|
delta: event.delta.partial_json,
|
|
593
778
|
partial: output,
|
|
594
779
|
});
|
|
595
780
|
}
|
|
596
781
|
}
|
|
597
782
|
else if (event.delta.type === "signature_delta") {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
block.thinkingSignature = block.thinkingSignature || "";
|
|
602
|
-
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;
|
|
603
786
|
}
|
|
604
787
|
}
|
|
605
788
|
}
|
|
606
789
|
else if (event.type === "content_block_stop") {
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
if (
|
|
610
|
-
|
|
790
|
+
const open = openBlock(event.index);
|
|
791
|
+
openBlocks.delete(event.index);
|
|
792
|
+
if (open) {
|
|
793
|
+
const { block, contentIndex: index } = open;
|
|
611
794
|
if (block.type === "text") {
|
|
612
795
|
stream.push({
|
|
613
796
|
type: "text_end",
|
|
@@ -624,11 +807,8 @@ export const stream = (model, context, options) => {
|
|
|
624
807
|
partial: output,
|
|
625
808
|
});
|
|
626
809
|
}
|
|
627
|
-
else if (block.type === "toolCall") {
|
|
628
|
-
Object.assign(block,
|
|
629
|
-
// Finalize in-place and strip the scratch buffer so replay only
|
|
630
|
-
// carries parsed arguments.
|
|
631
|
-
delete block.partialJson;
|
|
810
|
+
else if (block.type === "toolCall" && open.arguments !== undefined) {
|
|
811
|
+
Object.assign(block, open.arguments.finish());
|
|
632
812
|
stream.push({
|
|
633
813
|
type: "toolcall_end",
|
|
634
814
|
contentIndex: index,
|
|
@@ -680,6 +860,11 @@ export const stream = (model, context, options) => {
|
|
|
680
860
|
output.usage.totalTokens =
|
|
681
861
|
output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
|
|
682
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
|
+
}
|
|
683
868
|
}
|
|
684
869
|
}
|
|
685
870
|
if (options?.signal?.aborted) {
|
|
@@ -691,6 +876,8 @@ export const stream = (model, context, options) => {
|
|
|
691
876
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
692
877
|
throw new Error(output.errorMessage || "An unknown error occurred");
|
|
693
878
|
}
|
|
879
|
+
if (thread)
|
|
880
|
+
recordThreadReply(model, thread, params, output, isOAuth, compat.allowEmptySignature);
|
|
694
881
|
if (inputTransformations && inputTransformations.length > 0) {
|
|
695
882
|
appendAssistantMessageDiagnostic(output, {
|
|
696
883
|
type: "anthropic_input_transformations",
|
|
@@ -708,13 +895,8 @@ export const stream = (model, context, options) => {
|
|
|
708
895
|
stream.end();
|
|
709
896
|
}
|
|
710
897
|
catch (error) {
|
|
711
|
-
for (const block of output.content) {
|
|
712
|
-
delete block.index;
|
|
713
|
-
// partialJson is only a streaming scratch buffer; never persist it.
|
|
714
|
-
delete block.partialJson;
|
|
715
|
-
}
|
|
716
898
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
717
|
-
output.errorMessage = error instanceof Error ? error
|
|
899
|
+
output.errorMessage = error instanceof Error ? withCauseDetail(error) : JSON.stringify(error);
|
|
718
900
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
719
901
|
stream.end();
|
|
720
902
|
}
|
|
@@ -849,7 +1031,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
|
|
|
849
1031
|
});
|
|
850
1032
|
return { client, isOAuthToken: false };
|
|
851
1033
|
}
|
|
852
|
-
function getBetaFeatures(model, context, isOAuthToken, nativeToolChanges,
|
|
1034
|
+
function getBetaFeatures(model, context, options, { isOAuthToken, nativeToolChanges, messageThreads }) {
|
|
853
1035
|
let configuredFeatures;
|
|
854
1036
|
for (const headers of [model.headers, options?.headers]) {
|
|
855
1037
|
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
@@ -885,9 +1067,11 @@ function getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, option
|
|
|
885
1067
|
}
|
|
886
1068
|
if (nativeToolChanges)
|
|
887
1069
|
features.push(INLINE_TOOLS_BETA);
|
|
1070
|
+
if (messageThreads)
|
|
1071
|
+
features.push(MESSAGE_THREADS_BETA);
|
|
888
1072
|
return [...new Set(features)];
|
|
889
1073
|
}
|
|
890
|
-
function buildParams(model, context, isOAuthToken, options,
|
|
1074
|
+
function buildParams(model, context, isOAuthToken, options, flags) {
|
|
891
1075
|
const { cacheControl } = getCacheControl(model, options?.cacheRetention, options?.env);
|
|
892
1076
|
const compat = getAnthropicCompat(model);
|
|
893
1077
|
const initialSystemMessage = getInitialSystemMessage(context.messages);
|
|
@@ -900,11 +1084,8 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
|
|
|
900
1084
|
// active tool to anchor the placeholder. Otherwise the current tool list is sent.
|
|
901
1085
|
let hasPreferredStrictTools = false;
|
|
902
1086
|
const convertToolDefinitions = (tools, toolCacheControl) => {
|
|
903
|
-
const definitions = convertTools(tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl, disablePreferredStrictTools);
|
|
904
|
-
if (definitions.some((definition, index) => {
|
|
905
|
-
const config = tools[index].constrainedSampling;
|
|
906
|
-
return definition.strict === true && config && config.type === "json_schema" && config.strict === "prefer";
|
|
907
|
-
})) {
|
|
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]))) {
|
|
908
1089
|
hasPreferredStrictTools = true;
|
|
909
1090
|
}
|
|
910
1091
|
return definitions;
|
|
@@ -916,7 +1097,11 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
|
|
|
916
1097
|
const effortMessages = model.compat?.supportsMidConvoEffort === true && !thinkingDisabled;
|
|
917
1098
|
const converted = convertMessages(conversationMessages, isOAuthToken, cacheControl, compat.allowEmptySignature, effortMessages ? model.provider : undefined, nativeToolChanges ? convertToolDefinitions : undefined);
|
|
918
1099
|
const activeEffort = options?.effort ?? "high";
|
|
919
|
-
const betaFeatures = getBetaFeatures(model, context,
|
|
1100
|
+
const betaFeatures = getBetaFeatures(model, context, options, {
|
|
1101
|
+
isOAuthToken,
|
|
1102
|
+
nativeToolChanges,
|
|
1103
|
+
messageThreads: flags.messageThreads,
|
|
1104
|
+
});
|
|
920
1105
|
const params = {
|
|
921
1106
|
model: model.id,
|
|
922
1107
|
messages: effortMessages ? insertThinkingLevelMessages(converted, activeEffort) : converted.messages,
|
|
@@ -985,7 +1170,10 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
|
|
|
985
1170
|
display: options?.thinkingDisplay ?? "summarized",
|
|
986
1171
|
block_binding: { prefix_mismatch_behavior: "drop_block" },
|
|
987
1172
|
};
|
|
988
|
-
|
|
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" };
|
|
989
1177
|
}
|
|
990
1178
|
else if (model.reasoning && options?.thinkingEnabled) {
|
|
991
1179
|
// Default to "summarized" so Opus 4.7 and Mythos Preview behave like
|
|
@@ -1045,7 +1233,9 @@ convertToolDefinitions) {
|
|
|
1045
1233
|
const params = [];
|
|
1046
1234
|
const assistantLevels = new Map();
|
|
1047
1235
|
// Later system messages are held back and emitted directly before the next assistant
|
|
1048
|
-
// 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
|
|
1049
1239
|
// immediately follow their `tool_use`, so a system message between them is rejected; this
|
|
1050
1240
|
// also mirrors where the managed-effort system messages are inserted. As a result an
|
|
1051
1241
|
// update placed before a user message in the transcript lands after it on the wire.
|
|
@@ -1118,7 +1308,6 @@ convertToolDefinitions) {
|
|
|
1118
1308
|
}
|
|
1119
1309
|
}
|
|
1120
1310
|
else if (msg.role === "assistant") {
|
|
1121
|
-
flushPendingSystemMessages();
|
|
1122
1311
|
const blocks = [];
|
|
1123
1312
|
for (const block of msg.content) {
|
|
1124
1313
|
if (block.type === "text") {
|
|
@@ -1176,6 +1365,7 @@ convertToolDefinitions) {
|
|
|
1176
1365
|
}
|
|
1177
1366
|
if (blocks.length === 0)
|
|
1178
1367
|
continue;
|
|
1368
|
+
flushPendingSystemMessages();
|
|
1179
1369
|
const messageIndex = params.length;
|
|
1180
1370
|
params.push({
|
|
1181
1371
|
role: "assistant",
|
|
@@ -1299,16 +1489,19 @@ function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, supp
|
|
|
1299
1489
|
if (!tools)
|
|
1300
1490
|
return [];
|
|
1301
1491
|
return tools.map((tool, index) => {
|
|
1302
|
-
const
|
|
1303
|
-
const strict = disablePreferredStrictTools && config && config.type === "json_schema" && config.strict === "prefer"
|
|
1492
|
+
const strict = disablePreferredStrictTools && prefersJsonSchemaStrictSampling(tool)
|
|
1304
1493
|
? undefined
|
|
1305
1494
|
: resolveJsonSchemaStrictSampling(tool, supportsStrictTools, isAnthropicStrictUnsupportedKeyword);
|
|
1306
1495
|
const parameters = getJsonSchemaToolParameters(tool, strict);
|
|
1307
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.
|
|
1308
1499
|
const legacyInputSchema = {
|
|
1309
1500
|
type: "object",
|
|
1310
1501
|
properties: schema.properties ?? {},
|
|
1311
1502
|
required: schema.required ?? [],
|
|
1503
|
+
...(schema.$defs === undefined ? {} : { $defs: schema.$defs }),
|
|
1504
|
+
...(schema.definitions === undefined ? {} : { definitions: schema.definitions }),
|
|
1312
1505
|
};
|
|
1313
1506
|
const inputSchema = strict === true
|
|
1314
1507
|
? {
|