@juspay/neurolink 13.6.3 → 13.6.4

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.
@@ -86,6 +86,19 @@ export declare const hasNativeDoGenerate: (value: unknown) => value is {
86
86
  };
87
87
  /** A tool's inputSchema with its lazy (function) form, if any, resolved. */
88
88
  export declare const resolveToolInputSchema: (schema: Tool["inputSchema"]) => Exclude<Tool["inputSchema"], (...args: never[]) => unknown>;
89
+ /**
90
+ * The provider-neutral declaration of one tool: the shape every provider's
91
+ * `doGenerate` already converts to its own wire format. The loop uses it for
92
+ * the tools discovery loads mid-turn, and the providers that run the loop use
93
+ * it for their initial declarations, so a tool is declared the same way
94
+ * whether it is there at the start or joins later.
95
+ */
96
+ export declare const toNativeToolDeclaration: (name: string, tool: Tool) => {
97
+ type: "function";
98
+ name: string;
99
+ description: string;
100
+ inputSchema: object;
101
+ };
89
102
  /**
90
103
  * Spell a JSON Schema into the conversation's system turn.
91
104
  *
@@ -16,6 +16,7 @@
16
16
  import { observeGeneration } from "../telemetry/observation.js";
17
17
  import { StepSpans, withGeneration } from "../telemetry/withSpan.js";
18
18
  import { logger } from "../utils/logger.js";
19
+ import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
19
20
  import { guardToolExecutor } from "./toolExecutionGuards.js";
20
21
  import { resolveLiveTool } from "../tools/toolDiscovery.js";
21
22
  const DEFAULT_TOOL_CHOICE_STEPS = 1;
@@ -339,6 +340,35 @@ const parseToolInput = (raw) => {
339
340
  };
340
341
  /** A tool's inputSchema with its lazy (function) form, if any, resolved. */
341
342
  export const resolveToolInputSchema = (schema) => typeof schema === "function" ? resolveToolInputSchema(schema()) : schema;
