@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/neurolink.min.js +337 -332
  3. package/dist/core/modules/GenerationHandler.js +43 -1
  4. package/dist/core/modules/structuredOutputPolicy.d.ts +42 -6
  5. package/dist/core/modules/structuredOutputPolicy.js +58 -7
  6. package/dist/lib/core/modules/GenerationHandler.js +43 -1
  7. package/dist/lib/core/modules/structuredOutputPolicy.d.ts +42 -6
  8. package/dist/lib/core/modules/structuredOutputPolicy.js +58 -7
  9. package/dist/lib/processors/document/ExcelProcessor.js +9 -1
  10. package/dist/lib/providers/anthropic.js +37 -41
  11. package/dist/lib/providers/anthropicImageBlocks.d.ts +47 -0
  12. package/dist/lib/providers/anthropicImageBlocks.js +223 -0
  13. package/dist/lib/providers/googleNativeGemini3.d.ts +26 -0
  14. package/dist/lib/providers/googleNativeGemini3.js +48 -0
  15. package/dist/lib/providers/googleVertex.d.ts +16 -0
  16. package/dist/lib/providers/googleVertex.js +200 -24
  17. package/dist/lib/proxy/oauthFetch.js +26 -14
  18. package/dist/lib/proxy/proxyTracer.d.ts +7 -1
  19. package/dist/lib/proxy/proxyTracer.js +29 -0
  20. package/dist/lib/proxy/systemRelocation.d.ts +21 -0
  21. package/dist/lib/proxy/systemRelocation.js +51 -0
  22. package/dist/lib/server/routes/claudeProxyRoutes.js +147 -19
  23. package/dist/lib/types/proxy.d.ts +10 -0
  24. package/dist/processors/document/ExcelProcessor.js +9 -1
  25. package/dist/providers/anthropic.js +37 -41
  26. package/dist/providers/anthropicImageBlocks.d.ts +47 -0
  27. package/dist/providers/anthropicImageBlocks.js +222 -0
  28. package/dist/providers/googleNativeGemini3.d.ts +26 -0
  29. package/dist/providers/googleNativeGemini3.js +48 -0
  30. package/dist/providers/googleVertex.d.ts +16 -0
  31. package/dist/providers/googleVertex.js +200 -24
  32. package/dist/proxy/oauthFetch.js +26 -14
  33. package/dist/proxy/proxyTracer.d.ts +7 -1
  34. package/dist/proxy/proxyTracer.js +29 -0
  35. package/dist/proxy/systemRelocation.d.ts +21 -0
  36. package/dist/proxy/systemRelocation.js +50 -0
  37. package/dist/server/routes/claudeProxyRoutes.js +147 -19
  38. package/dist/types/proxy.d.ts +10 -0
  39. 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
- * This is a GEMINI-ONLY API limitation. Anthropic Claude — including when
7
- * hosted on Vertex (modelName starts with "claude-") — supports tools and
8
- * structured output simultaneously, so it must NOT be excluded. A gate keyed on
9
- * "any Vertex model" wrongly disables structured output for Vertex+Claude (the
10
- * primary production config) and forces fragile hand-parsed JSON.
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 provider. Mirrors the AI-SDK constraint exactly.
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
- * This is a GEMINI-ONLY API limitation. Anthropic Claude — including when
7
- * hosted on Vertex (modelName starts with "claude-") — supports tools and
8
- * structured output simultaneously, so it must NOT be excluded. A gate keyed on
9
- * "any Vertex model" wrongly disables structured output for Vertex+Claude (the
10
- * primary production config) and forces fragile hand-parsed JSON.
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 provider. Mirrors the AI-SDK constraint exactly.
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) && shouldUseTools && toolCount > 0);
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
- * This is a GEMINI-ONLY API limitation. Anthropic Claude — including when
7
- * hosted on Vertex (modelName starts with "claude-") — supports tools and
8
- * structured output simultaneously, so it must NOT be excluded. A gate keyed on
9
- * "any Vertex model" wrongly disables structured output for Vertex+Claude (the
10
- * primary production config) and forces fragile hand-parsed JSON.
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 provider. Mirrors the AI-SDK constraint exactly.
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
- * This is a GEMINI-ONLY API limitation. Anthropic Claude — including when
7
- * hosted on Vertex (modelName starts with "claude-") — supports tools and
8
- * structured output simultaneously, so it must NOT be excluded. A gate keyed on
9
- * "any Vertex model" wrongly disables structured output for Vertex+Claude (the
10
- * primary production config) and forces fragile hand-parsed JSON.
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 provider. Mirrors the AI-SDK constraint exactly.
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) && shouldUseTools && toolCount > 0);
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
- _exceljs = await import(/* @vite-ignore */ "exceljs");
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 && options.temperature !== null
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 && options.temperature !== null
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;