@juspay/neurolink 12.32.3 → 12.33.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.
@@ -85,7 +85,7 @@
85
85
  "tools": true,
86
86
  "toolsWithStreaming": true,
87
87
  "structuredOutput": true,
88
- "structuredOutputWithTools": true,
88
+ "structuredOutputWithTools": false,
89
89
  "embeddings": false,
90
90
  "thinking": false
91
91
  },
@@ -123,7 +123,7 @@
123
123
  },
124
124
  "liveMatrix": {
125
125
  "date": "2026-09-17",
126
- "result": "Verified live against https://global.api-route.com/v1: GET /v1/models returned 70 models. Chat completions, streaming, function calling, streamed function calling, and structured JSON output (json_object and json_schema) verified. Multimodal vision verified on claude-sonnet-4-6, claude-haiku-4-5, and gemini-3.8-flash. Invalid API key returns 401 (无效的令牌) and invalid model returns 503 (model_not_found)."
126
+ "result": "Verified live against https://global.api-route.com/v1: GET /v1/models returned 70 models. Chat completions, streaming, function calling, streamed function calling, and structured JSON output (json_object and json_schema) verified. Multimodal vision verified on claude-sonnet-4-6, claude-haiku-4-5, and gemini-3.8-flash. Invalid API key returns 401 (无效的令牌) and invalid model returns 503 (model_not_found). Tools and response_format were only probed SEPARATELY, never together in one request, so structuredOutputWithTools is corrected to false here pending a dedicated tools_plus_schema probe — the individual passes do not demonstrate the combined capability."
127
127
  },
128
128
  "addedInPR": "pending"
129
129
  }
@@ -131,6 +131,11 @@ export function buildCatalogEntries() {
131
131
  ...(typeof entry.capabilities.tools === "boolean"
132
132
  ? { supportsTools: entry.capabilities.tools }
133
133
  : {}),
134
+ // Unlike `tools`, `capabilities.structuredOutputWithTools` is a plain
135
+ // required boolean in the schema (no "model-dependent" member), so this
136
+ // is a direct passthrough — `false` and `true` both carry meaning to
137
+ // ConfiguredOpenAICompatProvider.suppressResponseFormatWithTools().
138
+ supportsStructuredOutputWithTools: entry.capabilities.structuredOutputWithTools,
134
139
  };
135
140
  const { baseURLTemplate } = entry.wire;
136
141
  if (baseURLTemplate) {
@@ -208,7 +208,7 @@
208
208
  "tools": true,
209
209
  "toolsWithStreaming": true,
210
210
  "structuredOutput": true,
211
- "structuredOutputWithTools": true,
211
+ "structuredOutputWithTools": false,
212
212
  "embeddings": false,
213
213
  "thinking": false
214
214
  },
@@ -245,7 +245,7 @@
245
245
  },
246
246
  "liveMatrix": {
247
247
  "date": "2026-08-30",
248
- "result": "17 ids answer normally. A chat probe alone was not enough to judge the rest, because Mistral rejects every non-chat model with the same generic 'Invalid model' text: cross-referencing the roster shows mistral-embed, codestral-embed, mistral-ocr-latest, mistral-ocr-2512, voxtral-mini-latest and voxtral-mini-2602 are all still listed and alive — they are embedding, OCR and audio models that a chat endpoint is right to refuse — so they keep production status. An id is marked retired only when BOTH signals agree: absent from the roster AND rejected by a live chat call. pixtral-12b is the one id where they disagree — the roster omits it but a real call returns 200, so it was moved back to production, on the rule that a live 200 outranks a roster omission. mistral-large-latest and mistral-large-2512 answer 403 'not available in your subscription tier': a plan limit on this account, not a retirement, so they stay production. visionModel is pinned to mistral-medium-latest because the otherwise-first vision id, mistral-large-latest, is behind that plan gate."
248
+ "result": "17 ids answer normally. A chat probe alone was not enough to judge the rest, because Mistral rejects every non-chat model with the same generic 'Invalid model' text: cross-referencing the roster shows mistral-embed, codestral-embed, mistral-ocr-latest, mistral-ocr-2512, voxtral-mini-latest and voxtral-mini-2602 are all still listed and alive — they are embedding, OCR and audio models that a chat endpoint is right to refuse — so they keep production status. An id is marked retired only when BOTH signals agree: absent from the roster AND rejected by a live chat call. pixtral-12b is the one id where they disagree — the roster omits it but a real call returns 200, so it was moved back to production, on the rule that a live 200 outranks a roster omission. mistral-large-latest and mistral-large-2512 answer 403 'not available in your subscription tier': a plan limit on this account, not a retirement, so they stay production. visionModel is pinned to mistral-medium-latest because the otherwise-first vision id, mistral-large-latest, is behind that plan gate. Combined tools plus response_format probes returned 429 twice, which is not successful capability evidence, so structuredOutputWithTools remains false pending a successful combined-request probe."
249
249
  },
250
250
  "addedInPR": "https://github.com/juspay/neurolink/pull/1587"
251
251
  }
@@ -9,8 +9,10 @@ import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
9
9
  * OPENAI_COMPAT_CATALOG in openaiCompatCatalog.ts for the entries.
10
10
  *
11
11
  * If a provider needs a real hook override (adjustRequestBody,
12
- * adjustBodyAfter400, getChatCompletionsURL, getAuthHeaders,
13
- * suppressResponseFormatWithTools, ...) it does NOT belong in the catalog —
12
+ * adjustBodyAfter400, getChatCompletionsURL, getAuthHeaders, ...) that isn't
13
+ * expressible as one of the catalog's data-driven quirks (messageContentFormat,
14
+ * responseFormatDowngrade, replayReasoningContent,
15
+ * supportsStructuredOutputWithTools, ...) it does NOT belong in the catalog —
14
16
  * write a dedicated subclass instead (see azureOpenai.ts).
15
17
  *
16
18
  * The exception is a WIRE DIALECT: a vendor that speaks OpenAI for ordinary
@@ -55,6 +57,22 @@ export declare class ConfiguredOpenAICompatProvider extends OpenAIChatCompletion
55
57
  * construction: BaseProvider merely closes over it.
56
58
  */
57
59
  supportsTools(): boolean;
60
+ /**
61
+ * The base class drops `response_format` from any request carrying tools,
62
+ * because a generic OpenAI-compatible backend may honour it over tool
63
+ * calling and answer with final-shape JSON on step one instead of running
64
+ * the agentic loop. For a catalog entry that is not a guess — the flag
65
+ * comes from a live wire probe (`capabilities.structuredOutputWithTools`)
66
+ * — so an entry that declares the combination supported sends both and
67
+ * gets the object from the vendor's own constrained decoding in ONE
68
+ * request, instead of falling back to a tool-free re-ask.
69
+ *
70
+ * Conservative by default: only an explicit `true` opts in. A vendor that
71
+ * declares support but rejects the combination at runtime is still caught
72
+ * by `isToolsSchemaConflictError` and retried without structured output,
73
+ * so a stale flag costs one request rather than the turn.
74
+ */
75
+ protected suppressResponseFormatWithTools(): boolean;
58
76
  protected adjustRequestBody(body: OpenAICompatChatRequest, modelId: string): OpenAICompatChatRequest;
59
77
  protected adjustResponseFormat(rf: OpenAICompatResponseFormat | undefined, modelId: string): OpenAICompatResponseFormat | undefined;
60
78
  protected replayReasoningContent(): boolean;
@@ -31,8 +31,10 @@ const flattenMessageContent = (content) => {
31
31
  * OPENAI_COMPAT_CATALOG in openaiCompatCatalog.ts for the entries.
32
32
  *
33
33
  * If a provider needs a real hook override (adjustRequestBody,
34
- * adjustBodyAfter400, getChatCompletionsURL, getAuthHeaders,
35
- * suppressResponseFormatWithTools, ...) it does NOT belong in the catalog —
34
+ * adjustBodyAfter400, getChatCompletionsURL, getAuthHeaders, ...) that isn't
35
+ * expressible as one of the catalog's data-driven quirks (messageContentFormat,
36
+ * responseFormatDowngrade, replayReasoningContent,
37
+ * supportsStructuredOutputWithTools, ...) it does NOT belong in the catalog —
36
38
  * write a dedicated subclass instead (see azureOpenai.ts).
