@juspay/neurolink 12.38.0 → 12.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -3
- package/dist/browser/neurolink.min.js +110 -110
- package/dist/core/modelConfiguration.js +1 -0
- package/dist/providers/anthropic/client.js +8 -1
- package/dist/providers/openRouter/client.js +1 -0
- package/dist/providers/openaiChatCompletionsBase.js +37 -1
- package/dist/types/openaiCompatible.d.ts +10 -0
- package/docs-site/static/search-index.json +10 -0
- package/package.json +2 -1
|
@@ -2307,7 +2307,14 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
2307
2307
|
return {
|
|
2308
2308
|
stream: transformedStream(),
|
|
2309
2309
|
provider: this.providerName,
|
|
2310
|
-
|
|
2310
|
+
// Cell-3 identity fix: `this.modelName` is the raw, possibly-unset
|
|
2311
|
+
// constructor arg (undefined whenever a caller relies on the default
|
|
2312
|
+
// model). `modelId` (resolved above as
|
|
2313
|
+
// `this.modelName || getDefaultAnthropicModel()`) is what was actually
|
|
2314
|
+
// put on the wire for this turn — the same value `buildParams` uses to
|
|
2315
|
+
// build the request. Reporting the raw field previously let a
|
|
2316
|
+
// default-model turn's StreamResult claim an undefined/empty model.
|
|
2317
|
+
model: modelId,
|
|
2311
2318
|
toolCalls: [],
|
|
2312
2319
|
toolResults: [],
|
|
2313
2320
|
metadata: turnMetadata,
|
|
@@ -1648,6 +1648,12 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
1648
1648
|
// middleware short-circuits the request — the case where there is no
|
|
1649
1649
|
// turn to build on at all.
|
|
1650
1650
|
let loopConversation;
|
|
1651
|
+
// Cell-3 identity fix: the last model id the SERVER actually echoed on
|
|
1652
|
+
// the wire (via `onModelObserved`, see `runStreamLoop`), distinct from
|
|
1653
|
+
// the pre-call resolved/requested `modelId` below. Stays undefined for
|
|
1654
|
+
// backends that never echo `model` on a chunk, in which case the
|
|
1655
|
+
// `result.model` getter falls back to `modelId` — unchanged behavior.
|
|
1656
|
+
let observedServerModel;
|
|
1651
1657
|
const providerNameForLoop = this.providerName;
|
|
1652
1658
|
const streamBaseModel = {
|
|
1653
1659
|
specificationVersion: "v3",
|
|
@@ -1700,6 +1706,9 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
1700
1706
|
resolveUsage,
|
|
1701
1707
|
resolveFinish,
|
|
1702
1708
|
responseFormat,
|
|
1709
|
+
onModelObserved: (model) => {
|
|
1710
|
+
observedServerModel = model;
|
|
1711
|
+
},
|
|
1703
1712
|
});
|
|
1704
1713
|
const completion = loopPromise.then(() => Promise.all([usagePromise, finishPromise]).then(([usage, reason]) => ({
|
|
1705
1714
|
type: "finish",
|
|
@@ -1930,6 +1939,24 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
1930
1939
|
toolsUsed,
|
|
1931
1940
|
metadata: streamMetadata,
|
|
1932
1941
|
};
|
|
1942
|
+
// Cell-3 identity fix: `model` starts as a plain `modelId` data property
|
|
1943
|
+
// above (satisfying StreamResult's type and giving every reader a value
|
|
1944
|
+
// even before the loop starts), then gets redefined as a live getter.
|
|
1945
|
+
// A plain reassignment (`result.model = observedServerModel`) would not
|
|
1946
|
+
// survive `preserveLiveStreamAccessors`' re-wrapping at the
|
|
1947
|
+
// BaseProvider/NeuroLink boundaries — those boundaries spread `result`
|
|
1948
|
+
// into a new object BEFORE the background loop has produced a single
|
|
1949
|
+
// chunk, which freezes a plain field at its construction-time value.
|
|
1950
|
+
// Re-defining as a getter (the same mechanism already used for
|
|
1951
|
+
// `toolExecutions` below) means every boundary's spread re-applies the
|
|
1952
|
+
// getter itself, so a caller reading `.model` after draining `.stream`
|
|
1953
|
+
// sees whatever the server actually echoed, falling back to the
|
|
1954
|
+
// pre-call resolved `modelId` when the backend never echoed one.
|
|
1955
|
+
Object.defineProperty(result, "model", {
|
|
1956
|
+
enumerable: true,
|
|
1957
|
+
configurable: true,
|
|
1958
|
+
get: () => observedServerModel ?? modelId,
|
|
1959
|
+
});
|
|
1933
1960
|
// Lazy getter: every read transforms the live `toolExecutionSummaries`
|
|
1934
1961
|
// through the canonical `transformToolExecutions()` so consumers see
|
|
1935
1962
|
// `{name, input, output, duration}[]` (codebase convention), while still
|
|
@@ -1952,7 +1979,7 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
1952
1979
|
return result;
|
|
1953
1980
|
}
|
|
1954
1981
|
async runStreamLoop(args) {
|
|
1955
|
-
const { maxSteps, modelId, url, fetchImpl, abortSignal, options, conversation, openAITools, openAIToolChoice, toolsRecord, toolNameFromWire, emitter, toolsUsed, toolExecutionSummaries, pushChunk, closeChannel, resolveUsage, resolveFinish, responseFormat, } = args;
|
|
1982
|
+
const { maxSteps, modelId, url, fetchImpl, abortSignal, options, conversation, openAITools, openAIToolChoice, toolsRecord, toolNameFromWire, emitter, toolsUsed, toolExecutionSummaries, pushChunk, closeChannel, resolveUsage, resolveFinish, responseFormat, onModelObserved, } = args;
|
|
1956
1983
|
// Hoisted above the try so the catch can resolve the usage accumulated
|
|
1957
1984
|
// by steps that completed BEFORE the failure — those steps were billed.
|
|
1958
1985
|
let stepFinish = null;
|
|
@@ -2060,6 +2087,15 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
2060
2087
|
if (stepResult.usage) {
|
|
2061
2088
|
stepUsage = mergeUsage(stepUsage, stepResult.usage);
|
|
2062
2089
|
}
|
|
2090
|
+
// Cell-3 identity fix: `stepResult.model` is what the SERVER echoed
|
|
2091
|
+
// on the wire (parseSSEStream captures `chunk.model`), which can
|
|
2092
|
+
// differ from the pre-call resolved `modelId` a gateway/router was
|
|
2093
|
+
// asked for. Surfaced via callback rather than the method's return
|
|
2094
|
+
// value so `executeStream` can update the already-returned
|
|
2095
|
+
// `StreamResult` in place (see `onModelObserved` wiring there).
|
|
2096
|
+
if (stepResult.model) {
|
|
2097
|
+
onModelObserved?.(stepResult.model);
|
|
2098
|
+
}
|
|
2063
2099
|
if (stepResult.toolCalls.size === 0) {
|
|
2064
2100
|
break;
|
|
2065
2101
|
}
|
|
@@ -255,6 +255,16 @@ export type StreamLoopArgs = {
|
|
|
255
255
|
* schema was requested, or when tools suppress it.
|
|
256
256
|
*/
|
|
257
257
|
responseFormat?: OpenAICompatResponseFormat;
|
|
258
|
+
/**
|
|
259
|
+
* Fired once per step that echoes a `model` field on the wire (SSE
|
|
260
|
+
* `chunk.model`, captured by `parseSSEStream` into `OpenAICompatSSEResult.model`).
|
|
261
|
+
* Lets the caller learn what the SERVER actually served, distinct from the
|
|
262
|
+
* pre-call resolved/requested `modelId` — a gateway or router can rewrite
|
|
263
|
+
* an alias to a concrete model id. Not called when a step's response omits
|
|
264
|
+
* `model` (some backends don't echo it), so the caller must keep its own
|
|
265
|
+
* fallback.
|
|
266
|
+
*/
|
|
267
|
+
onModelObserved?: (model: string) => void;
|
|
258
268
|
};
|
|
259
269
|
export type OpenAICompatBuildBodyArgs = {
|
|
260
270
|
modelId: string;
|
|
@@ -8930,6 +8930,16 @@
|
|
|
8930
8930
|
{"objectID":"0060ac273f386c349e42799641f9fc2ac514f569b1870b20a2cc8097d482bf2d","title":"7. Shared logging fetch — createLoggingFetch","url":"/docs/provider-integration/SAFETY-PRIMITIVES#7-shared-logging-fetch-createloggingfetch","content":"Use for: the fetch option on createOpenAI({...}) / similar SDK\nclient constructors when you want non-2xx upstream responses logged\nwith sanitized output.\n\nBody opt-in: response bodies are NOT logged by default. Set\nNEUROLINK_DEBUG_HTTP=1 to enable body logging — bodies are run\nthrough sanitizeForLog to redact tokens.\n\nPreviously duplicated in: cohere, xai, groq, togetherAi, fireworks,\nperplexity, cloudflare, llamaCpp, lmStudio, nvidiaNim, deepseek (11\nnear-identical copies with subtle differences). Now centralised.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Safety Primitives Reference","lvl2":"7. Shared logging fetch — createLoggingFetch","lvl3":""}},
|
|
8931
8931
|
{"objectID":"1ba0f0ac7f804aa5d9cabe5106734436439a83dc273dcf5e05984d218fae5b1f","title":"8. Test scripts","url":"/docs/provider-integration/SAFETY-PRIMITIVES#8-test-scripts","content":"⚠️ These three suites no longer exist. ssrf, log-sanitize and\nstream-span each imported the primitive out of src/lib/ and asserted on\nit directly, so they were removed when the suites became end-to-end only\n(CLAUDE.md rule 15).\n\n| Removed suite | Covered |\n| -------------------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| test/continuous-test-suite-ssrf.ts | H01 + H06 bypass categories, handler-coverage audit |\n| test/continuous-test-suite-log-sanitize.ts | H03 + H04 token formats, record/header sanitization, H04 regression grep |\n| test/continuous-test-suite-stream-span.ts | H07 span lifetime + error path + recordException ordering, M08 typed-error sweep, M09 brand check sweep |\n\nNothing has replaced them. The primitives themselves are unchanged and the\neslint rules that force callers through them still apply, but the bypass\ncategories above are now caught only by review. Treat the checklist below\nas the live control.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Safety Primitives Reference","lvl2":"8. Test scripts","lvl3":""}},
|
|
8932
8932
|
{"objectID":"2c421be98a40dada99b032fd6dab31bc387486db4c7060e3c4fd720b1135742d","title":"9. Universal safety checklist (paste into PR description)","url":"/docs/provider-integration/SAFETY-PRIMITIVES#9-universal-safety-checklist-paste-into-pr-description","content":"When adding any new provider / modality / handler, tick:\n[ ] All caller-influenced URL downloads go through safeDownload (or predictionLifecycle.downloadPredictionOutput for Replicate-based handlers)\n[ ] All HTTP response bodies sanitized via sanitizeForLog / sanitizeRecord / sanitizeHeaders (NO inline regex)\n[ ] Streaming spans wrapped in withClientStreamSpan (NOT withClientSpan)\n[ ] Provider SDK reference validated via isNeuroLink(sdk) (NOT duck-typing)\n[ ] formatProviderError returns typed errors (AuthenticationError / RateLimitError / InvalidModelError / NetworkError / ProviderError / NeuroLinkError) — never plain Error\n[ ] Reviewed by hand against §8 — the ssrf / log-sanitize / stream-span suites that used to gate this were removed with the unit suites\n\nIf a custom redaction or fetch pattern is genuinely required, add an\neslint-disable-next-line with a one-line justification rather than\nsilently bypassing the centralized helper.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Safety Primitives Reference","lvl2":"9. Universal safety checklist (paste into PR description)","lvl3":""}},
|
|
8933
|
+
{"objectID":"a38fecff2a45c5d93cfd8f93417b053e1d9b60919a6802b88a0876d3729d2c0d","title":"Credential-free provider acceptance gate","url":"/docs/provider-integration/acceptance-gate","content":"Credential-free provider acceptance gate\n\nSuite: test/continuous-test-suite-acceptance-gate.ts\nRun: pnpm run test:acceptance-gate\nScript: test:acceptance-gate in package.json\nMock server: test/helpers/acceptanceGateServer.ts\n\nWhy this exists\n\nThe prior full provider capability matrix (test/continuous-test-suite-provider-matrix.ts,\n1,037 cells) was audited and found to be able to report green without proving\nanything, in at least 10 ways: tool cells that passed with zero tool calls\nmade, no disableInternalFallback and no provider/model identity check on\nany cell, structured-output cells that asserted a value's type rather than\nits contents, and a harness that could exit 0 on zero coverage.\n\nThis gate replaces that surface area with a small, falsifiable one: at most\n9 cells per (provider, pinned model), fail-fast, cheapest first, run\nentirely against a local mock vendor HTTP server — no real vendor calls, no\nreal API keys, ever. Every provider is pointed at the mock through its own\ndocumented base-URL environment override, with a fake key, so the SDK and\nthe CLI both round-trip through the exact same server (that's what makes\ncell 9, CLI parity, meaningful instead of a re-derivation).\n\nThe 9 cells\n\nEach cell asserts an exact value the mock server returns, never a shape\nor a \"non-empty\" check. A row stops (remaining cells report SKIP, not a\nsecond FAIL) the first time a cell genuinely fails — this is the\n\"fail-fast\" contract; a capability-gated skip (e.g. a provider that doesn't\ndeclare tools) never counts as a failure and never poisons the rest of the\nrow.\nIdentity pin — every call carries\n { provider, model, disableInternalFallback: true }; the result's\n provider and model fields are asserted against what was requested.\nExact-output generate — generate()'s content is asserted equal to\n a specific constant (GATE_EXACT_VALUE), not merely non-empty.\nDrained stream, identity asserted — the stream is fully drained and\n the accumulated content is asserted equal to a constant. The\n StreamResult's provider/model are then asserted — and for the\n OpenAI-compatible protocol rows, against what the mock server actually\n served (${model}::mock-server-resolved), not what was requested. See\n \"The stream-identity defect\" below — this cell is the reason that defect\n was caught and fixed.\nTool-nonce proof — the test tool's execute() returns a fresh\n randomUUID() nonce that exists nowhere else. The final answer is\n asserted to contain ACCEPTANCE_GATE_TOOL_CONFIRMED:<nonce>, which the\n mock server only emits once it sees that exact nonce echoed back in a\n tool-result message — i.e. the assertion can only pass if a real tool\n call round-tripped through the model.\nStructured-exact — generate({ schema })'s parsed structuredData is\n asserted equal (via JSON.stringify) to an exact object, and\n jsonTruncated is asserted not true — truncation is surfaced, never\n silently swallowed.\nThinking proof — only for providers whose catalog/descriptor declares\n thinking: true (currently anthropic and the deepseek catalog row).\n Drives stream() with an explicit\n thinkingConfig: { enabled: true, budgetTokens: 2048 } (the SDK path has no bare thinkingLevel\n convenience — that folding only exists on the CLI's option parser) and\n asserts the accumulated reasoning deltas and the post-thinking answer\n both equal exact constants.\nEmbeddings proof — only for providers whose row declares\n embeddings: true. Goes through ProviderFactory.createProvider()\n directly (NeuroLink has no embed() method) and asserts the returned\n vector equals an exact constant array.\nBudget ceiling enforced — a dedicated, low-ceiling (1) mock\n server instance proves the ceiling mechanism itself is real: a first call\n succeeds, a second call against the same server must fail, and the\n server's own request counter is asserted to have observed both. This\n runs against its own server, never the shared one every other cell uses,\n so it can't be tripped by unrelated traffic.\nCLI parity — the same identity + exact-output assertions as cells 1-2,\n but driven through the built CLI\n (node dist/cli/index.js generate ... --format json), parsing its JSON\n stdout, against the same mock server.\n Not a separate re-derivation of the assertions — literally the same\n constants.\n\nKnown, documented gap in cell 9\n\nThe CLI's generate/stream commands have no flag that reaches\ndisableInternalFallback — commandFactory.ts's processOptions()\nwhitelist omits it (only the interactive REPL's separate options schema\nsupports it). Cell 9 therefore runs without disableInternalFallback. This\nis an intentional, documented gap, not an oversight: fixing it is a CLI\noption-surface change out of scope for this gate.\n\nWhat keeps the gap harmless is that the suite holds no real credential to\nfall back to. test/helpers/credentialFreeEnv.ts is its first import: it\npoints DOTENV_CONFIG_PATH at /dev/null, so neither the SDK's nor the\nharness's .env load r","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"","lvl3":""}},
|
|
8934
|
+
{"objectID":"6bfda42431fee55d45b08f169983782641ed449c8f73abe19f2f3c421f8666cb","title":"Credential-free provider acceptance gate","url":"/docs/provider-integration/acceptance-gate#credential-free-provider-acceptance-gate","content":"Suite: test/continuous-test-suite-acceptance-gate.ts\nRun: pnpm run test:acceptance-gate\nScript: test:acceptance-gate in package.json\nMock server: test/helpers/acceptanceGateServer.ts","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"Credential-free provider acceptance gate","lvl3":""}},
|
|
8935
|
+
{"objectID":"3ebd78a77f6b62003d9706d1608fb7eded2399f0dbd78b3964e14d5ac3d31f90","title":"Why this exists","url":"/docs/provider-integration/acceptance-gate#why-this-exists","content":"The prior full provider capability matrix (test/continuous-test-suite-provider-matrix.ts,\n1,037 cells) was audited and found to be able to report green without proving\nanything, in at least 10 ways: tool cells that passed with zero tool calls\nmade, no disableInternalFallback and no provider/model identity check on\nany cell, structured-output cells that asserted a value's type rather than\nits contents, and a harness that could exit 0 on zero coverage.\n\nThis gate replaces that surface area with a small, falsifiable one: at most\n9 cells per (provider, pinned model), fail-fast, cheapest first, run\nentirely against a local mock vendor HTTP server — no real vendor calls, no\nreal API keys, ever. Every provider is pointed at the mock through its own\ndocumented base-URL environment override, with a fake key, so the SDK and\nthe CLI both round-trip through the exact same server (that's what makes\ncell 9, CLI parity, meaningful instead of a re-derivation).","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"Why this exists","lvl3":""}},
|
|
8936
|
+
{"objectID":"0d725f9afdd86b7d8821e0e94ea518e7d06ef55da79a6781a8c9b96d17125ed8","title":"The 9 cells","url":"/docs/provider-integration/acceptance-gate#the-9-cells","content":"Each cell asserts an exact value the mock server returns, never a shape\nor a \"non-empty\" check. A row stops (remaining cells report SKIP, not a\nsecond FAIL) the first time a cell genuinely fails — this is the\n\"fail-fast\" contract; a capability-gated skip (e.g. a provider that doesn't\ndeclare tools) never counts as a failure and never poisons the rest of the\nrow.\nIdentity pin — every call carries\n { provider, model, disableInternalFallback: true }; the result's\n provider and model fields are asserted against what was requested.\nExact-output generate — generate()'s content is asserted equal to\n a specific constant (GATE_EXACT_VALUE), not merely non-empty.\nDrained stream, identity asserted — the stream is fully drained and\n the accumulated content is asserted equal to a constant. The\n StreamResult's provider/model are then asserted — and for the\n OpenAI-compatible protocol rows, against what the mock server actually\n served (${model}::mock-server-resolved), not what was requested. See\n \"The stream-identity defect\" below — this cell is the reason that defect\n was caught and fixed.\nTool-nonce proof — the test tool's execute() returns a fresh\n randomUUID() nonce that exists nowhere else. The final answer is\n asserted to contain ACCEPTANCE_GATE_TOOL_CONFIRMED:<nonce>, which the\n mock server only emits once it sees that exact nonce echoed back in a\n tool-result message — i.e. the assertion can only pass if a real tool\n call round-tripped through the model.\nStructured-exact — generate({ schema })'s parsed structuredData is\n asserted equal (via JSON.stringify) to an exact object, and\n jsonTruncated is asserted not true — truncation is surfaced, never\n silently swallowed.\nThinking proof — only for providers whose catalog/descriptor declares\n thinking: true (currently anthropic and the deepseek catalog row).\n Drives stream() with an explicit\n thinkingConfig: { enabled: true, budgetTokens: 2048 } (the SDK path has no bare thinkingLevel\n ","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"The 9 cells","lvl3":""}},
|
|
8937
|
+
{"objectID":"9e86b70c4ede7dc96da001ab3bf0327c39f525d7d1c62279bcf34ae41f54229a","title":"Known, documented gap in cell 9","url":"/docs/provider-integration/acceptance-gate#known-documented-gap-in-cell-9","content":"The CLI's generate/stream commands have no flag that reaches\ndisableInternalFallback — commandFactory.ts's processOptions()\nwhitelist omits it (only the interactive REPL's separate options schema\nsupports it). Cell 9 therefore runs without disableInternalFallback. This\nis an intentional, documented gap, not an oversight: fixing it is a CLI\noption-surface change out of scope for this gate.\n\nWhat keeps the gap harmless is that the suite holds no real credential to\nfall back to. test/helpers/credentialFreeEnv.ts is its first import: it\npoints DOTENV_CONFIG_PATH at /dev/null, so neither the SDK's nor the\nharness's .env load reads a developer's .env, it deletes every\ncredential-named variable the shell exported, and it points HOME at an\nempty temp directory. The last one matters as much as the first two: the\nAnthropic provider prefers a stored OAuth token\n(~/.neurolink/anthropic-credentials.json) over ANTHROPIC_API_KEY and\nrefreshes it against Anthropic's live token endpoint, so a gate that only\ncleaned the environment could still send a real refresh token to a real\nhost. The suite checks the environment and the home directory before it\nruns a row, and the CLI child inherits that stripped environment plus the\ncurrent row's fake key.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"Known, documented gap in cell 9","lvl3":""}},
|
|
8938
|
+
{"objectID":"d7fd57b5311bed74e1ac06949abeca0fc2aab02f46c7841d85da59920aafcd61","title":"The stream-identity defect (cell 3)","url":"/docs/provider-integration/acceptance-gate#the-stream-identity-defect-cell-3","content":"Confirmed and fixed in src.\n\nStreamResult.model (the public value cell 3 asserts against) was reporting\nthe requested model, not the model the server actually served. Internally,\nneurolink.ts already captured the resolved model — the bug was that this\nresolved value never reached the public StreamResult.\nOpenAI-compatible protocol path (src/lib/providers/openaiChatCompletionsBase.ts,\n src/lib/types/openaiCompatible.ts) — the substantive fix, and the one\n cell 3 actually exercises: added StreamLoopArgs.onModelObserved?: (model:\nstring) => void, fired once per step that echoes a model field on the\n wire (SSE chunk.model, captured by parseSSEStream into\n OpenAICompatSSEResult.model). executeStream's public result.model was\n a plain data property fixed at the pre-call resolved/requested modelId;\n it is now a live getter (observedServerModel ?? modelId) so a caller\n reading .model after draining .stream sees what the server actually\n served — the mock's ::mock-server-resolved suffix on the STREAM cell\n is what makes this difference observable and the fix provable. It has to\n be a getter, not a reassignment after the fact: preserveLiveStreamAccessors\n spreads result into a new object at each provider boundary before the\n background loop produces its first chunk, which would freeze a plain field\n at its construction-time value.\nAnthropic path (src/lib/providers/anthropic/client.ts) — a narrower,\n related fix: StreamResult.model was reporting the raw constructor\n argument this.modelName, which is undefined whenever a caller relies\n on the default model rather than passing one explicitly. It now reports\n modelId, the value already resolved as this.modelName ||\ngetDefaultAnthropicModel() and actually put on the wire. Note this is a\n different bug from the OpenAI-compatible one: the Anthropic Messages API\n always echoes the request's model back verbatim in its envelope, so\n there is no \"server resolved a different model than requested\" case on\n this protoc","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"The stream-identity defect (cell 3)","lvl3":""}},
|
|
8939
|
+
{"objectID":"d47c3d7913d5adb6968b60f5025f7c15472e8bbf76abe5b0b4277001a96c9691","title":"Additional src defects found and fixed by cells 4 and 8","url":"/docs/provider-integration/acceptance-gate#additional-src-defects-found-and-fixed-by-cells-4-and-8","content":"Building this gate surfaced three more real defects, all in provider-side\ntool-capability allowlists and one test-sizing constant — none in the mock\nitself. Each is a one-line, strictly capability-adding fix.\nollama cell 4 (tool-nonce proof) — OllamaProvider.supportsTools()\n checks the model name against modelConfiguration.ts's\n createOllamaConfig().modelBehavior.toolCapableModels allowlist. That list\n (OLLAMA_TOOL_CAPABLE_MODELS) included \"llama3.1\" but not \"llama3.2\" —\n the gate's own pinned default Ollama model\n (providerMatrix.ts's ollama.defaultModel, which declares tools:\ntrue). supportsTools() returned false for the provider's own default\n model, which silently dropped the user-supplied tool from both the\n wire tools field and the execution lookup (executeNativeGenerate()'s\n toolsRecord is built empty when supportsTools() is false), producing a\n \"Tool not found\" error instead of a real tool round-trip. Fixed in\n src/lib/core/modelConfiguration.ts by adding \"llama3.2\" to the default\n toolCapableModels array.\nopenrouter cell 4 (tool-nonce proof) — OpenRouterProvider.supportsTools()\n has two paths: a dynamically cached capability set (populated by\n cacheModelCapabilities(), which fetches /models and reads\n supported_parameters) and a hardcoded fallback pattern list used until\n that cache is populated. cacheModelCapabilities() is never invoked\n automatically anywhere in the real generate()/stream() code paths, so\n the fallback list is the only path ever exercised in practice. That list\n included \"meta-llama/llama-3.2\" and \"meta-llama/llama-3.3\" but not\n \"meta-llama/llama-3.1\" — the family of the gate's pinned default\n OpenRouter model, \"meta-llama/llama-3.1-8b-instruct\" (chosen, per an\n existing code comment, specifically because it supports every capability\n the matrix exercises). Same failure mode as the ollama defect above.\n Fixed in src/lib/providers/openRouter/client.ts by adding\n \"meta-llama/llama-3.1\" to knownToolCapablePatterns.\nli","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"Additional src defects found and fixed by cells 4 and 8","lvl3":""}},
|
|
8940
|
+
{"objectID":"0618b382f27d4d7ae674f8c803a793a4b1774ab7eecc3e1eb4738979361454ff","title":"Mock server","url":"/docs/provider-integration/acceptance-gate#mock-server","content":"test/helpers/acceptanceGateServer.ts is a real node:http server (not\nfetch interception) that speaks the two wire protocols the covered providers\nactually use:\nOpenAI-compatible chat completions — /chat/completions (including\n SSE streaming, tool calls, response_format, reasoning deltas), /models,\n /embeddings.\nAnthropic Messages API — /messages, for the one ANTHROPIC_BASE_URL\n row.\n\nEvery provider is redirected at it through its own documented base-URL\nenvironment override (<ID>_BASE_URL for catalog providers, each\nhand-written provider's own env var — see \"Coverage\" below) with a fake API\nkey (gate-fake-key-not-a-real-credential). The server enforces a\nrequest-count ceiling per instance and returns HTTP 400 past it (cell 8).\n\nEvery gate marker, exact value, and mock behavior the suite asserts against\nis exported from that file (GATE_MARKERS, GATE_EXACT_VALUE,\nGATE_STREAM_VALUE, GATE_STRUCTURED_VALUE, GATE_TOOL_NAME,\nGATE_TOOL_CONFIRM_PREFIX, GATE_THINKING_FULL, GATE_THINKING_ANSWER,\nGATE_EMBEDDING_VECTOR, GATE_SERVER_MODEL_SUFFIX, ...) so the suite and\nthe mock can never silently drift apart.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"Mock server","lvl3":""}},
|
|
8941
|
+
{"objectID":"22d04d6ef0ebc06dcdcf19ccf6ce15cb208747c36ecdf33dafb495ac9c1029c1","title":"Coverage","url":"/docs/provider-integration/acceptance-gate#coverage","content":"Provider rows are derived the same way test/helpers/providerMatrix.ts\nalready does: hand-written rows are listed explicitly, catalog rows are\nderived from the built catalog (dist/providers/catalog/index.generated.js\nand dist/providers/catalog/loader.js), matching every other suite that\nconsumes providerMatrix.ts.\n\n31 providers covered — 9 hand-written OpenAI-compatible providers\n(openai, openai-compatible, openrouter, ollama, litellm,\nnvidia-nim, lm-studio, llamacpp, cohere), azure (OpenAI-compatible\nprotocol via its own deployment URL), anthropic (Anthropic Messages\nprotocol), and 20 catalog providers whose base URL is a plain env override\n(every catalog provider except cloudflare, which needs a\ncomputedBaseURL/account-id template the mock can't satisfy — see below).\n\n13 providers not covered, each for a structural reason (no silent caps —\nthe suite itself throws at import time if a PROVIDERS entry is in neither\nlist):\n\n| Provider | Reason |\n| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| vertex | Signed Google Cloud client (service account / ADC), not a plain bearer-token HTTP call; no base-URL override, credentials can't be faked with a static string. |\n| google-ai | Speaks Google's Gemini wire protocol — a third protocol family this gate's mock does not implement (only OpenAI-compatible and Anthropic Messages are). |\n| bedrock | AWS SigV4-signed requests via the AWS SDK — same class of problem as vertex. |\n| sagemaker | AWS SigV4-signed requests via the AWS SDK — same class o","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"Coverage","lvl3":""}},
|
|
8942
|
+
{"objectID":"258a3149c7933854fa0749299620adb948d24476ea2c37630b2ed184ecea268d","title":"Running it","url":"/docs/provider-integration/acceptance-gate#running-it","content":"No environment variables or credentials are required, and none are used:\nreal ones are stripped at startup (see \"Known, documented gap in cell 9\"),\nand every credential the suite sets is a fake string, scoped per-row via\nwithEnv() and restored after each row.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Credential-free provider acceptance gate","lvl2":"Running it","lvl3":""}},
|
|
8933
8943
|
{"objectID":"2af9c7a79be94186d3044e77fe1680370e61eb985e81142a246e323fe990ef0b","title":"ADR-0001: `ProviderDescriptor` is the single source of truth for provider identity","url":"/docs/provider-integration/adr/0001-provider-descriptor-as-source-of-truth","content":"ADR-0001: ProviderDescriptor is the single source of truth for provider identity\n\nStatus: Accepted — 2026-08-15\nContext: Plans 00/11 (audit)\n\nContext\n\nBefore this redesign, \"what providers exist and what do they need\" was\nanswered by five independently hand-maintained places that had already\ndrifted from each other: providerRegistry.ts (registration + aliases),\ncommandFactory.ts (CLI --provider choices, re-listing every alias),\nproviderHealth.ts (hardcoded 8-of-31-provider health/auto-select list),\ncontextWindows.ts (per-model context windows), and\nproviderImageAdapter.ts (vision capability map). A sixth ad hoc identity\nmechanism existed for \"anthropic-subscription\". Google AI Studio alone\nhad five different spellings across these tables. None of this was\ntype-checked; typos were silent runtime misses (credentialKeyMap was\nalready missing a together-ai entry in production).\n\nAt 30 providers this was tolerable tech debt. At 200+ it is not: ~1,000+\nhand-authored string literals with zero compile-time linkage between them.\n\nDecision\n\nEvery provider gets exactly one ProviderDescriptor object\n(src/lib/types/providers.ts) held in one array,\nPROVIDER_DESCRIPTORS (src/lib/factories/providerDescriptors.ts), a\npure data module — no dynamic imports, no provider-class imports — so\nit is safe to import statically from anywhere, including the CLI's\ncommand-definition code (which needs --provider choices synchronously,\nbefore any provider is instantiated).\n\nEvery other subsystem that previously hardcoded its own provider list\n(CLI choices, health-check auto-selection, alias resolution) is expected\nto derive from PROVIDER_DESCRIPTORS / ProviderFactory.getAllDescriptors()\ninstead of maintaining a parallel list. ProviderFactory.normalizeProviderName\nreplaces its O(n) linear alias scan with an alias→canonical index built\nonce at registration time from the same descriptors.\n\nConsequences\nPositive: one array to review per new provider; CLI/health/alias\n tables can no longer drift because they no longer have independent\n data to drift from.\nPositive: ProviderFactory.getDescriptor(name) becomes the answer\n to \"does this provider exist and what does it need\" for every\n subsystem, including this plan's own CI gate (test:provider-structure,\n tools/verify-provider-onboarding.ts).\nNegative: PROVIDER_DESCRIPTORS becomes a single large file that\n every new provider touches — a predictable merge-conflict hotspot at\n high PR volume. Mitigated by keeping each entry a small, independent\n object literal (low conflict surface per line) and by the Tier 2 path\n needing only a ~10-line addition.\nNegative: subsystems that haven't yet been migrated to read from\n PROVIDER_DESCRIPTORS still need manual edits until they are. As of\n 2026-08-18, commandFactory.ts's main --provider choices and\n providerHealth.ts are already descriptor-driven, but the separate\n setup [provider] CLI subcommand still hand-hardcodes its own provider\n choices array — a concrete, currently-open gap, not a hypothetical one.\n The tier docs call this out explicitly rather than silently overclaiming\n that migration is complete.","hierarchy":{"lvl0":"Provider Integration","lvl1":"ADR-0001: `ProviderDescriptor` is the single source of truth for provider identity","lvl2":"","lvl3":""}},
|
|
8934
8944
|
{"objectID":"3be78e1b5992c80b18e70185f4e5aaf4a714dc6e71b302cc0f46b738a8988440","title":"ADR-0001: ProviderDescriptor is the single source of truth for provider identity","url":"/docs/provider-integration/adr/0001-provider-descriptor-as-source-of-truth#adr-0001-providerdescriptor-is-the-single-source-of-truth-for-provider-identity","content":"Status: Accepted — 2026-08-15\nContext: Plans 00/11 (audit)","hierarchy":{"lvl0":"Provider Integration","lvl1":"ADR-0001: `ProviderDescriptor` is the single source of truth for provider identity","lvl2":"ADR-0001: ProviderDescriptor is the single source of truth for provider identity","lvl3":""}},
|
|
8935
8945
|
{"objectID":"6e17dda13483d316edd838651701a44e9775d7a459f8f2d84247e38b469f7d9d","title":"Context","url":"/docs/provider-integration/adr/0001-provider-descriptor-as-source-of-truth#context","content":"Before this redesign, \"what providers exist and what do they need\" was\nanswered by five independently hand-maintained places that had already\ndrifted from each other: providerRegistry.ts (registration + aliases),\ncommandFactory.ts (CLI --provider choices, re-listing every alias),\nproviderHealth.ts (hardcoded 8-of-31-provider health/auto-select list),\ncontextWindows.ts (per-model context windows), and\nproviderImageAdapter.ts (vision capability map). A sixth ad hoc identity\nmechanism existed for \"anthropic-subscription\". Google AI Studio alone\nhad five different spellings across these tables. None of this was\ntype-checked; typos were silent runtime misses (credentialKeyMap was\nalready missing a together-ai entry in production).\n\nAt 30 providers this was tolerable tech debt. At 200+ it is not: ~1,000+\nhand-authored string literals with zero compile-time linkage between them.","hierarchy":{"lvl0":"Provider Integration","lvl1":"ADR-0001: `ProviderDescriptor` is the single source of truth for provider identity","lvl2":"Context","lvl3":""}},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.38.
|
|
3
|
+
"version": "12.38.1",
|
|
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": {
|
|
@@ -133,6 +133,7 @@
|
|
|
133
133
|
"test:stream-structured-output": "pnpm exec tsx test/continuous-test-suite-stream-structured-output.ts",
|
|
134
134
|
"test:browser-bundle": "pnpm exec tsx test/continuous-test-suite-browser-bundle.ts",
|
|
135
135
|
"test:providers-mocked": "pnpm exec tsx test/continuous-test-suite-providers-mocked.ts",
|
|
136
|
+
"test:acceptance-gate": "pnpm exec tsx test/continuous-test-suite-acceptance-gate.ts",
|
|
136
137
|
"scaffold:provider": "pnpm exec tsx tools/scaffold-provider.ts",
|
|
137
138
|
"verify:provider-onboarding": "pnpm exec tsx tools/verify-provider-onboarding.ts",
|
|
138
139
|
"test:openai-compat-catalog": "pnpm exec tsx test/continuous-test-suite-openai-compat-catalog.ts",
|