@sayknow-cli/ai 0.3.6 → 0.3.7
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/types/api-registry.d.ts +30 -0
- package/dist/types/auth-broker/client.d.ts +67 -0
- package/dist/types/auth-broker/index.d.ts +5 -0
- package/dist/types/auth-broker/refresher.d.ts +25 -0
- package/dist/types/auth-broker/remote-store.d.ts +98 -0
- package/dist/types/auth-broker/server.d.ts +32 -0
- package/dist/types/auth-broker/types.d.ts +110 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +442 -0
- package/dist/types/auth-gateway/http.d.ts +40 -0
- package/dist/types/auth-gateway/index.d.ts +3 -0
- package/dist/types/auth-gateway/server.d.ts +17 -0
- package/dist/types/auth-gateway/types.d.ts +115 -0
- package/dist/types/auth-storage.d.ts +677 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/index.d.ts +51 -0
- package/dist/types/model-cache.d.ts +17 -0
- package/dist/types/model-manager.d.ts +62 -0
- package/dist/types/model-thinking.d.ts +74 -0
- package/dist/types/models.d.ts +12 -0
- package/dist/types/provider-details.d.ts +24 -0
- package/dist/types/provider-models/bundled-references.d.ts +4 -0
- package/dist/types/provider-models/descriptors.d.ts +48 -0
- package/dist/types/provider-models/google.d.ts +20 -0
- package/dist/types/provider-models/index.d.ts +5 -0
- package/dist/types/provider-models/ollama.d.ts +7 -0
- package/dist/types/provider-models/openai-compat.d.ts +251 -0
- package/dist/types/provider-models/special.d.ts +19 -0
- package/dist/types/providers/amazon-bedrock.d.ts +60 -0
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
- package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
- package/dist/types/providers/anthropic.d.ts +208 -0
- package/dist/types/providers/aws-credentials.d.ts +43 -0
- package/dist/types/providers/aws-eventstream.d.ts +38 -0
- package/dist/types/providers/aws-sigv4.d.ts +55 -0
- package/dist/types/providers/azure-openai-responses.d.ts +15 -0
- package/dist/types/providers/composer-discipline.d.ts +26 -0
- package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
- package/dist/types/providers/cursor.d.ts +44 -0
- package/dist/types/providers/error-message.d.ts +27 -0
- package/dist/types/providers/github-copilot-headers.d.ts +40 -0
- package/dist/types/providers/gitlab-duo.d.ts +27 -0
- package/dist/types/providers/google-auth.d.ts +24 -0
- package/dist/types/providers/google-gemini-cli.d.ts +75 -0
- package/dist/types/providers/google-gemini-headers.d.ts +40 -0
- package/dist/types/providers/google-shared.d.ts +173 -0
- package/dist/types/providers/google-types.d.ts +138 -0
- package/dist/types/providers/google-vertex.d.ts +7 -0
- package/dist/types/providers/google.d.ts +4 -0
- package/dist/types/providers/grammar.d.ts +1 -0
- package/dist/types/providers/kimi.d.ts +27 -0
- package/dist/types/providers/mock.d.ts +177 -0
- package/dist/types/providers/ollama.d.ts +41 -0
- package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +815 -0
- package/dist/types/providers/openai-chat-server.d.ts +16 -0
- package/dist/types/providers/openai-codex/constants.d.ts +26 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +50 -0
- package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
- package/dist/types/providers/openai-codex-responses.d.ts +67 -0
- package/dist/types/providers/openai-completions-compat.d.ts +27 -0
- package/dist/types/providers/openai-completions.d.ts +33 -0
- package/dist/types/providers/openai-request-transform.d.ts +4 -0
- package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
- package/dist/types/providers/openai-responses-server.d.ts +17 -0
- package/dist/types/providers/openai-responses-shared.d.ts +104 -0
- package/dist/types/providers/openai-responses.d.ts +32 -0
- package/dist/types/providers/pi-native-client.d.ts +13 -0
- package/dist/types/providers/pi-native-server.d.ts +68 -0
- package/dist/types/providers/register-builtins.d.ts +31 -0
- package/dist/types/providers/synthetic.d.ts +26 -0
- package/dist/types/providers/transform-messages.d.ts +14 -0
- package/dist/types/providers/vision-guard.d.ts +8 -0
- package/dist/types/rate-limit-utils.d.ts +19 -0
- package/dist/types/stream.d.ts +43 -0
- package/dist/types/types.d.ts +830 -0
- package/dist/types/usage/claude.d.ts +3 -0
- package/dist/types/usage/gemini.d.ts +2 -0
- package/dist/types/usage/github-copilot.d.ts +7 -0
- package/dist/types/usage/google-antigravity.d.ts +2 -0
- package/dist/types/usage/grok-cli.d.ts +10 -0
- package/dist/types/usage/kimi.d.ts +2 -0
- package/dist/types/usage/minimax-code.d.ts +2 -0
- package/dist/types/usage/openai-codex.d.ts +3 -0
- package/dist/types/usage/shared.d.ts +1 -0
- package/dist/types/usage/zai.d.ts +2 -0
- package/dist/types/usage.d.ts +258 -0
- package/dist/types/utils/abort.d.ts +19 -0
- package/dist/types/utils/anthropic-auth.d.ts +31 -0
- package/dist/types/utils/discovery/antigravity.d.ts +61 -0
- package/dist/types/utils/discovery/codex.d.ts +38 -0
- package/dist/types/utils/discovery/cursor.d.ts +23 -0
- package/dist/types/utils/discovery/gemini.d.ts +25 -0
- package/dist/types/utils/discovery/index.d.ts +4 -0
- package/dist/types/utils/discovery/openai-compatible.d.ts +74 -0
- package/dist/types/utils/event-stream.d.ts +33 -0
- package/dist/types/utils/fireworks-model-id.d.ts +10 -0
- package/dist/types/utils/foundry.d.ts +1 -0
- package/dist/types/utils/h2-fetch.d.ts +22 -0
- package/dist/types/utils/http-inspector.d.ts +35 -0
- package/dist/types/utils/idle-iterator.d.ts +67 -0
- package/dist/types/utils/json-parse.d.ts +18 -0
- package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
- package/dist/types/utils/oauth/anthropic.d.ts +22 -0
- package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
- package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
- package/dist/types/utils/oauth/callback-server.d.ts +60 -0
- package/dist/types/utils/oauth/cerebras.d.ts +1 -0
- package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/cursor.d.ts +15 -0
- package/dist/types/utils/oauth/deepinfra.d.ts +1 -0
- package/dist/types/utils/oauth/deepseek.d.ts +10 -0
- package/dist/types/utils/oauth/firepass.d.ts +1 -0
- package/dist/types/utils/oauth/fireworks.d.ts +1 -0
- package/dist/types/utils/oauth/fugu.d.ts +1 -0
- package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
- package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
- package/dist/types/utils/oauth/glm-zcode.d.ts +71 -0
- package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
- package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
- package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
- package/dist/types/utils/oauth/huggingface.d.ts +19 -0
- package/dist/types/utils/oauth/index.d.ts +39 -0
- package/dist/types/utils/oauth/kagi.d.ts +17 -0
- package/dist/types/utils/oauth/kilo.d.ts +5 -0
- package/dist/types/utils/oauth/kimi.d.ts +21 -0
- package/dist/types/utils/oauth/litellm.d.ts +18 -0
- package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
- package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
- package/dist/types/utils/oauth/moonshot.d.ts +1 -0
- package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
- package/dist/types/utils/oauth/nvidia.d.ts +18 -0
- package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
- package/dist/types/utils/oauth/ollama.d.ts +18 -0
- package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
- package/dist/types/utils/oauth/opencode.d.ts +18 -0
- package/dist/types/utils/oauth/parallel.d.ts +17 -0
- package/dist/types/utils/oauth/perplexity.d.ts +9 -0
- package/dist/types/utils/oauth/pkce.d.ts +8 -0
- package/dist/types/utils/oauth/qianfan.d.ts +17 -0
- package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
- package/dist/types/utils/oauth/synthetic.d.ts +1 -0
- package/dist/types/utils/oauth/tavily.d.ts +17 -0
- package/dist/types/utils/oauth/together.d.ts +1 -0
- package/dist/types/utils/oauth/types.d.ts +45 -0
- package/dist/types/utils/oauth/venice.d.ts +18 -0
- package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/vllm.d.ts +16 -0
- package/dist/types/utils/oauth/xai.d.ts +30 -0
- package/dist/types/utils/oauth/xiaomi.d.ts +25 -0
- package/dist/types/utils/oauth/zai.d.ts +18 -0
- package/dist/types/utils/oauth/zenmux.d.ts +1 -0
- package/dist/types/utils/overflow.d.ts +64 -0
- package/dist/types/utils/parse-bind.d.ts +23 -0
- package/dist/types/utils/provider-response.d.ts +3 -0
- package/dist/types/utils/retry-after.d.ts +3 -0
- package/dist/types/utils/retry-budget.d.ts +1 -0
- package/dist/types/utils/retry.d.ts +26 -0
- package/dist/types/utils/schema/adapt.d.ts +24 -0
- package/dist/types/utils/schema/compatibility.d.ts +30 -0
- package/dist/types/utils/schema/dereference.d.ts +11 -0
- package/dist/types/utils/schema/draft.d.ts +10 -0
- package/dist/types/utils/schema/equality.d.ts +4 -0
- package/dist/types/utils/schema/fields.d.ts +49 -0
- package/dist/types/utils/schema/index.d.ts +14 -0
- package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
- package/dist/types/utils/schema/meta-validator.d.ts +2 -0
- package/dist/types/utils/schema/normalize.d.ts +93 -0
- package/dist/types/utils/schema/root-combinator.d.ts +12 -0
- package/dist/types/utils/schema/spill.d.ts +8 -0
- package/dist/types/utils/schema/stamps.d.ts +25 -0
- package/dist/types/utils/schema/types.d.ts +4 -0
- package/dist/types/utils/schema/wire.d.ts +54 -0
- package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
- package/dist/types/utils/sse-debug.d.ts +10 -0
- package/dist/types/utils/tool-call-healing.d.ts +71 -0
- package/dist/types/utils/tool-choice-capability.d.ts +41 -0
- package/dist/types/utils/tool-choice.d.ts +50 -0
- package/dist/types/utils/validation.d.ts +17 -0
- package/dist/types/utils.d.ts +35 -0
- package/package.json +25 -24
- package/src/providers/amazon-bedrock.ts +2 -2
- package/src/providers/anthropic-messages-server.ts +2 -1
- package/src/providers/anthropic.ts +107 -9
- package/src/providers/google-shared.ts +3 -3
- package/src/providers/openai-chat-server.ts +3 -2
- package/src/providers/openai-codex/request-transformer.ts +5 -0
- package/src/providers/openai-responses-server.ts +6 -3
- package/src/providers/openai-responses-shared.ts +3 -3
- package/src/rate-limit-utils.ts +9 -2
- package/src/utils.ts +28 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@sayknow-cli/ai",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.7",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://github.com/jaybeyond/Sayknow_CLI",
|
|
7
7
|
"author": "jaybeyond",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"api"
|
|
28
28
|
],
|
|
29
29
|
"main": "./src/index.ts",
|
|
30
|
-
"types": "./
|
|
30
|
+
"types": "./dist/types/index.d.ts",
|
|
31
31
|
"bin": {
|
|
32
32
|
"pi-ai": "./src/cli.ts"
|
|
33
33
|
},
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@anthropic-ai/sdk": "^0.94.0",
|
|
45
45
|
"@bufbuild/protobuf": "^2.12.0",
|
|
46
|
-
"@sayknow-cli/utils": "0.3.
|
|
46
|
+
"@sayknow-cli/utils": "0.3.7",
|
|
47
47
|
"openai": "^6.36.0",
|
|
48
48
|
"partial-json": "^0.1.7",
|
|
49
49
|
"zod": "4.4.3"
|
|
@@ -57,87 +57,88 @@
|
|
|
57
57
|
"files": [
|
|
58
58
|
"src",
|
|
59
59
|
"README.md",
|
|
60
|
-
"CHANGELOG.md"
|
|
60
|
+
"CHANGELOG.md",
|
|
61
|
+
"dist/types"
|
|
61
62
|
],
|
|
62
63
|
"exports": {
|
|
63
64
|
".": {
|
|
64
|
-
"types": "./
|
|
65
|
+
"types": "./dist/types/index.d.ts",
|
|
65
66
|
"import": "./src/index.ts"
|
|
66
67
|
},
|
|
67
68
|
"./*": {
|
|
68
|
-
"types": "./
|
|
69
|
+
"types": "./dist/types/*.d.ts",
|
|
69
70
|
"import": "./src/*.ts"
|
|
70
71
|
},
|
|
71
72
|
"./auth-broker": {
|
|
72
|
-
"types": "./
|
|
73
|
+
"types": "./dist/types/auth-broker/index.d.ts",
|
|
73
74
|
"import": "./src/auth-broker/index.ts"
|
|
74
75
|
},
|
|
75
76
|
"./auth-broker/*": {
|
|
76
|
-
"types": "./
|
|
77
|
+
"types": "./dist/types/auth-broker/*.d.ts",
|
|
77
78
|
"import": "./src/auth-broker/*.ts"
|
|
78
79
|
},
|
|
79
80
|
"./auth-gateway": {
|
|
80
|
-
"types": "./
|
|
81
|
+
"types": "./dist/types/auth-gateway/index.d.ts",
|
|
81
82
|
"import": "./src/auth-gateway/index.ts"
|
|
82
83
|
},
|
|
83
84
|
"./auth-gateway/*": {
|
|
84
|
-
"types": "./
|
|
85
|
+
"types": "./dist/types/auth-gateway/*.d.ts",
|
|
85
86
|
"import": "./src/auth-gateway/*.ts"
|
|
86
87
|
},
|
|
87
88
|
"./models.json": {
|
|
88
|
-
"types": "./
|
|
89
|
+
"types": "./dist/types/models.json.d.d.ts",
|
|
89
90
|
"import": "./src/models.json"
|
|
90
91
|
},
|
|
91
92
|
"./provider-models": {
|
|
92
|
-
"types": "./
|
|
93
|
+
"types": "./dist/types/provider-models/index.d.ts",
|
|
93
94
|
"import": "./src/provider-models/index.ts"
|
|
94
95
|
},
|
|
95
96
|
"./provider-models/*": {
|
|
96
|
-
"types": "./
|
|
97
|
+
"types": "./dist/types/provider-models/*.d.ts",
|
|
97
98
|
"import": "./src/provider-models/*.ts"
|
|
98
99
|
},
|
|
99
100
|
"./providers/*": {
|
|
100
|
-
"types": "./
|
|
101
|
+
"types": "./dist/types/providers/*.d.ts",
|
|
101
102
|
"import": "./src/providers/*.ts"
|
|
102
103
|
},
|
|
103
104
|
"./providers/cursor/gen/*": {
|
|
104
|
-
"types": "./
|
|
105
|
+
"types": "./dist/types/providers/cursor/gen/*.d.ts",
|
|
105
106
|
"import": "./src/providers/cursor/gen/*.ts"
|
|
106
107
|
},
|
|
107
108
|
"./providers/openai-codex/*": {
|
|
108
|
-
"types": "./
|
|
109
|
+
"types": "./dist/types/providers/openai-codex/*.d.ts",
|
|
109
110
|
"import": "./src/providers/openai-codex/*.ts"
|
|
110
111
|
},
|
|
111
112
|
"./usage/*": {
|
|
112
|
-
"types": "./
|
|
113
|
+
"types": "./dist/types/usage/*.d.ts",
|
|
113
114
|
"import": "./src/usage/*.ts"
|
|
114
115
|
},
|
|
115
116
|
"./utils/*": {
|
|
116
|
-
"types": "./
|
|
117
|
+
"types": "./dist/types/utils/*.d.ts",
|
|
117
118
|
"import": "./src/utils/*.ts"
|
|
118
119
|
},
|
|
119
120
|
"./utils/discovery": {
|
|
120
|
-
"types": "./
|
|
121
|
+
"types": "./dist/types/utils/discovery/index.d.ts",
|
|
121
122
|
"import": "./src/utils/discovery/index.ts"
|
|
122
123
|
},
|
|
123
124
|
"./utils/discovery/*": {
|
|
124
|
-
"types": "./
|
|
125
|
+
"types": "./dist/types/utils/discovery/*.d.ts",
|
|
125
126
|
"import": "./src/utils/discovery/*.ts"
|
|
126
127
|
},
|
|
127
128
|
"./utils/oauth": {
|
|
128
|
-
"types": "./
|
|
129
|
+
"types": "./dist/types/utils/oauth/index.d.ts",
|
|
129
130
|
"import": "./src/utils/oauth/index.ts"
|
|
130
131
|
},
|
|
131
132
|
"./utils/oauth/*": {
|
|
132
|
-
"types": "./
|
|
133
|
+
"types": "./dist/types/utils/oauth/*.d.ts",
|
|
133
134
|
"import": "./src/utils/oauth/*.ts"
|
|
134
135
|
},
|
|
135
136
|
"./utils/schema": {
|
|
136
|
-
"types": "./
|
|
137
|
+
"types": "./dist/types/utils/schema/index.d.ts",
|
|
137
138
|
"import": "./src/utils/schema/index.ts"
|
|
138
139
|
},
|
|
139
140
|
"./utils/schema/*": {
|
|
140
|
-
"types": "./
|
|
141
|
+
"types": "./dist/types/utils/schema/*.d.ts",
|
|
141
142
|
"import": "./src/utils/schema/*.ts"
|
|
142
143
|
},
|
|
143
144
|
"./*.js": "./src/*.ts"
|
|
@@ -28,7 +28,7 @@ import type {
|
|
|
28
28
|
ToolChoice,
|
|
29
29
|
ToolResultMessage,
|
|
30
30
|
} from "../types";
|
|
31
|
-
import { normalizeToolCallId, resolveCacheRetention } from "../utils";
|
|
31
|
+
import { normalizeToolCallId, resolveCacheRetention, sanitizeJsonStrings } from "../utils";
|
|
32
32
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
33
33
|
import { appendRawHttpRequestDumpFor400, type RawHttpRequestDump, withHttpStatus } from "../utils/http-inspector";
|
|
34
34
|
import { parseStreamingJson } from "../utils/json-parse";
|
|
@@ -701,7 +701,7 @@ function convertMessages(
|
|
|
701
701
|
toolUse: {
|
|
702
702
|
toolUseId: normalizeToolCallId(c.id),
|
|
703
703
|
name: c.name,
|
|
704
|
-
input: c.arguments,
|
|
704
|
+
input: sanitizeJsonStrings(c.arguments),
|
|
705
705
|
},
|
|
706
706
|
});
|
|
707
707
|
break;
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
ToolResultMessage,
|
|
14
14
|
UserMessage,
|
|
15
15
|
} from "../types";
|
|
16
|
+
import { sanitizeJsonStrings } from "../utils";
|
|
16
17
|
import {
|
|
17
18
|
type AnthropicAssistantContentBlock,
|
|
18
19
|
type AnthropicMessage,
|
|
@@ -433,7 +434,7 @@ function encodeContentBlocks(message: AssistantMessage): Record<string, unknown>
|
|
|
433
434
|
blocks.push({ type: "redacted_thinking", data: c.data });
|
|
434
435
|
break;
|
|
435
436
|
case "toolCall":
|
|
436
|
-
blocks.push({ type: "tool_use", id: c.id, name: c.name, input: c.arguments ?? {} });
|
|
437
|
+
blocks.push({ type: "tool_use", id: c.id, name: c.name, input: sanitizeJsonStrings(c.arguments ?? {}) });
|
|
437
438
|
break;
|
|
438
439
|
}
|
|
439
440
|
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "@sayknow-cli/utils";
|
|
22
22
|
import { hasOpus47ApiRestrictions, mapEffortToAnthropicAdaptiveEffort } from "../model-thinking";
|
|
23
23
|
import { calculateCost } from "../models";
|
|
24
|
+
import { isUsageLimitError } from "../rate-limit-utils";
|
|
24
25
|
import { getEnvApiKey, OUTPUT_FALLBACK_BUFFER } from "../stream";
|
|
25
26
|
import type {
|
|
26
27
|
Api,
|
|
@@ -52,6 +53,7 @@ import {
|
|
|
52
53
|
normalizeSystemPrompts,
|
|
53
54
|
normalizeToolCallId,
|
|
54
55
|
resolveCacheRetention,
|
|
56
|
+
sanitizeJsonStrings,
|
|
55
57
|
} from "../utils";
|
|
56
58
|
import { createAbortSourceTracker } from "../utils/abort";
|
|
57
59
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
@@ -62,6 +64,7 @@ import { parseJsonWithRepair, parseStreamingJson } from "../utils/json-parse";
|
|
|
62
64
|
import { parseGitHubCopilotApiKey } from "../utils/oauth/github-copilot";
|
|
63
65
|
import { notifyProviderResponse } from "../utils/provider-response";
|
|
64
66
|
import { isCopilotTransientModelError } from "../utils/retry";
|
|
67
|
+
import { getRetryAfterMsFromHeaders } from "../utils/retry-after";
|
|
65
68
|
import { resolveRetryBudget } from "../utils/retry-budget";
|
|
66
69
|
import {
|
|
67
70
|
COMBINATOR_KEYS,
|
|
@@ -724,6 +727,7 @@ export type AnthropicClientOptionsArgs = {
|
|
|
724
727
|
onSseEvent?: AnthropicOptions["onSseEvent"];
|
|
725
728
|
fetch?: FetchImpl;
|
|
726
729
|
requestMaxRetries?: number;
|
|
730
|
+
maxRetryDelayMs?: number;
|
|
727
731
|
};
|
|
728
732
|
|
|
729
733
|
export type AnthropicClientOptionsResult = {
|
|
@@ -877,6 +881,101 @@ function mergeHeaders(...headerSources: (Record<string, string> | undefined)[]):
|
|
|
877
881
|
return merged;
|
|
878
882
|
}
|
|
879
883
|
|
|
884
|
+
const ANTHROPIC_RETRY_DELAY_CAP_MS = 60_000;
|
|
885
|
+
const ANTHROPIC_RATE_LIMIT_HEADER_PREFIX = "anthropic-ratelimit-";
|
|
886
|
+
|
|
887
|
+
function getSafeAnthropicHeaderEvidence(headers: Headers): string[] {
|
|
888
|
+
const evidence: string[] = [];
|
|
889
|
+
for (const [name, value] of headers) {
|
|
890
|
+
const lowerName = name.toLowerCase();
|
|
891
|
+
if (
|
|
892
|
+
lowerName === "retry-after" ||
|
|
893
|
+
lowerName === "retry-after-ms" ||
|
|
894
|
+
lowerName.startsWith(ANTHROPIC_RATE_LIMIT_HEADER_PREFIX)
|
|
895
|
+
) {
|
|
896
|
+
evidence.push(`${lowerName}=${value}`);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return evidence.sort();
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function getStringProperty(source: unknown, key: string): string | undefined {
|
|
903
|
+
if (!isRecord(source)) return undefined;
|
|
904
|
+
const value = source[key];
|
|
905
|
+
return typeof value === "string" ? value : undefined;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function appendAnthropicRateLimitEvidence(bodyText: string, headers: Headers): string {
|
|
909
|
+
const evidence = getSafeAnthropicHeaderEvidence(headers);
|
|
910
|
+
if (evidence.length === 0) return bodyText;
|
|
911
|
+
|
|
912
|
+
const suffix = ` Anthropic rate-limit evidence: ${evidence.join(", ")}`;
|
|
913
|
+
try {
|
|
914
|
+
const parsed = JSON.parse(bodyText) as unknown;
|
|
915
|
+
if (isRecord(parsed)) {
|
|
916
|
+
const error = parsed.error;
|
|
917
|
+
if (isRecord(error) && typeof error.message === "string" && !error.message.includes(suffix)) {
|
|
918
|
+
return JSON.stringify({ ...parsed, error: { ...error, message: `${error.message}${suffix}` } });
|
|
919
|
+
}
|
|
920
|
+
if (typeof parsed.message === "string" && !parsed.message.includes(suffix)) {
|
|
921
|
+
return JSON.stringify({ ...parsed, message: `${parsed.message}${suffix}` });
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
} catch {}
|
|
925
|
+
|
|
926
|
+
return bodyText.includes(suffix) ? bodyText : `${bodyText}${suffix}`;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function isAnthropicUsageExhaustionResponse(
|
|
930
|
+
bodyText: string,
|
|
931
|
+
headers: Headers,
|
|
932
|
+
retryAfterMs: number | undefined,
|
|
933
|
+
retryDelayCapMs: number,
|
|
934
|
+
): boolean {
|
|
935
|
+
const overageReason = headers.get("anthropic-ratelimit-unified-overage-disabled-reason")?.toLowerCase();
|
|
936
|
+
if (overageReason === "out_of_credits") return true;
|
|
937
|
+
if (retryAfterMs !== undefined && retryAfterMs > retryDelayCapMs) return true;
|
|
938
|
+
|
|
939
|
+
try {
|
|
940
|
+
const parsed = JSON.parse(bodyText) as unknown;
|
|
941
|
+
const error = isRecord(parsed) ? parsed.error : undefined;
|
|
942
|
+
const type = getStringProperty(error, "type") ?? getStringProperty(parsed, "type");
|
|
943
|
+
const message = getStringProperty(error, "message") ?? getStringProperty(parsed, "message") ?? bodyText;
|
|
944
|
+
return (
|
|
945
|
+
/rate_limit_error/i.test(type ?? "") &&
|
|
946
|
+
(/request would exceed your account.?s rate limit/i.test(message) || /out_of_credits/i.test(message))
|
|
947
|
+
);
|
|
948
|
+
} catch {
|
|
949
|
+
return /request would exceed your account.?s rate limit|out_of_credits/i.test(bodyText);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function wrapAnthropicFetchForBoundedRateLimits(baseFetch: FetchImpl, maxRetryDelayMs: number | undefined): FetchImpl {
|
|
954
|
+
const retryDelayCapMs = maxRetryDelayMs ?? ANTHROPIC_RETRY_DELAY_CAP_MS;
|
|
955
|
+
return Object.assign(
|
|
956
|
+
async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
957
|
+
const response = await baseFetch(input, init);
|
|
958
|
+
if (response.status !== 429 || retryDelayCapMs === 0) return response;
|
|
959
|
+
|
|
960
|
+
const headers = new Headers(response.headers);
|
|
961
|
+
const retryAfterMs = getRetryAfterMsFromHeaders(headers);
|
|
962
|
+
const bodyText = await response
|
|
963
|
+
.clone()
|
|
964
|
+
.text()
|
|
965
|
+
.catch(() => "");
|
|
966
|
+
if (!isAnthropicUsageExhaustionResponse(bodyText, headers, retryAfterMs, retryDelayCapMs)) return response;
|
|
967
|
+
|
|
968
|
+
headers.set("x-should-retry", "false");
|
|
969
|
+
return new Response(appendAnthropicRateLimitEvidence(bodyText, headers), {
|
|
970
|
+
status: response.status,
|
|
971
|
+
statusText: response.statusText,
|
|
972
|
+
headers,
|
|
973
|
+
});
|
|
974
|
+
},
|
|
975
|
+
baseFetch.preconnect ? { preconnect: baseFetch.preconnect } : {},
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
|
|
880
979
|
// The Anthropic SDK logs malformed SSE frames directly before rethrowing them.
|
|
881
980
|
// We surface the resulting provider error ourselves, so keep the SDK quiet.
|
|
882
981
|
const ANTHROPIC_SDK_LOG_LEVEL = "off" as const;
|
|
@@ -1042,6 +1141,7 @@ export function isProviderRetryableError(error: unknown, provider?: string): boo
|
|
|
1042
1141
|
if (!(error instanceof Error)) return false;
|
|
1043
1142
|
if (provider === "github-copilot" && isCopilotTransientModelError(error)) return true;
|
|
1044
1143
|
const msg = error.message.toLowerCase();
|
|
1144
|
+
if (isUsageLimitError(error.message)) return false;
|
|
1045
1145
|
if (
|
|
1046
1146
|
isUnexpectedSocketCloseMessage(msg) ||
|
|
1047
1147
|
/rate.?limit|too many requests|overloaded|service.?unavailable|internal_error|stream error.*received from peer|1302|timed?\s*out while waiting for the first event|timeout waiting for first/i.test(
|
|
@@ -1166,6 +1266,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1166
1266
|
onSseEvent: options?.onSseEvent,
|
|
1167
1267
|
fetch: options?.fetch,
|
|
1168
1268
|
requestMaxRetries: options?.requestMaxRetries,
|
|
1269
|
+
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
1169
1270
|
});
|
|
1170
1271
|
client = created.client;
|
|
1171
1272
|
isOAuthToken = created.isOAuthToken;
|
|
@@ -1723,11 +1824,8 @@ export function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): A
|
|
|
1723
1824
|
const foundryCustomHeaders = resolveAnthropicCustomHeaders(model);
|
|
1724
1825
|
const tlsFetchOptions = buildClaudeCodeTlsFetchOptions(model, baseUrl);
|
|
1725
1826
|
const baseFetch = args.fetch ?? fetch;
|
|
1726
|
-
const
|
|
1727
|
-
|
|
1728
|
-
: args.fetch
|
|
1729
|
-
? baseFetch
|
|
1730
|
-
: undefined;
|
|
1827
|
+
const boundedFetch = wrapAnthropicFetchForBoundedRateLimits(baseFetch, args.maxRetryDelayMs);
|
|
1828
|
+
const debugFetch = onSseEvent ? wrapFetchForSseDebug(boundedFetch, event => onSseEvent(event, model)) : boundedFetch;
|
|
1731
1829
|
if (model.provider === "github-copilot") {
|
|
1732
1830
|
const copilotApiKey = parseGitHubCopilotApiKey(apiKey).accessToken;
|
|
1733
1831
|
const betaFeatures = [...extraBetas];
|
|
@@ -1755,7 +1853,7 @@ export function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): A
|
|
|
1755
1853
|
dangerouslyAllowBrowser: true,
|
|
1756
1854
|
defaultHeaders,
|
|
1757
1855
|
logLevel: ANTHROPIC_SDK_LOG_LEVEL,
|
|
1758
|
-
|
|
1856
|
+
fetch: debugFetch,
|
|
1759
1857
|
...(tlsFetchOptions ? { fetchOptions: tlsFetchOptions } : {}),
|
|
1760
1858
|
};
|
|
1761
1859
|
}
|
|
@@ -1789,7 +1887,7 @@ export function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): A
|
|
|
1789
1887
|
dangerouslyAllowBrowser: true,
|
|
1790
1888
|
defaultHeaders,
|
|
1791
1889
|
logLevel: ANTHROPIC_SDK_LOG_LEVEL,
|
|
1792
|
-
|
|
1890
|
+
fetch: debugFetch,
|
|
1793
1891
|
};
|
|
1794
1892
|
}
|
|
1795
1893
|
|
|
@@ -1802,7 +1900,7 @@ export function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): A
|
|
|
1802
1900
|
dangerouslyAllowBrowser: true,
|
|
1803
1901
|
defaultHeaders,
|
|
1804
1902
|
logLevel: ANTHROPIC_SDK_LOG_LEVEL,
|
|
1805
|
-
|
|
1903
|
+
fetch: debugFetch,
|
|
1806
1904
|
...(tlsFetchOptions ? { fetchOptions: tlsFetchOptions } : {}),
|
|
1807
1905
|
};
|
|
1808
1906
|
}
|
|
@@ -2394,7 +2492,7 @@ export function convertAnthropicMessages(
|
|
|
2394
2492
|
type: "tool_use",
|
|
2395
2493
|
id: block.id,
|
|
2396
2494
|
name: isOAuthToken ? applyClaudeToolPrefix(block.name) : block.name,
|
|
2397
|
-
input: block.arguments ?? {},
|
|
2495
|
+
input: sanitizeJsonStrings(block.arguments ?? {}),
|
|
2398
2496
|
});
|
|
2399
2497
|
}
|
|
2400
2498
|
}
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
Tool,
|
|
19
19
|
ToolCall,
|
|
20
20
|
} from "../types";
|
|
21
|
-
import { normalizeSystemPrompts } from "../utils";
|
|
21
|
+
import { normalizeSystemPrompts, sanitizeJsonStrings } from "../utils";
|
|
22
22
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
23
23
|
import { finalizeErrorMessage, type RawHttpRequestDump, withHttpStatus } from "../utils/http-inspector";
|
|
24
24
|
import { normalizeSchemaForCCA, normalizeSchemaForGoogle, toolWireSchema } from "../utils/schema";
|
|
@@ -237,7 +237,7 @@ export function convertMessages<T extends GoogleApiType>(model: Model<T>, contex
|
|
|
237
237
|
const part: Part = {
|
|
238
238
|
functionCall: {
|
|
239
239
|
name: block.name,
|
|
240
|
-
args: block.arguments ?? {},
|
|
240
|
+
args: sanitizeJsonStrings(block.arguments ?? {}) as Record<string, unknown>,
|
|
241
241
|
...(requiresToolCallId(model.id) ? { id: block.id } : {}),
|
|
242
242
|
},
|
|
243
243
|
};
|
|
@@ -467,7 +467,7 @@ export function pushToolCallEvents(
|
|
|
467
467
|
stream.push({
|
|
468
468
|
type: "toolcall_delta",
|
|
469
469
|
contentIndex,
|
|
470
|
-
delta: JSON.stringify(toolCall.arguments),
|
|
470
|
+
delta: JSON.stringify(sanitizeJsonStrings(toolCall.arguments)),
|
|
471
471
|
partial: output,
|
|
472
472
|
});
|
|
473
473
|
stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
ToolResultMessage,
|
|
20
20
|
TSchema,
|
|
21
21
|
} from "../types";
|
|
22
|
+
import { sanitizeJsonStrings } from "../utils";
|
|
22
23
|
import {
|
|
23
24
|
type OpenAIChatContentPart,
|
|
24
25
|
type OpenAIChatMessage,
|
|
@@ -453,9 +454,9 @@ function isOnlyRaw(args: Record<string, unknown>): boolean {
|
|
|
453
454
|
|
|
454
455
|
function stringifyArgs(args: Record<string, unknown>): string {
|
|
455
456
|
// `__raw` is our fallback marker for un-parseable inbound args; preserve it verbatim on the way out.
|
|
456
|
-
if (typeof args.__raw === "string" && isOnlyRaw(args)) return args.__raw;
|
|
457
|
+
if (typeof args.__raw === "string" && isOnlyRaw(args)) return args.__raw.toWellFormed();
|
|
457
458
|
try {
|
|
458
|
-
return JSON.stringify(args);
|
|
459
|
+
return JSON.stringify(sanitizeJsonStrings(args));
|
|
459
460
|
} catch {
|
|
460
461
|
return "{}";
|
|
461
462
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Effort } from "../../model-thinking";
|
|
2
2
|
import { requireSupportedEffort } from "../../model-thinking";
|
|
3
3
|
import type { Api, Model } from "../../types";
|
|
4
|
+
import { sanitizeJsonStrings } from "../../utils";
|
|
4
5
|
|
|
5
6
|
export interface ReasoningConfig {
|
|
6
7
|
effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
@@ -109,6 +110,10 @@ function normalizeInputTextPartFields(input: InputItem[] | undefined): InputItem
|
|
|
109
110
|
}
|
|
110
111
|
if (normalizedItem.type === "message") {
|
|
111
112
|
normalizedItem.content = normalizeTextPartFields(normalizedItem.content, `input[${itemIndex}].content`);
|
|
113
|
+
} else if (normalizedItem.type === "function_call" && "arguments" in itemRecord) {
|
|
114
|
+
itemRecord.arguments = sanitizeJsonStrings(itemRecord.arguments);
|
|
115
|
+
} else if (normalizedItem.type === "custom_tool_call" && typeof itemRecord.input === "string") {
|
|
116
|
+
itemRecord.input = itemRecord.input.toWellFormed();
|
|
112
117
|
}
|
|
113
118
|
return normalizedItem;
|
|
114
119
|
});
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
Tool,
|
|
23
23
|
ToolCall,
|
|
24
24
|
} from "../types";
|
|
25
|
+
import { sanitizeJsonStrings } from "../utils";
|
|
25
26
|
import {
|
|
26
27
|
type OpenAIResponsesFunctionCallItem,
|
|
27
28
|
type OpenAIResponsesFunctionCallOutputItem,
|
|
@@ -612,7 +613,8 @@ function buildOutputItems(message: AssistantMessage): OutputItem[] {
|
|
|
612
613
|
} else if (part.type === "toolCall") {
|
|
613
614
|
flushMessage();
|
|
614
615
|
if (part.customWireName) {
|
|
615
|
-
const rawInput =
|
|
616
|
+
const rawInput =
|
|
617
|
+
typeof part.arguments?.input === "string" ? (part.arguments.input as string).toWellFormed() : "";
|
|
616
618
|
out.push({
|
|
617
619
|
type: "custom_tool_call",
|
|
618
620
|
id: part.thoughtSignature ?? makeCustomCallId(),
|
|
@@ -627,7 +629,7 @@ function buildOutputItems(message: AssistantMessage): OutputItem[] {
|
|
|
627
629
|
id: part.thoughtSignature ?? makeFuncCallId(),
|
|
628
630
|
call_id: part.id,
|
|
629
631
|
name: part.name,
|
|
630
|
-
arguments: JSON.stringify(part.arguments ?? {}),
|
|
632
|
+
arguments: JSON.stringify(sanitizeJsonStrings(part.arguments ?? {})),
|
|
631
633
|
status: "completed",
|
|
632
634
|
});
|
|
633
635
|
}
|
|
@@ -1085,7 +1087,8 @@ export function encodeStream(
|
|
|
1085
1087
|
} else {
|
|
1086
1088
|
// Standard JSON tool: arguments object on the skc side, the
|
|
1087
1089
|
// wire wants the JSON string the model emitted (= streamed deltas).
|
|
1088
|
-
const argsJson =
|
|
1090
|
+
const argsJson =
|
|
1091
|
+
cur.argsText.toWellFormed() || JSON.stringify(sanitizeJsonStrings(tc.arguments ?? {}));
|
|
1089
1092
|
cur.argsText = argsJson;
|
|
1090
1093
|
emit("response.function_call_arguments.done", {
|
|
1091
1094
|
item_id: cur.itemId,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
type ToolCall,
|
|
29
29
|
type ToolResultMessage,
|
|
30
30
|
} from "../types";
|
|
31
|
-
import { normalizeResponsesToolCallId } from "../utils";
|
|
31
|
+
import { normalizeResponsesToolCallId, sanitizeJsonStrings } from "../utils";
|
|
32
32
|
import type { AssistantMessageEventStream } from "../utils/event-stream";
|
|
33
33
|
import { isCompleteJson, parseStreamingJson } from "../utils/json-parse";
|
|
34
34
|
import { joinTextWithImagePlaceholder, NON_VISION_IMAGE_PLACEHOLDER, partitionVisionContent } from "./vision-guard";
|
|
@@ -275,7 +275,7 @@ export function convertResponsesAssistantMessage<TApi extends Api>(
|
|
|
275
275
|
}
|
|
276
276
|
knownCallIds.add(normalized.callId);
|
|
277
277
|
if (block.customWireName) {
|
|
278
|
-
const rawInput = typeof block.arguments?.input === "string" ? block.arguments.input : "";
|
|
278
|
+
const rawInput = typeof block.arguments?.input === "string" ? block.arguments.input.toWellFormed() : "";
|
|
279
279
|
customCallIds?.add(normalized.callId);
|
|
280
280
|
outputItems.push({
|
|
281
281
|
type: "custom_tool_call",
|
|
@@ -291,7 +291,7 @@ export function convertResponsesAssistantMessage<TApi extends Api>(
|
|
|
291
291
|
id: itemId,
|
|
292
292
|
call_id: normalized.callId,
|
|
293
293
|
name: block.name,
|
|
294
|
-
arguments: JSON.stringify(block.arguments),
|
|
294
|
+
arguments: JSON.stringify(sanitizeJsonStrings(block.arguments ?? {})),
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
297
|
|
package/src/rate-limit-utils.ts
CHANGED
|
@@ -36,6 +36,14 @@ export function parseRateLimitReason(errorMessage: string): RateLimitReason {
|
|
|
36
36
|
return "MODEL_CAPACITY_EXHAUSTED";
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
if (
|
|
40
|
+
lower.includes("out_of_credits") ||
|
|
41
|
+
lower.includes("request would exceed your account's rate limit") ||
|
|
42
|
+
lower.includes("request would exceed your accounts rate limit")
|
|
43
|
+
) {
|
|
44
|
+
return "QUOTA_EXHAUSTED";
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
if (
|
|
40
48
|
lower.includes("per minute") ||
|
|
41
49
|
lower.includes("rate limit") ||
|
|
@@ -86,8 +94,7 @@ export function calculateRateLimitBackoffMs(reason: RateLimitReason): number {
|
|
|
86
94
|
|
|
87
95
|
/** Detect usage/quota limit errors in error messages (persistent, requires credential switch). */
|
|
88
96
|
const USAGE_LIMIT_PATTERN =
|
|
89
|
-
/usage.?limit|usage_limit_reached|usage_not_included|limit_reached|model.?limit|model_limit_reached|message.?limit|message_limit_reached|limit for this model|quota.?exceeded|resource has been exhausted[^\n]*(?:quota|limit)/i;
|
|
90
|
-
|
|
97
|
+
/usage.?limit|usage_limit_reached|usage_not_included|limit_reached|model.?limit|model_limit_reached|message.?limit|message_limit_reached|limit for this model|quota.?exceeded|out_of_credits|request would exceed your account.?s rate limit|resource has been exhausted[^\n]*(?:quota|limit)/i;
|
|
91
98
|
export function isUsageLimitError(errorMessage: string): boolean {
|
|
92
99
|
return USAGE_LIMIT_PATTERN.test(errorMessage);
|
|
93
100
|
}
|
package/src/utils.ts
CHANGED
|
@@ -11,6 +11,34 @@ export function normalizeSystemPrompts(systemPrompt: readonly string[] | string
|
|
|
11
11
|
return prompts.map(prompt => prompt.toWellFormed()).filter(prompt => prompt.length > 0);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export function sanitizeJsonStrings(value: unknown): unknown {
|
|
15
|
+
return sanitizeJsonStringsInner(value, new WeakMap<object, unknown>());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sanitizeJsonStringsInner(value: unknown, seen: WeakMap<object, unknown>): unknown {
|
|
19
|
+
if (typeof value === "string") return value.toWellFormed();
|
|
20
|
+
if (!value || typeof value !== "object") return value;
|
|
21
|
+
|
|
22
|
+
const cached = seen.get(value);
|
|
23
|
+
if (cached !== undefined) return cached;
|
|
24
|
+
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
const sanitized: unknown[] = [];
|
|
27
|
+
seen.set(value, sanitized);
|
|
28
|
+
for (const item of value) {
|
|
29
|
+
sanitized.push(sanitizeJsonStringsInner(item, seen));
|
|
30
|
+
}
|
|
31
|
+
return sanitized;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sanitized: Record<string, unknown> = {};
|
|
35
|
+
seen.set(value, sanitized);
|
|
36
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
37
|
+
sanitized[key.toWellFormed()] = sanitizeJsonStringsInner(nestedValue, seen);
|
|
38
|
+
}
|
|
39
|
+
return sanitized;
|
|
40
|
+
}
|
|
41
|
+
|
|
14
42
|
export function toNumber(value: unknown): number | undefined {
|
|
15
43
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
16
44
|
if (typeof value === "string" && value.trim()) {
|