phi-code-ai 0.56.4 → 0.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +258 -73
- package/dist/api-registry.d.ts.map +1 -1
- package/dist/api-registry.js.map +1 -1
- package/dist/bedrock-provider.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/env-api-keys.d.ts +9 -0
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +96 -30
- package/dist/env-api-keys.js.map +1 -1
- package/dist/image-models.d.ts +10 -0
- package/dist/image-models.d.ts.map +1 -0
- package/dist/image-models.generated.d.ts +305 -0
- package/dist/image-models.generated.d.ts.map +1 -0
- package/dist/image-models.generated.js +307 -0
- package/dist/image-models.generated.js.map +1 -0
- package/dist/image-models.js +23 -0
- package/dist/image-models.js.map +1 -0
- package/dist/images-api-registry.d.ts +14 -0
- package/dist/images-api-registry.d.ts.map +1 -0
- package/dist/images-api-registry.js +22 -0
- package/dist/images-api-registry.js.map +1 -0
- package/dist/images.d.ts +4 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +14 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +20 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +3 -9
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +6525 -2231
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +8992 -5524
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +28 -12
- package/dist/models.js.map +1 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts +23 -0
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +206 -44
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/anthropic.d.ts +23 -2
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +294 -63
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.js +47 -23
- package/dist/providers/azure-openai-responses.js.map +1 -1
- package/dist/providers/cloudflare.d.ts +13 -0
- package/dist/providers/cloudflare.d.ts.map +1 -0
- package/dist/providers/cloudflare.js +26 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/faux.d.ts +56 -0
- package/dist/providers/faux.d.ts.map +1 -0
- package/dist/providers/faux.js +368 -0
- package/dist/providers/faux.js.map +1 -0
- package/dist/providers/github-copilot-headers.d.ts.map +1 -1
- package/dist/providers/github-copilot-headers.js.map +1 -1
- package/dist/providers/google-shared.d.ts +7 -2
- package/dist/providers/google-shared.d.ts.map +1 -1
- package/dist/providers/google-shared.js +53 -24
- package/dist/providers/google-shared.js.map +1 -1
- package/dist/providers/google-vertex.d.ts +1 -1
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +87 -16
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +57 -9
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/images/openrouter.d.ts +3 -0
- package/dist/providers/images/openrouter.d.ts.map +1 -0
- package/dist/providers/images/openrouter.js +129 -0
- package/dist/providers/images/openrouter.js.map +1 -0
- package/dist/providers/images/register-builtins.d.ts +4 -0
- package/dist/providers/images/register-builtins.d.ts.map +1 -0
- package/dist/providers/images/register-builtins.js +34 -0
- package/dist/providers/images/register-builtins.js.map +1 -0
- package/dist/providers/mistral.d.ts +3 -0
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js +49 -9
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/openai-codex-responses.d.ts +21 -0
- package/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/dist/providers/openai-codex-responses.js +443 -86
- package/dist/providers/openai-codex-responses.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +5 -1
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +459 -225
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses-shared.d.ts +1 -0
- package/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/dist/providers/openai-responses-shared.js +95 -45
- package/dist/providers/openai-responses-shared.js.map +1 -1
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +66 -44
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/register-builtins.d.ts +27 -2
- package/dist/providers/register-builtins.d.ts.map +1 -1
- package/dist/providers/register-builtins.js +157 -52
- package/dist/providers/register-builtins.js.map +1 -1
- package/dist/providers/simple-options.d.ts.map +1 -1
- package/dist/providers/simple-options.js +5 -1
- package/dist/providers/simple-options.js.map +1 -1
- package/dist/providers/transform-messages.d.ts.map +1 -1
- package/dist/providers/transform-messages.js +63 -34
- package/dist/providers/transform-messages.js.map +1 -1
- package/dist/session-resources.d.ts +4 -0
- package/dist/session-resources.d.ts.map +1 -0
- package/dist/session-resources.js +22 -0
- package/dist/session-resources.js.map +1 -0
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js.map +1 -1
- package/dist/types.d.ts +219 -15
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/diagnostics.d.ts +19 -0
- package/dist/utils/diagnostics.d.ts.map +1 -0
- package/dist/utils/diagnostics.js +25 -0
- package/dist/utils/diagnostics.js.map +1 -0
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +7 -3
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/hash.d.ts.map +1 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/headers.d.ts +2 -0
- package/dist/utils/headers.d.ts.map +1 -0
- package/dist/utils/headers.js +8 -0
- package/dist/utils/headers.js.map +1 -0
- package/dist/utils/json-parse.d.ts +8 -1
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +89 -5
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/oauth/anthropic.d.ts +14 -6
- package/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/dist/utils/oauth/anthropic.js +288 -57
- package/dist/utils/oauth/anthropic.js.map +1 -1
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/dist/utils/oauth/github-copilot.js +23 -12
- package/dist/utils/oauth/github-copilot.js.map +1 -1
- package/dist/utils/oauth/index.d.ts +0 -4
- package/dist/utils/oauth/index.d.ts.map +1 -1
- package/dist/utils/oauth/index.js +0 -10
- package/dist/utils/oauth/index.js.map +1 -1
- package/dist/utils/oauth/oauth-page.d.ts +3 -0
- package/dist/utils/oauth/oauth-page.d.ts.map +1 -0
- package/dist/utils/oauth/oauth-page.js +105 -0
- package/dist/utils/oauth/oauth-page.js.map +1 -0
- package/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/dist/utils/oauth/openai-codex.js +51 -46
- package/dist/utils/oauth/openai-codex.js.map +1 -1
- package/dist/utils/oauth/pkce.d.ts.map +1 -1
- package/dist/utils/oauth/pkce.js.map +1 -1
- package/dist/utils/oauth/types.d.ts +10 -0
- package/dist/utils/oauth/types.d.ts.map +1 -1
- package/dist/utils/oauth/types.js.map +1 -1
- package/dist/utils/overflow.d.ts +7 -3
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +46 -13
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/sanitize-unicode.d.ts.map +1 -1
- package/dist/utils/sanitize-unicode.js.map +1 -1
- package/dist/utils/typebox-helpers.d.ts +1 -1
- package/dist/utils/typebox-helpers.d.ts.map +1 -1
- package/dist/utils/typebox-helpers.js +1 -1
- package/dist/utils/typebox-helpers.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +247 -38
- package/dist/utils/validation.js.map +1 -1
- package/package.json +43 -13
- package/bedrock-provider.d.ts +0 -1
- package/bedrock-provider.js +0 -1
- package/dist/providers/google-gemini-cli.d.ts +0 -74
- package/dist/providers/google-gemini-cli.d.ts.map +0 -1
- package/dist/providers/google-gemini-cli.js +0 -754
- package/dist/providers/google-gemini-cli.js.map +0 -1
- package/dist/utils/oauth/google-antigravity.d.ts +0 -26
- package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
- package/dist/utils/oauth/google-antigravity.js +0 -373
- package/dist/utils/oauth/google-antigravity.js.map +0 -1
- package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
- package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
- package/dist/utils/oauth/google-gemini-cli.js +0 -478
- package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
|
@@ -2,8 +2,10 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
2
2
|
import { getEnvApiKey } from "../env-api-keys.js";
|
|
3
3
|
import { calculateCost } from "../models.js";
|
|
4
4
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
5
|
-
import {
|
|
5
|
+
import { headersToRecord } from "../utils/headers.js";
|
|
6
|
+
import { parseJsonWithRepair, parseStreamingJson } from "../utils/json-parse.js";
|
|
6
7
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
8
|
+
import { resolveCloudflareBaseUrl } from "./cloudflare.js";
|
|
7
9
|
import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
|
|
8
10
|
import { adjustMaxTokensForThinking, buildBaseOptions } from "./simple-options.js";
|
|
9
11
|
import { transformMessages } from "./transform-messages.js";
|
|
@@ -20,19 +22,19 @@ function resolveCacheRetention(cacheRetention) {
|
|
|
20
22
|
}
|
|
21
23
|
return "short";
|
|
22
24
|
}
|
|
23
|
-
function getCacheControl(
|
|
25
|
+
function getCacheControl(model, cacheRetention) {
|
|
24
26
|
const retention = resolveCacheRetention(cacheRetention);
|
|
25
27
|
if (retention === "none") {
|
|
26
28
|
return { retention };
|
|
27
29
|
}
|
|
28
|
-
const ttl = retention === "long" &&
|
|
30
|
+
const ttl = retention === "long" && getAnthropicCompat(model).supportsLongCacheRetention ? "1h" : undefined;
|
|
29
31
|
return {
|
|
30
32
|
retention,
|
|
31
33
|
cacheControl: { type: "ephemeral", ...(ttl && { ttl }) },
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
// Stealth mode: Mimic Claude Code's tool naming exactly
|
|
35
|
-
const claudeCodeVersion = "2.1.
|
|
37
|
+
const claudeCodeVersion = "2.1.75";
|
|
36
38
|
// Claude Code 2.x tool names (canonical casing)
|
|
37
39
|
// Source: https://cchistory.mariozechner.at/data/prompts-2.1.11.md
|
|
38
40
|
// To update: https://github.com/badlogic/cchistory
|
|
@@ -103,6 +105,19 @@ function convertContentBlocks(content) {
|
|
|
103
105
|
}
|
|
104
106
|
return blocks;
|
|
105
107
|
}
|
|
108
|
+
const FINE_GRAINED_TOOL_STREAMING_BETA = "fine-grained-tool-streaming-2025-05-14";
|
|
109
|
+
const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
|
|
110
|
+
function getAnthropicCompat(model) {
|
|
111
|
+
// Auto-detect session affinity and cache control support from provider
|
|
112
|
+
const isFireworks = model.provider === "fireworks";
|
|
113
|
+
const isCloudflareAiGatewayAnthropic = model.provider === "cloudflare-ai-gateway" && model.baseUrl.includes("anthropic");
|
|
114
|
+
return {
|
|
115
|
+
supportsEagerToolInputStreaming: model.compat?.supportsEagerToolInputStreaming ?? !isFireworks,
|
|
116
|
+
supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? !isFireworks,
|
|
117
|
+
sendSessionAffinityHeaders: model.compat?.sendSessionAffinityHeaders ?? !!(isFireworks || isCloudflareAiGatewayAnthropic),
|
|
118
|
+
supportsCacheControlOnTools: model.compat?.supportsCacheControlOnTools ?? !isFireworks,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
106
121
|
function mergeHeaders(...headerSources) {
|
|
107
122
|
const merged = {};
|
|
108
123
|
for (const headers of headerSources) {
|
|
@@ -112,6 +127,157 @@ function mergeHeaders(...headerSources) {
|
|
|
112
127
|
}
|
|
113
128
|
return merged;
|
|
114
129
|
}
|
|
130
|
+
const ANTHROPIC_MESSAGE_EVENTS = new Set([
|
|
131
|
+
"message_start",
|
|
132
|
+
"message_delta",
|
|
133
|
+
"message_stop",
|
|
134
|
+
"content_block_start",
|
|
135
|
+
"content_block_delta",
|
|
136
|
+
"content_block_stop",
|
|
137
|
+
]);
|
|
138
|
+
function flushSseEvent(state) {
|
|
139
|
+
if (!state.event && state.data.length === 0) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const event = {
|
|
143
|
+
event: state.event,
|
|
144
|
+
data: state.data.join("\n"),
|
|
145
|
+
raw: [...state.raw],
|
|
146
|
+
};
|
|
147
|
+
state.event = null;
|
|
148
|
+
state.data = [];
|
|
149
|
+
state.raw = [];
|
|
150
|
+
return event;
|
|
151
|
+
}
|
|
152
|
+
function decodeSseLine(line, state) {
|
|
153
|
+
if (line === "") {
|
|
154
|
+
return flushSseEvent(state);
|
|
155
|
+
}
|
|
156
|
+
state.raw.push(line);
|
|
157
|
+
if (line.startsWith(":")) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
const delimiterIndex = line.indexOf(":");
|
|
161
|
+
const fieldName = delimiterIndex === -1 ? line : line.slice(0, delimiterIndex);
|
|
162
|
+
let value = delimiterIndex === -1 ? "" : line.slice(delimiterIndex + 1);
|
|
163
|
+
if (value.startsWith(" ")) {
|
|
164
|
+
value = value.slice(1);
|
|
165
|
+
}
|
|
166
|
+
if (fieldName === "event") {
|
|
167
|
+
state.event = value;
|
|
168
|
+
}
|
|
169
|
+
else if (fieldName === "data") {
|
|
170
|
+
state.data.push(value);
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
function nextLineBreakIndex(text) {
|
|
175
|
+
const carriageReturnIndex = text.indexOf("\r");
|
|
176
|
+
const newlineIndex = text.indexOf("\n");
|
|
177
|
+
if (carriageReturnIndex === -1) {
|
|
178
|
+
return newlineIndex;
|
|
179
|
+
}
|
|
180
|
+
if (newlineIndex === -1) {
|
|
181
|
+
return carriageReturnIndex;
|
|
182
|
+
}
|
|
183
|
+
return Math.min(carriageReturnIndex, newlineIndex);
|
|
184
|
+
}
|
|
185
|
+
function consumeLine(text) {
|
|
186
|
+
const lineBreakIndex = nextLineBreakIndex(text);
|
|
187
|
+
if (lineBreakIndex === -1) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
let nextIndex = lineBreakIndex + 1;
|
|
191
|
+
if (text[lineBreakIndex] === "\r" && text[nextIndex] === "\n") {
|
|
192
|
+
nextIndex += 1;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
line: text.slice(0, lineBreakIndex),
|
|
196
|
+
rest: text.slice(nextIndex),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
async function* iterateSseMessages(body, signal) {
|
|
200
|
+
const reader = body.getReader();
|
|
201
|
+
const decoder = new TextDecoder();
|
|
202
|
+
const state = { event: null, data: [], raw: [] };
|
|
203
|
+
let buffer = "";
|
|
204
|
+
try {
|
|
205
|
+
while (true) {
|
|
206
|
+
if (signal?.aborted) {
|
|
207
|
+
throw new Error("Request was aborted");
|
|
208
|
+
}
|
|
209
|
+
const { value, done } = await reader.read();
|
|
210
|
+
if (done) {
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
buffer += decoder.decode(value, { stream: true });
|
|
214
|
+
let consumed = consumeLine(buffer);
|
|
215
|
+
while (consumed) {
|
|
216
|
+
buffer = consumed.rest;
|
|
217
|
+
const event = decodeSseLine(consumed.line, state);
|
|
218
|
+
if (event) {
|
|
219
|
+
yield event;
|
|
220
|
+
}
|
|
221
|
+
consumed = consumeLine(buffer);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
buffer += decoder.decode();
|
|
225
|
+
let consumed = consumeLine(buffer);
|
|
226
|
+
while (consumed) {
|
|
227
|
+
buffer = consumed.rest;
|
|
228
|
+
const event = decodeSseLine(consumed.line, state);
|
|
229
|
+
if (event) {
|
|
230
|
+
yield event;
|
|
231
|
+
}
|
|
232
|
+
consumed = consumeLine(buffer);
|
|
233
|
+
}
|
|
234
|
+
if (buffer.length > 0) {
|
|
235
|
+
const event = decodeSseLine(buffer, state);
|
|
236
|
+
if (event) {
|
|
237
|
+
yield event;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const trailingEvent = flushSseEvent(state);
|
|
241
|
+
if (trailingEvent) {
|
|
242
|
+
yield trailingEvent;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
reader.releaseLock();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async function* iterateAnthropicEvents(response, signal) {
|
|
250
|
+
if (!response.body) {
|
|
251
|
+
throw new Error("Attempted to iterate over an Anthropic response with no body");
|
|
252
|
+
}
|
|
253
|
+
let sawMessageStart = false;
|
|
254
|
+
let sawMessageEnd = false;
|
|
255
|
+
for await (const sse of iterateSseMessages(response.body, signal)) {
|
|
256
|
+
if (sse.event === "error") {
|
|
257
|
+
throw new Error(sse.data);
|
|
258
|
+
}
|
|
259
|
+
if (!ANTHROPIC_MESSAGE_EVENTS.has(sse.event ?? "")) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const event = parseJsonWithRepair(sse.data);
|
|
264
|
+
if (event.type === "message_start") {
|
|
265
|
+
sawMessageStart = true;
|
|
266
|
+
}
|
|
267
|
+
else if (event.type === "message_stop") {
|
|
268
|
+
sawMessageEnd = true;
|
|
269
|
+
}
|
|
270
|
+
yield event;
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
274
|
+
throw new Error(`Could not parse Anthropic SSE event ${sse.event}: ${message}; data=${sse.data}; raw=${sse.raw.join("\\n")}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (sawMessageStart && !sawMessageEnd) {
|
|
278
|
+
throw new Error("Anthropic stream ended before message_stop");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
115
281
|
export const streamAnthropic = (model, context, options) => {
|
|
116
282
|
const stream = new AssistantMessageEventStream();
|
|
117
283
|
(async () => {
|
|
@@ -133,23 +299,45 @@ export const streamAnthropic = (model, context, options) => {
|
|
|
133
299
|
timestamp: Date.now(),
|
|
134
300
|
};
|
|
135
301
|
try {
|
|
136
|
-
|
|
137
|
-
let
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
302
|
+
let client;
|
|
303
|
+
let isOAuth;
|
|
304
|
+
if (options?.client) {
|
|
305
|
+
client = options.client;
|
|
306
|
+
isOAuth = false;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
|
|
310
|
+
let copilotDynamicHeaders;
|
|
311
|
+
if (model.provider === "github-copilot") {
|
|
312
|
+
const hasImages = hasCopilotVisionInput(context.messages);
|
|
313
|
+
copilotDynamicHeaders = buildCopilotDynamicHeaders({
|
|
314
|
+
messages: context.messages,
|
|
315
|
+
hasImages,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
const cacheRetention = options?.cacheRetention ?? resolveCacheRetention();
|
|
319
|
+
const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
|
|
320
|
+
const created = createClient(model, apiKey, options?.interleavedThinking ?? true, shouldUseFineGrainedToolStreamingBeta(model, context), options?.headers, copilotDynamicHeaders, cacheSessionId);
|
|
321
|
+
client = created.client;
|
|
322
|
+
isOAuth = created.isOAuthToken;
|
|
323
|
+
}
|
|
324
|
+
let params = buildParams(model, context, isOAuth, options);
|
|
325
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
326
|
+
if (nextParams !== undefined) {
|
|
327
|
+
params = nextParams;
|
|
144
328
|
}
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
329
|
+
const requestOptions = {
|
|
330
|
+
...(options?.signal ? { signal: options.signal } : {}),
|
|
331
|
+
...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
|
|
332
|
+
...(options?.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),
|
|
333
|
+
};
|
|
334
|
+
const response = await client.messages.create({ ...params, stream: true }, requestOptions).asResponse();
|
|
335
|
+
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
149
336
|
stream.push({ type: "start", partial: output });
|
|
150
337
|
const blocks = output.content;
|
|
151
|
-
for await (const event of
|
|
338
|
+
for await (const event of iterateAnthropicEvents(response, options?.signal)) {
|
|
152
339
|
if (event.type === "message_start") {
|
|
340
|
+
output.responseId = event.message.id;
|
|
153
341
|
// Capture initial token usage from message_start event
|
|
154
342
|
// This ensures we have input token counts even if the stream is aborted early
|
|
155
343
|
output.usage.input = event.message.usage.input_tokens || 0;
|
|
@@ -196,7 +384,7 @@ export const streamAnthropic = (model, context, options) => {
|
|
|
196
384
|
const block = {
|
|
197
385
|
type: "toolCall",
|
|
198
386
|
id: event.content_block.id,
|
|
199
|
-
name:
|
|
387
|
+
name: isOAuth
|
|
200
388
|
? fromClaudeCodeName(event.content_block.name, context.tools)
|
|
201
389
|
: event.content_block.name,
|
|
202
390
|
arguments: event.content_block.input ?? {},
|
|
@@ -280,6 +468,8 @@ export const streamAnthropic = (model, context, options) => {
|
|
|
280
468
|
}
|
|
281
469
|
else if (block.type === "toolCall") {
|
|
282
470
|
block.arguments = parseStreamingJson(block.partialJson);
|
|
471
|
+
// Finalize in-place and strip the scratch buffer so replay only
|
|
472
|
+
// carries parsed arguments.
|
|
283
473
|
delete block.partialJson;
|
|
284
474
|
stream.push({
|
|
285
475
|
type: "toolcall_end",
|
|
@@ -324,8 +514,11 @@ export const streamAnthropic = (model, context, options) => {
|
|
|
324
514
|
stream.end();
|
|
325
515
|
}
|
|
326
516
|
catch (error) {
|
|
327
|
-
for (const block of output.content)
|
|
517
|
+
for (const block of output.content) {
|
|
328
518
|
delete block.index;
|
|
519
|
+
// partialJson is only a streaming scratch buffer; never persist it.
|
|
520
|
+
delete block.partialJson;
|
|
521
|
+
}
|
|
329
522
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
330
523
|
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
331
524
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
@@ -335,31 +528,33 @@ export const streamAnthropic = (model, context, options) => {
|
|
|
335
528
|
return stream;
|
|
336
529
|
};
|
|
337
530
|
/**
|
|
338
|
-
* Check if a model supports adaptive thinking (Opus 4.6
|
|
531
|
+
* Check if a model supports adaptive thinking (Opus 4.6+, Sonnet 4.6)
|
|
339
532
|
*/
|
|
340
533
|
function supportsAdaptiveThinking(modelId) {
|
|
341
|
-
//
|
|
534
|
+
// Adaptive-thinking model IDs (with or without date suffix)
|
|
342
535
|
return (modelId.includes("opus-4-6") ||
|
|
343
536
|
modelId.includes("opus-4.6") ||
|
|
537
|
+
modelId.includes("opus-4-7") ||
|
|
538
|
+
modelId.includes("opus-4.7") ||
|
|
344
539
|
modelId.includes("sonnet-4-6") ||
|
|
345
540
|
modelId.includes("sonnet-4.6"));
|
|
346
541
|
}
|
|
347
542
|
/**
|
|
348
543
|
* Map ThinkingLevel to Anthropic effort levels for adaptive thinking.
|
|
349
|
-
* Note: effort "max" is only valid on Opus 4.6.
|
|
544
|
+
* Note: effort "max" is only valid on Opus 4.6, while Opus 4.7 supports "xhigh".
|
|
350
545
|
*/
|
|
351
|
-
function mapThinkingLevelToEffort(
|
|
546
|
+
function mapThinkingLevelToEffort(model, level) {
|
|
547
|
+
const mapped = level ? model.thinkingLevelMap?.[level] : undefined;
|
|
548
|
+
if (typeof mapped === "string")
|
|
549
|
+
return mapped;
|
|
352
550
|
switch (level) {
|
|
353
551
|
case "minimal":
|
|
354
|
-
return "low";
|
|
355
552
|
case "low":
|
|
356
553
|
return "low";
|
|
357
554
|
case "medium":
|
|
358
555
|
return "medium";
|
|
359
556
|
case "high":
|
|
360
557
|
return "high";
|
|
361
|
-
case "xhigh":
|
|
362
|
-
return modelId.includes("opus-4-6") || modelId.includes("opus-4.6") ? "max" : "high";
|
|
363
558
|
default:
|
|
364
559
|
return "high";
|
|
365
560
|
}
|
|
@@ -376,7 +571,7 @@ export const streamSimpleAnthropic = (model, context, options) => {
|
|
|
376
571
|
// For Opus 4.6 and Sonnet 4.6: use adaptive thinking with effort level
|
|
377
572
|
// For older models: use budget-based thinking
|
|
378
573
|
if (supportsAdaptiveThinking(model.id)) {
|
|
379
|
-
const effort = mapThinkingLevelToEffort(options.reasoning
|
|
574
|
+
const effort = mapThinkingLevelToEffort(model, options.reasoning);
|
|
380
575
|
return streamAnthropic(model, context, {
|
|
381
576
|
...base,
|
|
382
577
|
thinkingEnabled: true,
|
|
@@ -394,16 +589,36 @@ export const streamSimpleAnthropic = (model, context, options) => {
|
|
|
394
589
|
function isOAuthToken(apiKey) {
|
|
395
590
|
return apiKey.includes("sk-ant-oat");
|
|
396
591
|
}
|
|
397
|
-
function createClient(model, apiKey, interleavedThinking, optionsHeaders, dynamicHeaders) {
|
|
592
|
+
function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStreamingBeta, optionsHeaders, dynamicHeaders, sessionId) {
|
|
398
593
|
// Adaptive thinking models (Opus 4.6, Sonnet 4.6) have interleaved thinking built-in.
|
|
399
594
|
// The beta header is deprecated on Opus 4.6 and redundant on Sonnet 4.6, so skip it.
|
|
400
595
|
const needsInterleavedBeta = interleavedThinking && !supportsAdaptiveThinking(model.id);
|
|
401
|
-
|
|
596
|
+
const betaFeatures = [];
|
|
597
|
+
if (useFineGrainedToolStreamingBeta) {
|
|
598
|
+
betaFeatures.push(FINE_GRAINED_TOOL_STREAMING_BETA);
|
|
599
|
+
}
|
|
600
|
+
if (needsInterleavedBeta) {
|
|
601
|
+
betaFeatures.push(INTERLEAVED_THINKING_BETA);
|
|
602
|
+
}
|
|
603
|
+
if (model.provider === "cloudflare-ai-gateway") {
|
|
604
|
+
const client = new Anthropic({
|
|
605
|
+
apiKey: null,
|
|
606
|
+
authToken: null,
|
|
607
|
+
baseURL: resolveCloudflareBaseUrl(model),
|
|
608
|
+
dangerouslyAllowBrowser: true,
|
|
609
|
+
defaultHeaders: mergeHeaders({
|
|
610
|
+
accept: "application/json",
|
|
611
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
612
|
+
"cf-aig-authorization": `Bearer ${apiKey}`,
|
|
613
|
+
"x-api-key": null,
|
|
614
|
+
Authorization: null,
|
|
615
|
+
...(betaFeatures.length > 0 ? { "anthropic-beta": betaFeatures.join(",") } : {}),
|
|
616
|
+
}, model.headers, optionsHeaders),
|
|
617
|
+
});
|
|
618
|
+
return { client, isOAuthToken: false };
|
|
619
|
+
}
|
|
620
|
+
// Copilot: Bearer auth, selective betas.
|
|
402
621
|
if (model.provider === "github-copilot") {
|
|
403
|
-
const betaFeatures = [];
|
|
404
|
-
if (needsInterleavedBeta) {
|
|
405
|
-
betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
406
|
-
}
|
|
407
622
|
const client = new Anthropic({
|
|
408
623
|
apiKey: null,
|
|
409
624
|
authToken: apiKey,
|
|
@@ -417,10 +632,6 @@ function createClient(model, apiKey, interleavedThinking, optionsHeaders, dynami
|
|
|
417
632
|
});
|
|
418
633
|
return { client, isOAuthToken: false };
|
|
419
634
|
}
|
|
420
|
-
const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
|
|
421
|
-
if (needsInterleavedBeta) {
|
|
422
|
-
betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
423
|
-
}
|
|
424
635
|
// OAuth: Bearer auth, Claude Code identity headers
|
|
425
636
|
if (isOAuthToken(apiKey)) {
|
|
426
637
|
const client = new Anthropic({
|
|
@@ -431,7 +642,7 @@ function createClient(model, apiKey, interleavedThinking, optionsHeaders, dynami
|
|
|
431
642
|
defaultHeaders: mergeHeaders({
|
|
432
643
|
accept: "application/json",
|
|
433
644
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
434
|
-
"anthropic-beta":
|
|
645
|
+
"anthropic-beta": ["claude-code-20250219", "oauth-2025-04-20", ...betaFeatures].join(","),
|
|
435
646
|
"user-agent": `claude-cli/${claudeCodeVersion}`,
|
|
436
647
|
"x-app": "cli",
|
|
437
648
|
}, model.headers, optionsHeaders),
|
|
@@ -439,6 +650,7 @@ function createClient(model, apiKey, interleavedThinking, optionsHeaders, dynami
|
|
|
439
650
|
return { client, isOAuthToken: true };
|
|
440
651
|
}
|
|
441
652
|
// API key auth
|
|
653
|
+
const sessionAffinityHeaders = sessionId && getAnthropicCompat(model).sendSessionAffinityHeaders ? { "x-session-affinity": sessionId } : {};
|
|
442
654
|
const client = new Anthropic({
|
|
443
655
|
apiKey,
|
|
444
656
|
baseURL: model.baseUrl,
|
|
@@ -446,13 +658,13 @@ function createClient(model, apiKey, interleavedThinking, optionsHeaders, dynami
|
|
|
446
658
|
defaultHeaders: mergeHeaders({
|
|
447
659
|
accept: "application/json",
|
|
448
660
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
449
|
-
"anthropic-beta": betaFeatures.join(","),
|
|
450
|
-
}, model.headers, optionsHeaders),
|
|
661
|
+
...(betaFeatures.length > 0 ? { "anthropic-beta": betaFeatures.join(",") } : {}),
|
|
662
|
+
}, sessionAffinityHeaders, model.headers, optionsHeaders),
|
|
451
663
|
});
|
|
452
664
|
return { client, isOAuthToken: false };
|
|
453
665
|
}
|
|
454
666
|
function buildParams(model, context, isOAuthToken, options) {
|
|
455
|
-
const { cacheControl } = getCacheControl(model
|
|
667
|
+
const { cacheControl } = getCacheControl(model, options?.cacheRetention);
|
|
456
668
|
const params = {
|
|
457
669
|
model: model.id,
|
|
458
670
|
messages: convertMessages(context.messages, model, isOAuthToken, cacheControl),
|
|
@@ -490,24 +702,39 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
490
702
|
if (options?.temperature !== undefined && !options?.thinkingEnabled) {
|
|
491
703
|
params.temperature = options.temperature;
|
|
492
704
|
}
|
|
493
|
-
if (context.tools) {
|
|
494
|
-
|
|
705
|
+
if (context.tools && context.tools.length > 0) {
|
|
706
|
+
const compat = getAnthropicCompat(model);
|
|
707
|
+
params.tools = convertTools(context.tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsCacheControlOnTools ? cacheControl : undefined);
|
|
495
708
|
}
|
|
496
|
-
// Configure thinking mode: adaptive (Opus 4.6 and Sonnet 4.6)
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
709
|
+
// Configure thinking mode: adaptive (Opus 4.6+ and Sonnet 4.6),
|
|
710
|
+
// budget-based (older models), or explicitly disabled.
|
|
711
|
+
if (model.reasoning) {
|
|
712
|
+
if (options?.thinkingEnabled) {
|
|
713
|
+
// Default to "summarized" so Opus 4.7 and Mythos Preview behave like
|
|
714
|
+
// older Claude 4 models (whose API default is also "summarized").
|
|
715
|
+
const display = options.thinkingDisplay ?? "summarized";
|
|
716
|
+
if (supportsAdaptiveThinking(model.id)) {
|
|
717
|
+
// Adaptive thinking: Claude decides when and how much to think.
|
|
718
|
+
params.thinking = { type: "adaptive", display };
|
|
719
|
+
if (options.effort) {
|
|
720
|
+
// The Anthropic SDK types can lag newly supported effort values such as "xhigh".
|
|
721
|
+
params.output_config =
|
|
722
|
+
options.effort === "xhigh"
|
|
723
|
+
? { effort: options.effort }
|
|
724
|
+
: { effort: options.effort };
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
// Budget-based thinking for older models
|
|
729
|
+
params.thinking = {
|
|
730
|
+
type: "enabled",
|
|
731
|
+
budget_tokens: options.thinkingBudgetTokens || 1024,
|
|
732
|
+
display,
|
|
733
|
+
};
|
|
503
734
|
}
|
|
504
735
|
}
|
|
505
|
-
else {
|
|
506
|
-
|
|
507
|
-
params.thinking = {
|
|
508
|
-
type: "enabled",
|
|
509
|
-
budget_tokens: options.thinkingBudgetTokens || 1024,
|
|
510
|
-
};
|
|
736
|
+
else if (options?.thinkingEnabled === false) {
|
|
737
|
+
params.thinking = { type: "disabled" };
|
|
511
738
|
}
|
|
512
739
|
}
|
|
513
740
|
if (options?.metadata) {
|
|
@@ -564,8 +791,7 @@ function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
|
564
791
|
};
|
|
565
792
|
}
|
|
566
793
|
});
|
|
567
|
-
|
|
568
|
-
filteredBlocks = filteredBlocks.filter((b) => {
|
|
794
|
+
const filteredBlocks = blocks.filter((b) => {
|
|
569
795
|
if (b.type === "text") {
|
|
570
796
|
return b.text.trim().length > 0;
|
|
571
797
|
}
|
|
@@ -689,19 +915,24 @@ function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
|
689
915
|
}
|
|
690
916
|
return params;
|
|
691
917
|
}
|
|
692
|
-
function
|
|
918
|
+
function shouldUseFineGrainedToolStreamingBeta(model, context) {
|
|
919
|
+
return !!context.tools?.length && !getAnthropicCompat(model).supportsEagerToolInputStreaming;
|
|
920
|
+
}
|
|
921
|
+
function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, cacheControl) {
|
|
693
922
|
if (!tools)
|
|
694
923
|
return [];
|
|
695
|
-
return tools.map((tool) => {
|
|
696
|
-
const
|
|
924
|
+
return tools.map((tool, index) => {
|
|
925
|
+
const schema = tool.parameters;
|
|
697
926
|
return {
|
|
698
927
|
name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
|
|
699
928
|
description: tool.description,
|
|
929
|
+
...(supportsEagerToolInputStreaming ? { eager_input_streaming: true } : {}),
|
|
700
930
|
input_schema: {
|
|
701
931
|
type: "object",
|
|
702
|
-
properties:
|
|
703
|
-
required:
|
|
932
|
+
properties: schema.properties ?? {},
|
|
933
|
+
required: schema.required ?? [],
|
|
704
934
|
},
|
|
935
|
+
...(cacheControl && index === tools.length - 1 ? { cache_control: cacheControl } : {}),
|
|
705
936
|
};
|
|
706
937
|
});
|
|
707
938
|
}
|