37
39
  *
38
40
  * The exception is a WIRE DIALECT: a vendor that speaks OpenAI for ordinary
@@ -110,6 +112,24 @@ export class ConfiguredOpenAICompatProvider extends OpenAIChatCompletionsProvide
110
112
  }
111
113
  return super.supportsTools();
112
114
  }
115
+ /**
116
+ * The base class drops `response_format` from any request carrying tools,
117
+ * because a generic OpenAI-compatible backend may honour it over tool
118
+ * calling and answer with final-shape JSON on step one instead of running
119
+ * the agentic loop. For a catalog entry that is not a guess — the flag
120
+ * comes from a live wire probe (`capabilities.structuredOutputWithTools`)
121
+ * — so an entry that declares the combination supported sends both and
122
+ * gets the object from the vendor's own constrained decoding in ONE
123
+ * request, instead of falling back to a tool-free re-ask.
124
+ *
125
+ * Conservative by default: only an explicit `true` opts in. A vendor that
126
+ * declares support but rejects the combination at runtime is still caught
127
+ * by `isToolsSchemaConflictError` and retried without structured output,
128
+ * so a stale flag costs one request rather than the turn.
129
+ */
130
+ suppressResponseFormatWithTools() {
131
+ return this.entry.supportsStructuredOutputWithTools !== true;
132
+ }
113
133
  adjustRequestBody(body, modelId) {
114
134
  const adjusted = super.adjustRequestBody(body, modelId);
115
135
  if (this.entry.messageContentFormat !== "string") {
@@ -122,6 +122,16 @@ export type CatalogCapabilities = {
122
122
  tools: boolean | "model-dependent";
123
123
  toolsWithStreaming: boolean;
124
124
  structuredOutput: boolean;
125
+ /**
126
+ * Whether a live wire probe proved the vendor accepts native tool
127
+ * definitions and `response_format` in the SAME request (a 200, not a
128
+ * schema/tools conflict error). Maps to
129
+ * OpenAICompatCatalogEntry.supportsStructuredOutputWithTools; `true` lets
130
+ * ConfiguredOpenAICompatProvider send both instead of suppressing
131
+ * `response_format` on any request carrying tools (the conservative
132
+ * default for an unknown endpoint). Only an explicit `true` opts in —
133
+ * `false` is the same conservative behavior as leaving it unset.
134
+ */
125
135
  structuredOutputWithTools: boolean;
126
136
  embeddings: boolean;
127
137
  thinking: boolean;
@@ -749,6 +749,26 @@ export type OpenAICompatCatalogEntry = {
749
749
  * same default every hand-written provider uses.
750
750
  */
751
751
  supportsTools?: boolean;
752
+ /**
753
+ * Whether the vendor accepts native tool definitions and `response_format`
754
+ * in the SAME request, from the catalog's
755
+ * `capabilities.structuredOutputWithTools`.
756
+ *
757
+ * The base class (`suppressResponseFormatWithTools`) suppresses
758
+ * `response_format` whenever tools are attached, because a generic
759
+ * OpenAI-compatible backend may honour it over tool calling and answer
760
+ * with final-shape JSON on step 1 instead of running the agentic loop.
761
+ * That is the right default for an unknown endpoint, but for a catalog
762
+ * entry it is not a guess: the flag is set from a live wire probe. `true`
763
+ * lets the request carry both, so the object comes back from the vendor's
764
+ * own constrained decoding in ONE request instead of a tool-free re-ask.
765
+ *
766
+ * Omitted or false keeps the conservative default. A vendor that turns
767
+ * out to reject the combination anyway is still caught by
768
+ * `isToolsSchemaConflictError` and retried without structured output, so
769
+ * a stale `true` costs one request, not the turn.
770
+ */
771
+ supportsStructuredOutputWithTools?: boolean;
752
772
  /**
753
773
  * The literal passed as ProviderFactory.registerProvider()'s defaultModel
754
774
  * argument (resolved before the provider is constructed). Preserves each
@@ -4412,14 +4412,14 @@
4412
4412
  {"objectID":"b802832537c22912fff88f6ef9ed79b85fd26dc3b69b61b0a52eb261feff8297","title":"Common Errors","url":"/docs/features/streaming#common-errors","content":"| Error | Cause | Solution |\n| ------------------------- | ------------------------------------------ | --------------------------------------------------- |\n| SESSION_BUDGET_EXCEEDED | Session cost exceeded maxBudgetUsd limit | Increase budget or start a new session |\n| PROVIDER_AUTH_ERROR | Missing or invalid API key | Set the provider's API key environment variable |\n| TIMEOUT | Request exceeded timeout | Increase timeout or use abortSignal for control |\n| MODEL_NOT_FOUND | Invalid model name | Check provider docs for supported model names |","hierarchy":{"lvl0":"Features","lvl1":"Streaming Guide","lvl2":"Common Errors","lvl3":""}},
4413
4413
  {"objectID":"1949a330d8bfb85ec15c527f519bf4eefb0b571f2db1027d8d568edd39986091","title":"Provider Support","url":"/docs/features/streaming#provider-support","content":"All NeuroLink providers support streaming:\n\n| Provider | Streaming | Notes |\n| ----------------- | --------- | ----------------------------------------------------------- |\n| OpenAI | Yes | Full streaming with tool support |\n| Anthropic | Yes | Full streaming with tool support |\n| Google AI Studio | Yes | Full streaming with tool support |\n| Google Vertex AI | Yes | Full streaming with tool support |\n| Amazon Bedrock | Yes | Full streaming with tool support |\n| Azure OpenAI | Yes | Full streaming with tool support |\n| Mistral | Yes | Full streaming with tool support |\n| LiteLLM | Yes | Full streaming; tool support depends on underlying model |\n| Ollama | Yes | Full streaming; tool support depends on model |\n| Hugging Face | Yes | Streaming support; tool support varies by model |\n| Amazon SageMaker | Limited | Falls back to fake streaming (generate then emit as chunks) |\n| OpenAI-Compatible | Yes | Depends on the endpoint's streaming support |\n\nWhen real streaming is not available for a provider or model, NeuroLink transparently falls back to \"fake streaming\" -- it generates the full response and then emits it as chunks. Your consuming code does not need to change.","hierarchy":{"lvl0":"Features","lvl1":"Streaming Guide","lvl2":"Provider Support","lvl3":""}},
4414
4414
  {"objectID":"a65ae1b5264ffcf3bd257c1a2666704dd13916433e8fe9af65ee41cc79288854","title":"See Also","url":"/docs/features/streaming#see-also","content":"Advanced Streaming Guide -- Enterprise streaming patterns, backpressure, and event types\nTTS Guide -- Text-to-speech audio streaming\nRAG Guide -- Retrieval-augmented generation with streaming\nThinking Configuration -- Extended thinking with streaming\nMultimodal Guide -- Images, PDFs, and files with streaming","hierarchy":{"lvl0":"Features","lvl1":"Streaming Guide","lvl2":"See Also","lvl3":""}},
4415
- {"objectID":"5a947cc17681ddd7c40d18de744818974a4dbb4e1a3a1884ffedcd192f89c8ff","title":"Structured Output with Zod Schemas","url":"/docs/features/structured-output","content":"Structured Output with Zod Schemas\n\nGenerate type-safe, validated JSON responses using Zod schemas. Available in generate() function only (not stream()).\n\nQuick Start\n\nRequirements\nschema: A Zod schema defining the output structure — always required.\noutput.format: Must be \"json\" or \"structured\" to get a JSON string\n in result.content (defaults to \"text\" if not specified). This is\n independent of structuredData: passing schema alone, with no\n output.format, is enough for result.structuredData to be populated —\n this is the path the tools section below uses.\n\nComplex Schemas\n\nWorks with Tools\n\nStructured output works seamlessly with MCP tools:\n\nHow this works on OpenAI-compatible providers\n\nMost OpenAI-compatible vendors reject response_format and tools in the same\nrequest, so NeuroLink does not send them together. That leaves a turn with tools\nattached — which is most turns, since built-in and MCP tools ride along by\ndefault — with nothing telling the model to answer in JSON.\n\nNeuroLink closes that gap itself. The tool turn runs untouched, and if its\nanswer does not satisfy your schema, the SDK re-asks once with the tools\nremoved, which is what makes native response_format legal again. That second\npass only reformats an answer the model has already produced, so tool results\nstill drive the content; toolsUsed, toolExecutions and usage cover the\nwhole turn, not just the reformat.\n\nTwo consequences worth knowing:\nA generate({ schema }) call that needed the reformat costs two requests.\n Calls whose first answer already satisfies the schema cost one, as before.\nThe reformat is accepted only if it actually produced a schema-valid value.\n If it fails, if the SDK's own turn deadline is reached, or if it comes back as\n prose anyway, the original answer is returned rather than an error — so this\n can improve an outcome but never degrade one. A caller that cancels the\n request still gets its cancellation. In those cases structuredData may still\n be unset, which is the honest signal that the model never produced the value.\n\nThe schema is deliberately not injected into the tool turn's system prompt.\nSome models read a JSON Schema sitting next to a tool list as another tool and\nanswer by calling one that does not exist — Groq's llama-3.3-70b-versatile\ntries to call a tool named json, which the server rejects outright.\n\nThe re-ask is not a single attempt. It tries native response_format first,\nbecause removing the tools is precisely what makes that legal again. Some\nvendors then reject the schema itself rather than the request: Groq answers\na non-object root with invalid JSON schema for response_format: schema must\nhave type 'object', so an array- or scalar-rooted schema fails at this stage.\nWhen that happens the SDK degrades a second time and re-runs the same tools-free\npass with the schema spelled into the prompt instead. That is why a\nz.array(z.string()) schema returns [\"red\",\"blue\",\"yellow\"] with matching\nstructuredData rather than prose. Both stages run without tools; only the way\nthe schema is communicated changes.\n\nImportant: Google Gemini Providers Limitation\n\nGoogle API Constraint: Google Gemini (both Vertex AI and Google AI Studio) cannot combine function calling with structured output (JSON schema validation). This is a documented Google API limitation, not a NeuroLink issue.\n\nGemini 3 / Gemini 2.5 Note: This limitation applies to all Gemini models, including the latest Gemini 3 and Gemini 2.5 series (e.g., gemini-2.5-pro, gemini-2.5-flash). While these models have excellent JSON schema support for structured output, they still cannot use tools and JSON schema validation together in the same request.\n\nError Message:\n\nSolution: Use disableTools: true when using schemas with Google providers:\n\nThis is Industry Standard: All major AI frameworks (LangChain, Vercel AI SDK, Agno, Instructor) use the same approach - disabling tools when using response schemas with Google models.\n\nWorkarounds for Gemini Tools + Structured Output\n\nIf you need both tool execution and structured output with Gemini, consider these approaches:\nTwo-Step Approach: First call with tools enabled (no schema), then a second call with schema to format the result:\nUse a Different Provider: OpenAI and Anthropic support tools and structured output together:\nChoose One or the Other: Design your workflow to use either tools OR structured output per request, not both.\n\nRelated Limitation: Complex schemas may trigger \"Too many states for serving\" errors. Solutions:\nSimplify schema structure\nReduce nested objects\nUse disableTools: true to reduce state complexity\n\nImportant Notes\nOnly available in generate() - Not supported in stream() function\noutput.format controls result.content - If it is not \"json\" or \"structured\", result.content is plain text even with a schema; result.structuredData can still be populated from schema alone (see Requirements)\nAuto-validated, with a no-throw fallback when tools are involved - Without tools, an invalid re","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"","lvl3":""}},
4416
- {"objectID":"82da02b8aebfda79a43e92c60a33f5fe1bb8e84ba9ba8ac0a42d36f0df945844","title":"Structured Output with Zod Schemas","url":"/docs/features/structured-output#structured-output-with-zod-schemas","content":"Generate type-safe, validated JSON responses using Zod schemas. Available in generate() function only (not stream()).","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Structured Output with Zod Schemas","lvl3":""}},
4415
+ {"objectID":"5a947cc17681ddd7c40d18de744818974a4dbb4e1a3a1884ffedcd192f89c8ff","title":"Structured Output with Zod Schemas","url":"/docs/features/structured-output","content":"Structured Output with Zod Schemas\n\nGenerate type-safe, validated JSON responses using Zod schemas. Both generate() and stream() accept schema; generate() returns structuredData directly, while a fully drained stream exposes it as result.metadata.structuredData.\n\nQuick Start\n\nRequirements\nschema: A Zod schema defining the output structure — always required.\noutput.format: Must be \"json\" or \"structured\" to get a JSON string\n in result.content (defaults to \"text\" if not specified). This is\n independent of structuredData: passing schema alone, with no\n output.format, is enough for result.structuredData to be populated —\n this is the path the tools section below uses.\n\nComplex Schemas\n\nWorks with Tools\n\nStructured output works seamlessly with MCP tools:\n\nHow this works on OpenAI-compatible providers\n\nOpenAI-compatible vendors vary in whether they accept response_format and\ntools in the same request. NeuroLink is conservative by default and does not\nsend them together. For catalog providers whose combined request has been\nverified live, capabilities.structuredOutputWithTools: true opts both\ngenerate() and stream() into sending them together, so the vendor can use\nnative constrained decoding without an extra formatting request. Separate\nsuccessful tool and structured-output probes are not enough to enable the flag.\n\nWhen a provider is not opted in, the tool turn runs untouched. If its answer\ndoes not satisfy your schema, the SDK re-asks once with the tools removed,\nwhich is what makes native response_format legal again. That second pass only\nreformats an answer the model has already produced, so tool results still drive\nthe content; toolsUsed, toolExecutions and usage cover the whole turn, not\njust the reformat. If an opted-in provider later rejects the combined request,\nthe same conflict recovery retries without structured output, so a stale\ncapability flag costs one request rather than the turn.\n\nTwo consequences worth knowing:\nA generate({ schema }) call that needed the reformat costs two requests.\n Calls whose first answer already satisfies the schema cost one, as before.\nThe reformat is accepted only if it actually produced a schema-valid value.\n If it fails, if the SDK's own turn deadline is reached, or if it comes back as\n prose anyway, the original answer is returned rather than an error — so this\n can improve an outcome but never degrade one. A caller that cancels the\n request still gets its cancellation. In those cases structuredData may still\n be unset, which is the honest signal that the model never produced the value.\n\nThe schema is deliberately not injected into the tool turn's system prompt.\nSome models read a JSON Schema sitting next to a tool list as another tool and\nanswer by calling one that does not exist — Groq's llama-3.3-70b-versatile\ntries to call a tool named json, which the server rejects outright.\n\nThe re-ask is not a single attempt. It tries native response_format first,\nbecause removing the tools is precisely what makes that legal again. Some\nvendors then reject the schema itself rather than the request: Groq answers\na non-object root with invalid JSON schema for response_format: schema must\nhave type 'object', so an array- or scalar-rooted schema fails at this stage.\nWhen that happens the SDK degrades a second time and re-runs the same tools-free\npass with the schema spelled into the prompt instead. That is why a\nz.array(z.string()) schema returns [\"red\",\"blue\",\"yellow\"] with matching\nstructuredData rather than prose. Both stages run without tools; only the way\nthe schema is communicated changes.\n\nImportant: Google Gemini Providers Limitation\n\nGoogle API Constraint: Google Gemini (both Vertex AI and Google AI Studio) cannot combine function calling with structured output (JSON schema validation). This is a documented Google API limitation, not a NeuroLink issue.\n\nGemini 3 / Gemini 2.5 Note: This limitation applies to all Gemini models, including the latest Gemini 3 and Gemini 2.5 series (e.g., gemini-2.5-pro, gemini-2.5-flash). While these models have excellent JSON schema support for structured output, they still cannot use tools and JSON schema validation together in the same request.\n\nError Message:\n\nSolution: Use disableTools: true when using schemas with Google providers:\n\nThis is Industry Standard: All major AI frameworks (LangChain, Vercel AI SDK, Agno, Instructor) use the same approach - disabling tools when using response schemas with Google models.\n\nWorkarounds for Gemini Tools + Structured Output\n\nIf you need both tool execution and structured output with Gemini, consider these approaches:\nTwo-Step Approach: First call with tools enabled (no schema), then a second call with schema to format the result:\nUse a Different Provider: OpenAI and Anthropic support tools and structured output together:\nChoose One or the Other: Design your workflow to use either tools OR structured output per request, not both.\n\nRelated Limitation: Complex schemas may trigger \"Too many","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"","lvl3":""}},
4416
+ {"objectID":"82da02b8aebfda79a43e92c60a33f5fe1bb8e84ba9ba8ac0a42d36f0df945844","title":"Structured Output with Zod Schemas","url":"/docs/features/structured-output#structured-output-with-zod-schemas","content":"Generate type-safe, validated JSON responses using Zod schemas. Both generate() and stream() accept schema; generate() returns structuredData directly, while a fully drained stream exposes it as result.metadata.structuredData.","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Structured Output with Zod Schemas","lvl3":""}},
4417
4417
  {"objectID":"8786915973a894e8ce2e6fd18a7da976c109949f20b7549c63b50d98d05bd821","title":"Requirements","url":"/docs/features/structured-output#requirements","content":"schema: A Zod schema defining the output structure — always required.\noutput.format: Must be \"json\" or \"structured\" to get a JSON string\n in result.content (defaults to \"text\" if not specified). This is\n independent of structuredData: passing schema alone, with no\n output.format, is enough for result.structuredData to be populated —\n this is the path the tools section below uses.","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Requirements","lvl3":""}},
4418
4418
  {"objectID":"239e70c6bb43d051b5ce01d19d01e865b6eab69208cea7c37f849114a7766afb","title":"Works with Tools","url":"/docs/features/structured-output#works-with-tools","content":"Structured output works seamlessly with MCP tools:","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Works with Tools","lvl3":""}},
4419
- {"objectID":"970f3f5cf5c095a627ebce241052024f34fb08a9916fb006fb0c3dadd3ecc43f","title":"How this works on OpenAI-compatible providers","url":"/docs/features/structured-output#how-this-works-on-openai-compatible-providers","content":"Most OpenAI-compatible vendors reject response_format and tools in the same\nrequest, so NeuroLink does not send them together. That leaves a turn with tools\nattached — which is most turns, since built-in and MCP tools ride along by\ndefault — with nothing telling the model to answer in JSON.\n\nNeuroLink closes that gap itself. The tool turn runs untouched, and if its\nanswer does not satisfy your schema, the SDK re-asks once with the tools\nremoved, which is what makes native response_format legal again. That second\npass only reformats an answer the model has already produced, so tool results\nstill drive the content; toolsUsed, toolExecutions and usage cover the\nwhole turn, not just the reformat.\n\nTwo consequences worth knowing:\nA generate({ schema }) call that needed the reformat costs two requests.\n Calls whose first answer already satisfies the schema cost one, as before.\nThe reformat is accepted only if it actually produced a schema-valid value.\n If it fails, if the SDK's own turn deadline is reached, or if it comes back as\n prose anyway, the original answer is returned rather than an error — so this\n can improve an outcome but never degrade one. A caller that cancels the\n request still gets its cancellation. In those cases structuredData may still\n be unset, which is the honest signal that the model never produced the value.\n\nThe schema is deliberately not injected into the tool turn's system prompt.\nSome models read a JSON Schema sitting next to a tool list as another tool and\nanswer by calling one that does not exist — Groq's llama-3.3-70b-versatile\ntries to call a tool named json, which the server rejects outright.\n\nThe re-ask is not a single attempt. It tries native response_format first,\nbecause removing the tools is precisely what makes that legal again. Some\nvendors then reject the schema itself rather than the request: Groq answers\na non-object root with invalid JSON schema for response_format: schema must\nhave type 'object', so an array- or scalar-ro","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"How this works on OpenAI-compatible providers","lvl3":""}},
4419
+ {"objectID":"970f3f5cf5c095a627ebce241052024f34fb08a9916fb006fb0c3dadd3ecc43f","title":"How this works on OpenAI-compatible providers","url":"/docs/features/structured-output#how-this-works-on-openai-compatible-providers","content":"OpenAI-compatible vendors vary in whether they accept response_format and\ntools in the same request. NeuroLink is conservative by default and does not\nsend them together. For catalog providers whose combined request has been\nverified live, capabilities.structuredOutputWithTools: true opts both\ngenerate() and stream() into sending them together, so the vendor can use\nnative constrained decoding without an extra formatting request. Separate\nsuccessful tool and structured-output probes are not enough to enable the flag.\n\nWhen a provider is not opted in, the tool turn runs untouched. If its answer\ndoes not satisfy your schema, the SDK re-asks once with the tools removed,\nwhich is what makes native response_format legal again. That second pass only\nreformats an answer the model has already produced, so tool results still drive\nthe content; toolsUsed, toolExecutions and usage cover the whole turn, not\njust the reformat. If an opted-in provider later rejects the combined request,\nthe same conflict recovery retries without structured output, so a stale\ncapability flag costs one request rather than the turn.\n\nTwo consequences worth knowing:\nA generate({ schema }) call that needed the reformat costs two requests.\n Calls whose first answer already satisfies the schema cost one, as before.\nThe reformat is accepted only if it actually produced a schema-valid value.\n If it fails, if the SDK's own turn deadline is reached, or if it comes back as\n prose anyway, the original answer is returned rather than an error — so this\n can improve an outcome but never degrade one. A caller that cancels the\n request still gets its cancellation. In those cases structuredData may still\n be unset, which is the honest signal that the model never produced the value.\n\nThe schema is deliberately not injected into the tool turn's system prompt.\nSome models read a JSON Schema sitting next to a tool list as another tool and\nanswer by calling one that does not exist — Groq's llama-3.3-70b-versatile\n","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"How this works on OpenAI-compatible providers","lvl3":""}},
4420
4420
  {"objectID":"cc660b57647a3a31ebe3aca058136232c61bddb59022c34114e48727143b0a74","title":"Important: Google Gemini Providers Limitation","url":"/docs/features/structured-output#important-google-gemini-providers-limitation","content":"Google API Constraint: Google Gemini (both Vertex AI and Google AI Studio) cannot combine function calling with structured output (JSON schema validation). This is a documented Google API limitation, not a NeuroLink issue.\n\nGemini 3 / Gemini 2.5 Note: This limitation applies to all Gemini models, including the latest Gemini 3 and Gemini 2.5 series (e.g., gemini-2.5-pro, gemini-2.5-flash). While these models have excellent JSON schema support for structured output, they still cannot use tools and JSON schema validation together in the same request.\n\nError Message:\n\nSolution: Use disableTools: true when using schemas with Google providers:\n\nThis is Industry Standard: All major AI frameworks (LangChain, Vercel AI SDK, Agno, Instructor) use the same approach - disabling tools when using response schemas with Google models.","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Important: Google Gemini Providers Limitation","lvl3":""}},
4421
4421
  {"objectID":"72d39c1123edc80bab2c387126c4257c184aae7ad60046baf6698df58b5be31b","title":"Workarounds for Gemini Tools + Structured Output","url":"/docs/features/structured-output#workarounds-for-gemini-tools-structured-output","content":"If you need both tool execution and structured output with Gemini, consider these approaches:\nTwo-Step Approach: First call with tools enabled (no schema), then a second call with schema to format the result:\nUse a Different Provider: OpenAI and Anthropic support tools and structured output together:\nChoose One or the Other: Design your workflow to use either tools OR structured output per request, not both.\n\nRelated Limitation: Complex schemas may trigger \"Too many states for serving\" errors. Solutions:\nSimplify schema structure\nReduce nested objects\nUse disableTools: true to reduce state complexity","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Workarounds for Gemini Tools + Structured Output","lvl3":""}},
4422
- {"objectID":"1075ae3db0656103e73fad23d48fb13a44ce2db31f37066e7de3168378c5e6b8","title":"Important Notes","url":"/docs/features/structured-output#important-notes","content":"Only available in generate() - Not supported in stream() function\noutput.format controls result.content - If it is not \"json\" or \"structured\", result.content is plain text even with a schema; result.structuredData can still be populated from schema alone (see Requirements)\nAuto-validated, with a no-throw fallback when tools are involved - Without tools, an invalid response throws NoObjectGeneratedError with validation details. With tools attached, a failed schema match triggers the tool-free re-ask described in Works with Tools; if that also fails, the original answer is returned rather than an error, and structuredData is left unset\nProvider support - Works with OpenAI, Anthropic, Google AI Studio, Vertex AI\nGemini JSON Schema Support - Gemini 3 / Gemini 2.5 models have excellent native JSON schema support\nGemini Tools Limitation - All Gemini models (including Gemini 3) cannot combine tools with schemas - use disableTools: true","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Important Notes","lvl3":""}},
4422
+ {"objectID":"1075ae3db0656103e73fad23d48fb13a44ce2db31f37066e7de3168378c5e6b8","title":"Important Notes","url":"/docs/features/structured-output#important-notes","content":"Available in generate() and stream() - generate() exposes result.structuredData; after the stream is fully drained, stream() exposes result.metadata.structuredData\noutput.format controls result.content - If it is not \"json\" or \"structured\", result.content is plain text even with a schema; result.structuredData can still be populated from schema alone (see Requirements)\nAuto-validated, with a no-throw fallback when tools are involved - Without tools, an invalid response throws NoObjectGeneratedError with validation details. With tools attached, a failed schema match triggers the tool-free re-ask described in Works with Tools; if that also fails, the original answer is returned rather than an error, and structuredData is left unset\nProvider support - Works with OpenAI, Anthropic, Google AI Studio, Vertex AI\nGemini JSON Schema Support - Gemini 3 / Gemini 2.5 models have excellent native JSON schema support\nGemini Tools Limitation - All Gemini models (including Gemini 3) cannot combine tools with schemas - use disableTools: true","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"Important Notes","lvl3":""}},
4423
4423
  {"objectID":"406cc3985caef69880cd67e7ea04473aa14d3d76a8a2a49562bc264c5d75778f","title":"See Also","url":"/docs/features/structured-output#see-also","content":"API Reference\nCustom Tools\nMCP Integration","hierarchy":{"lvl0":"Features","lvl1":"Structured Output with Zod Schemas","lvl2":"See Also","lvl3":""}},
4424
4424
  {"objectID":"10887a08f4e43a2991f20cd46290c281df948396a9270718b61ca947b367641f","title":"TaskManager - Scheduled & Self-Running Tasks","url":"/docs/features/task-manager","content":"TaskManager - Scheduled & Self-Running Tasks\n\nOverview\n\nTaskManager adds scheduled and self-running task capabilities to NeuroLink. It enables AI agents to execute prompts on a schedule (cron, interval, or one-shot), with two execution modes: Isolated (fresh context per run) and Continuation (preserves conversation history across runs).\n\nThe system is available as both an SDK API and CLI commands, and ships with built-in tools so AI agents can self-schedule tasks during conversations.\n\nCore Concepts\n\nTask\n\nA Task is a unit of scheduled work. It contains:\nA prompt (what the AI should do)\nA schedule (when to run: cron expression, fixed interval, or one-shot)\nAn execution mode (isolated or continuation)\nOptional provider/model overrides\nOptional callbacks for results\n\nTaskManager\n\nThe orchestration layer that manages task lifecycle: creation, scheduling, execution, pausing, resuming, deletion, and logging. Accessed via neurolink.tasks.\n\nExecution Modes\n\n| Mode | Behavior | Use Case |\n| ---------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| Isolated | Each run gets a fresh NeuroLink context. No memory of previous runs. | One-off checks, stateless monitoring, report generation |\n| Continuation | Conversation history is preserved across runs. The AI \"remembers\" previous executions. | Trend analysis, progressive monitoring, iterative refinement |\n\nTask Backends\n\nThe scheduling/looping mechanism is abstracted behind a TaskBackend interface. Two implementations ship by default:\n\n| Backend | Default | Requires | Survives Restart | Best For |\n| --------------- | -------- | -------- | ---------------- | ----------------------------------------------------- |\n| BullMQ | Yes | Redis | Yes | Production, multi-process, reliable scheduling |\n| NodeTimeout | Fallback | Nothing | No | Development, zero-dependency setups, simple use cases |\n\nStorage Strategy\n\nStorage is automatically tied to the backend — users never configure it separately:\n\n| Backend | Task Store | Run Logs | Why |\n| --------------- | ---------- | ----------- | ------------------------------------------------------------------------------------------- |\n| BullMQ | Redis | Redis | Same Redis instance; multi-process safe, survives container restarts, works across replicas |\n| NodeTimeout | JSON file | JSONL files | No Redis available; local dev, single process, human-readable for debugging |\n\nThis means:\nProduction servers (BullMQ) → everything in Redis → horizontally scalable, no file I/O, container-friendly\nLocal dev / CLI (NodeTimeout) → JSON files → zero dependencies, inspectable with any text editor\n\nArchitecture\n\nFollows NeuroLink's established Factory + Registry pattern.\n\nDirectory Structure\n\nComponent Diagram\n\nStorage auto-selection: When backend: \"bullmq\", TaskManager creates a RedisTaskStore (task definitions + run logs in Redis). When backend: \"node-timeout\", it creates a FileTaskStore (JSON + JSONL on disk). The TaskStore interface abstracts this so all other components are storage-agnostic.\n\nHow It Fits Into NeuroLink\n\nType Definitions\n\nSDK API\n\nInitialization\n\nCreating Tasks\n\nManaging Tasks\n\nCLI Commands\n\nCLI Shorthand for Intervals\n\nThe --every flag accepts human-readable durations:\n\n| Input | Meaning |\n| ----- | ---------------- |\n| 30s | 30 seconds |\n| 5m | 5 minutes |\n| 2h | 2 hours |\n| 1d | 1 day |\n| 500 | 500 milliseconds |\n\nBuilt-in Agent Tools\n\nThese tools are registered as direct agent tools, available to the AI during any conversation. They allow the AI to self-schedule work.\n\ncreateTask\n\nThe AI can schedule follow-up tasks during a conversation.\n\nExample AI usage:\n\nUser: \"Monitor my API endpoint every 10 minutes and alert me if it goes down.\"\nAI calls createTask with { name: \"api-monitor\", prompt: \"Check https://api.example.com/health ...\", schedule: { type: \"interval\", every: 600000 }, mode: \"continuation\" }\n\nlistTasks\n\ngetTaskRuns\n\ndeleteTask\n\nrunTaskNow\n\nBackend Interface & Extensibility\n\nTaskBackend Interface\n\nAll backends implement this interface. To add a new backend (e.g., Agenda, Bree, pg-boss), implement TaskBackend and register it.\n\nRegistering a Custom Backend\n\nBullMQ Backend Details\nUses bullmq Queue + Worker + upsertJobScheduler()\nCron tasks → BullMQ repeatable jobs\nInterval tasks → BullMQ repeatable jobs with every option\nOne-shot tasks → BullMQ delayed jobs\nPause/Resume via task cancellation and re-scheduling through TaskManager\nSurvives process restarts (Redis-pers","hierarchy":{"lvl0":"Features","lvl1":"TaskManager - Scheduled & Self-Running Tasks","lvl2":"","lvl3":""}},
4425
4425
  {"objectID":"a97898fa912c3898f3a71316e3cfd14cfcf60aced0fe7fb2485eee25fd6a7000","title":"Overview","url":"/docs/features/task-manager#overview","content":"TaskManager adds scheduled and self-running task capabilities to NeuroLink. It enables AI agents to execute prompts on a schedule (cron, interval, or one-shot), with two execution modes: Isolated (fresh context per run) and Continuation (preserves conversation history across runs).\n\nThe system is available as both an SDK API and CLI commands, and ships with built-in tools so AI agents can self-schedule tasks during conversations.","hierarchy":{"lvl0":"Features","lvl1":"TaskManager - Scheduled & Self-Running Tasks","lvl2":"Overview","lvl3":""}},
@@ -8944,10 +8944,10 @@
8944
8944
  {"objectID":"14456bd556dd45363546785079362ce7acbd26bfa6a65a06c638acb656fbbce2","title":"Must remain class (13)","url":"/docs/provider-integration/migration-ledger#must-remain-class-13","content":"| Provider | Why |\n| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| bedrock | AWS SigV4/Converse protocol, custom loop, not OpenAI-compatible. Tier 4. |\n| openai | Chat portion is catalog-shaped, but embeddings, image generation, and OpenAI-specific telemetry/errors are executable behavior. |\n| vertex | Two native protocols (Google GenAI + Anthropic-on-Vertex) and multiple modality pipelines behind one provider identity. |\n| anthropic | Native Messages API auth/message/tool semantics are executable protocol behavior.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Must remain class (13)","lvl3":""}},
8945
8945
  {"objectID":"a4f69e09308f075830f2efa7c1b0126236c5ec0fc12bfe08be21764a627e47a5","title":"Must remain core class (1)","url":"/docs/provider-integration/migration-ledger#must-remain-core-class-1","content":"| Provider | Why |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| openai-compatible | This is the generic protocol surface itself, not a direct vendor. It should consume shared descriptor/config primitives but must never become a fixed-vendor JSON row — collapsing it into the catalog would conflate \"the adapter\" with \"an entry in the adapter's catalog.\" |","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Must remain core class (1)","lvl3":""}},
8946
8946
  {"objectID":"235850085d9102413140920b9d5e5e2ff6113d007c865f5d78012af80fbf1843","title":"Suggested execution order","url":"/docs/provider-integration/migration-ledger#suggested-execution-order","content":"Steps 1–2 below (mistral; huggingface, deepseek) are complete — see Migrated above. What remains:\nThree shared adapters (local-runtime, embedding-only, image-generation) — each unlocks 3 providers at once; build once, migrate three.\ngoogle-ai descriptor-only JSON metadata — proof of concept for moving static descriptor data out of a \"must remain class\" provider without touching its execution.\nDirect-vendor \"must remain class\" providers (bedrock, openai, vertex, anthropic, azure, sagemaker, nvidia-nim) get descriptor-only JSON metadata migrations, execution untouched — lower priority, cosmetic consolidation only.\nAggregators (litellm, openrouter), the core adapter (openai-compatible), and the decision-only providers (typesafe, laya) are excluded from the first-class-count migration priority entirely — the first three add no direct-vendor coverage however they're implemented, and typesafe/laya are decide-only providers with no generate/stream surface to migrate at all.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Suggested execution order","lvl3":""}},
8947
- {"objectID":"6122b2e83358a45c64da87c1ca80a2d579cdfb087a6d9dc58b1e02805296b14a","title":"OpenAI-Compatible Provider Catalog","url":"/docs/provider-integration/openai-compat-catalog","content":"OpenAI-Compatible Provider Catalog\n\nSixteen OpenAI-compatible providers — API Route, Baseten, SambaNova,\nCerebras, Groq, xAI, Together AI, Fireworks AI, Perplexity, Mistral,\nCloudflare Workers AI, GMI Cloud, Inception Labs, io.net Intelligence,\nMancer, Upstage — are registered from one JSON file each, under\nsrc/lib/providers/catalog/<id>.json, and served by one generic class,\nConfiguredOpenAICompatProvider\n(src/lib/providers/configuredOpenAICompat.ts). Adding another provider to\nthis family means adding one JSON file — no subclass, no registry edit, no\nhand-written enum member, no test edit.\n\nThe JSON is the source of truth\n\nOPENAI_COMPAT_CATALOG still exists and keeps its name and element type,\nbut it is now built by the loader (src/lib/providers/catalog/loader.ts)\nfrom the JSON files rather than hand-written. Two consumers read the JSON:\nCodegen (pnpm run codegen:catalog) writes the compile-time\n artifacts into marked regions — the AIProviderName member, the\n <Name>Models enum, the NeurolinkCredentials key — plus the generated\n index. Pre-commit and CI fail on stale output.\nThe loader builds the runtime entry; the descriptor, config options,\n context windows, pricing, vision map and model-choice tables all derive\n from it, as do the provider test suites' rows and counts.\n\nEach file is validated by a zod schema (src/lib/providers/catalog/schema.ts)\nwith a mirrored provider-catalog.schema.json for editor squiggles.\nProbe evidence (roster/auth/billing dates, live-matrix result, PR URL)\nlives in the file's evidence block — the old\ndocs/provider-integration/manifests/<id>.json files were folded into it.\n\nField-by-field reference and the escape hatches:\ntiers/tier-2-catalog-entry.md. Design rationale and the approved rulings:\ndocs/superpowers/plans/2026-08-28-provider-json-catalog-spec.md.\n\nWhen a provider belongs in the catalog\n\nA provider belongs in the JSON catalog if it needs only:\na credential (API key, optionally an extra field like Cloudflare's account id)\na base URL (static default + optional env override, or computed from an\n extra credential field)\na default/fallback model\nerror-message classification (auth / rate-limit / invalid-model / generic)\na named, closed catalog quirk. DeepSeek is the worked example: it 400s on\n json_schema structured-output requests, so deepseek.json sets\n quirks.responseFormatDowngrade: \"json-schema-to-json-object\" and the\n generic ConfiguredOpenAICompatProvider downgrades to json_object before\n sending — no subclass. It also sets quirks.replayReasoningContent: true:\n DeepSeek documents that each assistant turn's reasoning_content must go\n back on later requests once tools are in play, so the shared message\n converter and the streaming tool loop send it — for this quirk only, since\n strict OpenAI-compatible backends reject the unknown field.\n\nWhen a provider needs a dedicated subclass instead\n\nAzure OpenAI is deliberately not in the catalog because it overrides real\nrequest-shaping behavior that no named catalog quirk expresses:\nAzure OpenAI (src/lib/providers/azureOpenai.ts) overrides four hooks:\n getChatCompletionsURL (deployment-name URL routing across two Azure\n endpoint schemes), getAuthHeaders (Azure's api-key header instead of\n Authorization: Bearer), adjustRequestBody (renames max_tokens to\n max_completion_tokens for o-series/gpt-5+ deployments), and\n suppressResponseFormatWithTools (Azure supports both at once).\n\nIf a future provider needs any hook beyond the 3 mandatory ones\n(getProviderName, getDefaultModel, formatProviderError) or the 2\npurely-declarative optional ones (getFallbackModelName,\ngetFallbackModels) and that hook is not already a named catalog quirk, it\nneeds either a new closed quirk (the DeepSeek route) or a dedicated subclass\n(the Azure OpenAI route).\n\nError-message fidelity\n\nEach entry's errorRules is a direct, order-preserving translation of its\noriginal subclass's formatProviderError if/else ladder into rule data\n(status code and/or case-insensitive pattern), classified via\nclassifyProviderError()\n(src/lib/utils/errorClassifier.ts). Every bespoke message string is\npreserved verbatim — including xAI's \"top up your account\" quota URL and\nGroq's decommissioned-vs-not-found distinction — via each rule's own\nmessage field (string | ((ctx) => string)), with model-name\ninterpolation carried through ctx.modelName. There is no message-wording\nregression here. Timeout classification is likewise unchanged: 15 of the 16\nproviders map TimeoutError to NetworkError (the classifier's default),\nand Groq alone maps it to ProviderError. Groq's subclass override is\npreserved verbatim via the JSON's quirks.timeoutErrorClass, so no\nprovider's timeout class changed during migration.\n\nKnown pre-existing quirk this migration preserved (not fixed)\n\nMistral's provider registration passes a defaultModel value to\nProviderFactory.registerProvider() that does not check MISTRAL_MODEL\n(MistralModels.MISTRAL_LARGE_LATEST, a bare literal), while\nConfiguredOpenAI","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"","lvl3":""}},
8947
+ {"objectID":"6122b2e83358a45c64da87c1ca80a2d579cdfb087a6d9dc58b1e02805296b14a","title":"OpenAI-Compatible Provider Catalog","url":"/docs/provider-integration/openai-compat-catalog","content":"OpenAI-Compatible Provider Catalog\n\nSixteen OpenAI-compatible providers — API Route, Baseten, SambaNova,\nCerebras, Groq, xAI, Together AI, Fireworks AI, Perplexity, Mistral,\nCloudflare Workers AI, GMI Cloud, Inception Labs, io.net Intelligence,\nMancer, Upstage — are registered from one JSON file each, under\nsrc/lib/providers/catalog/<id>.json, and served by one generic class,\nConfiguredOpenAICompatProvider\n(src/lib/providers/configuredOpenAICompat.ts). Adding another provider to\nthis family means adding one JSON file — no subclass, no registry edit, no\nhand-written enum member, no test edit.\n\nThe JSON is the source of truth\n\nOPENAI_COMPAT_CATALOG still exists and keeps its name and element type,\nbut it is now built by the loader (src/lib/providers/catalog/loader.ts)\nfrom the JSON files rather than hand-written. Two consumers read the JSON:\nCodegen (pnpm run codegen:catalog) writes the compile-time\n artifacts into marked regions — the AIProviderName member, the\n <Name>Models enum, the NeurolinkCredentials key — plus the generated\n index. Pre-commit and CI fail on stale output.\nThe loader builds the runtime entry; the descriptor, config options,\n context windows, pricing, vision map and model-choice tables all derive\n from it, as do the provider test suites' rows and counts.\n\nEach file is validated by a zod schema (src/lib/providers/catalog/schema.ts)\nwith a mirrored provider-catalog.schema.json for editor squiggles.\nProbe evidence (roster/auth/billing dates, live-matrix result, PR URL)\nlives in the file's evidence block — the old\ndocs/provider-integration/manifests/<id>.json files were folded into it.\n\nField-by-field reference and the escape hatches:\ntiers/tier-2-catalog-entry.md. Design rationale and the approved rulings:\ndocs/superpowers/plans/2026-08-28-provider-json-catalog-spec.md.\n\nWhen a provider belongs in the catalog\n\nA provider belongs in the JSON catalog if it needs only:\na credential (API key, optionally an extra field like Cloudflare's account id)\na base URL (static default + optional env override, or computed from an\n extra credential field)\na default/fallback model\nerror-message classification (auth / rate-limit / invalid-model / generic)\na named, closed catalog quirk. DeepSeek is the worked example: it 400s on\n json_schema structured-output requests, so deepseek.json sets\n quirks.responseFormatDowngrade: \"json-schema-to-json-object\" and the\n generic ConfiguredOpenAICompatProvider downgrades to json_object before\n sending — no subclass. It also sets quirks.replayReasoningContent: true:\n DeepSeek documents that each assistant turn's reasoning_content must go\n back on later requests once tools are in play, so the shared message\n converter and the streaming tool loop send it — for this quirk only, since\n strict OpenAI-compatible backends reject the unknown field.\na wire-proven capability such as\n capabilities.structuredOutputWithTools. The generic provider suppresses\n response_format when tools are attached by default; an explicit true\n keeps it on the same generate() or stream() request. Set this only after\n a combined tools-plus-schema request returned successfully. Separate tool\n and structured-output probes are not evidence for the combined capability.\n A stale opt-in is still protected by the runtime conflict retry, which drops\n structured output and retries rather than losing the turn.\n\nThe currently opted-in providers are Baseten, DeepSeek, Fireworks AI, GMI\nCloud, Inception Labs, io.net Intelligence, Novita AI, Together AI, Upstage and\nxAI. API Route remains opted out because its evidence verifies the features\nseparately, not together in one request. Mistral remains opted out because its\ncombined probes returned 429 twice, not a successful capability response.\n\nWhen a provider needs a dedicated subclass instead\n\nAzure OpenAI is deliberately not in the catalog because it overrides real\nrequest-shaping behavior that no named catalog quirk expresses:\nAzure OpenAI (src/lib/providers/azureOpenai.ts) overrides four hooks:\n getChatCompletionsURL (deployment-name URL routing across two Azure\n endpoint schemes), getAuthHeaders (Azure's api-key header instead of\n Authorization: Bearer), adjustRequestBody (renames max_tokens to\n max_completion_tokens for o-series/gpt-5+ deployments), and\n suppressResponseFormatWithTools (Azure supports both at once).\n\nIf a future provider needs any hook beyond the 3 mandatory ones\n(getProviderName, getDefaultModel, formatProviderError) or the 2\npurely-declarative optional ones (getFallbackModelName,\ngetFallbackModels) and that hook is not already a named catalog quirk, it\nneeds either a new closed quirk (the DeepSeek route) or a dedicated subclass\n(the Azure OpenAI route).\n\nError-message fidelity\n\nEach entry's errorRules is a direct, order-preserving translation of its\noriginal subclass's formatProviderError if/else ladder into rule data\n(status code and/or case-insensitive pattern), classified via\nclassifyProviderError()\n(src/lib/utils/err","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"","lvl3":""}},
8948
8948
  {"objectID":"9a91a5b4a32a9f1e89c6a28f762c3594c4a39d96d32cc9e6e1fa0f5cb3f609e4","title":"OpenAI-Compatible Provider Catalog","url":"/docs/provider-integration/openai-compat-catalog#openai-compatible-provider-catalog","content":"Sixteen OpenAI-compatible providers — API Route, Baseten, SambaNova,\nCerebras, Groq, xAI, Together AI, Fireworks AI, Perplexity, Mistral,\nCloudflare Workers AI, GMI Cloud, Inception Labs, io.net Intelligence,\nMancer, Upstage — are registered from one JSON file each, under\nsrc/lib/providers/catalog/<id>.json, and served by one generic class,\nConfiguredOpenAICompatProvider\n(src/lib/providers/configuredOpenAICompat.ts). Adding another provider to\nthis family means adding one JSON file — no subclass, no registry edit, no\nhand-written enum member, no test edit.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"OpenAI-Compatible Provider Catalog","lvl3":""}},
8949
8949
  {"objectID":"16e60ee66171009a6638b5110cc8693f90c1a2cfde796c6932c3512d7f7421fb","title":"The JSON is the source of truth","url":"/docs/provider-integration/openai-compat-catalog#the-json-is-the-source-of-truth","content":"OPENAI_COMPAT_CATALOG still exists and keeps its name and element type,\nbut it is now built by the loader (src/lib/providers/catalog/loader.ts)\nfrom the JSON files rather than hand-written. Two consumers read the JSON:\nCodegen (pnpm run codegen:catalog) writes the compile-time\n artifacts into marked regions — the AIProviderName member, the\n <Name>Models enum, the NeurolinkCredentials key — plus the generated\n index. Pre-commit and CI fail on stale output.\nThe loader builds the runtime entry; the descriptor, config options,\n context windows, pricing, vision map and model-choice tables all derive\n from it, as do the provider test suites' rows and counts.\n\nEach file is validated by a zod schema (src/lib/providers/catalog/schema.ts)\nwith a mirrored provider-catalog.schema.json for editor squiggles.\nProbe evidence (roster/auth/billing dates, live-matrix result, PR URL)\nlives in the file's evidence block — the old\ndocs/provider-integration/manifests/<id>.json files were folded into it.\n\nField-by-field reference and the escape hatches:\ntiers/tier-2-catalog-entry.md. Design rationale and the approved rulings:\ndocs/superpowers/plans/2026-08-28-provider-json-catalog-spec.md.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"The JSON is the source of truth","lvl3":""}},
8950
- {"objectID":"00a7366eb480e85f5377e0cb8575403e7fd9b33cc028bf95cf0fe48cc94fcea9","title":"When a provider belongs in the catalog","url":"/docs/provider-integration/openai-compat-catalog#when-a-provider-belongs-in-the-catalog","content":"A provider belongs in the JSON catalog if it needs only:\na credential (API key, optionally an extra field like Cloudflare's account id)\na base URL (static default + optional env override, or computed from an\n extra credential field)\na default/fallback model\nerror-message classification (auth / rate-limit / invalid-model / generic)\na named, closed catalog quirk. DeepSeek is the worked example: it 400s on\n json_schema structured-output requests, so deepseek.json sets\n quirks.responseFormatDowngrade: \"json-schema-to-json-object\" and the\n generic ConfiguredOpenAICompatProvider downgrades to json_object before\n sending — no subclass. It also sets quirks.replayReasoningContent: true:\n DeepSeek documents that each assistant turn's reasoning_content must go\n back on later requests once tools are in play, so the shared message\n converter and the streaming tool loop send it — for this quirk only, since\n strict OpenAI-compatible backends reject the unknown field.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"When a provider belongs in the catalog","lvl3":""}},
8950
+ {"objectID":"00a7366eb480e85f5377e0cb8575403e7fd9b33cc028bf95cf0fe48cc94fcea9","title":"When a provider belongs in the catalog","url":"/docs/provider-integration/openai-compat-catalog#when-a-provider-belongs-in-the-catalog","content":"A provider belongs in the JSON catalog if it needs only:\na credential (API key, optionally an extra field like Cloudflare's account id)\na base URL (static default + optional env override, or computed from an\n extra credential field)\na default/fallback model\nerror-message classification (auth / rate-limit / invalid-model / generic)\na named, closed catalog quirk. DeepSeek is the worked example: it 400s on\n json_schema structured-output requests, so deepseek.json sets\n quirks.responseFormatDowngrade: \"json-schema-to-json-object\" and the\n generic ConfiguredOpenAICompatProvider downgrades to json_object before\n sending — no subclass. It also sets quirks.replayReasoningContent: true:\n DeepSeek documents that each assistant turn's reasoning_content must go\n back on later requests once tools are in play, so the shared message\n converter and the streaming tool loop send it — for this quirk only, since\n strict OpenAI-compatible backends reject the unknown field.\na wire-proven capability such as\n capabilities.structuredOutputWithTools. The generic provider suppresses\n response_format when tools are attached by default; an explicit true\n keeps it on the same generate() or stream() request. Set this only after\n a combined tools-plus-schema request returned successfully. Separate tool\n and structured-output probes are not evidence for the combined capability.\n A stale opt-in is still protected by the runtime conflict retry, which drops\n structured output and retries rather than losing the turn.\n\nThe currently opted-in providers are Baseten, DeepSeek, Fireworks AI, GMI\nCloud, Inception Labs, io.net Intelligence, Novita AI, Together AI, Upstage and\nxAI. API Route remains opted out because its evidence verifies the features\nseparately, not together in one request. Mistral remains opted out because its\ncombined probes returned 429 twice, not a successful capability response.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"When a provider belongs in the catalog","lvl3":""}},
8951
8951
  {"objectID":"c2a9e3425a4cecb520467f900df8b5fa78fc4178514eba6385040bb9506e48ee","title":"When a provider needs a dedicated subclass instead","url":"/docs/provider-integration/openai-compat-catalog#when-a-provider-needs-a-dedicated-subclass-instead","content":"Azure OpenAI is deliberately not in the catalog because it overrides real\nrequest-shaping behavior that no named catalog quirk expresses:\nAzure OpenAI (src/lib/providers/azureOpenai.ts) overrides four hooks:\n getChatCompletionsURL (deployment-name URL routing across two Azure\n endpoint schemes), getAuthHeaders (Azure's api-key header instead of\n Authorization: Bearer), adjustRequestBody (renames max_tokens to\n max_completion_tokens for o-series/gpt-5+ deployments), and\n suppressResponseFormatWithTools (Azure supports both at once).\n\nIf a future provider needs any hook beyond the 3 mandatory ones\n(getProviderName, getDefaultModel, formatProviderError) or the 2\npurely-declarative optional ones (getFallbackModelName,\ngetFallbackModels) and that hook is not already a named catalog quirk, it\nneeds either a new closed quirk (the DeepSeek route) or a dedicated subclass\n(the Azure OpenAI route).","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"When a provider needs a dedicated subclass instead","lvl3":""}},
8952
8952
  {"objectID":"a5276d835ceba15da6ec36261ab78001e17f8b78ba9c8cb1c5e1282aa2f377e3","title":"Error-message fidelity","url":"/docs/provider-integration/openai-compat-catalog#error-message-fidelity","content":"Each entry's errorRules is a direct, order-preserving translation of its\noriginal subclass's formatProviderError if/else ladder into rule data\n(status code and/or case-insensitive pattern), classified via\nclassifyProviderError()\n(src/lib/utils/errorClassifier.ts). Every bespoke message string is\npreserved verbatim — including xAI's \"top up your account\" quota URL and\nGroq's decommissioned-vs-not-found distinction — via each rule's own\nmessage field (string | ((ctx) => string)), with model-name\ninterpolation carried through ctx.modelName. There is no message-wording\nregression here. Timeout classification is likewise unchanged: 15 of the 16\nproviders map TimeoutError to NetworkError (the classifier's default),\nand Groq alone maps it to ProviderError. Groq's subclass override is\npreserved verbatim via the JSON's quirks.timeoutErrorClass, so no\nprovider's timeout class changed during migration.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"Error-message fidelity","lvl3":""}},
8953
8953
  {"objectID":"2a22fddc1a2c383a5e75a53e2d512d764f01ad66ab03bfe6edf12acb5d204814","title":"Known pre-existing quirk this migration preserved (not fixed)","url":"/docs/provider-integration/openai-compat-catalog#known-pre-existing-quirk-this-migration-preserved-not-fixed","content":"Mistral's provider registration passes a defaultModel value to\nProviderFactory.registerProvider() that does not check MISTRAL_MODEL\n(MistralModels.MISTRAL_LARGE_LATEST, a bare literal), while\nConfiguredOpenAICompatProvider.getDefaultModel() for Mistral does\ncheck MISTRAL_MODEL (falling back to MistralModels.MISTRAL_SMALL_2506).\nEvery other catalog provider's registry default and class default agree.\nThis is expressed via the JSON's quirks.registryDefaultIgnoresModelEnvVar\n(true only for Mistral). The JSON migration did reconcile one half of it:\ngetDefaultModel(MISTRAL) now returns the real generation default\n(mistral-small-2506) rather than the registry literal — a disclosed\nbug-fix-grade delta, since the two disagreed before.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"Known pre-existing quirk this migration preserved (not fixed)","lvl3":""}},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.32.3",
3
+ "version": "12.33.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {