@oh-my-pi/pi-ai 17.1.2 → 17.1.4
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/CHANGELOG.md +54 -0
- package/README.md +1 -1
- package/dist/types/auth-broker/client.d.ts +7 -1
- package/dist/types/auth-broker/remote-store.d.ts +4 -1
- package/dist/types/auth-broker/types.d.ts +6 -1
- package/dist/types/auth-broker/wire-schemas.d.ts +41 -0
- package/dist/types/auth-storage.d.ts +55 -0
- package/dist/types/providers/anthropic.d.ts +1 -7
- package/dist/types/providers/claude-code-fingerprint.d.ts +15 -0
- package/dist/types/providers/cursor.d.ts +30 -7
- package/dist/types/providers/openai-codex/request-transformer.d.ts +5 -4
- package/dist/types/providers/openai-codex-responses.d.ts +3 -0
- package/dist/types/registry/alibaba-token-plan.d.ts +11 -0
- package/dist/types/registry/oauth/anthropic-constants.d.ts +12 -0
- package/dist/types/registry/oauth/anthropic.d.ts +1 -0
- package/dist/types/registry/oauth/index.d.ts +1 -0
- package/dist/types/registry/oauth/types.d.ts +8 -0
- package/dist/types/types.d.ts +71 -1
- package/dist/types/usage/minimax-code.d.ts +1 -0
- package/dist/types/usage.d.ts +10 -0
- package/dist/types/utils/idle-iterator.d.ts +6 -4
- package/package.json +4 -4
- package/src/auth-broker/client.ts +24 -1
- package/src/auth-broker/remote-store.ts +12 -0
- package/src/auth-broker/server.ts +7 -0
- package/src/auth-broker/types.ts +7 -0
- package/src/auth-broker/wire-schemas.ts +23 -0
- package/src/auth-storage.ts +279 -29
- package/src/error/flags.ts +1 -1
- package/src/providers/__tests__/kimi-code-thinking.test.ts +40 -5
- package/src/providers/amazon-bedrock.ts +3 -4
- package/src/providers/anthropic.ts +98 -32
- package/src/providers/azure-openai-responses.ts +36 -5
- package/src/providers/claude-code-fingerprint.ts +19 -0
- package/src/providers/cursor.ts +525 -40
- package/src/providers/openai-codex/request-transformer.ts +27 -7
- package/src/providers/openai-codex-responses.ts +37 -14
- package/src/providers/openai-completions.ts +2 -1
- package/src/providers/openai-responses.ts +16 -9
- package/src/providers/openai-shared.ts +12 -1
- package/src/registry/alibaba-token-plan.ts +103 -21
- package/src/registry/oauth/anthropic-constants.ts +12 -0
- package/src/registry/oauth/anthropic.ts +3 -1
- package/src/registry/oauth/index.ts +1 -0
- package/src/registry/oauth/types.ts +8 -0
- package/src/stream.ts +7 -2
- package/src/types.ts +79 -1
- package/src/usage/alibaba-token-plan.ts +34 -6
- package/src/usage/claude.ts +7 -2
- package/src/usage/minimax-code.ts +280 -19
- package/src/usage/openai-codex.ts +61 -14
- package/src/usage.ts +10 -0
- package/src/utils/idle-iterator.ts +8 -6
- package/src/utils.ts +5 -5
|
@@ -271,7 +271,7 @@ describe("Kimi K2.7 Code thinking policy", () => {
|
|
|
271
271
|
expect(model.compat.disableReasoningOnForcedToolChoice).toBe(true);
|
|
272
272
|
});
|
|
273
273
|
|
|
274
|
-
it("
|
|
274
|
+
it("downgrades the forced tool choice on Kimi Code's Anthropic endpoint", async () => {
|
|
275
275
|
const model = getBundledModel<"openai-completions">("kimi-code", "kimi-for-coding");
|
|
276
276
|
let payload: MessageCreateParamsStreaming | undefined;
|
|
277
277
|
const stream = streamOpenAIAnthropicShim(
|
|
@@ -295,10 +295,45 @@ describe("Kimi K2.7 Code thinking policy", () => {
|
|
|
295
295
|
|
|
296
296
|
await stream.result();
|
|
297
297
|
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
// api.kimi.com keeps thinking enabled server-side no matter what the
|
|
299
|
+
// request carries: an omitted thinking block defaults to enabled, and an
|
|
300
|
+
// explicit disabled block is rejected (#3852). Either way a forced
|
|
301
|
+
// tool_choice 400s (`tool_choice 'specified' is incompatible with
|
|
302
|
+
// thinking enabled`), so the only viable path is downgrading the choice
|
|
303
|
+
// to auto while keeping the tool available — thinking stays on.
|
|
304
|
+
expect(payload?.tool_choice).toEqual({ type: "auto" });
|
|
305
|
+
expect(payload?.thinking).toBeDefined();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("downgrades forced tool choice for every thinking-locked Kimi Code alias", async () => {
|
|
309
|
+
// The kimi-code catalog aliases the mandatory-thinking K2.7 Code family
|
|
310
|
+
// as `kimi-for-coding[-highspeed]` and K3 as `k3`; none match the native
|
|
311
|
+
// `kimi-k2.7-code*` id pattern, so each must be recognised explicitly.
|
|
312
|
+
for (const id of ["k3", "kimi-for-coding", "kimi-for-coding-highspeed"]) {
|
|
313
|
+
const model = getBundledModel<"openai-completions">("kimi-code", id);
|
|
314
|
+
let payload: MessageCreateParamsStreaming | undefined;
|
|
315
|
+
const stream = streamOpenAIAnthropicShim(
|
|
316
|
+
model,
|
|
317
|
+
TITLE_CONTEXT,
|
|
318
|
+
{
|
|
319
|
+
apiKey: "test-key",
|
|
320
|
+
maxTokens: 1024,
|
|
321
|
+
toolChoice: { type: "tool", name: "set_title" },
|
|
322
|
+
onPayload: body => {
|
|
323
|
+
payload = body as MessageCreateParamsStreaming;
|
|
324
|
+
throw new Error("stop after payload capture");
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
anthropicBaseUrl: "https://api.kimi.com/coding",
|
|
329
|
+
defaultFormat: "anthropic",
|
|
330
|
+
},
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
await stream.result();
|
|
334
|
+
|
|
335
|
+
expect(payload?.tool_choice).toEqual({ type: "auto" });
|
|
336
|
+
}
|
|
302
337
|
});
|
|
303
338
|
|
|
304
339
|
it("uses the configured Kimi base URL for Anthropic requests", async () => {
|
|
@@ -830,10 +830,9 @@ function convertMessages(
|
|
|
830
830
|
case "thinking":
|
|
831
831
|
// Skip empty thinking blocks
|
|
832
832
|
if (c.thinking.trim().length === 0) continue;
|
|
833
|
-
//
|
|
834
|
-
//
|
|
835
|
-
|
|
836
|
-
if (supportsThinkingSignature(model) && c.thinkingSignature) {
|
|
833
|
+
// A captured signature is authoritative even when the model id is an opaque ARN.
|
|
834
|
+
// Without one, known non-Claude families use unsigned reasoning; known Claude ids demote to text.
|
|
835
|
+
if (c.thinkingSignature) {
|
|
837
836
|
contentBlocks.push({
|
|
838
837
|
reasoningContent: {
|
|
839
838
|
reasoningText: { text: c.thinking.toWellFormed(), signature: c.thinkingSignature },
|
|
@@ -63,6 +63,7 @@ import { COMBINATOR_KEYS, NO_STRICT, toolWireSchema } from "../utils/schema";
|
|
|
63
63
|
import { spillToDescription } from "../utils/schema/spill";
|
|
64
64
|
import { createSdkStreamRequestOptions } from "../utils/sdk-stream-timeout";
|
|
65
65
|
import { notifyRawSseEvent } from "../utils/sse-debug";
|
|
66
|
+
import { isForcedToolChoice } from "../utils/tool-choice";
|
|
66
67
|
import {
|
|
67
68
|
AnthropicApiError,
|
|
68
69
|
AnthropicConnectionTimeoutError,
|
|
@@ -84,6 +85,14 @@ import {
|
|
|
84
85
|
type RawMessageStreamEvent,
|
|
85
86
|
type TextBlockParam,
|
|
86
87
|
} from "./anthropic-wire";
|
|
88
|
+
import {
|
|
89
|
+
CLAUDE_CODE_MAX_OUTPUT_TOKENS,
|
|
90
|
+
claudeAgentSdkVersion,
|
|
91
|
+
claudeClientVersion,
|
|
92
|
+
claudeCodeSystemInstruction,
|
|
93
|
+
claudeCodeVersion,
|
|
94
|
+
claudeToolPrefix,
|
|
95
|
+
} from "./claude-code-fingerprint";
|
|
87
96
|
import {
|
|
88
97
|
buildCopilotDynamicHeaders,
|
|
89
98
|
hasCopilotVisionInput,
|
|
@@ -129,6 +138,22 @@ export function buildBetaHeader(baseBetas: readonly string[], extraBetas: readon
|
|
|
129
138
|
return result.join(",");
|
|
130
139
|
}
|
|
131
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Merge an extra Anthropic beta into a caller-provided `anthropic-beta` header,
|
|
143
|
+
* preserving the caller's key casing and deduping the tokens. Returns a
|
|
144
|
+
* single-entry header record for a per-request `headers` override — used to
|
|
145
|
+
* attach a required beta to injected SDK clients that bypass the client-level
|
|
146
|
+
* beta construction.
|
|
147
|
+
*/
|
|
148
|
+
function mergeAnthropicBetaHeader(callerHeaders: Record<string, string>, beta: string): Record<string, string> {
|
|
149
|
+
for (const key in callerHeaders) {
|
|
150
|
+
if (key.toLowerCase() === "anthropic-beta") {
|
|
151
|
+
return { [key]: buildBetaHeader(normalizeExtraBetas(callerHeaders[key]), [beta]) };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return { "anthropic-beta": beta };
|
|
155
|
+
}
|
|
156
|
+
|
|
132
157
|
const midConversationSystemBeta = "mid-conversation-system-2026-04-07";
|
|
133
158
|
const contextManagementBeta = "context-management-2025-06-27";
|
|
134
159
|
const structuredOutputsBeta = "structured-outputs-2025-12-15";
|
|
@@ -463,16 +488,9 @@ function getCacheControl(
|
|
|
463
488
|
};
|
|
464
489
|
}
|
|
465
490
|
|
|
466
|
-
// Stealth mode: mimic Claude Code's request fingerprint.
|
|
467
|
-
|
|
468
|
-
export
|
|
469
|
-
export const claudeClientVersion = "1.11187.4";
|
|
470
|
-
export const claudeToolPrefix: string = "_";
|
|
471
|
-
export const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
|
|
472
|
-
// Claude Code caps requested output at 64k tokens even when the model ceiling is
|
|
473
|
-
// higher (e.g. Opus 4.8 supports 128k); OAuth requests clamp to match the wire
|
|
474
|
-
// fingerprint. API-key requests keep the full model ceiling.
|
|
475
|
-
export const CLAUDE_CODE_MAX_OUTPUT_TOKENS = 64000;
|
|
491
|
+
// Stealth mode: mimic Claude Code's request fingerprint. Constants live in the
|
|
492
|
+
// leaf module so registry/usage consumers avoid an init cycle through this file.
|
|
493
|
+
export * from "./claude-code-fingerprint";
|
|
476
494
|
|
|
477
495
|
export function mapStainlessOs(platform: string): "MacOS" | "Windows" | "Linux" | "FreeBSD" | `Other::${string}` {
|
|
478
496
|
switch (platform.toLowerCase()) {
|
|
@@ -1840,20 +1858,19 @@ const streamAnthropicOnce = (
|
|
|
1840
1858
|
if (options?.taskBudget && !extraBetas.includes(taskBudgetBeta)) {
|
|
1841
1859
|
extraBetas.push(taskBudgetBeta);
|
|
1842
1860
|
}
|
|
1843
|
-
// `output_config.effort` ships on thinking-on requests
|
|
1844
|
-
// thinking-off adaptive
|
|
1845
|
-
//
|
|
1846
|
-
// MiniMax uses `thinking.type:"adaptive"` itself
|
|
1847
|
-
// so the sentinel "adaptive" value intentionally
|
|
1848
|
-
// Skip Vertex rawPredict: that adapter needs betas
|
|
1849
|
-
// (`anthropic_beta`), not as an `anthropic-beta` HTTP header,
|
|
1850
|
-
// effort field is dropped from the body there too (see buildParams)
|
|
1851
|
-
// advertising the beta would only earn a 400 (#5614).
|
|
1861
|
+
// `output_config.effort` ships on thinking-on requests, explicit
|
|
1862
|
+
// thinking-off adaptive pins, and forced-tool adaptive pins. The beta
|
|
1863
|
+
// must accompany the field even when direct streamAnthropic callers omit
|
|
1864
|
+
// thinkingEnabled (#6589). MiniMax uses `thinking.type:"adaptive"` itself
|
|
1865
|
+
// as the control surface, so the sentinel "adaptive" value intentionally
|
|
1866
|
+
// sends no output_config. Skip Vertex rawPredict: that adapter needs betas
|
|
1867
|
+
// in the body (`anthropic_beta`), not as an `anthropic-beta` HTTP header,
|
|
1868
|
+
// so the effort field is dropped from the body there too (see buildParams)
|
|
1869
|
+
// and advertising the beta would only earn a 400 (#5614).
|
|
1852
1870
|
const sendsAdaptiveEffortPin =
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
!usesAdaptiveThinkingTagOnly(model);
|
|
1871
|
+
isAdaptiveOnlyThinking(model) &&
|
|
1872
|
+
(options?.thinkingEnabled === false ||
|
|
1873
|
+
(model.compat.supportsForcedToolChoice && isForcedToolChoice(options?.toolChoice)));
|
|
1857
1874
|
if (
|
|
1858
1875
|
model.reasoning &&
|
|
1859
1876
|
model.provider !== "google-vertex" &&
|
|
@@ -2064,10 +2081,27 @@ const streamAnthropicOnce = (
|
|
|
2064
2081
|
// to zero even when no watchdog timeout is configured (the helper only
|
|
2065
2082
|
// pins it alongside a timeout; a client retry budget of 5 would otherwise
|
|
2066
2083
|
// multiply with PROVIDER_MAX_RETRIES into up to 66 wire attempts).
|
|
2084
|
+
// Injected SDK clients (`options.client`) bypass the client-level
|
|
2085
|
+
// `anthropic-beta` construction below, so any `output_config.effort` the
|
|
2086
|
+
// body carries — the adaptive-only thinking-off / forced-tool pins and
|
|
2087
|
+
// enabled-effort turns alike — would reach Anthropic without the required
|
|
2088
|
+
// `effort-2025-11-24` beta and 400. `create()` accepts per-request headers
|
|
2089
|
+
// (already used for the gateway web-search header), so merge the beta with
|
|
2090
|
+
// any caller-provided `anthropic-beta` (deduped) and attach it there. Vertex
|
|
2091
|
+
// never carries the effort field (dropped in buildParams), so it is unaffected.
|
|
2092
|
+
const injectedClientEffortHeaders =
|
|
2093
|
+
options?.client !== undefined &&
|
|
2094
|
+
(params.output_config as AnthropicOutputConfig | undefined)?.effort !== undefined
|
|
2095
|
+
? mergeAnthropicBetaHeader(mergedCallerHeaders, effortBeta)
|
|
2096
|
+
: undefined;
|
|
2097
|
+
const perRequestHeaders =
|
|
2098
|
+
umansGatewayWebSearchHeader || injectedClientEffortHeaders
|
|
2099
|
+
? { ...umansGatewayWebSearchHeader, ...injectedClientEffortHeaders }
|
|
2100
|
+
: undefined;
|
|
2067
2101
|
const requestOptions = {
|
|
2068
2102
|
...createSdkStreamRequestOptions(requestSignal, requestTimeoutMs),
|
|
2069
2103
|
maxRetries: 0,
|
|
2070
|
-
...(
|
|
2104
|
+
...(perRequestHeaders ? { headers: perRequestHeaders } : {}),
|
|
2071
2105
|
};
|
|
2072
2106
|
const anthropicRequest: unknown =
|
|
2073
2107
|
isOAuthToken && client.beta
|
|
@@ -2960,13 +2994,32 @@ function createClient(
|
|
|
2960
2994
|
return { client, isOAuthToken: oauthToken };
|
|
2961
2995
|
}
|
|
2962
2996
|
|
|
2963
|
-
function disableThinkingIfToolChoiceForced(
|
|
2997
|
+
function disableThinkingIfToolChoiceForced(
|
|
2998
|
+
params: MessageCreateParamsStreaming,
|
|
2999
|
+
model: Model<"anthropic-messages">,
|
|
3000
|
+
): void {
|
|
2964
3001
|
const toolChoice = params.tool_choice;
|
|
2965
3002
|
if (!toolChoice) return;
|
|
2966
3003
|
if (toolChoice.type !== "any" && toolChoice.type !== "tool") return;
|
|
2967
3004
|
|
|
2968
3005
|
delete params.thinking;
|
|
2969
3006
|
delete params.context_management;
|
|
3007
|
+
|
|
3008
|
+
// Adaptive-only models can't be switched off by omitting `thinking` — a bare
|
|
3009
|
+
// omission defaults to adaptive thinking ON, so a forced-tool turn would still
|
|
3010
|
+
// reason instead of calling the tool (#6589). Pin the lowest adaptive effort
|
|
3011
|
+
// instead of dropping it, mirroring the disable branch in buildParams. Vertex
|
|
3012
|
+
// rawPredict is the sole exception: it can only carry the effort beta in the
|
|
3013
|
+
// body (dropped there too, see buildParams), so it keeps the delete behavior.
|
|
3014
|
+
// The effort beta itself is attached at the request site — including per-request
|
|
3015
|
+
// for injected SDK clients that bypass client-level beta construction.
|
|
3016
|
+
if (isAdaptiveOnlyThinking(model) && model.provider !== "google-vertex") {
|
|
3017
|
+
const outputConfig = (params.output_config as AnthropicOutputConfig | undefined) ?? {};
|
|
3018
|
+
outputConfig.effort = "low";
|
|
3019
|
+
params.output_config = outputConfig;
|
|
3020
|
+
return;
|
|
3021
|
+
}
|
|
3022
|
+
|
|
2970
3023
|
const outputConfig = params.output_config as AnthropicOutputConfig | undefined;
|
|
2971
3024
|
if (!outputConfig) return;
|
|
2972
3025
|
|
|
@@ -3229,6 +3282,22 @@ function usesAdaptiveThinkingTagOnly(model: Model<"anthropic-messages">): boolea
|
|
|
3229
3282
|
return thinking.efforts.length > 0;
|
|
3230
3283
|
}
|
|
3231
3284
|
|
|
3285
|
+
/**
|
|
3286
|
+
* True for adaptive-only Claude models (Opus 4.6+, Sonnet 4.6+, Fable/Mythos 5)
|
|
3287
|
+
* that reject `thinking.type: "disabled"`. Turning thinking off on these models
|
|
3288
|
+
* means omitting the `thinking` field entirely and pinning the lowest adaptive
|
|
3289
|
+
* effort — a bare omission defaults to adaptive thinking ON. Excludes MiniMax,
|
|
3290
|
+
* which drives adaptive thinking through the `thinking.type: "adaptive"` tag
|
|
3291
|
+
* itself rather than `output_config.effort`.
|
|
3292
|
+
*/
|
|
3293
|
+
function isAdaptiveOnlyThinking(model: Model<"anthropic-messages">): boolean {
|
|
3294
|
+
return (
|
|
3295
|
+
model.thinking?.mode === "anthropic-adaptive" &&
|
|
3296
|
+
!model.compat.disableAdaptiveThinking &&
|
|
3297
|
+
!usesAdaptiveThinkingTagOnly(model)
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3232
3301
|
function resolveAnthropicAdaptiveEffort(
|
|
3233
3302
|
model: Model<"anthropic-messages">,
|
|
3234
3303
|
options: AnthropicOptions,
|
|
@@ -3352,17 +3421,14 @@ function buildParams(
|
|
|
3352
3421
|
if (mode === "anthropic-budget-effort" && effort && effort !== "adaptive") outputConfigEffort = effort;
|
|
3353
3422
|
}
|
|
3354
3423
|
} else if (options?.thinkingEnabled === false) {
|
|
3355
|
-
|
|
3356
|
-
if (
|
|
3357
|
-
model.thinking?.mode === "anthropic-adaptive" &&
|
|
3358
|
-
!compat.disableAdaptiveThinking &&
|
|
3359
|
-
!usesAdaptiveThinkingTagOnly(model)
|
|
3360
|
-
) {
|
|
3424
|
+
if (isAdaptiveOnlyThinking(model)) {
|
|
3361
3425
|
// Adaptive-only Claude models (Opus 4.6+, Sonnet 4.6+, Fable/Mythos 5) reject
|
|
3362
3426
|
// `thinking.type: "disabled"` — adaptive thinking cannot be switched off.
|
|
3363
3427
|
// Omit the thinking field (the API defaults to adaptive) and pin the
|
|
3364
3428
|
// lowest effort so "thinking off" calls stay cheap instead of failing
|
|
3365
3429
|
// the request with a 400 (a hidden-thinking toggle must never break it).
|
|
3430
|
+
// The effort field requires the `effort-2025-11-24` beta; it is attached
|
|
3431
|
+
// at the request site, including per-request for injected SDK clients.
|
|
3366
3432
|
outputConfigEffort = "low";
|
|
3367
3433
|
} else {
|
|
3368
3434
|
thinking = { type: "disabled" };
|
|
@@ -3484,7 +3550,7 @@ function buildParams(
|
|
|
3484
3550
|
}
|
|
3485
3551
|
}
|
|
3486
3552
|
|
|
3487
|
-
disableThinkingIfToolChoiceForced(params);
|
|
3553
|
+
disableThinkingIfToolChoiceForced(params, model);
|
|
3488
3554
|
ensureMaxTokensForThinking(params, maxOutputTokens);
|
|
3489
3555
|
applyPromptCaching(params, cacheControl);
|
|
3490
3556
|
enforceCacheControlLimit(params, 4);
|
|
@@ -131,9 +131,10 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|
|
131
131
|
|
|
132
132
|
try {
|
|
133
133
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
134
|
-
const { url, headers } = buildAzureResponsesRequest(model, apiKey, options);
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
const { url, headers, baseUrl } = buildAzureResponsesRequest(model, apiKey, options);
|
|
135
|
+
const requestModel = modelForAzureEndpoint(model, baseUrl);
|
|
136
|
+
let params = buildParams(requestModel, context, options, deploymentName);
|
|
137
|
+
const replacementPayload = await options?.onPayload?.(params, requestModel);
|
|
137
138
|
if (replacementPayload !== undefined) {
|
|
138
139
|
params = replacementPayload as typeof params;
|
|
139
140
|
}
|
|
@@ -294,6 +295,23 @@ function resolveAzureConfig(
|
|
|
294
295
|
};
|
|
295
296
|
}
|
|
296
297
|
|
|
298
|
+
function modelForAzureEndpoint(
|
|
299
|
+
model: Model<"azure-openai-responses">,
|
|
300
|
+
baseUrl: string,
|
|
301
|
+
): Model<"azure-openai-responses"> {
|
|
302
|
+
if (model.supportsComputerUseConfig !== undefined || model.supportsComputerUse !== true) return model;
|
|
303
|
+
try {
|
|
304
|
+
const url = new URL(baseUrl);
|
|
305
|
+
if (
|
|
306
|
+
url.protocol === "https:" &&
|
|
307
|
+
(url.hostname.endsWith(".openai.azure.com") || url.hostname === "models.inference.ai.azure.com")
|
|
308
|
+
) {
|
|
309
|
+
return model;
|
|
310
|
+
}
|
|
311
|
+
} catch {}
|
|
312
|
+
return { ...model, supportsComputerUse: false };
|
|
313
|
+
}
|
|
314
|
+
|
|
297
315
|
/**
|
|
298
316
|
* Replicates the `AzureOpenAI` SDK client's request shape for `/responses`:
|
|
299
317
|
* a string api key becomes a single `api-key` header (azure.mjs `authHeaders`;
|
|
@@ -307,7 +325,7 @@ function buildAzureResponsesRequest(
|
|
|
307
325
|
model: Model<"azure-openai-responses">,
|
|
308
326
|
apiKey: string,
|
|
309
327
|
options?: AzureOpenAIResponsesOptions,
|
|
310
|
-
): { url: string; headers: Record<string, string
|
|
328
|
+
): { url: string; headers: Record<string, string>; baseUrl: string } {
|
|
311
329
|
if (!apiKey) {
|
|
312
330
|
const envKey = $env.AZURE_OPENAI_API_KEY;
|
|
313
331
|
if (!envKey) {
|
|
@@ -329,6 +347,7 @@ function buildAzureResponsesRequest(
|
|
|
329
347
|
return {
|
|
330
348
|
url: `${baseUrl}/responses?api-version=${encodeURIComponent(apiVersion)}`,
|
|
331
349
|
headers,
|
|
350
|
+
baseUrl,
|
|
332
351
|
};
|
|
333
352
|
}
|
|
334
353
|
|
|
@@ -386,8 +405,20 @@ function buildParams(
|
|
|
386
405
|
if (serializedTools.length > 0) {
|
|
387
406
|
params.tools = serializedTools;
|
|
388
407
|
if (options?.toolChoice) {
|
|
389
|
-
|
|
408
|
+
let toolChoice = mapToOpenAIResponsesToolChoice(options.toolChoice);
|
|
390
409
|
const hasComputerTool = serializedTools.some(tool => tool.type === "computer");
|
|
410
|
+
if (toolChoice && typeof toolChoice !== "string" && toolChoice.type === "computer" && !hasComputerTool) {
|
|
411
|
+
const computer = context.tools.find(tool => tool.native?.type === "computer");
|
|
412
|
+
if (computer && serializedTools.some(tool => tool.type === "function" && tool.name === computer.name)) {
|
|
413
|
+
toolChoice = { type: "function", name: computer.name };
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (toolChoice && typeof toolChoice !== "string" && toolChoice.type === "function" && hasComputerTool) {
|
|
417
|
+
const computer = context.tools.find(tool => tool.native?.type === "computer");
|
|
418
|
+
if (computer?.name === toolChoice.name) {
|
|
419
|
+
toolChoice = { type: "computer" };
|
|
420
|
+
}
|
|
421
|
+
}
|
|
391
422
|
if (
|
|
392
423
|
toolChoice &&
|
|
393
424
|
(typeof toolChoice === "string" ||
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code stealth-fingerprint constants, kept in a leaf module so
|
|
3
|
+
* fingerprint consumers outside the provider (`registry/oauth/anthropic`,
|
|
4
|
+
* `usage/claude`) don't import the heavy `providers/anthropic` module.
|
|
5
|
+
* That import edge was a live init cycle: `providers/anthropic` → `stream` →
|
|
6
|
+
* `registry` → `registry/oauth/anthropic` → back into the still-initializing
|
|
7
|
+
* provider module, which threw a TDZ ReferenceError whenever
|
|
8
|
+
* `providers/anthropic` was the first module loaded.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const claudeCodeVersion = "2.1.165";
|
|
12
|
+
export const claudeAgentSdkVersion = "0.3.165";
|
|
13
|
+
export const claudeClientVersion = "1.11187.4";
|
|
14
|
+
export const claudeToolPrefix: string = "_";
|
|
15
|
+
export const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
|
|
16
|
+
// Claude Code caps requested output at 64k tokens even when the model ceiling is
|
|
17
|
+
// higher (e.g. Opus 4.8 supports 128k); OAuth requests clamp to match the wire
|
|
18
|
+
// fingerprint. API-key requests keep the full model ceiling.
|
|
19
|
+
export const CLAUDE_CODE_MAX_OUTPUT_TOKENS = 64000;
|