@juspay/neurolink 9.79.1 → 9.79.3
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 +12 -0
- package/dist/browser/neurolink.min.js +337 -332
- package/dist/core/modules/GenerationHandler.js +43 -1
- package/dist/core/modules/structuredOutputPolicy.d.ts +42 -6
- package/dist/core/modules/structuredOutputPolicy.js +58 -7
- package/dist/lib/core/modules/GenerationHandler.js +43 -1
- package/dist/lib/core/modules/structuredOutputPolicy.d.ts +42 -6
- package/dist/lib/core/modules/structuredOutputPolicy.js +58 -7
- package/dist/lib/processors/document/ExcelProcessor.js +9 -1
- package/dist/lib/providers/anthropic.js +37 -41
- package/dist/lib/providers/anthropicImageBlocks.d.ts +47 -0
- package/dist/lib/providers/anthropicImageBlocks.js +223 -0
- package/dist/lib/providers/googleNativeGemini3.d.ts +26 -0
- package/dist/lib/providers/googleNativeGemini3.js +48 -0
- package/dist/lib/providers/googleVertex.d.ts +16 -0
- package/dist/lib/providers/googleVertex.js +200 -24
- package/dist/lib/proxy/oauthFetch.js +26 -14
- package/dist/lib/proxy/proxyTracer.d.ts +7 -1
- package/dist/lib/proxy/proxyTracer.js +29 -0
- package/dist/lib/proxy/systemRelocation.d.ts +21 -0
- package/dist/lib/proxy/systemRelocation.js +51 -0
- package/dist/lib/server/routes/claudeProxyRoutes.js +147 -19
- package/dist/lib/types/proxy.d.ts +10 -0
- package/dist/processors/document/ExcelProcessor.js +9 -1
- package/dist/providers/anthropic.js +37 -41
- package/dist/providers/anthropicImageBlocks.d.ts +47 -0
- package/dist/providers/anthropicImageBlocks.js +222 -0
- package/dist/providers/googleNativeGemini3.d.ts +26 -0
- package/dist/providers/googleNativeGemini3.js +48 -0
- package/dist/providers/googleVertex.d.ts +16 -0
- package/dist/providers/googleVertex.js +200 -24
- package/dist/proxy/oauthFetch.js +26 -14
- package/dist/proxy/proxyTracer.d.ts +7 -1
- package/dist/proxy/proxyTracer.js +29 -0
- package/dist/proxy/systemRelocation.d.ts +21 -0
- package/dist/proxy/systemRelocation.js +50 -0
- package/dist/server/routes/claudeProxyRoutes.js +147 -19
- package/dist/types/proxy.d.ts +10 -0
- package/package.json +8 -2
|
@@ -21,7 +21,7 @@ import { calculateCost } from "../../utils/pricing.js";
|
|
|
21
21
|
import { withProviderRetry } from "../../utils/providerRetry.js";
|
|
22
22
|
import { calculateCacheSavingsPercent, extractCacheCreationTokens, extractCacheReadTokens, extractTokenUsage, } from "../../utils/tokenUtils.js";
|
|
23
23
|
import { DEFAULT_MAX_STEPS } from "../constants.js";
|
|
24
|
-
import { isToolsSchemaConflictError, isToolsSchemaExclusionInForce, } from "./structuredOutputPolicy.js";
|
|
24
|
+
import { isTemperatureDeprecatedError, isToolsSchemaConflictError, isToolsSchemaExclusionInForce, } from "./structuredOutputPolicy.js";
|
|
25
25
|
import { coerceJsonToSchema } from "../../utils/json/coerce.js";
|
|
26
26
|
import { NoObjectGeneratedError } from "../../utils/generationErrors.js";
|
|
27
27
|
import { Output, stepCountIs } from "../../utils/tool.js";
|
|
@@ -342,6 +342,48 @@ export class GenerationHandler {
|
|
|
342
342
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
343
343
|
return result;
|
|
344
344
|
}
|
|
345
|
+
// Retry once without `temperature` when the model deprecated it. The
|
|
346
|
+
// newest Anthropic models (e.g. claude-opus-4-8 with tools + advanced
|
|
347
|
+
// beta features) reject `temperature` — "`temperature` is deprecated
|
|
348
|
+
// for this model." — in favour of reasoning-effort controls. Structured
|
|
349
|
+
// output is already excluded for the native anthropic surface, so this
|
|
350
|
+
// is the dominant failure mode for Opus there.
|
|
351
|
+
if (isTemperatureDeprecatedError(error) &&
|
|
352
|
+
typeof options.temperature === "number") {
|
|
353
|
+
span.setAttribute("neurolink.has_fallback", true);
|
|
354
|
+
span.addEvent("retry.initial_failure", {
|
|
355
|
+
"error.message": error instanceof Error ? error.message : String(error),
|
|
356
|
+
"retry.attempt": 1,
|
|
357
|
+
"retry.reason": "temperature_deprecated",
|
|
358
|
+
});
|
|
359
|
+
logger.debug("[GenerationHandler] temperature-deprecated error caught - retrying without temperature", {
|
|
360
|
+
provider: this.providerName,
|
|
361
|
+
model: this.modelName,
|
|
362
|
+
error: error instanceof Error ? error.message : String(error),
|
|
363
|
+
});
|
|
364
|
+
const result = await withProviderRetry(() => this.callGenerateText(model, messages, tools, { ...options, temperature: undefined }, shouldUseTools, true), span, "generateText(no-temperature)");
|
|
365
|
+
span.addEvent("retry.recovered", {
|
|
366
|
+
"retry.attempts": 2,
|
|
367
|
+
"retry.strategy": "temperature_omitted",
|
|
368
|
+
});
|
|
369
|
+
span.setAttribute("retry.count", 1);
|
|
370
|
+
if (result.usage) {
|
|
371
|
+
span.setAttribute("gen_ai.usage.input_tokens", result.usage.inputTokens || 0);
|
|
372
|
+
span.setAttribute("gen_ai.usage.output_tokens", result.usage.outputTokens || 0);
|
|
373
|
+
const noTempCost = calculateCost(this.providerName, this.modelName, {
|
|
374
|
+
input: result.usage.inputTokens || 0,
|
|
375
|
+
output: result.usage.outputTokens || 0,
|
|
376
|
+
total: (result.usage.inputTokens || 0) +
|
|
377
|
+
(result.usage.outputTokens || 0),
|
|
378
|
+
});
|
|
379
|
+
span.setAttribute("neurolink.cost", noTempCost ?? 0);
|
|
380
|
+
}
|
|
381
|
+
if (result.finishReason) {
|
|
382
|
+
span.setAttribute("gen_ai.response.finish_reason", result.finishReason);
|
|
383
|
+
}
|
|
384
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
345
387
|
span.setStatus({
|
|
346
388
|
code: SpanStatusCode.ERROR,
|
|
347
389
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -3,17 +3,32 @@
|
|
|
3
3
|
* disabled because the provider cannot combine tool calls with JSON-schema
|
|
4
4
|
* enforcement.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Two provider surfaces have this conflict:
|
|
7
|
+
* - Gemini (google-ai, or Vertex with a non-Claude model).
|
|
8
|
+
* - The native Anthropic Messages API surface (provider "anthropic"/"bedrock",
|
|
9
|
+
* including via a proxy/base-URL override). experimental_output silently
|
|
10
|
+
* drops tool_use blocks when tools are also present (finishReason=tool-calls
|
|
11
|
+
* but zero parsed tool calls), so structured output must be disabled there too.
|
|
12
|
+
*
|
|
13
|
+
* Vertex+Claude (provider "vertex", modelName starts with "claude-") uses a
|
|
14
|
+
* different transport that supports both simultaneously and must NOT be excluded —
|
|
15
|
+
* a gate keyed on "any Vertex model" wrongly disables it for the primary
|
|
16
|
+
* production config and forces fragile hand-parsed JSON.
|
|
11
17
|
*/
|
|
12
18
|
/** True when the provider+model is a Gemini model (the only family with the tools↔schema conflict). */
|
|
13
19
|
export declare function isGeminiProvider(providerName: string, modelName: string | undefined): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* True when the provider is the native Anthropic Messages API surface
|
|
22
|
+
* (provider "anthropic" — including via a proxy/base-URL override — or "bedrock").
|
|
23
|
+
* experimental_output + tools silently drops tool_use blocks on this surface, so
|
|
24
|
+
* structured output must be disabled when tools are active. Vertex+Claude is NOT
|
|
25
|
+
* matched here (different transport, no conflict).
|
|
26
|
+
*/
|
|
27
|
+
export declare function isNativeAnthropicProvider(providerName: string): boolean;
|
|
14
28
|
/**
|
|
15
29
|
* True when structured output must be disabled for this call because tools are
|
|
16
|
-
* active on a Gemini
|
|
30
|
+
* active on a provider that cannot combine them (Gemini, or the native Anthropic
|
|
31
|
+
* Messages API surface). Mirrors the AI-SDK constraint exactly.
|
|
17
32
|
*/
|
|
18
33
|
export declare function isToolsSchemaExclusionInForce(providerName: string, modelName: string | undefined, shouldUseTools: boolean, toolCount: number): boolean;
|
|
19
34
|
/**
|
|
@@ -26,3 +41,24 @@ export declare function isToolsSchemaExclusionInForce(providerName: string, mode
|
|
|
26
41
|
* and retried without structured output instead of failing the call.
|
|
27
42
|
*/
|
|
28
43
|
export declare function isToolsSchemaConflictError(error: unknown): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* True when a provider error indicates the request was rejected because the
|
|
46
|
+
* `temperature` parameter is deprecated / unsupported for the model. The newest
|
|
47
|
+
* Anthropic models (e.g. claude-opus-4-8, with tools + advanced beta features)
|
|
48
|
+
* reject `temperature` — "`temperature` is deprecated for this model." — in
|
|
49
|
+
* favour of reasoning-effort controls. Detect this so the call can be retried
|
|
50
|
+
* once without `temperature` instead of failing the turn.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isTemperatureDeprecatedError(error: unknown): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* True when the model is known to reject the `temperature` parameter (the
|
|
55
|
+
* reasoning-effort Anthropic models — claude-opus-4-8 and newer — deprecate it
|
|
56
|
+
* in favour of effort controls). Used to omit `temperature` proactively so the
|
|
57
|
+
* request does not fail-then-retry on every turn: the reactive
|
|
58
|
+
* isTemperatureDeprecatedError() retry remains the safety net for any model not
|
|
59
|
+
* matched here, but a guaranteed-to-fail first request is pure wasted latency.
|
|
60
|
+
*
|
|
61
|
+
* Matches opus 4.8+ (4-8, 4-9, 4-10, …) while leaving 4.1/4.5/4.6 and Sonnet/
|
|
62
|
+
* Haiku — which still accept `temperature` — untouched.
|
|
63
|
+
*/
|
|
64
|
+
export declare function modelDeprecatesTemperature(modelName: string | undefined): boolean;
|
|
@@ -3,11 +3,17 @@
|
|
|
3
3
|
* disabled because the provider cannot combine tool calls with JSON-schema
|
|
4
4
|
* enforcement.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Two provider surfaces have this conflict:
|
|
7
|
+
* - Gemini (google-ai, or Vertex with a non-Claude model).
|
|
8
|
+
* - The native Anthropic Messages API surface (provider "anthropic"/"bedrock",
|
|
9
|
+
* including via a proxy/base-URL override). experimental_output silently
|
|
10
|
+
* drops tool_use blocks when tools are also present (finishReason=tool-calls
|
|
11
|
+
* but zero parsed tool calls), so structured output must be disabled there too.
|
|
12
|
+
*
|
|
13
|
+
* Vertex+Claude (provider "vertex", modelName starts with "claude-") uses a
|
|
14
|
+
* different transport that supports both simultaneously and must NOT be excluded —
|
|
15
|
+
* a gate keyed on "any Vertex model" wrongly disables it for the primary
|
|
16
|
+
* production config and forces fragile hand-parsed JSON.
|
|
11
17
|
*/
|
|
12
18
|
/** True when the provider+model is a Gemini model (the only family with the tools↔schema conflict). */
|
|
13
19
|
export function isGeminiProvider(providerName, modelName) {
|
|
@@ -21,12 +27,26 @@ export function isGeminiProvider(providerName, modelName) {
|
|
|
21
27
|
}
|
|
22
28
|
return false;
|
|
23
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* True when the provider is the native Anthropic Messages API surface
|
|
32
|
+
* (provider "anthropic" — including via a proxy/base-URL override — or "bedrock").
|
|
33
|
+
* experimental_output + tools silently drops tool_use blocks on this surface, so
|
|
34
|
+
* structured output must be disabled when tools are active. Vertex+Claude is NOT
|
|
35
|
+
* matched here (different transport, no conflict).
|
|
36
|
+
*/
|
|
37
|
+
export function isNativeAnthropicProvider(providerName) {
|
|
38
|
+
return providerName === "anthropic" || providerName === "bedrock";
|
|
39
|
+
}
|
|
24
40
|
/**
|
|
25
41
|
* True when structured output must be disabled for this call because tools are
|
|
26
|
-
* active on a Gemini
|
|
42
|
+
* active on a provider that cannot combine them (Gemini, or the native Anthropic
|
|
43
|
+
* Messages API surface). Mirrors the AI-SDK constraint exactly.
|
|
27
44
|
*/
|
|
28
45
|
export function isToolsSchemaExclusionInForce(providerName, modelName, shouldUseTools, toolCount) {
|
|
29
|
-
return (isGeminiProvider(providerName, modelName)
|
|
46
|
+
return ((isGeminiProvider(providerName, modelName) ||
|
|
47
|
+
isNativeAnthropicProvider(providerName)) &&
|
|
48
|
+
shouldUseTools &&
|
|
49
|
+
toolCount > 0);
|
|
30
50
|
}
|
|
31
51
|
/**
|
|
32
52
|
* True when a provider error indicates the request was rejected because JSON /
|
|
@@ -48,3 +68,34 @@ export function isToolsSchemaConflictError(error) {
|
|
|
48
68
|
/(tool|function)[\s-]?call[^.]{0,60}json[\s_-]?(mode|schema)/i.test(message) ||
|
|
49
69
|
/response_format[^.]{0,60}(tool|function)/i.test(message));
|
|
50
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* True when a provider error indicates the request was rejected because the
|
|
73
|
+
* `temperature` parameter is deprecated / unsupported for the model. The newest
|
|
74
|
+
* Anthropic models (e.g. claude-opus-4-8, with tools + advanced beta features)
|
|
75
|
+
* reject `temperature` — "`temperature` is deprecated for this model." — in
|
|
76
|
+
* favour of reasoning-effort controls. Detect this so the call can be retried
|
|
77
|
+
* once without `temperature` instead of failing the turn.
|
|
78
|
+
*/
|
|
79
|
+
export function isTemperatureDeprecatedError(error) {
|
|
80
|
+
const message = error instanceof Error
|
|
81
|
+
? error.message
|
|
82
|
+
: typeof error === "string"
|
|
83
|
+
? error
|
|
84
|
+
: "";
|
|
85
|
+
return /\btemperature\b[^.]{0,40}\b(deprecated|unsupported|not[\s_-]?(supported|allowed))\b/i.test(message);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* True when the model is known to reject the `temperature` parameter (the
|
|
89
|
+
* reasoning-effort Anthropic models — claude-opus-4-8 and newer — deprecate it
|
|
90
|
+
* in favour of effort controls). Used to omit `temperature` proactively so the
|
|
91
|
+
* request does not fail-then-retry on every turn: the reactive
|
|
92
|
+
* isTemperatureDeprecatedError() retry remains the safety net for any model not
|
|
93
|
+
* matched here, but a guaranteed-to-fail first request is pure wasted latency.
|
|
94
|
+
*
|
|
95
|
+
* Matches opus 4.8+ (4-8, 4-9, 4-10, …) while leaving 4.1/4.5/4.6 and Sonnet/
|
|
96
|
+
* Haiku — which still accept `temperature` — untouched.
|
|
97
|
+
*/
|
|
98
|
+
export function modelDeprecatesTemperature(modelName) {
|
|
99
|
+
const m = (modelName ?? "").toLowerCase();
|
|
100
|
+
return /opus[-_.]?4[-_.]?(?:[89]|\d{2,})\b/.test(m);
|
|
101
|
+
}
|
|
@@ -21,7 +21,7 @@ import { calculateCost } from "../../utils/pricing.js";
|
|
|
21
21
|
import { withProviderRetry } from "../../utils/providerRetry.js";
|
|
22
22
|
import { calculateCacheSavingsPercent, extractCacheCreationTokens, extractCacheReadTokens, extractTokenUsage, } from "../../utils/tokenUtils.js";
|
|
23
23
|
import { DEFAULT_MAX_STEPS } from "../constants.js";
|
|
24
|
-
import { isToolsSchemaConflictError, isToolsSchemaExclusionInForce, } from "./structuredOutputPolicy.js";
|
|
24
|
+
import { isTemperatureDeprecatedError, isToolsSchemaConflictError, isToolsSchemaExclusionInForce, } from "./structuredOutputPolicy.js";
|
|
25
25
|
import { coerceJsonToSchema } from "../../utils/json/coerce.js";
|
|
26
26
|
import { NoObjectGeneratedError } from "../../utils/generationErrors.js";
|
|
27
27
|
import { Output, stepCountIs } from "../../utils/tool.js";
|
|
@@ -342,6 +342,48 @@ export class GenerationHandler {
|
|
|
342
342
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
343
343
|
return result;
|
|
344
344
|
}
|
|
345
|
+
// Retry once without `temperature` when the model deprecated it. The
|
|
346
|
+
// newest Anthropic models (e.g. claude-opus-4-8 with tools + advanced
|
|
347
|
+
// beta features) reject `temperature` — "`temperature` is deprecated
|
|
348
|
+
// for this model." — in favour of reasoning-effort controls. Structured
|
|
349
|
+
// output is already excluded for the native anthropic surface, so this
|
|
350
|
+
// is the dominant failure mode for Opus there.
|
|
351
|
+
if (isTemperatureDeprecatedError(error) &&
|
|
352
|
+
typeof options.temperature === "number") {
|
|
353
|
+
span.setAttribute("neurolink.has_fallback", true);
|
|
354
|
+
span.addEvent("retry.initial_failure", {
|
|
355
|
+
"error.message": error instanceof Error ? error.message : String(error),
|
|
356
|
+
"retry.attempt": 1,
|
|
357
|
+
"retry.reason": "temperature_deprecated",
|
|
358
|
+
});
|
|
359
|
+
logger.debug("[GenerationHandler] temperature-deprecated error caught - retrying without temperature", {
|
|
360
|
+
provider: this.providerName,
|
|
361
|
+
model: this.modelName,
|
|
362
|
+
error: error instanceof Error ? error.message : String(error),
|
|
363
|
+
});
|
|
364
|
+
const result = await withProviderRetry(() => this.callGenerateText(model, messages, tools, { ...options, temperature: undefined }, shouldUseTools, true), span, "generateText(no-temperature)");
|
|
365
|
+
span.addEvent("retry.recovered", {
|
|
366
|
+
"retry.attempts": 2,
|
|
367
|
+
"retry.strategy": "temperature_omitted",
|
|
368
|
+
});
|
|
369
|
+
span.setAttribute("retry.count", 1);
|
|
370
|
+
if (result.usage) {
|
|
371
|
+
span.setAttribute("gen_ai.usage.input_tokens", result.usage.inputTokens || 0);
|
|
372
|
+
span.setAttribute("gen_ai.usage.output_tokens", result.usage.outputTokens || 0);
|
|
373
|
+
const noTempCost = calculateCost(this.providerName, this.modelName, {
|
|
374
|
+
input: result.usage.inputTokens || 0,
|
|
375
|
+
output: result.usage.outputTokens || 0,
|
|
376
|
+
total: (result.usage.inputTokens || 0) +
|
|
377
|
+
(result.usage.outputTokens || 0),
|
|
378
|
+
});
|
|
379
|
+
span.setAttribute("neurolink.cost", noTempCost ?? 0);
|
|
380
|
+
}
|
|
381
|
+
if (result.finishReason) {
|
|
382
|
+
span.setAttribute("gen_ai.response.finish_reason", result.finishReason);
|
|
383
|
+
}
|
|
384
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
345
387
|
span.setStatus({
|
|
346
388
|
code: SpanStatusCode.ERROR,
|
|
347
389
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -3,17 +3,32 @@
|
|
|
3
3
|
* disabled because the provider cannot combine tool calls with JSON-schema
|
|
4
4
|
* enforcement.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Two provider surfaces have this conflict:
|
|
7
|
+
* - Gemini (google-ai, or Vertex with a non-Claude model).
|
|
8
|
+
* - The native Anthropic Messages API surface (provider "anthropic"/"bedrock",
|
|
9
|
+
* including via a proxy/base-URL override). experimental_output silently
|
|
10
|
+
* drops tool_use blocks when tools are also present (finishReason=tool-calls
|
|
11
|
+
* but zero parsed tool calls), so structured output must be disabled there too.
|
|
12
|
+
*
|
|
13
|
+
* Vertex+Claude (provider "vertex", modelName starts with "claude-") uses a
|
|
14
|
+
* different transport that supports both simultaneously and must NOT be excluded —
|
|
15
|
+
* a gate keyed on "any Vertex model" wrongly disables it for the primary
|
|
16
|
+
* production config and forces fragile hand-parsed JSON.
|
|
11
17
|
*/
|
|
12
18
|
/** True when the provider+model is a Gemini model (the only family with the tools↔schema conflict). */
|
|
13
19
|
export declare function isGeminiProvider(providerName: string, modelName: string | undefined): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* True when the provider is the native Anthropic Messages API surface
|
|
22
|
+
* (provider "anthropic" — including via a proxy/base-URL override — or "bedrock").
|
|
23
|
+
* experimental_output + tools silently drops tool_use blocks on this surface, so
|
|
24
|
+
* structured output must be disabled when tools are active. Vertex+Claude is NOT
|
|
25
|
+
* matched here (different transport, no conflict).
|
|
26
|
+
*/
|
|
27
|
+
export declare function isNativeAnthropicProvider(providerName: string): boolean;
|
|
14
28
|
/**
|
|
15
29
|
* True when structured output must be disabled for this call because tools are
|
|
16
|
-
* active on a Gemini
|
|
30
|
+
* active on a provider that cannot combine them (Gemini, or the native Anthropic
|
|
31
|
+
* Messages API surface). Mirrors the AI-SDK constraint exactly.
|
|
17
32
|
*/
|
|
18
33
|
export declare function isToolsSchemaExclusionInForce(providerName: string, modelName: string | undefined, shouldUseTools: boolean, toolCount: number): boolean;
|
|
19
34
|
/**
|
|
@@ -26,3 +41,24 @@ export declare function isToolsSchemaExclusionInForce(providerName: string, mode
|
|
|
26
41
|
* and retried without structured output instead of failing the call.
|
|
27
42
|
*/
|
|
28
43
|
export declare function isToolsSchemaConflictError(error: unknown): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* True when a provider error indicates the request was rejected because the
|
|
46
|
+
* `temperature` parameter is deprecated / unsupported for the model. The newest
|
|
47
|
+
* Anthropic models (e.g. claude-opus-4-8, with tools + advanced beta features)
|
|
48
|
+
* reject `temperature` — "`temperature` is deprecated for this model." — in
|
|
49
|
+
* favour of reasoning-effort controls. Detect this so the call can be retried
|
|
50
|
+
* once without `temperature` instead of failing the turn.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isTemperatureDeprecatedError(error: unknown): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* True when the model is known to reject the `temperature` parameter (the
|
|
55
|
+
* reasoning-effort Anthropic models — claude-opus-4-8 and newer — deprecate it
|
|
56
|
+
* in favour of effort controls). Used to omit `temperature` proactively so the
|
|
57
|
+
* request does not fail-then-retry on every turn: the reactive
|
|
58
|
+
* isTemperatureDeprecatedError() retry remains the safety net for any model not
|
|
59
|
+
* matched here, but a guaranteed-to-fail first request is pure wasted latency.
|
|
60
|
+
*
|
|
61
|
+
* Matches opus 4.8+ (4-8, 4-9, 4-10, …) while leaving 4.1/4.5/4.6 and Sonnet/
|
|
62
|
+
* Haiku — which still accept `temperature` — untouched.
|
|
63
|
+
*/
|
|
64
|
+
export declare function modelDeprecatesTemperature(modelName: string | undefined): boolean;
|
|
@@ -3,11 +3,17 @@
|
|
|
3
3
|
* disabled because the provider cannot combine tool calls with JSON-schema
|
|
4
4
|
* enforcement.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Two provider surfaces have this conflict:
|
|
7
|
+
* - Gemini (google-ai, or Vertex with a non-Claude model).
|
|
8
|
+
* - The native Anthropic Messages API surface (provider "anthropic"/"bedrock",
|
|
9
|
+
* including via a proxy/base-URL override). experimental_output silently
|
|
10
|
+
* drops tool_use blocks when tools are also present (finishReason=tool-calls
|
|
11
|
+
* but zero parsed tool calls), so structured output must be disabled there too.
|
|
12
|
+
*
|
|
13
|
+
* Vertex+Claude (provider "vertex", modelName starts with "claude-") uses a
|
|
14
|
+
* different transport that supports both simultaneously and must NOT be excluded —
|
|
15
|
+
* a gate keyed on "any Vertex model" wrongly disables it for the primary
|
|
16
|
+
* production config and forces fragile hand-parsed JSON.
|
|
11
17
|
*/
|
|
12
18
|
/** True when the provider+model is a Gemini model (the only family with the tools↔schema conflict). */
|
|
13
19
|
export function isGeminiProvider(providerName, modelName) {
|
|
@@ -21,12 +27,26 @@ export function isGeminiProvider(providerName, modelName) {
|
|
|
21
27
|
}
|
|
22
28
|
return false;
|
|
23
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* True when the provider is the native Anthropic Messages API surface
|
|
32
|
+
* (provider "anthropic" — including via a proxy/base-URL override — or "bedrock").
|
|
33
|
+
* experimental_output + tools silently drops tool_use blocks on this surface, so
|
|
34
|
+
* structured output must be disabled when tools are active. Vertex+Claude is NOT
|
|
35
|
+
* matched here (different transport, no conflict).
|
|
36
|
+
*/
|
|
37
|
+
export function isNativeAnthropicProvider(providerName) {
|
|
38
|
+
return providerName === "anthropic" || providerName === "bedrock";
|
|
39
|
+
}
|
|
24
40
|
/**
|
|
25
41
|
* True when structured output must be disabled for this call because tools are
|
|
26
|
-
* active on a Gemini
|
|
42
|
+
* active on a provider that cannot combine them (Gemini, or the native Anthropic
|
|
43
|
+
* Messages API surface). Mirrors the AI-SDK constraint exactly.
|
|
27
44
|
*/
|
|
28
45
|
export function isToolsSchemaExclusionInForce(providerName, modelName, shouldUseTools, toolCount) {
|
|
29
|
-
return (isGeminiProvider(providerName, modelName)
|
|
46
|
+
return ((isGeminiProvider(providerName, modelName) ||
|
|
47
|
+
isNativeAnthropicProvider(providerName)) &&
|
|
48
|
+
shouldUseTools &&
|
|
49
|
+
toolCount > 0);
|
|
30
50
|
}
|
|
31
51
|
/**
|
|
32
52
|
* True when a provider error indicates the request was rejected because JSON /
|
|
@@ -48,4 +68,35 @@ export function isToolsSchemaConflictError(error) {
|
|
|
48
68
|
/(tool|function)[\s-]?call[^.]{0,60}json[\s_-]?(mode|schema)/i.test(message) ||
|
|
49
69
|
/response_format[^.]{0,60}(tool|function)/i.test(message));
|
|
50
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* True when a provider error indicates the request was rejected because the
|
|
73
|
+
* `temperature` parameter is deprecated / unsupported for the model. The newest
|
|
74
|
+
* Anthropic models (e.g. claude-opus-4-8, with tools + advanced beta features)
|
|
75
|
+
* reject `temperature` — "`temperature` is deprecated for this model." — in
|
|
76
|
+
* favour of reasoning-effort controls. Detect this so the call can be retried
|
|
77
|
+
* once without `temperature` instead of failing the turn.
|
|
78
|
+
*/
|
|
79
|
+
export function isTemperatureDeprecatedError(error) {
|
|
80
|
+
const message = error instanceof Error
|
|
81
|
+
? error.message
|
|
82
|
+
: typeof error === "string"
|
|
83
|
+
? error
|
|
84
|
+
: "";
|
|
85
|
+
return /\btemperature\b[^.]{0,40}\b(deprecated|unsupported|not[\s_-]?(supported|allowed))\b/i.test(message);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* True when the model is known to reject the `temperature` parameter (the
|
|
89
|
+
* reasoning-effort Anthropic models — claude-opus-4-8 and newer — deprecate it
|
|
90
|
+
* in favour of effort controls). Used to omit `temperature` proactively so the
|
|
91
|
+
* request does not fail-then-retry on every turn: the reactive
|
|
92
|
+
* isTemperatureDeprecatedError() retry remains the safety net for any model not
|
|
93
|
+
* matched here, but a guaranteed-to-fail first request is pure wasted latency.
|
|
94
|
+
*
|
|
95
|
+
* Matches opus 4.8+ (4-8, 4-9, 4-10, …) while leaving 4.1/4.5/4.6 and Sonnet/
|
|
96
|
+
* Haiku — which still accept `temperature` — untouched.
|
|
97
|
+
*/
|
|
98
|
+
export function modelDeprecatesTemperature(modelName) {
|
|
99
|
+
const m = (modelName ?? "").toLowerCase();
|
|
100
|
+
return /opus[-_.]?4[-_.]?(?:[89]|\d{2,})\b/.test(m);
|
|
101
|
+
}
|
|
51
102
|
//# sourceMappingURL=structuredOutputPolicy.js.map
|
|
@@ -44,7 +44,15 @@ async function loadExcelJS() {
|
|
|
44
44
|
return _exceljs;
|
|
45
45
|
}
|
|
46
46
|
try {
|
|
47
|
-
|
|
47
|
+
const mod = await import(/* @vite-ignore */ "exceljs");
|
|
48
|
+
// exceljs is a CommonJS module. Under Node ESM (and some bundlers) the
|
|
49
|
+
// `Workbook` constructor is exposed at runtime on the namespace's `default`
|
|
50
|
+
// export rather than on the namespace itself — so a bare
|
|
51
|
+
// `new ExcelJS.Workbook()` throws "ExcelJS.Workbook is not a constructor"
|
|
52
|
+
// (TS still types it as present via esModuleInterop, masking the bug).
|
|
53
|
+
// Normalise here so the constructor is reachable regardless of interop style.
|
|
54
|
+
const ns = mod;
|
|
55
|
+
_exceljs = (ns.Workbook ? ns : (ns.default ?? ns));
|
|
48
56
|
return _exceljs;
|
|
49
57
|
}
|
|
50
58
|
catch (err) {
|
|
@@ -22,6 +22,8 @@ import { NoOutputGeneratedError } from "../utils/generationErrors.js";
|
|
|
22
22
|
import { buildNoOutputSentinel, stampNoOutputSpan, } from "../utils/noOutputSentinel.js";
|
|
23
23
|
import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
|
|
24
24
|
import { resolveClaudeMaxTokens } from "../utils/tokenLimits.js";
|
|
25
|
+
import { toAnthropicImageBlock, fileToAnthropicBlock, } from "./anthropicImageBlocks.js";
|
|
26
|
+
import { modelDeprecatesTemperature } from "../core/modules/structuredOutputPolicy.js";
|
|
25
27
|
import { createChunkQueue, createDeferredAnalytics, stringifyToolInput, } from "./openaiChatCompletionsClient.js";
|
|
26
28
|
/**
|
|
27
29
|
* Beta headers for Claude Code integration.
|
|
@@ -209,45 +211,6 @@ const parseRateLimitHeaders = (headers) => {
|
|
|
209
211
|
// ───────────────────────────────────────────────────────────────────────────
|
|
210
212
|
// Native Messages-API conversion helpers (NeuroLink/V3 shapes → Anthropic)
|
|
211
213
|
// ───────────────────────────────────────────────────────────────────────────
|
|
212
|
-
/**
|
|
213
|
-
* Convert an image part (data URL, bare base64, https URL, or byte array)
|
|
214
|
-
* into an Anthropic image block. Returns undefined for unusable inputs.
|
|
215
|
-
*/
|
|
216
|
-
const toAnthropicImageBlock = (data) => {
|
|
217
|
-
if (data instanceof Uint8Array) {
|
|
218
|
-
return {
|
|
219
|
-
type: "image",
|
|
220
|
-
source: {
|
|
221
|
-
type: "base64",
|
|
222
|
-
media_type: "image/png",
|
|
223
|
-
data: Buffer.from(data).toString("base64"),
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
if (typeof data !== "string" && !(data instanceof URL)) {
|
|
228
|
-
return undefined;
|
|
229
|
-
}
|
|
230
|
-
const str = data instanceof URL ? data.toString() : data;
|
|
231
|
-
const dataUrlMatch = str.match(/^data:(image\/[a-z+.-]+);base64,(.+)$/i);
|
|
232
|
-
if (dataUrlMatch) {
|
|
233
|
-
return {
|
|
234
|
-
type: "image",
|
|
235
|
-
source: {
|
|
236
|
-
type: "base64",
|
|
237
|
-
media_type: dataUrlMatch[1],
|
|
238
|
-
data: dataUrlMatch[2],
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
if (/^https?:\/\//i.test(str)) {
|
|
243
|
-
return { type: "image", source: { type: "url", url: str } };
|
|
244
|
-
}
|
|
245
|
-
// Bare base64 payload — assume PNG (matches the OpenAI-compat client).
|
|
246
|
-
return {
|
|
247
|
-
type: "image",
|
|
248
|
-
source: { type: "base64", media_type: "image/png", data: str },
|
|
249
|
-
};
|
|
250
|
-
};
|
|
251
214
|
/**
|
|
252
215
|
* Read an Anthropic cache breakpoint from a message/part/tool carrier.
|
|
253
216
|
* MessageBuilder marks system messages (and GenerationHandler marks the last
|
|
@@ -347,6 +310,35 @@ const messagesToAnthropic = (msgs) => {
|
|
|
347
310
|
blocks.push(cc ? { ...img, cache_control: cc } : img);
|
|
348
311
|
}
|
|
349
312
|
}
|
|
313
|
+
else if (p?.type === "file") {
|
|
314
|
+
// AI-SDK v6 encodes images AND PDFs as `type:"file"` parts in the
|
|
315
|
+
// LanguageModel prompt that `doGenerate` receives. Without this
|
|
316
|
+
// branch the image is dropped on the tool-using generate path and
|
|
317
|
+
// the model never sees it ("no image detected").
|
|
318
|
+
//
|
|
319
|
+
// Runtime guard: p comes from message parsing and may not match the
|
|
320
|
+
// expected shape. Verify p is an object and that mediaType, if
|
|
321
|
+
// present, is a string (not an object/array from a malformed part).
|
|
322
|
+
// Skip gracefully rather than passing a bad shape to fileToAnthropicBlock.
|
|
323
|
+
const isValidFilePart = typeof p === "object" &&
|
|
324
|
+
p !== null &&
|
|
325
|
+
("mediaType" in p
|
|
326
|
+
? typeof p.mediaType === "string"
|
|
327
|
+
: true);
|
|
328
|
+
const block = isValidFilePart
|
|
329
|
+
? fileToAnthropicBlock(p)
|
|
330
|
+
: undefined;
|
|
331
|
+
if (block) {
|
|
332
|
+
const cc = cacheControlOf(p);
|
|
333
|
+
if (cc) {
|
|
334
|
+
// block is a fresh object from fileToAnthropicBlock; mutate in
|
|
335
|
+
// place to keep the discriminated-union type (a spread widens it
|
|
336
|
+
// past ContentBlockParam).
|
|
337
|
+
block.cache_control = cc;
|
|
338
|
+
}
|
|
339
|
+
blocks.push(block);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
350
342
|
}
|
|
351
343
|
if (blocks.length > 0) {
|
|
352
344
|
applyMessageCacheControl(blocks, msg);
|
|
@@ -1136,7 +1128,9 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1136
1128
|
messages,
|
|
1137
1129
|
max_tokens: resolveClaudeMaxTokens(modelId, options.maxOutputTokens),
|
|
1138
1130
|
...(system ? { system } : {}),
|
|
1139
|
-
...(options.temperature !== undefined &&
|
|
1131
|
+
...(options.temperature !== undefined &&
|
|
1132
|
+
options.temperature !== null &&
|
|
1133
|
+
!modelDeprecatesTemperature(modelId)
|
|
1140
1134
|
? { temperature: options.temperature }
|
|
1141
1135
|
: {}),
|
|
1142
1136
|
...(options.topP !== undefined && options.topP !== null
|
|
@@ -1386,7 +1380,9 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1386
1380
|
max_tokens: resolveClaudeMaxTokens(modelId, options.maxTokens),
|
|
1387
1381
|
stream: true,
|
|
1388
1382
|
...(payload.system ? { system: payload.system } : {}),
|
|
1389
|
-
...(options.temperature !== undefined &&
|
|
1383
|
+
...(options.temperature !== undefined &&
|
|
1384
|
+
options.temperature !== null &&
|
|
1385
|
+
!modelDeprecatesTemperature(modelId)
|
|
1390
1386
|
? { temperature: options.temperature }
|
|
1391
1387
|
: {}),
|
|
1392
1388
|
...(anthropicTools && anthropicTools.length > 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure converters from multimodal content parts into native Anthropic
|
|
3
|
+
* Messages-API content blocks (image / document).
|
|
4
|
+
*
|
|
5
|
+
* Two input shapes reach the native Anthropic surface:
|
|
6
|
+
*
|
|
7
|
+
* 1. NeuroLink/V3 multimodal parts — `{ type: "image", image }` produced by
|
|
8
|
+
* the multimodal message builder (base64, data URL, https URL, or bytes).
|
|
9
|
+
*
|
|
10
|
+
* 2. AI-SDK LanguageModel prompt parts — `{ type: "file", mediaType, data }`.
|
|
11
|
+
* This is how `ai@6` encodes BOTH images and PDFs in the prompt that the
|
|
12
|
+
* provider's `doGenerate(options.prompt)` receives. Before this module the
|
|
13
|
+
* converter only handled `type:"image"`, so on the tool-using generate
|
|
14
|
+
* path (which always normalises images to `type:"file"`) the image part
|
|
15
|
+
* was silently dropped — the model never saw the image ("no image
|
|
16
|
+
* detected"). Gemini/Vertex are immune because they read `input.images`
|
|
17
|
+
* directly in their own builders instead of going through this conversion.
|
|
18
|
+
*
|
|
19
|
+
* Media type is taken from the AI-SDK-provided `mediaType` when present, then
|
|
20
|
+
* sniffed from magic bytes, and only then defaulted — a hardcoded `image/png`
|
|
21
|
+
* default silently corrupts JPEG/GIF/WebP uploads (the Anthropic API rejects a
|
|
22
|
+
* mislabeled base64 image with HTTP 400 and the image vanishes).
|
|
23
|
+
*/
|
|
24
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
25
|
+
/**
|
|
26
|
+
* Detect a supported image media type from a buffer's magic bytes. Returns
|
|
27
|
+
* undefined when the bytes are not one of the four Anthropic-supported formats.
|
|
28
|
+
*/
|
|
29
|
+
export declare function sniffImageMediaType(bytes: Uint8Array): Anthropic.Messages.Base64ImageSource["media_type"] | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Convert an image part (data URL, bare base64, https URL, byte array, or
|
|
32
|
+
* ArrayBuffer) into an Anthropic image block. Honors `mediaTypeHint` (the
|
|
33
|
+
* AI-SDK `mediaType`), falls back to magic-byte sniffing, then to image/png.
|
|
34
|
+
* Returns undefined for unusable inputs.
|
|
35
|
+
*/
|
|
36
|
+
export declare function toAnthropicImageBlock(data: unknown, mediaTypeHint?: string): Anthropic.Messages.ImageBlockParam | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Convert an AI-SDK `{ type: "file", mediaType, data }` content part into the
|
|
39
|
+
* matching Anthropic content block: image/* → image block (media type honored,
|
|
40
|
+
* not hardcoded), application/pdf → document block. Returns undefined for
|
|
41
|
+
* unsupported media types (so the caller simply omits them rather than 400ing).
|
|
42
|
+
* When `mediaType` is absent, image bytes are still salvaged via sniffing.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fileToAnthropicBlock(part: {
|
|
45
|
+
mediaType?: string;
|
|
46
|
+
data?: unknown;
|
|
47
|
+
}): Anthropic.Messages.ContentBlockParam | undefined;
|