@sayknow-cli/ai 0.3.15 → 0.4.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/package.json +24 -25
- package/src/auth-gateway/server.ts +164 -22
- package/src/auth-storage.ts +76 -43
- package/src/context-cap-policy.ts +59 -0
- package/src/index.ts +2 -0
- package/src/model-cache.ts +10 -0
- package/src/model-manager.ts +30 -17
- package/src/model-thinking.ts +14 -8
- package/src/providers/amazon-bedrock.ts +9 -1
- package/src/providers/anthropic-messages-server.ts +178 -17
- package/src/providers/anthropic.ts +199 -213
- package/src/providers/azure-openai-responses.ts +6 -1
- package/src/providers/google-gemini-cli.ts +26 -13
- package/src/providers/google-shared.ts +7 -1
- package/src/providers/ollama.ts +7 -1
- package/src/providers/openai-chat-server.ts +93 -5
- package/src/providers/openai-codex/response-handler.ts +11 -3
- package/src/providers/openai-codex-responses.ts +107 -20
- package/src/providers/openai-responses-server.ts +111 -36
- package/src/providers/openai-responses-shared.ts +71 -12
- package/src/providers/openai-responses.ts +26 -3
- package/src/providers/pi-native-client.ts +24 -12
- package/src/providers/pi-native-server.ts +275 -21
- package/src/types.ts +53 -4
- package/src/utils/discovery/codex.ts +3 -12
- package/src/utils/event-stream.ts +146 -58
- package/src/utils/fallback-transport.ts +269 -0
- package/src/utils/overflow.ts +72 -31
- package/src/utils/retry.ts +2 -2
- package/src/utils/validation.ts +6 -1
- package/src/utils.ts +120 -12
- package/dist/types/api-registry.d.ts +0 -30
- package/dist/types/auth-broker/client.d.ts +0 -67
- package/dist/types/auth-broker/index.d.ts +0 -5
- package/dist/types/auth-broker/refresher.d.ts +0 -25
- package/dist/types/auth-broker/remote-store.d.ts +0 -99
- package/dist/types/auth-broker/server.d.ts +0 -32
- package/dist/types/auth-broker/types.d.ts +0 -110
- package/dist/types/auth-broker/wire-schemas.d.ts +0 -443
- package/dist/types/auth-gateway/http.d.ts +0 -40
- package/dist/types/auth-gateway/index.d.ts +0 -3
- package/dist/types/auth-gateway/server.d.ts +0 -17
- package/dist/types/auth-gateway/types.d.ts +0 -115
- package/dist/types/auth-storage.d.ts +0 -695
- package/dist/types/cli.d.ts +0 -2
- package/dist/types/index.d.ts +0 -51
- package/dist/types/model-cache.d.ts +0 -17
- package/dist/types/model-manager.d.ts +0 -62
- package/dist/types/model-retirements.d.ts +0 -6
- package/dist/types/model-thinking.d.ts +0 -74
- package/dist/types/models.d.ts +0 -21
- package/dist/types/provider-details.d.ts +0 -24
- package/dist/types/provider-models/bundled-references.d.ts +0 -4
- package/dist/types/provider-models/descriptors.d.ts +0 -48
- package/dist/types/provider-models/google.d.ts +0 -20
- package/dist/types/provider-models/index.d.ts +0 -5
- package/dist/types/provider-models/ollama.d.ts +0 -7
- package/dist/types/provider-models/openai-compat.d.ts +0 -256
- package/dist/types/provider-models/special.d.ts +0 -19
- package/dist/types/providers/amazon-bedrock.d.ts +0 -60
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +0 -450
- package/dist/types/providers/anthropic-messages-server.d.ts +0 -17
- package/dist/types/providers/anthropic.d.ts +0 -208
- package/dist/types/providers/aws-credential-config.d.ts +0 -19
- package/dist/types/providers/aws-credentials.d.ts +0 -43
- package/dist/types/providers/aws-eventstream.d.ts +0 -38
- package/dist/types/providers/aws-sigv4.d.ts +0 -55
- package/dist/types/providers/azure-openai-responses.d.ts +0 -15
- package/dist/types/providers/composer-discipline.d.ts +0 -26
- package/dist/types/providers/cursor/client-version.d.ts +0 -10
- package/dist/types/providers/cursor/gen/agent_pb.d.ts +0 -13022
- package/dist/types/providers/cursor.d.ts +0 -45
- package/dist/types/providers/error-message.d.ts +0 -27
- package/dist/types/providers/github-copilot-headers.d.ts +0 -40
- package/dist/types/providers/gitlab-duo.d.ts +0 -27
- package/dist/types/providers/google-auth.d.ts +0 -24
- package/dist/types/providers/google-gemini-cli.d.ts +0 -75
- package/dist/types/providers/google-gemini-headers.d.ts +0 -43
- package/dist/types/providers/google-shared.d.ts +0 -179
- package/dist/types/providers/google-types.d.ts +0 -138
- package/dist/types/providers/google-vertex.d.ts +0 -7
- package/dist/types/providers/google.d.ts +0 -4
- package/dist/types/providers/grammar.d.ts +0 -1
- package/dist/types/providers/kimi.d.ts +0 -27
- package/dist/types/providers/mock.d.ts +0 -177
- package/dist/types/providers/ollama.d.ts +0 -41
- package/dist/types/providers/openai-anthropic-shim.d.ts +0 -31
- package/dist/types/providers/openai-bounded-rate-limits.d.ts +0 -3
- package/dist/types/providers/openai-chat-server-schema.d.ts +0 -815
- package/dist/types/providers/openai-chat-server.d.ts +0 -16
- package/dist/types/providers/openai-codex/constants.d.ts +0 -26
- package/dist/types/providers/openai-codex/request-transformer.d.ts +0 -50
- package/dist/types/providers/openai-codex/response-handler.d.ts +0 -17
- package/dist/types/providers/openai-codex-responses.d.ts +0 -67
- package/dist/types/providers/openai-completions-compat.d.ts +0 -27
- package/dist/types/providers/openai-completions.d.ts +0 -33
- package/dist/types/providers/openai-request-transform.d.ts +0 -4
- package/dist/types/providers/openai-responses-server-schema.d.ts +0 -392
- package/dist/types/providers/openai-responses-server.d.ts +0 -17
- package/dist/types/providers/openai-responses-shared.d.ts +0 -104
- package/dist/types/providers/openai-responses.d.ts +0 -32
- package/dist/types/providers/pi-native-client.d.ts +0 -13
- package/dist/types/providers/pi-native-server.d.ts +0 -68
- package/dist/types/providers/register-builtins.d.ts +0 -31
- package/dist/types/providers/synthetic.d.ts +0 -26
- package/dist/types/providers/transform-messages.d.ts +0 -14
- package/dist/types/providers/vision-guard.d.ts +0 -8
- package/dist/types/rate-limit-utils.d.ts +0 -19
- package/dist/types/stream.d.ts +0 -43
- package/dist/types/types.d.ts +0 -831
- package/dist/types/usage/claude.d.ts +0 -3
- package/dist/types/usage/gemini.d.ts +0 -2
- package/dist/types/usage/github-copilot.d.ts +0 -7
- package/dist/types/usage/google-antigravity.d.ts +0 -2
- package/dist/types/usage/grok-cli.d.ts +0 -10
- package/dist/types/usage/kimi.d.ts +0 -2
- package/dist/types/usage/minimax-code.d.ts +0 -2
- package/dist/types/usage/openai-codex.d.ts +0 -3
- package/dist/types/usage/shared.d.ts +0 -1
- package/dist/types/usage/zai.d.ts +0 -2
- package/dist/types/usage.d.ts +0 -258
- package/dist/types/utils/abort.d.ts +0 -19
- package/dist/types/utils/anthropic-auth.d.ts +0 -31
- package/dist/types/utils/discovery/antigravity.d.ts +0 -67
- package/dist/types/utils/discovery/codex.d.ts +0 -38
- package/dist/types/utils/discovery/cursor.d.ts +0 -23
- package/dist/types/utils/discovery/gemini.d.ts +0 -25
- package/dist/types/utils/discovery/index.d.ts +0 -4
- package/dist/types/utils/discovery/openai-compatible.d.ts +0 -74
- package/dist/types/utils/event-stream.d.ts +0 -33
- package/dist/types/utils/fireworks-model-id.d.ts +0 -10
- package/dist/types/utils/foundry.d.ts +0 -1
- package/dist/types/utils/h2-fetch.d.ts +0 -22
- package/dist/types/utils/http-inspector.d.ts +0 -35
- package/dist/types/utils/idle-iterator.d.ts +0 -67
- package/dist/types/utils/json-parse.d.ts +0 -18
- package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +0 -18
- package/dist/types/utils/oauth/anthropic.d.ts +0 -22
- package/dist/types/utils/oauth/api-key-login.d.ts +0 -35
- package/dist/types/utils/oauth/api-key-validation.d.ts +0 -27
- package/dist/types/utils/oauth/callback-server.d.ts +0 -60
- package/dist/types/utils/oauth/cerebras.d.ts +0 -1
- package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +0 -18
- package/dist/types/utils/oauth/cursor.d.ts +0 -15
- package/dist/types/utils/oauth/deepinfra.d.ts +0 -1
- package/dist/types/utils/oauth/deepseek.d.ts +0 -10
- package/dist/types/utils/oauth/firepass.d.ts +0 -1
- package/dist/types/utils/oauth/fireworks.d.ts +0 -1
- package/dist/types/utils/oauth/fugu.d.ts +0 -1
- package/dist/types/utils/oauth/github-copilot.d.ts +0 -38
- package/dist/types/utils/oauth/gitlab-duo.d.ts +0 -3
- package/dist/types/utils/oauth/glm-zcode.d.ts +0 -71
- package/dist/types/utils/oauth/google-antigravity.d.ts +0 -11
- package/dist/types/utils/oauth/google-gemini-cli.d.ts +0 -10
- package/dist/types/utils/oauth/google-oauth-shared.d.ts +0 -28
- package/dist/types/utils/oauth/huggingface.d.ts +0 -19
- package/dist/types/utils/oauth/index.d.ts +0 -39
- package/dist/types/utils/oauth/kagi.d.ts +0 -17
- package/dist/types/utils/oauth/kilo.d.ts +0 -5
- package/dist/types/utils/oauth/kimi.d.ts +0 -21
- package/dist/types/utils/oauth/litellm.d.ts +0 -18
- package/dist/types/utils/oauth/lm-studio.d.ts +0 -17
- package/dist/types/utils/oauth/minimax-code.d.ts +0 -28
- package/dist/types/utils/oauth/moonshot.d.ts +0 -1
- package/dist/types/utils/oauth/nanogpt.d.ts +0 -1
- package/dist/types/utils/oauth/nvidia.d.ts +0 -18
- package/dist/types/utils/oauth/ollama-cloud.d.ts +0 -2
- package/dist/types/utils/oauth/ollama.d.ts +0 -18
- package/dist/types/utils/oauth/openai-codex.d.ts +0 -21
- package/dist/types/utils/oauth/opencode.d.ts +0 -18
- package/dist/types/utils/oauth/parallel.d.ts +0 -17
- package/dist/types/utils/oauth/perplexity.d.ts +0 -9
- package/dist/types/utils/oauth/pkce.d.ts +0 -8
- package/dist/types/utils/oauth/qianfan.d.ts +0 -17
- package/dist/types/utils/oauth/qwen-portal.d.ts +0 -19
- package/dist/types/utils/oauth/synthetic.d.ts +0 -1
- package/dist/types/utils/oauth/tavily.d.ts +0 -17
- package/dist/types/utils/oauth/together.d.ts +0 -1
- package/dist/types/utils/oauth/types.d.ts +0 -45
- package/dist/types/utils/oauth/venice.d.ts +0 -18
- package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +0 -18
- package/dist/types/utils/oauth/vllm.d.ts +0 -16
- package/dist/types/utils/oauth/xai.d.ts +0 -30
- package/dist/types/utils/oauth/xiaomi.d.ts +0 -25
- package/dist/types/utils/oauth/zai.d.ts +0 -18
- package/dist/types/utils/oauth/zenmux.d.ts +0 -1
- package/dist/types/utils/overflow.d.ts +0 -64
- package/dist/types/utils/parse-bind.d.ts +0 -23
- package/dist/types/utils/provider-response.d.ts +0 -3
- package/dist/types/utils/retry-after.d.ts +0 -3
- package/dist/types/utils/retry-budget.d.ts +0 -1
- package/dist/types/utils/retry.d.ts +0 -26
- package/dist/types/utils/schema/adapt.d.ts +0 -24
- package/dist/types/utils/schema/compatibility.d.ts +0 -30
- package/dist/types/utils/schema/dereference.d.ts +0 -11
- package/dist/types/utils/schema/draft.d.ts +0 -10
- package/dist/types/utils/schema/equality.d.ts +0 -4
- package/dist/types/utils/schema/fields.d.ts +0 -49
- package/dist/types/utils/schema/index.d.ts +0 -14
- package/dist/types/utils/schema/json-schema-validator.d.ts +0 -12
- package/dist/types/utils/schema/meta-validator.d.ts +0 -2
- package/dist/types/utils/schema/normalize.d.ts +0 -93
- package/dist/types/utils/schema/root-combinator.d.ts +0 -12
- package/dist/types/utils/schema/spill.d.ts +0 -8
- package/dist/types/utils/schema/stamps.d.ts +0 -25
- package/dist/types/utils/schema/types.d.ts +0 -4
- package/dist/types/utils/schema/wire.d.ts +0 -54
- package/dist/types/utils/schema/zod-decontaminate.d.ts +0 -31
- package/dist/types/utils/sse-debug.d.ts +0 -10
- package/dist/types/utils/tool-call-healing.d.ts +0 -71
- package/dist/types/utils/tool-choice-capability.d.ts +0 -41
- package/dist/types/utils/tool-choice.d.ts +0 -50
- package/dist/types/utils/validation.d.ts +0 -17
- package/dist/types/utils.d.ts +0 -35
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
} from "../utils";
|
|
58
58
|
import { createAbortSourceTracker } from "../utils/abort";
|
|
59
59
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
60
|
+
import { transportFailureFacts } from "../utils/fallback-transport";
|
|
60
61
|
import { isFoundryEnabled } from "../utils/foundry";
|
|
61
62
|
import { finalizeErrorMessage, type RawHttpRequestDump, rewriteCopilotError } from "../utils/http-inspector";
|
|
62
63
|
import { getStreamFirstEventTimeoutMs, getStreamIdleTimeoutMs, iterateWithIdleTimeout } from "../utils/idle-iterator";
|
|
@@ -434,27 +435,31 @@ function getCacheControl(
|
|
|
434
435
|
model: Model<"anthropic-messages">,
|
|
435
436
|
baseUrl: string,
|
|
436
437
|
cacheRetention?: CacheRetention,
|
|
437
|
-
): {
|
|
438
|
-
// Default Anthropic prompt caching to long (1h) retention. The provider
|
|
439
|
-
// default of ~5m is too fragile for long-running Codex/Sayknow-CLI subagent
|
|
440
|
-
// workflows, where the prefix is frequently evicted between turns. Explicit
|
|
441
|
-
// request/model `cacheRetention` and the SKC_CACHE_RETENTION /
|
|
442
|
-
// PI_CACHE_RETENTION env overrides still win.
|
|
438
|
+
): { mode: AnthropicCacheMode; cacheControl?: AnthropicCacheControl } {
|
|
443
439
|
const retention = resolveCacheRetention(cacheRetention, "long");
|
|
444
|
-
if (retention === "none") {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
440
|
+
if (retention === "none") return { mode: "none" };
|
|
441
|
+
|
|
442
|
+
const isCanonicalApi = isAnthropicApiBaseUrl(baseUrl);
|
|
443
|
+
const promptCacheMode = model.compat?.promptCacheMode;
|
|
444
|
+
const mode: AnthropicCacheMode =
|
|
445
|
+
promptCacheMode === "none"
|
|
446
|
+
? "none"
|
|
447
|
+
: promptCacheMode === "explicit"
|
|
448
|
+
? "explicit"
|
|
449
|
+
: isCanonicalApi
|
|
450
|
+
? "automatic"
|
|
451
|
+
: "none";
|
|
452
|
+
if (mode === "none") return { mode };
|
|
453
|
+
|
|
454
|
+
const supportsLongCacheRetention = isCanonicalApi
|
|
455
|
+
? getAnthropicCompat(model).supportsLongCacheRetention
|
|
456
|
+
: model.compat?.supportsLongCacheRetention === true;
|
|
455
457
|
return {
|
|
456
|
-
|
|
457
|
-
cacheControl: {
|
|
458
|
+
mode,
|
|
459
|
+
cacheControl: {
|
|
460
|
+
type: "ephemeral",
|
|
461
|
+
...(retention === "long" && supportsLongCacheRetention ? { ttl: "1h" } : {}),
|
|
462
|
+
},
|
|
458
463
|
};
|
|
459
464
|
}
|
|
460
465
|
|
|
@@ -1080,6 +1085,7 @@ function getAnthropicCompat(
|
|
|
1080
1085
|
supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true,
|
|
1081
1086
|
supportsToolChoice: model.compat?.supportsToolChoice ?? true,
|
|
1082
1087
|
supportsForcedToolChoice: model.compat?.supportsForcedToolChoice ?? true,
|
|
1088
|
+
promptCacheMode: model.compat?.promptCacheMode ?? "none",
|
|
1083
1089
|
toolChoiceSupport: model.compat?.toolChoiceSupport,
|
|
1084
1090
|
};
|
|
1085
1091
|
}
|
|
@@ -1303,6 +1309,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1303
1309
|
if (replacementPayload !== undefined) {
|
|
1304
1310
|
nextParams = replacementPayload as typeof nextParams;
|
|
1305
1311
|
}
|
|
1312
|
+
validateCacheControls(nextParams as AnthropicCacheParams);
|
|
1306
1313
|
rawRequestDump = {
|
|
1307
1314
|
provider: model.provider,
|
|
1308
1315
|
api: output.api,
|
|
@@ -1323,6 +1330,13 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1323
1330
|
) & { index: number };
|
|
1324
1331
|
const blocks = output.content as Block[];
|
|
1325
1332
|
const blocksByAnthropicIndex = new Map<number, Block>();
|
|
1333
|
+
// Derive from the ACTUAL request shape, not the option default: the request
|
|
1334
|
+
// only sends `display: "summarized"` on specific paths (adaptive display is
|
|
1335
|
+
// omitted for models where supportsAdaptiveThinkingDisplay is false). Defaulting
|
|
1336
|
+
// to summarized would mislabel raw thinking as a provider-displayable summary.
|
|
1337
|
+
const summarizedThinking =
|
|
1338
|
+
(params.thinking as { display?: AnthropicThinkingDisplay } | undefined)?.display === "summarized";
|
|
1339
|
+
const reasoningBuffers = new WeakMap<object, string>();
|
|
1326
1340
|
const getBlockByAnthropicIndex = (anthropicIndex: number) => {
|
|
1327
1341
|
const block = blocksByAnthropicIndex.get(anthropicIndex);
|
|
1328
1342
|
if (!block) return { block: undefined, contentIndex: -1 };
|
|
@@ -1454,11 +1468,24 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1454
1468
|
};
|
|
1455
1469
|
output.content.push(block);
|
|
1456
1470
|
trackBlockByAnthropicIndex(event.index, block);
|
|
1471
|
+
// Emit thinking_start FIRST so a reasoning item is open before any
|
|
1472
|
+
// summary-start: the Responses SSE encoder only accepts a summary
|
|
1473
|
+
// start when state.open.kind === "reasoning", otherwise the
|
|
1474
|
+
// reasoning_summary_part.added frame is dropped and deltas arrive
|
|
1475
|
+
// out of order.
|
|
1457
1476
|
stream.push({
|
|
1458
1477
|
type: "thinking_start",
|
|
1459
1478
|
contentIndex: output.content.length - 1,
|
|
1460
1479
|
partial: output,
|
|
1461
1480
|
});
|
|
1481
|
+
if (summarizedThinking) {
|
|
1482
|
+
reasoningBuffers.set(block, "");
|
|
1483
|
+
stream.push({
|
|
1484
|
+
type: "reasoning_summary_start",
|
|
1485
|
+
contentIndex: output.content.length - 1,
|
|
1486
|
+
partial: output,
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1462
1489
|
} else if (event.content_block.type === "redacted_thinking") {
|
|
1463
1490
|
const block: Block = {
|
|
1464
1491
|
type: "redactedThinking",
|
|
@@ -1503,12 +1530,23 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1503
1530
|
const { block, contentIndex: index } = getBlockByAnthropicIndex(event.index);
|
|
1504
1531
|
if (block && block.type === "thinking") {
|
|
1505
1532
|
block.thinking += event.delta.thinking;
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1533
|
+
if (summarizedThinking) {
|
|
1534
|
+
const summary = (reasoningBuffers.get(block) ?? "") + event.delta.thinking;
|
|
1535
|
+
reasoningBuffers.set(block, summary);
|
|
1536
|
+
stream.push({
|
|
1537
|
+
type: "reasoning_summary_delta",
|
|
1538
|
+
contentIndex: index,
|
|
1539
|
+
delta: event.delta.thinking,
|
|
1540
|
+
partial: output,
|
|
1541
|
+
});
|
|
1542
|
+
} else {
|
|
1543
|
+
stream.push({
|
|
1544
|
+
type: "thinking_delta",
|
|
1545
|
+
contentIndex: index,
|
|
1546
|
+
delta: event.delta.thinking,
|
|
1547
|
+
partial: output,
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1512
1550
|
}
|
|
1513
1551
|
} else if (event.delta.type === "input_json_delta") {
|
|
1514
1552
|
const { block, contentIndex: index } = getBlockByAnthropicIndex(event.index);
|
|
@@ -1542,6 +1580,21 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1542
1580
|
partial: output,
|
|
1543
1581
|
});
|
|
1544
1582
|
} else if (block.type === "thinking") {
|
|
1583
|
+
if (summarizedThinking) {
|
|
1584
|
+
const summaryText = reasoningBuffers.get(block) ?? "";
|
|
1585
|
+
const mutable = block as {
|
|
1586
|
+
provenance?: "summary" | "raw" | "mixed";
|
|
1587
|
+
summaryText?: string;
|
|
1588
|
+
};
|
|
1589
|
+
if (mutable.summaryText === undefined) mutable.summaryText = summaryText;
|
|
1590
|
+
if (mutable.provenance === undefined) mutable.provenance = "summary";
|
|
1591
|
+
stream.push({
|
|
1592
|
+
type: "reasoning_summary_end",
|
|
1593
|
+
contentIndex: index,
|
|
1594
|
+
content: summaryText,
|
|
1595
|
+
partial: output,
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1545
1598
|
stream.push({
|
|
1546
1599
|
type: "thinking_end",
|
|
1547
1600
|
contentIndex: index,
|
|
@@ -1637,6 +1690,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1637
1690
|
throw streamFailure;
|
|
1638
1691
|
}
|
|
1639
1692
|
if (
|
|
1693
|
+
!options?.fallbackManaged &&
|
|
1640
1694
|
!disableStrictTools &&
|
|
1641
1695
|
firstTokenTime === undefined &&
|
|
1642
1696
|
hasStrictAnthropicTools(params) &&
|
|
@@ -1655,6 +1709,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1655
1709
|
if (
|
|
1656
1710
|
!droppedForcedToolChoice &&
|
|
1657
1711
|
firstTokenTime === undefined &&
|
|
1712
|
+
!options?.fallbackManaged &&
|
|
1658
1713
|
isSentForcedAnthropicToolChoice(params.tool_choice) &&
|
|
1659
1714
|
isForcedToolChoiceUnsupportedError(streamFailure, true)
|
|
1660
1715
|
) {
|
|
@@ -1681,6 +1736,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1681
1736
|
continue;
|
|
1682
1737
|
}
|
|
1683
1738
|
if (
|
|
1739
|
+
!options?.fallbackManaged &&
|
|
1684
1740
|
!thinkingRepairAttempted &&
|
|
1685
1741
|
firstTokenTime === undefined &&
|
|
1686
1742
|
isAnthropicThinkingBlockMutationError(streamFailure)
|
|
@@ -1696,6 +1752,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1696
1752
|
continue;
|
|
1697
1753
|
}
|
|
1698
1754
|
if (
|
|
1755
|
+
!options?.fallbackManaged &&
|
|
1699
1756
|
!dropFastMode &&
|
|
1700
1757
|
resolveServiceTier(options?.serviceTier, model.provider) === "priority" &&
|
|
1701
1758
|
firstTokenTime === undefined &&
|
|
@@ -1752,6 +1809,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1752
1809
|
const firstEventTimeoutError = activeAbortTracker.getLocalAbortReason();
|
|
1753
1810
|
output.stopReason = activeAbortTracker.wasCallerAbort() ? "aborted" : "error";
|
|
1754
1811
|
output.errorStatus = extractHttpStatusFromError(error);
|
|
1812
|
+
output.transportFailure = transportFailureFacts(error);
|
|
1755
1813
|
if (output.errorKind !== "provider_safety_stop" || !output.errorMessage) {
|
|
1756
1814
|
output.errorMessage =
|
|
1757
1815
|
firstEventTimeoutError?.message ?? (await finalizeErrorMessage(error, rawRequestDump));
|
|
@@ -1991,232 +2049,159 @@ type CacheControlBlock = {
|
|
|
1991
2049
|
cache_control?: AnthropicCacheControl | null;
|
|
1992
2050
|
};
|
|
1993
2051
|
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
): void {
|
|
1998
|
-
if (blocks.length === 0) return;
|
|
1999
|
-
const lastIndex = blocks.length - 1;
|
|
2000
|
-
blocks[lastIndex] = { ...blocks[lastIndex], cache_control: cacheControl };
|
|
2001
|
-
}
|
|
2002
|
-
|
|
2003
|
-
function applyCacheControlToLastTextBlock(
|
|
2004
|
-
blocks: Array<ContentBlockParam & CacheControlBlock>,
|
|
2005
|
-
cacheControl: AnthropicCacheControl,
|
|
2006
|
-
): void {
|
|
2007
|
-
if (blocks.length === 0) return;
|
|
2008
|
-
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
2009
|
-
if (blocks[i].type === "text") {
|
|
2010
|
-
blocks[i] = { ...blocks[i], cache_control: cacheControl };
|
|
2011
|
-
return;
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
applyCacheControlToLastBlock(blocks, cacheControl);
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
function applyPromptCaching(params: MessageCreateParamsStreaming, cacheControl?: AnthropicCacheControl): void {
|
|
2018
|
-
if (!cacheControl) return;
|
|
2019
|
-
|
|
2020
|
-
// Skip if cache_control breakpoints were already placed externally on messages.
|
|
2021
|
-
for (const message of params.messages) {
|
|
2022
|
-
if (Array.isArray(message.content)) {
|
|
2023
|
-
if ((message.content as Array<ContentBlockParam & CacheControlBlock>).some(b => b.cache_control != null))
|
|
2024
|
-
return;
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2052
|
+
type AnthropicCacheParams = MessageCreateParamsStreaming & {
|
|
2053
|
+
cache_control?: AnthropicCacheControl;
|
|
2054
|
+
};
|
|
2027
2055
|
|
|
2028
|
-
|
|
2029
|
-
let cacheBreakpointsUsed = 0;
|
|
2056
|
+
type AnthropicCacheMode = "automatic" | "explicit" | "none";
|
|
2030
2057
|
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2058
|
+
function isCacheableContentBlock(block: ContentBlockParam): boolean {
|
|
2059
|
+
if (block.type === "thinking" || block.type === "redacted_thinking") return false;
|
|
2060
|
+
return block.type !== "text" || block.text.trim().length > 0;
|
|
2061
|
+
}
|
|
2035
2062
|
|
|
2036
|
-
|
|
2063
|
+
function cacheControlError(path: string, reason: string): Error {
|
|
2064
|
+
return new Error(`Invalid Anthropic cache_control at ${path}: ${reason}`);
|
|
2065
|
+
}
|
|
2037
2066
|
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2067
|
+
function validateCacheControl(control: unknown, path: string, seenFiveMinute: { value: boolean }): void {
|
|
2068
|
+
if (!isRecord(control) || control.type !== "ephemeral") {
|
|
2069
|
+
throw cacheControlError(path, 'expected { type: "ephemeral" }');
|
|
2041
2070
|
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
const userIndexes = params.messages
|
|
2046
|
-
.map((message, index) => (message.role === "user" ? index : -1))
|
|
2047
|
-
.filter(index => index >= 0);
|
|
2048
|
-
|
|
2049
|
-
if (userIndexes.length >= 2) {
|
|
2050
|
-
const penultimateUserIndex = userIndexes[userIndexes.length - 2];
|
|
2051
|
-
const penultimateUser = params.messages[penultimateUserIndex];
|
|
2052
|
-
if (penultimateUser) {
|
|
2053
|
-
if (typeof penultimateUser.content === "string") {
|
|
2054
|
-
const contentBlock: ContentBlockParam & CacheControlBlock = {
|
|
2055
|
-
type: "text",
|
|
2056
|
-
text: penultimateUser.content,
|
|
2057
|
-
cache_control: cacheControl,
|
|
2058
|
-
};
|
|
2059
|
-
penultimateUser.content = [contentBlock];
|
|
2060
|
-
cacheBreakpointsUsed++;
|
|
2061
|
-
} else if (Array.isArray(penultimateUser.content) && penultimateUser.content.length > 0) {
|
|
2062
|
-
applyCacheControlToLastTextBlock(
|
|
2063
|
-
penultimateUser.content as Array<ContentBlockParam & CacheControlBlock>,
|
|
2064
|
-
cacheControl,
|
|
2065
|
-
);
|
|
2066
|
-
cacheBreakpointsUsed++;
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2071
|
+
if (control.ttl !== undefined && control.ttl !== "5m" && control.ttl !== "1h") {
|
|
2072
|
+
throw cacheControlError(path, 'ttl must be "5m" or "1h"');
|
|
2069
2073
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
if (userIndexes.length >= 1) {
|
|
2074
|
-
const lastUserIndex = userIndexes[userIndexes.length - 1];
|
|
2075
|
-
const lastUser = params.messages[lastUserIndex];
|
|
2076
|
-
if (lastUser) {
|
|
2077
|
-
if (typeof lastUser.content === "string") {
|
|
2078
|
-
const contentBlock: ContentBlockParam & CacheControlBlock = {
|
|
2079
|
-
type: "text",
|
|
2080
|
-
text: lastUser.content,
|
|
2081
|
-
cache_control: cacheControl,
|
|
2082
|
-
};
|
|
2083
|
-
lastUser.content = [contentBlock];
|
|
2084
|
-
} else if (Array.isArray(lastUser.content) && lastUser.content.length > 0) {
|
|
2085
|
-
applyCacheControlToLastTextBlock(
|
|
2086
|
-
lastUser.content as Array<ContentBlockParam & CacheControlBlock>,
|
|
2087
|
-
cacheControl,
|
|
2088
|
-
);
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
function normalizeCacheControlBlockTtl(block: CacheControlBlock, seenFiveMinute: { value: boolean }): void {
|
|
2095
|
-
const cacheControl = block.cache_control;
|
|
2096
|
-
if (!cacheControl) return;
|
|
2097
|
-
if (cacheControl.ttl !== "1h") {
|
|
2098
|
-
seenFiveMinute.value = true;
|
|
2074
|
+
if (control.ttl === "1h") {
|
|
2075
|
+
if (seenFiveMinute.value) throw cacheControlError(path, "1h TTL must precede 5m TTL");
|
|
2099
2076
|
return;
|
|
2100
2077
|
}
|
|
2101
|
-
|
|
2102
|
-
delete cacheControl.ttl;
|
|
2103
|
-
}
|
|
2078
|
+
seenFiveMinute.value = true;
|
|
2104
2079
|
}
|
|
2105
2080
|
|
|
2106
|
-
function
|
|
2081
|
+
function validateCacheControls(params: AnthropicCacheParams): void {
|
|
2107
2082
|
const seenFiveMinute = { value: false };
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2083
|
+
let count = 0;
|
|
2084
|
+
const validate = (control: unknown, path: string): void => {
|
|
2085
|
+
if (control == null) return;
|
|
2086
|
+
count++;
|
|
2087
|
+
validateCacheControl(control, path, seenFiveMinute);
|
|
2088
|
+
};
|
|
2089
|
+
if (!Array.isArray(params.messages)) throw cacheControlError("messages", "must be an array");
|
|
2090
|
+
|
|
2091
|
+
validate(params.cache_control, "cache_control");
|
|
2092
|
+
for (const [index, tool] of (params.tools ?? []).entries()) {
|
|
2093
|
+
validate((tool as CacheControlBlock).cache_control, `tools[${index}].cache_control`);
|
|
2112
2094
|
}
|
|
2113
|
-
if (
|
|
2114
|
-
for (const block of params.system
|
|
2115
|
-
|
|
2095
|
+
if (Array.isArray(params.system)) {
|
|
2096
|
+
for (const [index, block] of params.system.entries()) {
|
|
2097
|
+
validate((block as CacheControlBlock).cache_control, `system[${index}].cache_control`);
|
|
2116
2098
|
}
|
|
2117
2099
|
}
|
|
2118
|
-
for (const message of params.messages) {
|
|
2100
|
+
for (const [messageIndex, message] of params.messages.entries()) {
|
|
2119
2101
|
if (!Array.isArray(message.content)) continue;
|
|
2120
|
-
for (const block of message.content
|
|
2121
|
-
|
|
2102
|
+
for (const [blockIndex, block] of message.content.entries()) {
|
|
2103
|
+
const control = (block as CacheControlBlock).cache_control;
|
|
2104
|
+
if (control != null && !isCacheableContentBlock(block)) {
|
|
2105
|
+
throw cacheControlError(
|
|
2106
|
+
`messages[${messageIndex}].content[${blockIndex}].cache_control`,
|
|
2107
|
+
"block is not cacheable",
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
validate(control, `messages[${messageIndex}].content[${blockIndex}].cache_control`);
|
|
2122
2111
|
}
|
|
2123
2112
|
}
|
|
2113
|
+
if (count > 4) throw cacheControlError("cache_control", "at most four total breakpoints are allowed");
|
|
2124
2114
|
}
|
|
2125
2115
|
|
|
2126
|
-
function
|
|
2116
|
+
function applyCacheControlToLastCacheableBlock(
|
|
2117
|
+
blocks: Array<ContentBlockParam & CacheControlBlock>,
|
|
2118
|
+
cacheControl: AnthropicCacheControl,
|
|
2119
|
+
): boolean {
|
|
2127
2120
|
for (let index = blocks.length - 1; index >= 0; index--) {
|
|
2128
|
-
|
|
2121
|
+
const block = blocks[index];
|
|
2122
|
+
if (!isCacheableContentBlock(block)) continue;
|
|
2123
|
+
blocks[index] = { ...block, cache_control: { ...cacheControl } };
|
|
2124
|
+
return true;
|
|
2129
2125
|
}
|
|
2130
|
-
return
|
|
2126
|
+
return false;
|
|
2131
2127
|
}
|
|
2132
2128
|
|
|
2133
|
-
function
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
): void {
|
|
2138
|
-
for (let index = 0; index < blocks.length && excessCounter.value > 0; index++) {
|
|
2139
|
-
if (index === preserveIndex) continue;
|
|
2140
|
-
if (!blocks[index]?.cache_control) continue;
|
|
2141
|
-
delete blocks[index].cache_control;
|
|
2142
|
-
excessCounter.value--;
|
|
2143
|
-
}
|
|
2129
|
+
function isHumanUserMessage(message: MessageCreateParamsStreaming["messages"][number]): boolean {
|
|
2130
|
+
if (message.role !== "user") return false;
|
|
2131
|
+
if (typeof message.content === "string") return true;
|
|
2132
|
+
return message.content.some(block => block.type !== "tool_result");
|
|
2144
2133
|
}
|
|
2145
2134
|
|
|
2146
|
-
function
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2135
|
+
function applyExplicitPromptCaching(params: AnthropicCacheParams, cacheControl: AnthropicCacheControl): void {
|
|
2136
|
+
if (countCacheControlBreakpoints(params) >= 4) return;
|
|
2137
|
+
|
|
2138
|
+
const currentUserIndex = params.messages.findLastIndex(isHumanUserMessage);
|
|
2139
|
+
if (currentUserIndex < 0) return;
|
|
2140
|
+
const currentUser = params.messages[currentUserIndex];
|
|
2141
|
+
if (!currentUser) return;
|
|
2142
|
+
|
|
2143
|
+
// A tool result is encoded as role "user" on the wire, but belongs to the
|
|
2144
|
+
// preceding assistant turn. Anchor that assistant turn, not the tool result,
|
|
2145
|
+
// so changing tool output does not invalidate the reusable conversation prefix.
|
|
2146
|
+
for (let index = currentUserIndex - 1; index >= 0; index--) {
|
|
2147
|
+
const message = params.messages[index];
|
|
2148
|
+
if (message?.role !== "assistant" || !Array.isArray(message.content)) continue;
|
|
2149
|
+
if (
|
|
2150
|
+
applyCacheControlToLastCacheableBlock(
|
|
2151
|
+
message.content as Array<ContentBlockParam & CacheControlBlock>,
|
|
2152
|
+
cacheControl,
|
|
2153
|
+
)
|
|
2154
|
+
) {
|
|
2155
|
+
break;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
if (countCacheControlBreakpoints(params) >= 4) return;
|
|
2160
|
+
if (typeof currentUser.content === "string" && currentUser.content.trim()) {
|
|
2161
|
+
currentUser.content = [{ type: "text", text: currentUser.content, cache_control: { ...cacheControl } }];
|
|
2162
|
+
} else if (Array.isArray(currentUser.content)) {
|
|
2163
|
+
applyCacheControlToLastCacheableBlock(
|
|
2164
|
+
currentUser.content as Array<ContentBlockParam & CacheControlBlock>,
|
|
2165
|
+
cacheControl,
|
|
2166
|
+
);
|
|
2152
2167
|
}
|
|
2153
2168
|
}
|
|
2154
2169
|
|
|
2155
|
-
function
|
|
2156
|
-
|
|
2157
|
-
|
|
2170
|
+
function applyPromptCaching(
|
|
2171
|
+
params: AnthropicCacheParams,
|
|
2172
|
+
cacheMode: AnthropicCacheMode,
|
|
2173
|
+
cacheControl?: AnthropicCacheControl,
|
|
2158
2174
|
): void {
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
if (!block.cache_control) continue;
|
|
2165
|
-
delete block.cache_control;
|
|
2166
|
-
excessCounter.value--;
|
|
2167
|
-
}
|
|
2175
|
+
if (!cacheControl || cacheMode === "none") return;
|
|
2176
|
+
validateCacheControls(params);
|
|
2177
|
+
if (cacheMode === "automatic") {
|
|
2178
|
+
params.cache_control = { ...cacheControl };
|
|
2179
|
+
return;
|
|
2168
2180
|
}
|
|
2181
|
+
applyExplicitPromptCaching(params, cacheControl);
|
|
2182
|
+
validateCacheControls(params);
|
|
2169
2183
|
}
|
|
2170
2184
|
|
|
2171
|
-
function
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
if (
|
|
2179
|
-
for (const block of params.system
|
|
2180
|
-
if (block.cache_control) total++;
|
|
2181
|
-
}
|
|
2185
|
+
export function normalizeCacheControlTtlOrdering(params: MessageCreateParamsStreaming): void {
|
|
2186
|
+
validateCacheControls(params as AnthropicCacheParams);
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
function countCacheControlBreakpoints(params: AnthropicCacheParams): number {
|
|
2190
|
+
let total = params.cache_control ? 1 : 0;
|
|
2191
|
+
for (const tool of params.tools ?? []) if ((tool as CacheControlBlock).cache_control) total++;
|
|
2192
|
+
if (Array.isArray(params.system)) {
|
|
2193
|
+
for (const block of params.system) if ((block as CacheControlBlock).cache_control) total++;
|
|
2182
2194
|
}
|
|
2183
2195
|
for (const message of params.messages) {
|
|
2184
2196
|
if (!Array.isArray(message.content)) continue;
|
|
2185
|
-
for (const block of message.content
|
|
2186
|
-
if (block.cache_control) total++;
|
|
2187
|
-
}
|
|
2197
|
+
for (const block of message.content) if ((block as CacheControlBlock).cache_control) total++;
|
|
2188
2198
|
}
|
|
2189
2199
|
return total;
|
|
2190
2200
|
}
|
|
2191
2201
|
|
|
2192
2202
|
function enforceCacheControlLimit(params: MessageCreateParamsStreaming, maxBreakpoints: number): void {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
const excessCounter = { value: total - maxBreakpoints };
|
|
2196
|
-
const systemBlocks =
|
|
2197
|
-
params.system && Array.isArray(params.system)
|
|
2198
|
-
? (params.system as Array<AnthropicSystemBlock & CacheControlBlock>)
|
|
2199
|
-
: [];
|
|
2200
|
-
const toolBlocks = (params.tools ?? []) as Array<Anthropic.Messages.Tool & CacheControlBlock>;
|
|
2201
|
-
const lastSystemIndex = findLastCacheControlIndex(systemBlocks);
|
|
2202
|
-
const lastToolIndex = findLastCacheControlIndex(toolBlocks);
|
|
2203
|
-
if (systemBlocks.length > 0) {
|
|
2204
|
-
stripCacheControlExceptIndex(systemBlocks, lastSystemIndex, excessCounter);
|
|
2205
|
-
}
|
|
2206
|
-
if (excessCounter.value <= 0) return;
|
|
2207
|
-
if (toolBlocks.length > 0) {
|
|
2208
|
-
stripCacheControlExceptIndex(toolBlocks, lastToolIndex, excessCounter);
|
|
2209
|
-
}
|
|
2210
|
-
if (excessCounter.value <= 0) return;
|
|
2211
|
-
stripMessageCacheControl(params.messages, excessCounter);
|
|
2212
|
-
if (excessCounter.value <= 0) return;
|
|
2213
|
-
if (systemBlocks.length > 0) {
|
|
2214
|
-
stripAllCacheControl(systemBlocks, excessCounter);
|
|
2215
|
-
}
|
|
2216
|
-
if (excessCounter.value <= 0) return;
|
|
2217
|
-
if (toolBlocks.length > 0) {
|
|
2218
|
-
stripAllCacheControl(toolBlocks, excessCounter);
|
|
2219
|
-
}
|
|
2203
|
+
if (maxBreakpoints !== 4) throw new Error("Anthropic supports exactly four cache breakpoints");
|
|
2204
|
+
validateCacheControls(params as AnthropicCacheParams);
|
|
2220
2205
|
}
|
|
2221
2206
|
function buildParams(
|
|
2222
2207
|
model: Model<"anthropic-messages">,
|
|
@@ -2227,7 +2212,8 @@ function buildParams(
|
|
|
2227
2212
|
disableStrictTools = false,
|
|
2228
2213
|
repairLatestAssistantThinking = false,
|
|
2229
2214
|
): MessageCreateParamsStreaming {
|
|
2230
|
-
const { cacheControl } = getCacheControl(model, baseUrl, options?.cacheRetention);
|
|
2215
|
+
const { mode: cacheMode, cacheControl } = getCacheControl(model, baseUrl, options?.cacheRetention);
|
|
2216
|
+
|
|
2231
2217
|
const params: AnthropicSamplingParams = {
|
|
2232
2218
|
model: model.id,
|
|
2233
2219
|
messages: convertAnthropicMessages(context.messages, model, isOAuthToken, { repairLatestAssistantThinking }),
|
|
@@ -2358,7 +2344,7 @@ function buildParams(
|
|
|
2358
2344
|
}
|
|
2359
2345
|
disableThinkingIfToolChoiceForced(params);
|
|
2360
2346
|
ensureMaxTokensForThinking(params, model);
|
|
2361
|
-
applyPromptCaching(params, cacheControl);
|
|
2347
|
+
applyPromptCaching(params as AnthropicCacheParams, cacheMode, cacheControl);
|
|
2362
2348
|
enforceCacheControlLimit(params, 4);
|
|
2363
2349
|
normalizeCacheControlTtlOrdering(params);
|
|
2364
2350
|
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
import { normalizeSystemPrompts } from "../utils";
|
|
20
20
|
import { createAbortSourceTracker } from "../utils/abort";
|
|
21
21
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
22
|
+
import { transportFailureFacts } from "../utils/fallback-transport";
|
|
22
23
|
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
|
23
24
|
import {
|
|
24
25
|
createWatchdog,
|
|
@@ -140,7 +141,10 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|
|
140
141
|
try {
|
|
141
142
|
openaiStream = await client.responses.create(params, { signal: requestSignal });
|
|
142
143
|
} catch (error) {
|
|
143
|
-
if (
|
|
144
|
+
if (
|
|
145
|
+
!isForcedToolChoiceUnsupportedError(error, isForcedAzureResponsesToolChoice(params.tool_choice)) ||
|
|
146
|
+
options?.fallbackManaged
|
|
147
|
+
) {
|
|
144
148
|
throw error;
|
|
145
149
|
}
|
|
146
150
|
const reason = await finalizeErrorMessage(error, rawRequestDump);
|
|
@@ -204,6 +208,7 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|
|
204
208
|
const firstEventTimeoutError = abortTracker.getLocalAbortReason();
|
|
205
209
|
output.stopReason = abortTracker.wasCallerAbort() ? "aborted" : "error";
|
|
206
210
|
output.errorStatus = extractHttpStatusFromError(error);
|
|
211
|
+
output.transportFailure = transportFailureFacts(error);
|
|
207
212
|
output.errorMessage = firstEventTimeoutError?.message ?? (await finalizeErrorMessage(error, rawRequestDump));
|
|
208
213
|
output.duration = Date.now() - startTime;
|
|
209
214
|
if (firstTokenTime) output.ttft = firstTokenTime - startTime;
|