343
+ /**
344
+ * The provider-neutral declaration of one tool: the shape every provider's
345
+ * `doGenerate` already converts to its own wire format. The loop uses it for
346
+ * the tools discovery loads mid-turn, and the providers that run the loop use
347
+ * it for their initial declarations, so a tool is declared the same way
348
+ * whether it is there at the start or joins later.
349
+ */
350
+ export const toNativeToolDeclaration = (name, tool) => ({
351
+ type: "function",
352
+ name,
353
+ description: tool.description ?? "",
354
+ inputSchema: declaredInputSchema(name, tool),
355
+ });
356
+ // A lazy schema that throws must not take the whole request down: SageMaker and
357
+ // Anthropic declared any function-valued schema as the empty object schema
358
+ // before the lazy form was resolved here, so a broken one falls back to that.
359
+ function declaredInputSchema(name, tool) {
360
+ const empty = { type: "object", properties: {} };
361
+ if (!tool.inputSchema) {
362
+ return empty;
363
+ }
364
+ try {
365
+ return convertZodToJsonSchema(resolveToolInputSchema(tool.inputSchema));
366
+ }
367
+ catch (error) {
368
+ logger.warn(`[nativeGenerateLoop] input schema of tool "${name}" could not be resolved; declaring it with an empty object schema`, { error: error instanceof Error ? error.message : String(error) });
369
+ return empty;
370
+ }
371
+ }
342
372
  /**
343
373
  * Validate parsed input against the tool's own schema when it exposes one.
344
374
  *
@@ -486,20 +516,23 @@ async function runGenerateSteps(args, toolExecutionSummaries, stepSpans) {
486
516
  // answer is that result — not a thrown turn.
487
517
  let preReask;
488
518
  const hasTools = Boolean(args.tools && args.tools.length > 0);
489
- // The declarations actually sent. Starts as the caller's list and grows when
490
- // a tool joins toolsRecord mid-turn (discovery loads tools into it).
491
- const liveTools = args.tools ? [...args.tools] : undefined;
519
+ // The declarations actually sent. It is the caller's own array and grows in
520
+ // place when a tool joins toolsRecord mid-turn (discovery loads tools into
521
+ // it), so anything the caller sized from it, such as the context guard's
522
+ // fixed-overhead estimate, counts the new tools too.
523
+ const liveTools = args.tools;
524
+ const declare = args.declareTool ?? toNativeToolDeclaration;
492
525
  const declaredNames = declaredToolRecord(args.tools);
493
526
  // Completed-step records for `prepareStep`; one per step that returned.
494
527
  const stepRecords = [];
495
528
  for (let step = 0; step < args.maxSteps; step++) {
496
529
  steps = step + 1;
497
530
  stepSpans.begin(steps);
498
- if (liveTools && args.declareTool) {
531
+ if (liveTools) {
499
532
  const loaded = [];
500
533
  for (const name of Object.keys(args.toolsRecord)) {
501
534
  if (!declaredNames[name]) {
502
- liveTools.push(args.declareTool(name, args.toolsRecord[name]));
535
+ liveTools.push(declare(name, args.toolsRecord[name]));
503
536
  declaredNames[name] = true;
504
537
  loaded.push(name);
505
538
  }
@@ -9,7 +9,7 @@ import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
9
9
  import { withProviderRetry } from "../utils/providerRetry.js";
10
10
  import { resolveToolChoice } from "../utils/toolChoice.js";
11
11
  import { DEFAULT_MAX_STEPS } from "../core/constants.js";
12
- import { hasNativeDoGenerate, runNativeGenerateLoop, } from "../core/nativeGenerateLoop.js";
12
+ import { hasNativeDoGenerate, runNativeGenerateLoop, toNativeToolDeclaration, } from "../core/nativeGenerateLoop.js";
13
13
  import { generationName } from "../telemetry/observationNames.js";
14
14
  import { observeGeneration } from "../telemetry/observation.js";
15
15
  import { tracers } from "../telemetry/tracers.js";
@@ -208,17 +208,7 @@ export class AmazonSageMakerProvider extends BaseProvider {
208
208
  const toolsRecord = shouldUseTools
209
209
  ? options.tools || {}
210
210
  : {};
211
- const v3Tools = Object.entries(toolsRecord).map(([name, t]) => {
212
- const tool = t;
213
- return {
214
- type: "function",
215
- name,
216
- description: tool.description ?? "",
217
- inputSchema: (tool.inputSchema
218
- ? convertZodToJsonSchema(tool.inputSchema)
219
- : { type: "object", properties: {} }),
220
- };
221
- });
211
+ const v3Tools = Object.entries(toolsRecord).map(([name, t]) => toNativeToolDeclaration(name, t));
222
212
  // Structured output was dropped entirely on this path: the schema never
223
213
  // reached the request, and nothing downstream re-imposed it, so a caller
224
214
  // asking for an object got whatever JSON coerceJsonToSchema could scrape
@@ -25,7 +25,7 @@ import { stringifyAnthropicToolOutput } from "./toolOutput.js";
25
25
  import { createAnthropicLoopAdapter } from "./loopAdapter.js";
26
26
  import { DEFAULT_BEFORE_STEP_TIMEOUT_MS } from "../../utils/parameterValidation.js";
27
27
  import { runAgenticLoop } from "../../core/loopEngine.js";
28
- import { hasNativeDoGenerate, resolveStepToolChoice, runNativeGenerateLoop, toPrepareStepRecord, } from "../../core/nativeGenerateLoop.js";
28
+ import { hasNativeDoGenerate, resolveStepToolChoice, runNativeGenerateLoop, toNativeToolDeclaration, toPrepareStepRecord, } from "../../core/nativeGenerateLoop.js";
29
29
  import { withProviderRetry } from "../../utils/providerRetry.js";
30
30
  import { resolveRequestKind } from "../../core/resolveRequestKind.js";
31
31
  import { resolveToolExecutionRecords, toolCallsFromSummaries, } from "../../core/toolExecutionRecorder.js";
@@ -1528,17 +1528,7 @@ export class AnthropicProvider extends BaseProvider {
1528
1528
  const toolsRecord = shouldUseTools
1529
1529
  ? options.tools || {}
1530
1530
  : {};
1531
- const v3Tools = Object.entries(toolsRecord).map(([name, t]) => {
1532
- const tool = t;
1533
- return {
1534
- type: "function",
1535
- name,
1536
- description: tool.description ?? "",
1537
- inputSchema: (tool.inputSchema
1538
- ? convertZodToJsonSchema(tool.inputSchema)
1539
- : { type: "object", properties: {} }),
1540
- };
1541
- });
1531
+ const v3Tools = Object.entries(toolsRecord).map(([name, t]) => toNativeToolDeclaration(name, t));
1542
1532
  const hasTools = v3Tools.length > 0;
1543
1533
  // Two structured-output routes, and doGenerate implements both. With no
1544
1534
  // tools it replaces the tool list with one forced json tool; with tools it
@@ -36,7 +36,7 @@ import { NoOutputGeneratedError } from "../utils/generationErrors.js";
36
36
  import { buildNoOutputSentinel, stampNoOutputSpan, } from "../utils/noOutputSentinel.js";
37
37
  import { composeAbortSignalsScoped, createTimeoutController, mergeAbortSignals, TimeoutError, } from "../utils/timeout.js";
38
38
  import { resolveRequestKind } from "../core/resolveRequestKind.js";
39
- import { appendJsonSchemaInstruction, hasNativeDoGenerate, resolveToolInputSchema, resolveStepToolChoice, runNativeGenerateLoop, toPrepareStepRecord, } from "../core/nativeGenerateLoop.js";
39
+ import { appendJsonSchemaInstruction, hasNativeDoGenerate, resolveStepToolChoice, runNativeGenerateLoop, toNativeToolDeclaration, toPrepareStepRecord, } from "../core/nativeGenerateLoop.js";
40
40
  import { resolveToolExecutionRecords, toolCallsFromSummaries, } from "../core/toolExecutionRecorder.js";
41
41
  import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
42
42
  import { coerceJsonToSchema, recoverScalarRoot, schemaAccepts, } from "../utils/json/coerce.js";
@@ -925,19 +925,8 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
925
925
  ? options.tools || (await this.getAllTools())
926
926
  : {};
927
927
  // v3 tool shape — the same one doGenerate already converts internally.
928
- // Also handed to the loop, which declares tools discovery loads mid-turn.
929
- const declareTool = (name, tool) => {
930
- return {
931
- type: "function",
932
- name,
933
- description: tool.description ?? "",
934
- inputSchema: tool.inputSchema
935
- ? convertZodToJsonSchema(resolveToolInputSchema(tool.inputSchema))
936
- : { type: "object", properties: {} },
937
- };
938
- };
939
928
  const v3Tools = shouldUseTools
940
- ? Object.entries(toolsRecord).map(([name, t]) => declareTool(name, t))
929
+ ? Object.entries(toolsRecord).map(([name, t]) => toNativeToolDeclaration(name, t))
941
930
  : undefined;
942
931
  const hasTools = !!v3Tools && v3Tools.length > 0;
943
932
  // Structured output rides response_format, which is what Output.object did
@@ -965,14 +954,9 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
965
954
  // model has already given. Dropping them is what makes `response_format`
966
955
  // legal again on a vendor that refuses the two together.
967
956
  withTools = true) => {
968
- // What this pass sends: the initial declarations plus any the loop adds
969
- // for tools discovery loads mid-turn, so the guard counts them too.
957
+ // What this pass sends: the initial declarations, which the loop appends
958
+ // to for tools discovery loads mid-turn, so the guard counts them too.
970
959
  const passTools = withTools && v3Tools ? [...v3Tools] : undefined;
971
- const declarePassTool = (name, tool) => {
972
- const declaration = declareTool(name, tool);
973
- passTools?.push(declaration);
974
- return declaration;
975
- };
976
960
  return runNativeGenerateLoop({
977
961
  doGenerate,
978
962
  provider: this.providerName,
@@ -997,9 +981,7 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
997
981
  }), this.providerName),
998
982
  }),
999
983
  conversation: conv,
1000
- ...(passTools
1001
- ? { tools: [...passTools], declareTool: declarePassTool }
1002
- : {}),
984
+ ...(passTools ? { tools: passTools } : {}),
1003
985
  toolsRecord,
1004
986
  ...(withTools && hasTools && options.toolChoice
1005
987
  ? { toolChoice: resolveToolChoice(options, toolsRecord, true) }
@@ -1846,11 +1846,12 @@ export type NativeGenerateLoopArgs = {
1846
1846
  */
1847
1847
  toolsRecord: Record<string, Tool>;
1848
1848
  /**
1849
- * Builds the v3 declaration for one tool. When set, every step declares any
1850
- * tool that joined `toolsRecord` mid-turn (discovery's search_tools loads
1851
- * tools into it), so the model can call what it just loaded. Only the
1852
- * OpenAI-compatible `generate()` path supplies it; the other providers that
1853
- * run this loop do not declare a tool `search_tools` loaded mid-turn.
1849
+ * Builds the declaration for a tool that joined `toolsRecord` mid-turn
1850
+ * (discovery's search_tools loads tools into it). Optional: by default the
1851
+ * loop declares such a tool in the provider-neutral shape every provider's
1852
+ * `doGenerate` converts (`toNativeToolDeclaration`), for every provider that
1853
+ * runs this loop. Set it only to declare in another shape. The loop appends
1854
+ * the declarations to `tools` in place.
1854
1855
  */
1855
1856
  declareTool?: (name: string, tool: Tool) => Record<string, unknown>;
1856
1857
  /** The turn's resolved tool choice, applied per step via `resolveStepToolChoice`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "13.6.3",
3
+ "version": "13.6.4",
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": {