@juspay/neurolink 12.11.3 → 12.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +3 -4
  2. package/dist/browser/neurolink.min.js +533 -581
  3. package/dist/constants/enums.d.ts +13 -0
  4. package/dist/constants/enums.js +14 -0
  5. package/dist/core/baseProvider.d.ts +71 -3
  6. package/dist/core/baseProvider.js +152 -44
  7. package/dist/core/modules/GenerationHandler.d.ts +22 -24
  8. package/dist/core/modules/GenerationHandler.js +28 -463
  9. package/dist/core/nativeGenerateLoop.d.ts +35 -0
  10. package/dist/core/nativeGenerateLoop.js +261 -0
  11. package/dist/files/fileTools.d.ts +5 -5
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +4 -0
  14. package/dist/middleware/builtin/guardrails.d.ts +0 -5
  15. package/dist/middleware/builtin/guardrails.js +33 -5
  16. package/dist/middleware/factory.js +1 -1
  17. package/dist/middleware/wrapLanguageModel.d.ts +18 -0
  18. package/dist/middleware/wrapLanguageModel.js +53 -0
  19. package/dist/processors/media/AudioProcessor.js +46 -11
  20. package/dist/providers/amazonSagemaker.d.ts +17 -1
  21. package/dist/providers/amazonSagemaker.js +110 -0
  22. package/dist/providers/anthropic/client.d.ts +11 -0
  23. package/dist/providers/anthropic/client.js +148 -1
  24. package/dist/providers/catalog/index.generated.d.ts +1 -1
  25. package/dist/providers/catalog/index.generated.js +3 -0
  26. package/dist/providers/catalog/loader.js +1 -0
  27. package/dist/providers/catalog/mancer.json +192 -0
  28. package/dist/providers/configuredOpenAICompat.d.ts +11 -0
  29. package/dist/providers/configuredOpenAICompat.js +16 -0
  30. package/dist/providers/googleVertex/client.d.ts +0 -9
  31. package/dist/providers/googleVertex/client.js +0 -33
  32. package/dist/providers/openaiChatCompletionsBase.d.ts +21 -1
  33. package/dist/providers/openaiChatCompletionsBase.js +178 -0
  34. package/dist/providers/providerTypeUtils.d.ts +1 -2
  35. package/dist/providers/providerTypeUtils.js +5 -1
  36. package/dist/types/aiCompat.d.ts +485 -0
  37. package/dist/types/aiCompat.js +17 -0
  38. package/dist/types/conversation.d.ts +1 -1
  39. package/dist/types/generate.d.ts +52 -0
  40. package/dist/types/middleware.d.ts +3 -6
  41. package/dist/types/providerCatalog.generated.d.ts +2 -2
  42. package/dist/types/providers.d.ts +14 -1
  43. package/dist/types/tools.d.ts +2 -2
  44. package/dist/utils/generationErrors.d.ts +78 -6
  45. package/dist/utils/generationErrors.js +114 -6
  46. package/dist/utils/nativeSingleShot.d.ts +3 -0
  47. package/dist/utils/nativeSingleShot.js +83 -0
  48. package/dist/utils/tool.d.ts +30 -5
  49. package/dist/utils/tool.js +43 -5
  50. package/package.json +3 -6
  51. package/dist/utils/generation.d.ts +0 -8
  52. package/dist/utils/generation.js +0 -8
@@ -1,6 +1,13 @@
1
1
  import { BaseProvider } from "../core/baseProvider.js";
2
2
  import { createStreamChannel } from "../core/streamChannel.js";
3
3
  import { logger } from "../utils/logger.js";
4
+ import { resolveRequestKind } from "../core/resolveRequestKind.js";
5
+ import { resolveToolExecutionRecords } from "../core/toolExecutionRecorder.js";
6
+ import { transformToolExecutions } from "../utils/transformationUtils.js";
7
+ import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
8
+ import { withProviderRetry } from "../utils/providerRetry.js";
9
+ import { DEFAULT_MAX_STEPS } from "../core/constants.js";
10
+ import { hasNativeDoGenerate, runNativeGenerateLoop, } from "../core/nativeGenerateLoop.js";
4
11
  import { withSpan } from "../telemetry/withSpan.js";
5
12
  import { tracers } from "../telemetry/tracers.js";
6
13
  // SageMaker-specific imports
@@ -93,6 +100,109 @@ export class AmazonSageMakerProvider extends BaseProvider {
93
100
  const smModel = this.sagemakerModel;
94
101
  return smModel;
95
102
  }
