@juspay/neurolink 12.37.4 → 12.37.5
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 +3 -2
- package/README.md +1 -1
- package/dist/browser/neurolink.min.js +421 -421
- package/dist/core/baseProvider.d.ts +10 -1
- package/dist/core/baseProvider.js +35 -7
- package/dist/models/manifests/anthropic.js +2 -2
- package/dist/models/modelRegistry.d.ts +12 -1
- package/dist/models/modelRegistry.js +57 -5
- package/dist/providers/amazonSagemaker.js +1 -1
- package/dist/providers/anthropic/client.js +29 -5
- package/dist/providers/anthropic/structuredOutput.d.ts +4 -1
- package/dist/providers/anthropic/structuredOutput.js +13 -5
- package/dist/providers/catalog/cerebras.json +7 -8
- package/dist/providers/catalog/fireworks.json +10 -12
- package/dist/providers/googleVertex/client.d.ts +3 -3
- package/dist/providers/googleVertex/client.js +79 -26
- package/dist/providers/openaiChatCompletionsBase.js +1 -1
- package/dist/proxy/vertexAnthropicFallback.d.ts +1 -12
- package/dist/proxy/vertexAnthropicFallback.js +43 -2
- package/dist/types/model.d.ts +6 -0
- package/docs-site/static/search-index.json +14 -14
- package/package.json +1 -1
|
@@ -262,12 +262,21 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
262
262
|
* and a retired default model stops degrading to the next live one in the
|
|
263
263
|
* catalog — which is exactly what the provider contract gate checks.
|
|
264
264
|
*/
|
|
265
|
-
protected runGenerateWithModelFallback(attempt: () => Promise<EnhancedGenerateResult | null>, callerOwnsFallback: boolean): Promise<EnhancedGenerateResult | null>;
|
|
265
|
+
protected runGenerateWithModelFallback(attempt: () => Promise<EnhancedGenerateResult | null>, callerOwnsFallback: boolean, options?: TextGenerationOptions): Promise<EnhancedGenerateResult | null>;
|
|
266
266
|
/**
|
|
267
267
|
* Model ids to try when this provider rejects its current model as invalid.
|
|
268
268
|
* Empty by default; catalog-driven providers return their `fallbacks`.
|
|
269
269
|
*/
|
|
270
270
|
protected getModelFallbacks(): string[];
|
|
271
|
+
/**
|
|
272
|
+
* The fallbacks an invalid-model retry may try for this request. A catalog
|
|
273
|
+
* orders `fallbacks` for text, so a request carrying an image keeps only the
|
|
274
|
+
* vision-capable ones: a text-only candidate fails the vision guard, and
|
|
275
|
+
* that error names the fallback instead of the retired model the caller
|
|
276
|
+
* asked for. With no vision-capable candidate left, the caller gets the
|
|
277
|
+
* original invalid-model error.
|
|
278
|
+
*/
|
|
279
|
+
protected modelFallbacksFor(options: TextGenerationOptions | StreamOptions, requestedModel: string): string[];
|
|
271
280
|
/**
|
|
272
281
|
* Alias for generate method - implements AIProvider interface
|
|
273
282
|
*/
|
|
@@ -46,6 +46,7 @@ import { StreamHandler } from "./modules/StreamHandler.js";
|
|
|
46
46
|
import { TelemetryHandler } from "./modules/TelemetryHandler.js";
|
|
47
47
|
import { ToolsManager } from "./modules/ToolsManager.js";
|
|
48
48
|
import { Utilities } from "./modules/Utilities.js";
|
|
49
|
+
import { ProviderImageAdapter } from "../adapters/providerImageAdapter.js";
|
|
49
50
|
import { generateOnceNative } from "../utils/nativeSingleShot.js";
|
|
50
51
|
import { validateExecutionControl } from "../utils/parameterValidation.js";
|
|
51
52
|
import { extractTokenUsage } from "../utils/tokenUtils.js";
|
|
@@ -107,6 +108,18 @@ function isProviderErrorClassified(error) {
|
|
|
107
108
|
}
|
|
108
109
|
return error[PROVIDER_ERROR_CLASSIFIED] === true;
|
|
109
110
|
}
|
|
111
|
+
function requestCarriesImages(options) {
|
|
112
|
+
const input = options.input;
|
|
113
|
+
if (!input) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if ((input.images?.length ?? 0) > 0) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return ("content" in input &&
|
|
120
|
+
Array.isArray(input.content) &&
|
|
121
|
+
input.content.some((part) => part.type === "image"));
|
|
122
|
+
}
|
|
110
123
|
export class BaseProvider {
|
|
111
124
|
// Not `readonly` because providers that auto-discover the model from a
|
|
112
125
|
// /v1/models endpoint (lm-studio, llamacpp) need to update modelName after
|
|
@@ -523,9 +536,7 @@ export class BaseProvider {
|
|
|
523
536
|
throw error;
|
|
524
537
|
}
|
|
525
538
|
const requestedModel = provider.modelName;
|
|
526
|
-
const candidates = provider
|
|
527
|
-
.getModelFallbacks()
|
|
528
|
-
.filter((model) => model !== requestedModel);
|
|
539
|
+
const candidates = provider.modelFallbacksFor(options, requestedModel);
|
|
529
540
|
for (const candidate of candidates) {
|
|
530
541
|
logger.warn(`[${provider.providerName}] model "${requestedModel}" was rejected as invalid — retrying stream with fallback "${candidate}". This provider's catalog entry is stale; run "pnpm run check:models".`);
|
|
531
542
|
provider.refreshHandlersForModel(candidate);
|
|
@@ -671,7 +682,7 @@ export class BaseProvider {
|
|
|
671
682
|
return undefined;
|
|
672
683
|
}
|
|
673
684
|
const requestedModel = this.modelName;
|
|
674
|
-
const candidates = this.
|
|
685
|
+
const candidates = this.modelFallbacksFor(options, requestedModel);
|
|
675
686
|
for (const candidate of candidates) {
|
|
676
687
|
logger.warn(`[${this.providerName}] model "${requestedModel}" was rejected as invalid — retrying stream with fallback "${candidate}". This provider's catalog entry is stale; run "pnpm run check:models".`);
|
|
677
688
|
this.refreshHandlersForModel(candidate);
|
|
@@ -1493,7 +1504,7 @@ export class BaseProvider {
|
|
|
1493
1504
|
// or a router that retries on its own) pass the flag on both paths;
|
|
1494
1505
|
// TextGenerationOptions declares it, so a plain read is enough here.
|
|
1495
1506
|
const callerOwnsFallback = options.disableInternalFallback === true;
|
|
1496
|
-
return await this.runGenerateWithModelFallback(attempt, callerOwnsFallback);
|
|
1507
|
+
return await this.runGenerateWithModelFallback(attempt, callerOwnsFallback, options);
|
|
1497
1508
|
}
|
|
1498
1509
|
/**
|
|
1499
1510
|
* Models the catalog names are the models a vendor served when the entry was
|
|
@@ -1520,7 +1531,7 @@ export class BaseProvider {
|
|
|
1520
1531
|
* and a retired default model stops degrading to the next live one in the
|
|
1521
1532
|
* catalog — which is exactly what the provider contract gate checks.
|
|
1522
1533
|
*/
|
|
1523
|
-
async runGenerateWithModelFallback(attempt, callerOwnsFallback) {
|
|
1534
|
+
async runGenerateWithModelFallback(attempt, callerOwnsFallback, options) {
|
|
1524
1535
|
const requestedModel = this.modelName;
|
|
1525
1536
|
try {
|
|
1526
1537
|
return await attempt();
|
|
@@ -1529,7 +1540,9 @@ export class BaseProvider {
|
|
|
1529
1540
|
if (callerOwnsFallback || !isInvalidModelError(error)) {
|
|
1530
1541
|
throw error;
|
|
1531
1542
|
}
|
|
1532
|
-
const candidates =
|
|
1543
|
+
const candidates = options
|
|
1544
|
+
? this.modelFallbacksFor(options, requestedModel)
|
|
1545
|
+
: this.getModelFallbacks().filter((model) => model !== requestedModel);
|
|
1533
1546
|
if (candidates.length === 0) {
|
|
1534
1547
|
throw error;
|
|
1535
1548
|
}
|
|
@@ -1563,6 +1576,21 @@ export class BaseProvider {
|
|
|
1563
1576
|
getModelFallbacks() {
|
|
1564
1577
|
return [];
|
|
1565
1578
|
}
|
|
1579
|
+
/**
|
|
1580
|
+
* The fallbacks an invalid-model retry may try for this request. A catalog
|
|
1581
|
+
* orders `fallbacks` for text, so a request carrying an image keeps only the
|
|
1582
|
+
* vision-capable ones: a text-only candidate fails the vision guard, and
|
|
1583
|
+
* that error names the fallback instead of the retired model the caller
|
|
1584
|
+
* asked for. With no vision-capable candidate left, the caller gets the
|
|
1585
|
+
* original invalid-model error.
|
|
1586
|
+
*/
|
|
1587
|
+
modelFallbacksFor(options, requestedModel) {
|
|
1588
|
+
const candidates = this.getModelFallbacks().filter((model) => model !== requestedModel);
|
|
1589
|
+
if (!requestCarriesImages(options)) {
|
|
1590
|
+
return candidates;
|
|
1591
|
+
}
|
|
1592
|
+
return candidates.filter((model) => ProviderImageAdapter.supportsVision(this.providerName, model));
|
|
1593
|
+
}
|
|
1566
1594
|
/**
|
|
1567
1595
|
* Alias for generate method - implements AIProvider interface
|
|
1568
1596
|
*/
|
|
@@ -38,7 +38,7 @@ export const anthropicManifest = {
|
|
|
38
38
|
samplingParams: false, // matches SAMPLING_PARAM_REJECTING_FAMILIES /opus[-_.]?5(?![0-9])/i
|
|
39
39
|
},
|
|
40
40
|
"claude-sonnet-5-5": {
|
|
41
|
-
aliases: ["sonnet-5.5"],
|
|
41
|
+
aliases: ["sonnet-5.5", "claude-sonnet"],
|
|
42
42
|
displayName: "Claude Sonnet 5.5",
|
|
43
43
|
contextWindow: 1_000_000,
|
|
44
44
|
maxOutputTokens: 128_000,
|
|
@@ -66,7 +66,7 @@ export const anthropicManifest = {
|
|
|
66
66
|
samplingParams: false, // matches SAMPLING_PARAM_REJECTING_FAMILIES /fable/i
|
|
67
67
|
},
|
|
68
68
|
"claude-sonnet-5": {
|
|
69
|
-
aliases: ["sonnet-5"
|
|
69
|
+
aliases: ["sonnet-5"],
|
|
70
70
|
displayName: "Claude Sonnet 5",
|
|
71
71
|
contextWindow: 1_000_000,
|
|
72
72
|
// Was 64_000; corrected to 128_000 to match the official spec
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Part of Phase 4.1 - Models Command System
|
|
5
5
|
*/
|
|
6
6
|
import { AIProviderName } from "../constants/enums.js";
|
|
7
|
-
import type { JsonValue, ModelCapabilities, ModelInfo } from "../types/index.js";
|
|
7
|
+
import type { ClaudeDisabledThinkingReplacement, JsonValue, ModelCapabilities, ModelInfo } from "../types/index.js";
|
|
8
8
|
/**
|
|
9
9
|
* Public MODEL_REGISTRY: the manifest-derived entries merged over the
|
|
10
10
|
* hand-authored LEGACY_MODEL_REGISTRY base. Spread order matters — manifest
|
|
@@ -90,6 +90,17 @@ export declare function resolveSamplingParams(provider: string, model: string |
|
|
|
90
90
|
temperature?: number;
|
|
91
91
|
topP?: number;
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Whether a Claude model accepts a forced `tool_choice` (`any` or a named
|
|
95
|
+
* `tool`). Unknown models default to accepting it.
|
|
96
|
+
*/
|
|
97
|
+
export declare function modelSupportsForcedToolChoice(model: string | undefined): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* What to send instead of `thinking: {type: "disabled"}` to a Claude model.
|
|
100
|
+
* `between_tools` is refused at `xhigh` and `max` effort, so those fall back
|
|
101
|
+
* to adaptive thinking (`omit`).
|
|
102
|
+
*/
|
|
103
|
+
export declare function claudeDisabledThinkingReplacement(model: string | undefined, effort: string | undefined): ClaudeDisabledThinkingReplacement;
|
|
93
104
|
/**
|
|
94
105
|
* Get models by provider
|
|
95
106
|
*/
|
|
@@ -1168,14 +1168,14 @@ const LEGACY_MODEL_REGISTRY = {
|
|
|
1168
1168
|
translation: 9,
|
|
1169
1169
|
summarization: 9,
|
|
1170
1170
|
},
|
|
1171
|
-
aliases: ["sonnet-5"
|
|
1171
|
+
aliases: ["sonnet-5"],
|
|
1172
1172
|
deprecated: false,
|
|
1173
1173
|
isLocal: false,
|
|
1174
1174
|
releaseDate: "2026-06-30",
|
|
1175
1175
|
category: "general",
|
|
1176
1176
|
},
|
|
1177
|
-
// "claude-sonnet-latest"
|
|
1178
|
-
//
|
|
1177
|
+
// Owns "claude-sonnet-latest", but sits after CLAUDE_SONNET_5 so resolver
|
|
1178
|
+
// score ties still go to Sonnet 5.
|
|
1179
1179
|
[AnthropicModels.CLAUDE_SONNET_5_5]: {
|
|
1180
1180
|
id: AnthropicModels.CLAUDE_SONNET_5_5,
|
|
1181
1181
|
name: "Claude Sonnet 5.5",
|
|
@@ -1210,7 +1210,7 @@ const LEGACY_MODEL_REGISTRY = {
|
|
|
1210
1210
|
translation: 9,
|
|
1211
1211
|
summarization: 9,
|
|
1212
1212
|
},
|
|
1213
|
-
aliases: ["sonnet-5.5"],
|
|
1213
|
+
aliases: ["sonnet-5.5", "claude-sonnet-latest"],
|
|
1214
1214
|
deprecated: false,
|
|
1215
1215
|
isLocal: false,
|
|
1216
1216
|
releaseDate: "2026-09-28",
|
|
@@ -1374,7 +1374,7 @@ const LEGACY_MODEL_REGISTRY = {
|
|
|
1374
1374
|
translation: 8,
|
|
1375
1375
|
summarization: 8,
|
|
1376
1376
|
},
|
|
1377
|
-
// "claude-sonnet-latest" now belongs to
|
|
1377
|
+
// "claude-sonnet-latest" now belongs to CLAUDE_SONNET_5_5 — see the note
|
|
1378
1378
|
// on CLAUDE_OPUS_4_5's aliases.
|
|
1379
1379
|
aliases: ["claude-4.5-sonnet", "sonnet-4.5"],
|
|
1380
1380
|
deprecated: false,
|
|
@@ -2846,6 +2846,58 @@ export function resolveSamplingParams(provider, model, params, site) {
|
|
|
2846
2846
|
}
|
|
2847
2847
|
return {};
|
|
2848
2848
|
}
|
|
2849
|
+
/**
|
|
2850
|
+
* Claude models that answer a forced `tool_choice` (`any` or `tool`) with a
|
|
2851
|
+
* 400: "tool_choice: type "tool" and "any" are not supported for this model."
|
|
2852
|
+
* Sonnet 5.5 and Opus 5.5 were probed live on Vertex; the Fable 5.1 docs state
|
|
2853
|
+
* the same, and Mythos 5.1 shares Fable 5.1's capabilities. The same families
|
|
2854
|
+
* also refuse `thinking: {type: "disabled"}`.
|
|
2855
|
+
*/
|
|
2856
|
+
const FORCED_TOOL_CHOICE_REJECTING_FAMILIES = [
|
|
2857
|
+
/sonnet[-_.]?5[-_.]5(?![0-9])/i,
|
|
2858
|
+
/opus[-_.]?5[-_.]5(?![0-9])/i,
|
|
2859
|
+
/(?:fable|mythos)[-_.]?5[-_.]1(?![0-9])/i,
|
|
2860
|
+
];
|
|
2861
|
+
/**
|
|
2862
|
+
* Of those, the ones that accept `thinking: {type: "between_tools"}` as their
|
|
2863
|
+
* lowest setting. The rest think adaptively, always on, and take no
|
|
2864
|
+
* `thinking` field in its place.
|
|
2865
|
+
*/
|
|
2866
|
+
const BETWEEN_TOOLS_THINKING_FAMILIES = [
|
|
2867
|
+
/sonnet[-_.]?5[-_.]5(?![0-9])/i,
|
|
2868
|
+
];
|
|
2869
|
+
function matchesClaudeFamily(families, model) {
|
|
2870
|
+
const normalizedModel = (model ?? "").toLowerCase();
|
|
2871
|
+
if (!normalizedModel) {
|
|
2872
|
+
return false;
|
|
2873
|
+
}
|
|
2874
|
+
const modelId = MODEL_ALIASES[normalizedModel] ?? normalizedModel;
|
|
2875
|
+
return families.some((family) => family.test(modelId) || family.test(normalizedModel));
|
|
2876
|
+
}
|
|
2877
|
+
/**
|
|
2878
|
+
* Whether a Claude model accepts a forced `tool_choice` (`any` or a named
|
|
2879
|
+
* `tool`). Unknown models default to accepting it.
|
|
2880
|
+
*/
|
|
2881
|
+
export function modelSupportsForcedToolChoice(model) {
|
|
2882
|
+
return !matchesClaudeFamily(FORCED_TOOL_CHOICE_REJECTING_FAMILIES, model);
|
|
2883
|
+
}
|
|
2884
|
+
/**
|
|
2885
|
+
* What to send instead of `thinking: {type: "disabled"}` to a Claude model.
|
|
2886
|
+
* `between_tools` is refused at `xhigh` and `max` effort, so those fall back
|
|
2887
|
+
* to adaptive thinking (`omit`).
|
|
2888
|
+
*/
|
|
2889
|
+
export function claudeDisabledThinkingReplacement(model, effort) {
|
|
2890
|
+
if (!matchesClaudeFamily(FORCED_TOOL_CHOICE_REJECTING_FAMILIES, model)) {
|
|
2891
|
+
return "keep";
|
|
2892
|
+
}
|
|
2893
|
+
const level = effort?.toLowerCase();
|
|
2894
|
+
if (matchesClaudeFamily(BETWEEN_TOOLS_THINKING_FAMILIES, model) &&
|
|
2895
|
+
level !== "xhigh" &&
|
|
2896
|
+
level !== "max") {
|
|
2897
|
+
return "between_tools";
|
|
2898
|
+
}
|
|
2899
|
+
return "omit";
|
|
2900
|
+
}
|
|
2849
2901
|
/**
|
|
2850
2902
|
* Get models by provider
|
|
2851
2903
|
*/
|
|
@@ -126,7 +126,7 @@ export class AmazonSageMakerProvider extends BaseProvider {
|
|
|
126
126
|
options.disableInternalFallback === true;
|
|
127
127
|
// The native loop bypasses BaseProvider.executeGeneration, so the turn
|
|
128
128
|
// budget has to be composed here or it stops existing for this provider.
|
|
129
|
-
return this.runGenerateWithModelFallback(() => this.withTurnTimeout({ ...options, tools: mergedTools }, this.getDescriptorGenerateMs(), (timedOptions) => this.executeNativeGenerate(timedOptions)), callerOwnsFallback);
|
|
129
|
+
return this.runGenerateWithModelFallback(() => this.withTurnTimeout({ ...options, tools: mergedTools }, this.getDescriptorGenerateMs(), (timedOptions) => this.executeNativeGenerate(timedOptions)), callerOwnsFallback, options);
|
|
130
130
|
}
|
|
131
131
|
async executeNativeGenerate(options) {
|
|
132
132
|
const startTime = Date.now();
|
|
@@ -41,14 +41,14 @@ import { buildNoOutputSentinel, stampNoOutputSpan, } from "../../utils/noOutputS
|
|
|
41
41
|
import { convertZodToJsonSchema } from "../../utils/schemaConversion.js";
|
|
42
42
|
import { resolveClaudeMaxTokens } from "../../utils/tokenLimits.js";
|
|
43
43
|
import { toAnthropicImageBlock, fileToAnthropicBlock, } from "../anthropicImageBlocks.js";
|
|
44
|
-
import { resolveSamplingParams } from "../../models/modelRegistry.js";
|
|
44
|
+
import { modelSupportsForcedToolChoice, resolveSamplingParams, } from "../../models/modelRegistry.js";
|
|
45
45
|
import { createDeferredAnalytics, stringifyToolInput, } from "../openaiChatCompletionsClient.js";
|
|
46
46
|
import { createStreamChannel } from "../../core/streamChannel.js";
|
|
47
47
|
import { toNativeToolDeclarations } from "../../core/nativeToolFormat.js";
|
|
48
48
|
import { ANTHROPIC_BETA_HEADERS } from "./constants.js";
|
|
49
49
|
import { cacheControlOf, withLastToolCacheBreakpoint } from "./cacheControl.js";
|
|
50
50
|
import { createNativeGenerateGuard } from "../../context/nativeGenerateGuard.js";
|
|
51
|
-
import { appendFinalResultInstruction, appendFinalResultTool, FINAL_RESULT_TOOL_NAME, } from "./structuredOutput.js";
|
|
51
|
+
import { appendFinalResultInstruction, appendFinalResultTool, appendToolAnswerInstruction, FINAL_RESULT_TOOL_NAME, } from "./structuredOutput.js";
|
|
52
52
|
// AnthropicProviderConfig is imported from types/providers.ts
|
|
53
53
|
// Re-export for backward compatibility
|
|
54
54
|
// Configuration helpers - now using consolidated utility
|
|
@@ -424,6 +424,23 @@ const toolChoiceToAnthropic = (choice) => {
|
|
|
424
424
|
}
|
|
425
425
|
return undefined;
|
|
426
426
|
};
|
|
427
|
+
/**
|
|
428
|
+
* Claude 5.5 / 5.1 models answer a forced choice with a 400, so for them it
|
|
429
|
+
* becomes `auto`: the tool stays declared and the model still picks it.
|
|
430
|
+
*/
|
|
431
|
+
const relaxForcedToolChoice = (modelId, choice) => {
|
|
432
|
+
if ((choice?.type !== "any" && choice?.type !== "tool") ||
|
|
433
|
+
modelSupportsForcedToolChoice(modelId)) {
|
|
434
|
+
return choice;
|
|
435
|
+
}
|
|
436
|
+
logger.warn(`[anthropic] ${modelId} rejects tool_choice "${choice.type}"; sending "auto" instead`);
|
|
437
|
+
return {
|
|
438
|
+
type: "auto",
|
|
439
|
+
...(choice.disable_parallel_tool_use !== undefined
|
|
440
|
+
? { disable_parallel_tool_use: choice.disable_parallel_tool_use }
|
|
441
|
+
: {}),
|
|
442
|
+
};
|
|
443
|
+
};
|
|
427
444
|
/** Map Anthropic stop_reason onto the V3 unified finish reason. */
|
|
428
445
|
const mapAnthropicStopReason = (raw) => {
|
|
429
446
|
switch (raw) {
|
|
@@ -1107,8 +1124,15 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1107
1124
|
input_schema: schema,
|
|
1108
1125
|
},
|
|
1109
1126
|
];
|
|
1110
|
-
|
|
1127
|
+
if (modelSupportsForcedToolChoice(modelId)) {
|
|
1128
|
+
toolChoice = { type: "tool", name: jsonTool };
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
toolChoice = undefined;
|
|
1132
|
+
system = appendToolAnswerInstruction(system, jsonTool);
|
|
1133
|
+
}
|
|
1111
1134
|
}
|
|
1135
|
+
toolChoice = relaxForcedToolChoice(modelId, toolChoice);
|
|
1112
1136
|
// Additive structured output: when the caller wants a schema AND real
|
|
1113
1137
|
// tools, the forced-json path above cannot be used (it replaces the
|
|
1114
1138
|
// tools array), and structured output is excluded for this surface
|
|
@@ -1458,7 +1482,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1458
1482
|
options.disableInternalFallback === true;
|
|
1459
1483
|
// The native loop bypasses BaseProvider.executeGeneration, so the turn
|
|
1460
1484
|
// budget has to be composed here or it stops existing for this provider.
|
|
1461
|
-
return this.runGenerateWithModelFallback(() => this.withTurnTimeout({ ...options, tools: mergedTools }, this.getDescriptorGenerateMs(), (timedOptions) => this.executeNativeGenerate(timedOptions)), callerOwnsFallback);
|
|
1485
|
+
return this.runGenerateWithModelFallback(() => this.withTurnTimeout({ ...options, tools: mergedTools }, this.getDescriptorGenerateMs(), (timedOptions) => this.executeNativeGenerate(timedOptions)), callerOwnsFallback, options);
|
|
1462
1486
|
}
|
|
1463
1487
|
async executeNativeGenerate(options) {
|
|
1464
1488
|
const startTime = Date.now();
|
|
@@ -1717,7 +1741,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1717
1741
|
}
|
|
1718
1742
|
const modelId = this.modelName || getDefaultAnthropicModel();
|
|
1719
1743
|
const anthropicToolChoice = shouldUseTools && anthropicTools && anthropicTools.length > 0
|
|
1720
|
-
? toolChoiceToAnthropic(resolveToolChoice(options, toolsRecord, shouldUseTools))
|
|
1744
|
+
? relaxForcedToolChoice(modelId, toolChoiceToAnthropic(resolveToolChoice(options, toolsRecord, shouldUseTools)))
|
|
1721
1745
|
: undefined;
|
|
1722
1746
|
// Extended thinking: enabled when the caller supplies an explicit token
|
|
1723
1747
|
// budget (mirrors the previous experimental_thinking gating). Thinking
|
|
@@ -17,8 +17,10 @@ import type Anthropic from "@anthropic-ai/sdk";
|
|
|
17
17
|
*/
|
|
18
18
|
/** Internal tool name — filtered out of every returned tool call / execution. */
|
|
19
19
|
export declare const FINAL_RESULT_TOOL_NAME = "final_result";
|
|
20
|
+
/** The system-prompt instruction to answer through the named tool. */
|
|
21
|
+
export declare function toolAnswerInstruction(toolName: string): string;
|
|
20
22
|
/** Appended to the system prompt whenever the final_result tool is in play. */
|
|
21
|
-
export declare const FINAL_RESULT_INSTRUCTION
|
|
23
|
+
export declare const FINAL_RESULT_INSTRUCTION: string;
|
|
22
24
|
/**
|
|
23
25
|
* Build the `final_result` tool definition from a JSON Schema.
|
|
24
26
|
*
|
|
@@ -47,6 +49,7 @@ export declare function appendFinalResultTool(tools: Anthropic.Messages.Tool[] |
|
|
|
47
49
|
* existing one: rewriting a block that carries a `cache_control` marker would
|
|
48
50
|
* change the cached prefix and invalidate the prompt cache on every turn.
|
|
49
51
|
*/
|
|
52
|
+
export declare function appendToolAnswerInstruction(system: string | Anthropic.Messages.TextBlockParam[] | undefined, toolName: string): string | Anthropic.Messages.TextBlockParam[];
|
|
50
53
|
export declare function appendFinalResultInstruction(system: string | Anthropic.Messages.TextBlockParam[] | undefined): string | Anthropic.Messages.TextBlockParam[];
|
|
51
54
|
/**
|
|
52
55
|
* Canonical JSON text for a `final_result` payload.
|
|
@@ -19,8 +19,12 @@ import { inlineJsonSchema } from "../../utils/schemaConversion.js";
|
|
|
19
19
|
/** Internal tool name — filtered out of every returned tool call / execution. */
|
|
20
20
|
export const FINAL_RESULT_TOOL_NAME = "final_result";
|
|
21
21
|
const FINAL_RESULT_TOOL_DESCRIPTION = "Return the final structured result. You MUST call this tool when you have gathered all information and are ready to provide the final answer. The arguments should contain the structured data matching the expected schema.";
|
|
22
|
+
/** The system-prompt instruction to answer through the named tool. */
|
|
23
|
+
export function toolAnswerInstruction(toolName) {
|
|
24
|
+
return `\n\nIMPORTANT: You MUST call the '${toolName}' tool to return your response in the required structured format. Do not respond with plain text - always use the ${toolName} tool.`;
|
|
25
|
+
}
|
|
22
26
|
/** Appended to the system prompt whenever the final_result tool is in play. */
|
|
23
|
-
export const FINAL_RESULT_INSTRUCTION =
|
|
27
|
+
export const FINAL_RESULT_INSTRUCTION = toolAnswerInstruction(FINAL_RESULT_TOOL_NAME);
|
|
24
28
|
/**
|
|
25
29
|
* Build the `final_result` tool definition from a JSON Schema.
|
|
26
30
|
*
|
|
@@ -71,14 +75,18 @@ export function appendFinalResultTool(tools, jsonSchema) {
|
|
|
71
75
|
* existing one: rewriting a block that carries a `cache_control` marker would
|
|
72
76
|
* change the cached prefix and invalidate the prompt cache on every turn.
|
|
73
77
|
*/
|
|
74
|
-
export function
|
|
78
|
+
export function appendToolAnswerInstruction(system, toolName) {
|
|
79
|
+
const instruction = toolAnswerInstruction(toolName);
|
|
75
80
|
if (system === undefined) {
|
|
76
|
-
return
|
|
81
|
+
return instruction.trim();
|
|
77
82
|
}
|
|
78
83
|
if (typeof system === "string") {
|
|
79
|
-
return system +
|
|
84
|
+
return system + instruction;
|
|
80
85
|
}
|
|
81
|
-
return [...system, { type: "text", text:
|
|
86
|
+
return [...system, { type: "text", text: instruction.trim() }];
|
|
87
|
+
}
|
|
88
|
+
export function appendFinalResultInstruction(system) {
|
|
89
|
+
return appendToolAnswerInstruction(system, FINAL_RESULT_TOOL_NAME);
|
|
82
90
|
}
|
|
83
91
|
/**
|
|
84
92
|
* Canonical JSON text for a `final_result` payload.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"models": {
|
|
11
11
|
"default": "gpt-oss-120b",
|
|
12
|
-
"fallbacks": ["gpt-oss-120b"
|
|
12
|
+
"fallbacks": ["gpt-oss-120b"],
|
|
13
13
|
"defaultContextWindow": 65536,
|
|
14
14
|
"defaultMaxOutputTokens": 8192,
|
|
15
15
|
"catalog": {
|
|
@@ -29,13 +29,12 @@
|
|
|
29
29
|
"input": 0.99,
|
|
30
30
|
"output": 1.49
|
|
31
31
|
},
|
|
32
|
-
"vision":
|
|
33
|
-
"status": "
|
|
34
|
-
"description": "
|
|
32
|
+
"vision": false,
|
|
33
|
+
"status": "retired",
|
|
34
|
+
"description": "Retired 2026-09 — gone from the authenticated /v1/models roster; the nightly vision test has failed on it since 2026-09-10"
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
"
|
|
38
|
-
"topModels": ["gpt-oss-120b", "gemma-4-31b"]
|
|
37
|
+
"topModels": ["gpt-oss-120b"]
|
|
39
38
|
},
|
|
40
39
|
"capabilities": {
|
|
41
40
|
"text": true,
|
|
@@ -68,8 +67,8 @@
|
|
|
68
67
|
},
|
|
69
68
|
"evidence": {
|
|
70
69
|
"rosterVerified": {
|
|
71
|
-
"date": "2026-
|
|
72
|
-
"method": "authenticated GET /v1/models"
|
|
70
|
+
"date": "2026-09-28",
|
|
71
|
+
"method": "authenticated GET /v1/models — roster is now gpt-oss-120b and qwen-3.8-27b; gemma-4-31b is gone, so it is retired and Cerebras has no verified vision model. qwen-3.8-27b is not added: the account answers 402 payment_required, so no chat call could verify it"
|
|
73
72
|
},
|
|
74
73
|
"authProbe": {
|
|
75
74
|
"date": "2026-08-26",
|
|
@@ -8,12 +8,11 @@
|
|
|
8
8
|
"baseURL": "https://api.fireworks.ai/inference/v1"
|
|
9
9
|
},
|
|
10
10
|
"models": {
|
|
11
|
-
"default": "accounts/fireworks/models/kimi-
|
|
11
|
+
"default": "accounts/fireworks/models/kimi-k3",
|
|
12
12
|
"fallbackModelName": "accounts/fireworks/models/gpt-oss-120b",
|
|
13
13
|
"fallbacks": [
|
|
14
|
-
"accounts/fireworks/models/kimi-k2p6",
|
|
15
|
-
"accounts/fireworks/models/gpt-oss-120b",
|
|
16
14
|
"accounts/fireworks/models/kimi-k3",
|
|
15
|
+
"accounts/fireworks/models/gpt-oss-120b",
|
|
17
16
|
"accounts/fireworks/models/qwen3p8-max",
|
|
18
17
|
"accounts/fireworks/models/glm-5p3"
|
|
19
18
|
],
|
|
@@ -22,9 +21,9 @@
|
|
|
22
21
|
"catalog": {
|
|
23
22
|
"accounts/fireworks/models/kimi-k2p6": {
|
|
24
23
|
"enumMember": "KIMI_K2P6",
|
|
25
|
-
"vision":
|
|
26
|
-
"status": "
|
|
27
|
-
"description": "
|
|
24
|
+
"vision": false,
|
|
25
|
+
"status": "retired",
|
|
26
|
+
"description": "Retired 2026-09 — still on the Fireworks roster, but a real chat call returns 404 'not deployed' (since the 2026-09-26 nightly)"
|
|
28
27
|
},
|
|
29
28
|
"accounts/fireworks/models/gpt-oss-120b": {
|
|
30
29
|
"enumMember": "GPT_OSS_120B",
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
"enumMember": "KIMI_K3",
|
|
37
36
|
"vision": true,
|
|
38
37
|
"status": "production",
|
|
39
|
-
"description": "Kimi K3
|
|
38
|
+
"description": "Recommended - Kimi K3, newest Moonshot generation; vision-capable"
|
|
40
39
|
},
|
|
41
40
|
"accounts/fireworks/models/qwen3p8-max": {
|
|
42
41
|
"enumMember": "QWEN3P8_MAX",
|
|
@@ -106,9 +105,8 @@
|
|
|
106
105
|
}
|
|
107
106
|
},
|
|
108
107
|
"topModels": [
|
|
109
|
-
"accounts/fireworks/models/kimi-k2p6",
|
|
110
|
-
"accounts/fireworks/models/gpt-oss-120b",
|
|
111
108
|
"accounts/fireworks/models/kimi-k3",
|
|
109
|
+
"accounts/fireworks/models/gpt-oss-120b",
|
|
112
110
|
"accounts/fireworks/models/qwen3p8-max"
|
|
113
111
|
]
|
|
114
112
|
},
|
|
@@ -143,12 +141,12 @@
|
|
|
143
141
|
},
|
|
144
142
|
"evidence": {
|
|
145
143
|
"rosterVerified": {
|
|
146
|
-
"date": "2026-
|
|
144
|
+
"date": "2026-09-28",
|
|
147
145
|
"method": "GET /models against the live account, cross-checked with a real chat call per id"
|
|
148
146
|
},
|
|
149
147
|
"liveMatrix": {
|
|
150
|
-
"date": "2026-
|
|
151
|
-
"result": "
|
|
148
|
+
"date": "2026-09-28",
|
|
149
|
+
"result": "kimi-k2p6 is still listed on /models but a real chat call now returns 404 'not deployed', which failed the nightly model-liveness gate from 2026-09-26 and sent the matrix vision test down the text-only fallback. kimi-k3 answers a text call and a call carrying a PNG with 200, so it becomes the default; gpt-oss-120b, qwen3p8-max and glm-5p3 also answer text calls. Earlier (2026-08-30): deepseek-v4-pro was listed but hung past 120s, six ids returned 404 'not deployed', and glm-5p3 answers 'does not support image inputs'."
|
|
152
150
|
},
|
|
153
151
|
"addedInPR": "https://github.com/juspay/neurolink/pull/1587"
|
|
154
152
|
}
|
|
@@ -222,9 +222,9 @@ export declare class GoogleVertexProvider extends BaseProvider {
|
|
|
222
222
|
* clamped below the ceiling rather than passed through — an over-large
|
|
223
223
|
* budget is a 400 from the API.
|
|
224
224
|
*
|
|
225
|
-
* `
|
|
226
|
-
* a forced `tool_choice:{type:"any"}` in the request built right
|
|
227
|
-
* call — Anthropic hard-rejects `thinking` combined with a `tool_choice`
|
|
225
|
+
* `forcesToolChoice` (schema mode on a model that accepts a forced choice)
|
|
226
|
+
* pairs with a forced `tool_choice:{type:"any"}` in the request built right
|
|
227
|
+
* after this call — Anthropic hard-rejects `thinking` combined with a `tool_choice`
|
|
228
228
|
* that forces tool use: a live 400, "Thinking may not be enabled when
|
|
229
229
|
* tool_choice forces tool use." So `thinking` is omitted entirely in that
|
|
230
230
|
* mode, preserving the prior (pre-thinking-fix) behaviour of silently
|