103
+ /**
104
+ * Native non-streaming generate.
105
+ *
106
+ * SageMaker's doGenerate makes one invokeEndpoint call and already returns
107
+ * toolCalls; no streaming is involved, so the wire hazard that reverted the
108
+ * first migration does not apply here. This supplies only the multi-step
109
+ * iteration the ai package used to.
110
+ *
111
+ * NOT EXERCISED LIVE. This machine has no SageMaker endpoint or credentials.
112
+ * The single-step shape is identical by construction — with no tool calls the
113
+ * loop breaks after exactly one doGenerate carrying the same options the ai
114
+ * loop passed. The multi-step branch is the new code and wants a real
115
+ * endpoint before it is trusted.
116
+ */
117
+ async generate(optionsOrPrompt, analysisSchema) {
118
+ await this.ensureModelLimits();
119
+ const options = this.normalizeTextOptions(optionsOrPrompt);
120
+ if (resolveRequestKind(options, this.modelName) !== "text") {
121
+ return super.generate(options, analysisSchema);
122
+ }
123
+ this.validateOptions(options);
124
+ const mergedTools = await this.getToolsForStream(options);
125
+ const callerOwnsFallback = "disableInternalFallback" in options &&
126
+ options.disableInternalFallback === true;
127
+ // The native loop bypasses BaseProvider.executeGeneration, so the turn
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);
130
+ }
131
+ async executeNativeGenerate(options) {
132
+ const startTime = Date.now();
133
+ // Middleware must wrap the model here. The native loop bypasses
134
+ // BaseProvider.executeGeneration, and with it the only place middleware was
135
+ // ever applied — a probe showed a caller's wrapGenerate running zero times
136
+ // on every native provider while their onFinish still fired, because
137
+ // onFinish had been special-cased and nothing else had.
138
+ const model = await this.getAISDKModelWithMiddleware(options);
139
+ if (!hasNativeDoGenerate(model)) {
140
+ throw this.handleProviderError(new Error("sagemaker: model handle exposes no doGenerate()"));
141
+ }
142
+ const doGenerate = model.doGenerate.bind(model);
143
+ const shouldUseTools = !options.disableTools && this.supportsTools();
144
+ const toolsRecord = shouldUseTools
145
+ ? options.tools || {}
146
+ : {};
147
+ const v3Tools = Object.entries(toolsRecord).map(([name, t]) => {
148
+ const tool = t;
149
+ return {
150
+ type: "function",
151
+ name,
152
+ description: tool.description ?? "",
153
+ inputSchema: (tool.inputSchema
154
+ ? convertZodToJsonSchema(tool.inputSchema)
155
+ : { type: "object", properties: {} }),
156
+ };
157
+ });
158
+ // Structured output was dropped entirely on this path: the schema never
159
+ // reached the request, and nothing downstream re-imposed it, so a caller
160
+ // asking for an object got whatever JSON coerceJsonToSchema could scrape
161
+ // out of prose.
162
+ const responseFormat = options.schema
163
+ ? {
164
+ type: "json",
165
+ schema: convertZodToJsonSchema(options.schema),
166
+ }
167
+ : undefined;
168
+ const conversation = (await this.buildMessagesForStream(options));
169
+ const toolExecutionSummaries = [];
170
+ const loop = await runNativeGenerateLoop({
171
+ doGenerate,
172
+ conversation,
173
+ ...(responseFormat ? { responseFormat } : {}),
174
+ ...(v3Tools.length > 0 ? { tools: v3Tools } : {}),
175
+ toolsRecord,
176
+ maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
177
+ ...(options.maxTokens ? { maxOutputTokens: options.maxTokens } : {}),
178
+ ...(options.temperature !== undefined
179
+ ? { temperature: options.temperature }
180
+ : {}),
181
+ ...(options.abortSignal ? { abortSignal: options.abortSignal } : {}),
182
+ ...(options.toolTimeoutMs !== undefined
183
+ ? { toolTimeoutMs: options.toolTimeoutMs }
184
+ : {}),
185
+ runStep: (call) => withProviderRetry(call, undefined, "sagemaker generate").catch((err) => {
186
+ throw this.handleProviderError(err);
187
+ }),
188
+ }, toolExecutionSummaries);
189
+ const enhanced = {
190
+ content: loop.text,
191
+ provider: this.providerName,
192
+ model: this.modelName,
193
+ finishReason: loop.finishReason,
194
+ usage: {
195
+ input: loop.inputTokens,
196
+ output: loop.outputTokens,
197
+ total: loop.inputTokens + loop.outputTokens,
198
+ },
199
+ responseTime: Date.now() - startTime,
200
+ toolsUsed: loop.toolsUsed,
201
+ toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
202
+ enhancedWithTools: loop.toolsUsed.length > 0,
203
+ };
204
+ return this.finalizeNativeGenerate(enhanced, options, startTime);
205
+ }
96
206
  /**
97
207
  * Streaming was previously an `executeStream` override that unconditionally
98
208
  * threw "not yet fully implemented" — while `SageMakerLanguageModel.doStream`
@@ -133,6 +133,17 @@ export declare class AnthropicProvider extends BaseProvider {
133
133
  * BaseProvider so that expired tokens are renewed automatically.
134
134
  */
135
135
  generate(optionsOrPrompt: TextGenerationOptions | string, analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
136
+ /**
137
+ * Text turns run natively; every other request kind still goes to
138
+ * BaseProvider.generate().
139
+ *
140
+ * The loop runs over this provider's own delegating-model `doGenerate`,
141
+ * which issues a NON-streaming `messages.create`. That matters: the streaming
142
+ * loop adapter hardcodes `stream: true`, and an earlier attempt that routed
143
+ * generate through it silently changed the wire.
144
+ */
145
+ private dispatchGenerate;
146
+ private executeNativeGenerate;
136
147
  /**
137
148
  * Fold a captured snapshot into the provider's usage bookkeeping and log it.
138
149
  *
@@ -25,6 +25,11 @@ import { calculateCost } from "../../utils/pricing.js";
25
25
  import { stringifyAnthropicToolOutput } from "./toolOutput.js";
26
26
  import { createAnthropicLoopAdapter } from "./loopAdapter.js";
27
27
  import { runAgenticLoop } from "../../core/loopEngine.js";
28
+ import { hasNativeDoGenerate, runNativeGenerateLoop, } from "../../core/nativeGenerateLoop.js";
29
+ import { withProviderRetry } from "../../utils/providerRetry.js";
30
+ import { resolveRequestKind } from "../../core/resolveRequestKind.js";
31
+ import { resolveToolExecutionRecords } from "../../core/toolExecutionRecorder.js";
32
+ import { transformToolExecutions } from "../../utils/transformationUtils.js";
28
33
  import { createAnthropicConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
29
34
  import { composeAbortSignals, createTimeoutController, mergeAbortSignals, TimeoutError, } from "../../utils/timeout.js";
30
35
  import { resolveToolChoice } from "../../utils/toolChoice.js";
@@ -1327,7 +1332,7 @@ export class AnthropicProvider extends BaseProvider {
1327
1332
  // rather than on the instance is what makes it concurrency-safe: several
1328
1333
  // generate() calls can be in flight on one provider instance, and an
1329
1334
  // instance field would attribute one call's limits to another.
1330
- const { result, snapshot } = await withLimitCapture(() => super.generate(optionsOrPrompt, analysisSchema));
1335
+ const { result, snapshot } = await withLimitCapture(() => this.dispatchGenerate(optionsOrPrompt, analysisSchema));
1331
1336
  if (result && snapshot) {
1332
1337
  this.recordLimitSnapshot(snapshot);
1333
1338
  result.limits = snapshot;
@@ -1337,6 +1342,148 @@ export class AnthropicProvider extends BaseProvider {
1337
1342
  }
1338
1343
  return result;
1339
1344
  }
1345
+ /**
1346
+ * Text turns run natively; every other request kind still goes to
1347
+ * BaseProvider.generate().
1348
+ *
1349
+ * The loop runs over this provider's own delegating-model `doGenerate`,
1350
+ * which issues a NON-streaming `messages.create`. That matters: the streaming
1351
+ * loop adapter hardcodes `stream: true`, and an earlier attempt that routed
1352
+ * generate through it silently changed the wire.
1353
+ */
1354
+ async dispatchGenerate(optionsOrPrompt, analysisSchema) {
1355
+ await this.ensureModelLimits();
1356
+ const options = this.normalizeTextOptions(optionsOrPrompt);
1357
+ if (resolveRequestKind(options, this.modelName) !== "text") {
1358
+ return super.generate(options, analysisSchema);
1359
+ }
1360
+ this.validateOptions(options);
1361
+ const mergedTools = await this.getToolsForStream(options);
1362
+ const callerOwnsFallback = "disableInternalFallback" in options &&
1363
+ options.disableInternalFallback === true;
1364
+ // The native loop bypasses BaseProvider.executeGeneration, so the turn
1365
+ // budget has to be composed here or it stops existing for this provider.
1366
+ return this.runGenerateWithModelFallback(() => this.withTurnTimeout({ ...options, tools: mergedTools }, this.getDescriptorGenerateMs(), (timedOptions) => this.executeNativeGenerate(timedOptions)), callerOwnsFallback);
1367
+ }
1368
+ async executeNativeGenerate(options) {
1369
+ const startTime = Date.now();
1370
+ const modelId = this.modelName || getDefaultAnthropicModel();
1371
+ // Middleware must wrap the model here. The native loop bypasses
1372
+ // BaseProvider.executeGeneration, and with it the only place middleware was
1373
+ // ever applied — a probe showed a caller's wrapGenerate running zero times
1374
+ // on every native provider while their onFinish still fired, because
1375
+ // onFinish had been special-cased and nothing else had.
1376
+ const model = await this.getAISDKModelWithMiddleware(options);
1377
+ if (!hasNativeDoGenerate(model)) {
1378
+ throw this.handleProviderError(new Error("anthropic: model handle exposes no doGenerate()"));
1379
+ }
1380
+ const doGenerate = model.doGenerate.bind(model);
1381
+ const shouldUseTools = !options.disableTools && this.supportsTools();
1382
+ const toolsRecord = shouldUseTools
1383
+ ? options.tools || {}
1384
+ : {};
1385
+ const v3Tools = Object.entries(toolsRecord).map(([name, t]) => {
1386
+ const tool = t;
1387
+ return {
1388
+ type: "function",
1389
+ name,
1390
+ description: tool.description ?? "",
1391
+ inputSchema: (tool.inputSchema
1392
+ ? convertZodToJsonSchema(tool.inputSchema)
1393
+ : { type: "object", properties: {} }),
1394
+ };
1395
+ });
1396
+ const hasTools = v3Tools.length > 0;
1397
+ // Two structured-output routes, and doGenerate implements both. With no
1398
+ // tools it replaces the tool list with one forced json tool; with tools it
1399
+ // APPENDS final_result so the real tools stay callable. Picking the wrong
1400
+ // one is what dropped structuredData to null on the first attempt.
1401
+ const schemaJson = options.schema
1402
+ ? convertZodToJsonSchema(options.schema)
1403
+ : undefined;
1404
+ const responseFormat = schemaJson && !hasTools
1405
+ ? { type: "json", schema: schemaJson }
1406
+ : undefined;
1407
+ const anthropicNamespace = {};
1408
+ if (schemaJson && hasTools) {
1409
+ anthropicNamespace.finalResultSchema = schemaJson;
1410
+ }
1411
+ if (options.thinkingConfig?.enabled &&
1412
+ options.thinkingConfig.budgetTokens) {
1413
+ anthropicNamespace.thinking = {
1414
+ type: "enabled",
1415
+ budget_tokens: options.thinkingConfig.budgetTokens,
1416
+ };
1417
+ }
1418
+ // The per-call `timeout` keeps its per-MODEL-CALL meaning once
1419
+ // `turnTimeoutMs` owns the whole-turn deadline, and it reaches the model
1420
+ // layer only through providerOptions.neurolink. Without it each step fell
1421
+ // back to the provider default.
1422
+ const mergedProviderOptions = {};
1423
+ if (Object.keys(anthropicNamespace).length > 0) {
1424
+ mergedProviderOptions.anthropic = anthropicNamespace;
1425
+ }
1426
+ if (typeof options.timeout === "number") {
1427
+ mergedProviderOptions.neurolink = { timeoutMs: options.timeout };
1428
+ }
1429
+ const providerOptions = Object.keys(mergedProviderOptions).length > 0
1430
+ ? mergedProviderOptions
1431
+ : undefined;
1432
+ const conversation = (await this.buildMessagesForStream(options));
1433
+ const toolExecutionSummaries = [];
1434
+ const loop = await runNativeGenerateLoop({
1435
+ doGenerate,
1436
+ conversation,
1437
+ ...(hasTools ? { tools: v3Tools } : {}),
1438
+ toolsRecord,
1439
+ // A caller's toolChoice was dropped here while the streaming path and
1440
+ // the OpenAI-compatible native path both forwarded it, so
1441
+ // `toolChoice: "required"` and named-tool choices silently degraded to
1442
+ // Anthropic's default `auto` on generate().
1443
+ ...(hasTools && options.toolChoice
1444
+ ? { toolChoice: resolveToolChoice(options, toolsRecord, true) }
1445
+ : {}),
1446
+ ...(responseFormat ? { responseFormat } : {}),
1447
+ ...(providerOptions ? { providerOptions } : {}),
1448
+ maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
1449
+ ...(options.maxTokens ? { maxOutputTokens: options.maxTokens } : {}),
1450
+ ...(options.temperature !== undefined
1451
+ ? { temperature: options.temperature }
1452
+ : {}),
1453
+ ...(options.abortSignal ? { abortSignal: options.abortSignal } : {}),
1454
+ ...(options.toolTimeoutMs !== undefined
1455
+ ? { toolTimeoutMs: options.toolTimeoutMs }
1456
+ : {}),
1457
+ runStep: (call) => withProviderRetry(call, trace.getActiveSpan() ?? undefined, "anthropic generate").catch((err) => {
1458
+ throw this.handleProviderError(err);
1459
+ }),
1460
+ }, toolExecutionSummaries);
1461
+ const enhanced = {
1462
+ content: loop.text,
1463
+ provider: this.providerName,
1464
+ model: modelId,
1465
+ finishReason: loop.finishReason,
1466
+ ...(loop.rawFinishReason
1467
+ ? { rawFinishReason: loop.rawFinishReason }
1468
+ : {}),
1469
+ usage: {
1470
+ input: loop.inputTokens,
1471
+ output: loop.outputTokens,
1472
+ total: loop.inputTokens + loop.outputTokens,
1473
+ ...(loop.cacheReadTokens
1474
+ ? { cacheReadTokens: loop.cacheReadTokens }
1475
+ : {}),
1476
+ ...(loop.cacheWriteTokens
1477
+ ? { cacheCreationTokens: loop.cacheWriteTokens }
1478
+ : {}),
1479
+ },
1480
+ responseTime: Date.now() - startTime,
1481
+ toolsUsed: loop.toolsUsed,
1482
+ toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
1483
+ enhancedWithTools: loop.toolsUsed.length > 0,
1484
+ };
1485
+ return this.finalizeNativeGenerate(enhanced, options, startTime);
1486
+ }
1340
1487
  /**
1341
1488
  * Fold a captured snapshot into the provider's usage bookkeeping and log it.
1342
1489
  *
@@ -1,3 +1,3 @@
1
1
  import type { ProviderCatalogJson } from "../../types/index.js";
2
2
  export declare const CATALOG_JSON_ENTRIES: ProviderCatalogJson[];
3
- export declare const CATALOG_PROVIDER_IDS: readonly ["baseten", "cerebras", "cloudflare", "fireworks", "gmicloud", "groq", "inception-labs", "io-intelligence", "mistral", "perplexity", "sambanova", "together-ai", "upstage", "xai"];
3
+ export declare const CATALOG_PROVIDER_IDS: readonly ["baseten", "cerebras", "cloudflare", "fireworks", "gmicloud", "groq", "inception-labs", "io-intelligence", "mancer", "mistral", "perplexity", "sambanova", "together-ai", "upstage", "xai"];
@@ -8,6 +8,7 @@ import gmicloudJson from "./gmicloud.json" with { type: "json" };
8
8
  import groqJson from "./groq.json" with { type: "json" };
9
9
  import inceptionLabsJson from "./inception-labs.json" with { type: "json" };
10
10
  import ioIntelligenceJson from "./io-intelligence.json" with { type: "json" };
11
+ import mancerJson from "./mancer.json" with { type: "json" };
11
12
  import mistralJson from "./mistral.json" with { type: "json" };
12
13
  import perplexityJson from "./perplexity.json" with { type: "json" };
13
14
  import sambanovaJson from "./sambanova.json" with { type: "json" };
@@ -23,6 +24,7 @@ export const CATALOG_JSON_ENTRIES = [
23
24
  groqJson,
24
25
  inceptionLabsJson,
25
26
  ioIntelligenceJson,
27
+ mancerJson,
26
28
  mistralJson,
27
29
  perplexityJson,
28
30
  sambanovaJson,
@@ -39,6 +41,7 @@ export const CATALOG_PROVIDER_IDS = [
39
41
  "groq",
40
42
  "inception-labs",
41
43
  "io-intelligence",
44
+ "mancer",
42
45
  "mistral",
43
46
  "perplexity",
44
47
  "sambanova",
@@ -89,6 +89,7 @@ export function buildCatalogEntries() {
89
89
  entry.models.fallbacks[0],
90
90
  fallbackModels: [...entry.models.fallbacks],
91
91
  errorRules: buildErrorRules(entry),
92
+ supportsTools: entry.capabilities.tools,
92
93
  };
93
94
  const { baseURLTemplate } = entry.wire;
94
95
  if (baseURLTemplate) {
@@ -0,0 +1,192 @@
1
+ {
2
+ "$schema": "./provider-catalog.schema.json",
3
+ "id": "mancer",
4
+ "displayName": "Mancer",
5
+ "aliases": ["mancer-tech"],
6
+ "tier": 2,
7
+ "wire": {
8
+ "baseURL": "https://neuro.mancer.tech/oai/v1"
9
+ },
10
+ "models": {
11
+ "default": "deepseek-v4-flash",
12
+ "fallbacks": ["deepseek-v4-flash", "gpt-oss-120b"],
13
+ "defaultContextWindow": 1048576,
14
+ "defaultMaxOutputTokens": 1048576,
15
+ "catalog": {
16
+ "mythomax": {
17
+ "contextWindow": 8192,
18
+ "maxOutputTokens": 8192,
19
+ "pricingPerMTok": {
20
+ "input": 0.14,
21
+ "output": 0.24
22
+ },
23
+ "vision": false,
24
+ "status": "production",
25
+ "description": "MythoMax (LLaMA 2, Simplified Alpaca format) — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
26
+ },
27
+ "deepseek-v4-flash": {
28
+ "contextWindow": 1048576,
29
+ "maxOutputTokens": 1048576,
30
+ "pricingPerMTok": {
31
+ "input": 0.07,
32
+ "output": 0.2
33
+ },
34
+ "vision": false,
35
+ "status": "production",
36
+ "description": "DeepSeek V4 Flash — Mancer's flagship general model; paid credits required — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
37
+ },
38
+ "deepseek-v4-flash-0731": {
39
+ "contextWindow": 1048576,
40
+ "maxOutputTokens": 1048576,
41
+ "pricingPerMTok": {
42
+ "input": 0.07,
43
+ "output": 0.2
44
+ },
45
+ "vision": false,
46
+ "status": "production",
47
+ "description": "DeepSeek V4 Flash, 2026-07-31 snapshot; paid credits required — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
48
+ },
49
+ "mytholite": {
50
+ "contextWindow": 2560,
51
+ "maxOutputTokens": 150,
52
+ "pricingPerMTok": {
53
+ "input": 0,
54
+ "output": 0
55
+ },
56
+ "vision": false,
57
+ "status": "production",
58
+ "description": "MythoLite — Mancer's free demo model (2,560-token context, 150-token completions, Simplified Alpaca format); the only model usable with a zero balance — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
59
+ },
60
+ "remm-slerp": {
61
+ "contextWindow": 6144,
62
+ "maxOutputTokens": 6144,
63
+ "pricingPerMTok": {
64
+ "input": 0.14,
65
+ "output": 0.26
66
+ },
67
+ "vision": false,
68
+ "status": "production",
69
+ "description": "ReMM-SLERP (Simplified Alpaca format) — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
70
+ },
71
+ "magnum-72b-v4": {
72
+ "contextWindow": 32768,
73
+ "maxOutputTokens": 4096,
74
+ "pricingPerMTok": {
75
+ "input": 1,
76
+ "output": 2
77
+ },
78
+ "vision": false,
79
+ "status": "production",
80
+ "description": "Magnum 72B v4 (ChatML format) — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
81
+ },
82
+ "glm-4.7": {
83
+ "contextWindow": 131072,
84
+ "maxOutputTokens": 131072,
85
+ "pricingPerMTok": {
86
+ "input": 0.28,
87
+ "output": 1
88
+ },
89
+ "vision": false,
90
+ "status": "production",
91
+ "description": "GLM-4.7 — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
92
+ },
93
+ "gpt-oss-120b": {
94
+ "contextWindow": 131072,
95
+ "maxOutputTokens": 131072,
96
+ "pricingPerMTok": {
97
+ "input": 0.022,
98
+ "output": 0.2
99
+ },
100
+ "vision": false,
101
+ "status": "production",
102
+ "description": "GPT-OSS 120B — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
103
+ },
104
+ "weaver-alpha": {
105
+ "contextWindow": 8000,
106
+ "maxOutputTokens": 6000,
107
+ "pricingPerMTok": {
108
+ "input": 0.16,
109
+ "output": 0.3
110
+ },
111
+ "vision": false,
112
+ "status": "production",
113
+ "description": "Weaver Alpha (Simplified Alpaca format) — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
114
+ },
115
+ "dans-pe-1.3-24b": {
116
+ "contextWindow": 32768,
117
+ "maxOutputTokens": 8192,
118
+ "pricingPerMTok": {
119
+ "input": 0.2,
120
+ "output": 0.8
121
+ },
122
+ "vision": false,
123
+ "status": "production",
124
+ "description": "Dan's PersonalityEngine 1.3 24B — pricing and limits from the authenticated /oai/v1/models roster, 2026-09-03"
125
+ }
126
+ },
127
+ "topModels": ["deepseek-v4-flash", "gpt-oss-120b", "glm-4.7", "mytholite"],
128
+ "testModel": "mytholite"
129
+ },
130
+ "capabilities": {
131
+ "text": true,
132
+ "streaming": true,
133
+ "tools": false,
134
+ "toolsWithStreaming": false,
135
+ "structuredOutput": true,
136
+ "structuredOutputWithTools": false,
137
+ "embeddings": false,
138
+ "thinking": false
139
+ },
140
+ "errorRules": [
141
+ {
142
+ "status": 401,
143
+ "pattern": "Bad API key",
144
+ "class": "authentication",
145
+ "message": "Invalid Mancer API key. Check {apiKeyEnvVar}. Get one at https://mancer.tech/dashboard"
146
+ },
147
+ {
148
+ "pattern": "Unknown Model",
149
+ "class": "invalid-model",
150
+ "message": "Mancer model '{model}' is unknown. Pick a current model from the /oai/v1/models roster or https://mancer.tech/pricing."
151
+ },
152
+ {
153
+ "status": 402,
154
+ "pattern": "requires paid credits",
155
+ "class": "provider",
156
+ "message": "Mancer model '{model}' requires paid credits; with a zero balance only the free model 'mytholite' works. Add credits at https://mancer.tech/dashboard or see https://mancer.tech/pricing."
157
+ }
158
+ ],
159
+ "setup": {
160
+ "url": "https://mancer.tech/dashboard",
161
+ "apiKeyFormat": "^mcr_[A-Za-z0-9]+$",
162
+ "billingPolicy": "free-tier",
163
+ "instructions": [
164
+ "1. Visit: https://mancer.tech/dashboard and sign in",
165
+ "2. Create an API key (prefix mcr_)",
166
+ "3. Without credits only the free model 'mytholite' answers; every other model returns 402 until you add credits at https://mancer.tech/pricing",
167
+ "4. Set {apiKeyEnvVar} in your .env file"
168
+ ],
169
+ "description": "OpenAI-compatible endpoint at https://neuro.mancer.tech/oai/v1. One free demo model; paid credits unlock the rest of the roster."
170
+ },
171
+ "evidence": {
172
+ "rosterVerified": {
173
+ "date": "2026-09-03",
174
+ "method": "authenticated GET /oai/v1/models; full response retained as evidence/mancer-roster-authenticated.json in the campaign scratchpad and every catalog price/limit machine-checked against it (Mancer re-prices — gpt-oss-120b input moved 0.024 → 0.022 within the day)",
175
+ "status": 200
176
+ },
177
+ "authProbe": {
178
+ "date": "2026-09-03",
179
+ "status": 401
180
+ },
181
+ "billingProbe": {
182
+ "date": "2026-09-03",
183
+ "status": 402,
184
+ "method": "POST /oai/v1/chat/completions on deepseek-v4-flash with a zero balance: {\"error\":{\"type\":\"CANT_AFFORD\",\"message\":\"This model requires paid credits to use!\"}}"
185
+ },
186
+ "liveMatrix": {
187
+ "date": "2026-09-03",
188
+ "result": "18-probe harness on the free model mytholite: roster, chat, max_completion_tokens, system role, content parts, sampling params, SSE stream (usage chunk + [DONE]), json_schema (valid JSON matching schema) and json_object all 200; tools and tools+schema 400 BAD_PARAMETERS ('\"auto\" tool choice requires --enable-auto-tool-choice and --tool-call-parser') so tools are declared false; vision 200 but text-only (image ignored); bad key 401 'Bad API key'; unknown model 400 'Unknown Model'. Paid models could not be capability-probed without credits — tools may work there and can be re-declared once probed. Nightly matrix pins testModel mytholite."
189
+ },
190
+ "addedInPR": "pending"
191
+ }
192
+ }
@@ -36,6 +36,17 @@ export declare class ConfiguredOpenAICompatProvider extends OpenAIChatCompletion
36
36
  protected getDefaultModel(): string;
37
37
  protected getFallbackModelName(): string;
38
38
  protected getFallbackModels(): string[];
39
+ /**
40
+ * The catalog's `capabilities.tools` is the vendor's own answer, probed on
41
+ * the wire when the entry was written; the model registry (the base
42
+ * default) knows nothing about Tier-2 models and answers "supported" for
43
+ * every unknown id. A vendor that declares tools: false must never receive
44
+ * a `tools` array — Mancer's free model rejects one with 400 — so the
45
+ * declaration wins here and the registry is only consulted otherwise.
46
+ * Like the other entry-reading overrides above, this runs only after
47
+ * construction: BaseProvider merely closes over it for GenerationHandler.
48
+ */
49
+ supportsTools(): boolean;
39
50
  protected adjustRequestBody(body: OpenAICompatChatRequest, modelId: string): OpenAICompatChatRequest;
40
51
  protected formatProviderError(error: unknown): Error;
41
52
  }
@@ -86,6 +86,22 @@ export class ConfiguredOpenAICompatProvider extends OpenAIChatCompletionsProvide
86
86
  getFallbackModels() {
87
87
  return this.entry.fallbackModels;
88
88
  }
89
+ /**
90
+ * The catalog's `capabilities.tools` is the vendor's own answer, probed on
91
+ * the wire when the entry was written; the model registry (the base
92
+ * default) knows nothing about Tier-2 models and answers "supported" for
93
+ * every unknown id. A vendor that declares tools: false must never receive
94
+ * a `tools` array — Mancer's free model rejects one with 400 — so the
95
+ * declaration wins here and the registry is only consulted otherwise.
96
+ * Like the other entry-reading overrides above, this runs only after
97
+ * construction: BaseProvider merely closes over it for GenerationHandler.
98
+ */
99
+ supportsTools() {
100
+ if (this.entry.supportsTools === false) {
101
+ return false;
102
+ }
103
+ return super.supportsTools();
104
+ }
89
105
  adjustRequestBody(body, modelId) {
90
106
  const adjusted = super.adjustRequestBody(body, modelId);
91
107
  if (this.entry.messageContentFormat !== "string") {
@@ -242,15 +242,6 @@ export declare class GoogleVertexProvider extends BaseProvider {
242
242
  * No more @ai-sdk/google-vertex dependency
243
243
  */
244
244
  generate(optionsOrPrompt: TextGenerationOptions | string): Promise<EnhancedGenerateResult | null>;
245
- /**
246
- * Invoke `options.onFinish` with the lifecycle payload shape consumers
247
- * (and `test:middleware`) expect. Pulled out so generate / image-gen /
248
- * Anthropic / Gemini code paths share one implementation. Errors thrown
249
- * by the user's callback are swallowed so they cannot poison the
250
- * primary generate path — same contract as the AI SDK middleware
251
- * wrapGenerate uses.
252
- */
253
- private fireGenerateOnFinish;
254
245
  /**
255
246
  * Invoke `options.onError` with the lifecycle payload shape consumers
256
247
  * (and `test:middleware`) expect. Mirrors {@link fireGenerateOnFinish}.
@@ -5342,39 +5342,6 @@ export class GoogleVertexProvider extends BaseProvider {
5342
5342
  }
5343
5343
  });
5344
5344
  }
5345
- /**
5346
- * Invoke `options.onFinish` with the lifecycle payload shape consumers
5347
- * (and `test:middleware`) expect. Pulled out so generate / image-gen /
5348
- * Anthropic / Gemini code paths share one implementation. Errors thrown
5349
- * by the user's callback are swallowed so they cannot poison the
5350
- * primary generate path — same contract as the AI SDK middleware
5351
- * wrapGenerate uses.
5352
- */
5353
- fireGenerateOnFinish(options, result, startTime) {
5354
- const onFinish = options
5355
- .onFinish;
5356
- if (typeof onFinish !== "function") {
5357
- return;
5358
- }
5359
- try {
5360
- const usage = result?.usage;
5361
- const callbackResult = onFinish({
5362
- text: result?.content || "",
5363
- usage: usage
5364
- ? {
5365
- promptTokens: usage.input ?? 0,
5366
- completionTokens: usage.output ?? 0,
5367
- }
5368
- : undefined,
5369
- duration: Date.now() - startTime,
5370
- finishReason: result?.finishReason ?? "stop",
5371
- });
5372
- Promise.resolve(callbackResult).catch((err) => logger.warn(`[GoogleVertex] onFinish callback rejected: ${err instanceof Error ? err.message : String(err)}`));
5373
- }
5374
- catch (err) {
5375
- logger.warn(`[GoogleVertex] onFinish callback threw: ${err instanceof Error ? err.message : String(err)}`);
5376
- }
5377
- }
5378
5345
  /**
5379
5346
  * Invoke `options.onError` with the lifecycle payload shape consumers
5380
5347
  * (and `test:middleware`) expect. Mirrors {@link fireGenerateOnFinish}.