@juspay/neurolink 12.11.2 → 12.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/browser/neurolink.min.js +533 -581
  3. package/dist/constants/enums.d.ts +13 -0
  4. package/dist/constants/enums.js +14 -0
  5. package/dist/core/baseProvider.d.ts +71 -3
  6. package/dist/core/baseProvider.js +152 -44
  7. package/dist/core/modules/GenerationHandler.d.ts +22 -24
  8. package/dist/core/modules/GenerationHandler.js +28 -463
  9. package/dist/core/nativeGenerateLoop.d.ts +35 -0
  10. package/dist/core/nativeGenerateLoop.js +261 -0
  11. package/dist/files/fileTools.d.ts +5 -5
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +4 -0
  14. package/dist/mcp/toolRegistry.js +7 -0
  15. package/dist/middleware/builtin/guardrails.d.ts +0 -5
  16. package/dist/middleware/builtin/guardrails.js +33 -5
  17. package/dist/middleware/factory.js +1 -1
  18. package/dist/middleware/wrapLanguageModel.d.ts +18 -0
  19. package/dist/middleware/wrapLanguageModel.js +53 -0
  20. package/dist/neurolink.d.ts +7 -0
  21. package/dist/neurolink.js +61 -11
  22. package/dist/processors/media/AudioProcessor.js +46 -11
  23. package/dist/providers/amazonSagemaker.d.ts +17 -1
  24. package/dist/providers/amazonSagemaker.js +110 -0
  25. package/dist/providers/anthropic/client.d.ts +11 -0
  26. package/dist/providers/anthropic/client.js +148 -1
  27. package/dist/providers/catalog/index.generated.d.ts +1 -1
  28. package/dist/providers/catalog/index.generated.js +3 -0
  29. package/dist/providers/catalog/loader.js +1 -0
  30. package/dist/providers/catalog/mancer.json +192 -0
  31. package/dist/providers/configuredOpenAICompat.d.ts +11 -0
  32. package/dist/providers/configuredOpenAICompat.js +16 -0
  33. package/dist/providers/googleVertex/client.d.ts +0 -9
  34. package/dist/providers/googleVertex/client.js +0 -33
  35. package/dist/providers/openaiChatCompletionsBase.d.ts +21 -1
  36. package/dist/providers/openaiChatCompletionsBase.js +178 -0
  37. package/dist/providers/providerTypeUtils.d.ts +1 -2
  38. package/dist/providers/providerTypeUtils.js +5 -1
  39. package/dist/types/aiCompat.d.ts +485 -0
  40. package/dist/types/aiCompat.js +17 -0
  41. package/dist/types/conversation.d.ts +1 -1
  42. package/dist/types/generate.d.ts +52 -0
  43. package/dist/types/middleware.d.ts +3 -6
  44. package/dist/types/providerCatalog.generated.d.ts +2 -2
  45. package/dist/types/providers.d.ts +14 -1
  46. package/dist/types/tools.d.ts +25 -2
  47. package/dist/utils/errorHandling.d.ts +20 -3
  48. package/dist/utils/errorHandling.js +22 -5
  49. package/dist/utils/generationErrors.d.ts +78 -6
  50. package/dist/utils/generationErrors.js +114 -6
  51. package/dist/utils/mcpDefaults.d.ts +1 -1
  52. package/dist/utils/mcpDefaults.js +4 -1
  53. package/dist/utils/nativeSingleShot.d.ts +3 -0
  54. package/dist/utils/nativeSingleShot.js +83 -0
  55. package/dist/utils/tool.d.ts +30 -5
  56. package/dist/utils/tool.js +43 -5
  57. package/package.json +3 -6
  58. package/dist/utils/generation.d.ts +0 -8
  59. package/dist/utils/generation.js +0 -8
@@ -36,6 +36,17 @@ export declare class ConfiguredOpenAICompatProvider extends OpenAIChatCompletion
36
36
  protected getDefaultModel(): string;
37
37
  protected getFallbackModelName(): string;
38
38
  protected getFallbackModels(): string[];
39
+ /**
40
+ * The catalog's `capabilities.tools` is the vendor's own answer, probed on
41
+ * the wire when the entry was written; the model registry (the base
42
+ * default) knows nothing about Tier-2 models and answers "supported" for
43
+ * every unknown id. A vendor that declares tools: false must never receive
44
+ * a `tools` array — Mancer's free model rejects one with 400 — so the
45
+ * declaration wins here and the registry is only consulted otherwise.
46
+ * Like the other entry-reading overrides above, this runs only after
47
+ * construction: BaseProvider merely closes over it for GenerationHandler.
48
+ */
49
+ supportsTools(): boolean;
39
50
  protected adjustRequestBody(body: OpenAICompatChatRequest, modelId: string): OpenAICompatChatRequest;
40
51
  protected formatProviderError(error: unknown): Error;
41
52
  }
@@ -86,6 +86,22 @@ export class ConfiguredOpenAICompatProvider extends OpenAIChatCompletionsProvide
86
86
  getFallbackModels() {
87
87
  return this.entry.fallbackModels;
88
88
  }
89
+ /**
90
+ * The catalog's `capabilities.tools` is the vendor's own answer, probed on
91
+ * the wire when the entry was written; the model registry (the base
92
+ * default) knows nothing about Tier-2 models and answers "supported" for
93
+ * every unknown id. A vendor that declares tools: false must never receive
94
+ * a `tools` array — Mancer's free model rejects one with 400 — so the
95
+ * declaration wins here and the registry is only consulted otherwise.
96
+ * Like the other entry-reading overrides above, this runs only after
97
+ * construction: BaseProvider merely closes over it for GenerationHandler.
98
+ */
99
+ supportsTools() {
100
+ if (this.entry.supportsTools === false) {
101
+ return false;
102
+ }
103
+ return super.supportsTools();
104
+ }
89
105
  adjustRequestBody(body, modelId) {
90
106
  const adjusted = super.adjustRequestBody(body, modelId);
91
107
  if (this.entry.messageContentFormat !== "string") {
@@ -242,15 +242,6 @@ export declare class GoogleVertexProvider extends BaseProvider {
242
242
  * No more @ai-sdk/google-vertex dependency
243
243
  */
244
244
  generate(optionsOrPrompt: TextGenerationOptions | string): Promise<EnhancedGenerateResult | null>;
245
- /**
246
- * Invoke `options.onFinish` with the lifecycle payload shape consumers
247
- * (and `test:middleware`) expect. Pulled out so generate / image-gen /
248
- * Anthropic / Gemini code paths share one implementation. Errors thrown
249
- * by the user's callback are swallowed so they cannot poison the
250
- * primary generate path — same contract as the AI SDK middleware
251
- * wrapGenerate uses.
252
- */
253
- private fireGenerateOnFinish;
254
245
  /**
255
246
  * Invoke `options.onError` with the lifecycle payload shape consumers
256
247
  * (and `test:middleware`) expect. Mirrors {@link fireGenerateOnFinish}.
@@ -5342,39 +5342,6 @@ export class GoogleVertexProvider extends BaseProvider {
5342
5342
  }
5343
5343
  });
5344
5344
  }
5345
- /**
5346
- * Invoke `options.onFinish` with the lifecycle payload shape consumers
5347
- * (and `test:middleware`) expect. Pulled out so generate / image-gen /
5348
- * Anthropic / Gemini code paths share one implementation. Errors thrown
5349
- * by the user's callback are swallowed so they cannot poison the
5350
- * primary generate path — same contract as the AI SDK middleware
5351
- * wrapGenerate uses.
5352
- */
5353
- fireGenerateOnFinish(options, result, startTime) {
5354
- const onFinish = options
5355
- .onFinish;
5356
- if (typeof onFinish !== "function") {
5357
- return;
5358
- }
5359
- try {
5360
- const usage = result?.usage;
5361
- const callbackResult = onFinish({
5362
- text: result?.content || "",
5363
- usage: usage
5364
- ? {
5365
- promptTokens: usage.input ?? 0,
5366
- completionTokens: usage.output ?? 0,
5367
- }
5368
- : undefined,
5369
- duration: Date.now() - startTime,
5370
- finishReason: result?.finishReason ?? "stop",
5371
- });
5372
- Promise.resolve(callbackResult).catch((err) => logger.warn(`[GoogleVertex] onFinish callback rejected: ${err instanceof Error ? err.message : String(err)}`));
5373
- }
5374
- catch (err) {
5375
- logger.warn(`[GoogleVertex] onFinish callback threw: ${err instanceof Error ? err.message : String(err)}`);
5376
- }
5377
- }
5378
5345
  /**
5379
5346
  * Invoke `options.onError` with the lifecycle payload shape consumers
5380
5347
  * (and `test:middleware`) expect. Mirrors {@link fireGenerateOnFinish}.
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import type { AIProviderName } from "../constants/enums.js";
21
21
  import { BaseProvider } from "../core/baseProvider.js";
22
- import type { LanguageModel, OpenAICompatBuildBodyArgs, OpenAICompatChatMessage, OpenAICompatChatRequest, OpenAICompatChatTool, OpenAICompatResponseFormat, OpenAICompatStreamLifecycleListeners, Schema, StreamOptions, StreamResult, ZodUnknownSchema } from "../types/index.js";
22
+ import type { LanguageModel, OpenAICompatBuildBodyArgs, OpenAICompatChatMessage, OpenAICompatChatRequest, OpenAICompatChatTool, OpenAICompatResponseFormat, OpenAICompatStreamLifecycleListeners, Schema, EnhancedGenerateResult, TextGenerationOptions, ValidationSchema, StreamOptions, StreamResult, ZodUnknownSchema } from "../types/index.js";
23
23
  /**
24
24
  * Abstract HTTP+SSE provider for OpenAI chat-completions-shaped endpoints.
25
25
  */
@@ -227,6 +227,26 @@ export declare abstract class OpenAIChatCompletionsProvider extends BaseProvider
227
227
  * streamText, no AI SDK orchestrator. Tool calls, multi-step loops,
228
228
  * telemetry, abort handling all inline.
229
229
  */
230
+ /**
231
+ * Native non-streaming generate.
232
+ *
233
+ * Drives the SAME `doGenerate` the ai loop drove — `buildDelegatingModel`'s,
234
+ * reached through `getAISDKModel()` — and supplies only the multi-step tool
235
+ * iteration around it. That matters: `doGenerate` is where the JSON-versus-SSE
236
+ * wire choice lives (`useStreamingWireForGenerate()`, false by default), along
237
+ * with the 400 retry, the context-overflow refit and the invalid-model
238
+ * fallback. An earlier attempt ran generate through the STREAMING loop
239
+ * instead and silently began sending `stream: true` on a path that had always
240
+ * sent plain JSON; ten providers returned empty content against a
241
+ * non-streaming body and a stream-rejecting backend failed outright. Loop
242
+ * around doGenerate, never around streamOneStep.
243
+ *
244
+ * Tool turns are appended in the message-builder shape, which
245
+ * `messageBuilderToOpenAI` already round-trips: an assistant message carrying
246
+ * `tool-call` parts, then one `tool` message of `tool-result` parts.
247
+ */
248
+ generate(optionsOrPrompt: TextGenerationOptions | string, analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
249
+ private executeNativeGenerate;
230
250
  protected executeStream(options: StreamOptions, _analysisSchema?: ZodUnknownSchema | Schema<unknown>): Promise<StreamResult>;
231
251
  private runStreamLoop;
232
252
  private streamOneStep;
@@ -32,9 +32,14 @@ import { NoOutputGeneratedError } from "../utils/generationErrors.js";
32
32
  import { buildNoOutputSentinel, stampNoOutputSpan, } from "../utils/noOutputSentinel.js";
33
33
  import { composeAbortSignalsScoped, createTimeoutController, mergeAbortSignals, } from "../utils/timeout.js";
34
34
  import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
35
+ import { resolveRequestKind } from "../core/resolveRequestKind.js";
36
+ import { appendJsonSchemaInstruction, hasNativeDoGenerate, runNativeGenerateLoop, } from "../core/nativeGenerateLoop.js";
37
+ import { resolveToolExecutionRecords } from "../core/toolExecutionRecorder.js";
38
+ import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
35
39
  import { resolveToolChoice } from "../utils/toolChoice.js";
36
40
  import { transformToolExecutions } from "../utils/transformationUtils.js";
37
41
  import { withProviderRetry } from "../utils/providerRetry.js";
42
+ import { isSchemaComplexityError, isToolsSchemaConflictError, } from "../core/modules/structuredOutputPolicy.js";
38
43
  import { resolveDeferredTool } from "../tools/toolDiscovery.js";
39
44
  import { buildAPIError, buildBody, buildToolsForOpenAI, buildWireToolNameMaps, createDeferredAnalytics, ensureJsonWordInBody, estimateWireTokens, mapNeuroLinkToolChoice, mergeUsage, messageBuilderToOpenAI, parseSSEStream, stringifyToolOutput, stripTrailingSlash, v3ResponseFormatToOpenAI, v3ToolChoiceToOpenAI, v3ToolsToOpenAI, } from "./openaiChatCompletionsClient.js";
40
45
  import { createStreamChannel } from "../core/streamChannel.js";
@@ -707,6 +712,179 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
707
712
  * streamText, no AI SDK orchestrator. Tool calls, multi-step loops,
708
713
  * telemetry, abort handling all inline.
709
714
  */
715
+ /**
716
+ * Native non-streaming generate.
717
+ *
718
+ * Drives the SAME `doGenerate` the ai loop drove — `buildDelegatingModel`'s,
719
+ * reached through `getAISDKModel()` — and supplies only the multi-step tool
720
+ * iteration around it. That matters: `doGenerate` is where the JSON-versus-SSE
721
+ * wire choice lives (`useStreamingWireForGenerate()`, false by default), along
722
+ * with the 400 retry, the context-overflow refit and the invalid-model
723
+ * fallback. An earlier attempt ran generate through the STREAMING loop
724
+ * instead and silently began sending `stream: true` on a path that had always
725
+ * sent plain JSON; ten providers returned empty content against a
726
+ * non-streaming body and a stream-rejecting backend failed outright. Loop
727
+ * around doGenerate, never around streamOneStep.
728
+ *
729
+ * Tool turns are appended in the message-builder shape, which
730
+ * `messageBuilderToOpenAI` already round-trips: an assistant message carrying
731
+ * `tool-call` parts, then one `tool` message of `tool-result` parts.
732
+ */
733
+ async generate(optionsOrPrompt, analysisSchema) {
734
+ await this.ensureModelLimits();
735
+ const options = this.normalizeTextOptions(optionsOrPrompt);
736
+ if (resolveRequestKind(options, this.modelName) !== "text") {
737
+ return super.generate(options, analysisSchema);
738
+ }
739
+ this.validateOptions(options);
740
+ const mergedTools = await this.getToolsForStream(options);
741
+ // Reuse BaseProvider's invalid-model fallback. Overriding generate() skips
742
+ // it otherwise, and a retired default stops degrading to the next live
743
+ // model in the catalog entry.
744
+ const callerOwnsFallback = "disableInternalFallback" in options &&
745
+ options.disableInternalFallback === true;
746
+ // The native loop bypasses BaseProvider.executeGeneration, so the turn
747
+ // budget has to be composed here or it stops existing for this provider.
748
+ return this.runGenerateWithModelFallback(() => this.withTurnTimeout({ ...options, tools: mergedTools }, this.getDescriptorGenerateMs(), (timedOptions) => this.executeNativeGenerate(timedOptions)), callerOwnsFallback);
749
+ }
750
+ async executeNativeGenerate(options) {
751
+ const startTime = Date.now();
752
+ const modelId = await this.resolveModelName();
753
+ // Middleware must wrap the model here. The native loop bypasses
754
+ // BaseProvider.executeGeneration, and with it the only place middleware was
755
+ // ever applied — a probe showed a caller's wrapGenerate running zero times
756
+ // on every native provider while their onFinish still fired, because
757
+ // onFinish had been special-cased and nothing else had.
758
+ const model = await this.getAISDKModelWithMiddleware(options);
759
+ // Runtime guard rather than an assertion: `LanguageModel` is a union that
760
+ // includes a bare string id, and a double assertion through unknown is
761
+ // banned by Critical Rule 14.
762
+ if (!hasNativeDoGenerate(model)) {
763
+ throw this.handleProviderError(new Error(`${this.providerName}: model handle exposes no doGenerate()`));
764
+ }
765
+ const doGenerate = model.doGenerate.bind(model);
766
+ const shouldUseTools = !options.disableTools && this.supportsTools();
767
+ const toolsRecord = shouldUseTools
768
+ ? options.tools || (await this.getAllTools())
769
+ : {};
770
+ // v3 tool shape — the same one doGenerate already converts internally.
771
+ const v3Tools = shouldUseTools
772
+ ? Object.entries(toolsRecord).map(([name, t]) => {
773
+ const tool = t;
774
+ return {
775
+ type: "function",
776
+ name,
777
+ description: tool.description ?? "",
778
+ inputSchema: (tool.inputSchema
779
+ ? convertZodToJsonSchema(tool.inputSchema)
780
+ : { type: "object", properties: {} }),
781
+ };
782
+ })
783
+ : undefined;
784
+ const hasTools = !!v3Tools && v3Tools.length > 0;
785
+ // Structured output rides response_format, which is what Output.object did
786
+ // on the ai path. Suppressed where the provider says the combination with
787
+ // tools is rejected.
788
+ const responseFormat = options.schema && !(hasTools && this.suppressResponseFormatWithTools())
789
+ ? {
790
+ type: "json",
791
+ schema: convertZodToJsonSchema(options.schema),
792
+ }
793
+ : undefined;
794
+ const conversation = (await this.buildMessagesForStream(options));
795
+ const toolExecutionSummaries = [];
796
+ const runLoop = (conv, format) => runNativeGenerateLoop({
797
+ doGenerate,
798
+ conversation: conv,
799
+ ...(v3Tools ? { tools: v3Tools } : {}),
800
+ toolsRecord,
801
+ ...(hasTools && options.toolChoice
802
+ ? { toolChoice: resolveToolChoice(options, toolsRecord, true) }
803
+ : {}),
804
+ // The per-call `timeout` keeps its per-MODEL-CALL meaning once
805
+ // `turnTimeoutMs` owns the whole-turn deadline, and it reaches the
806
+ // model layer only through this channel. Without it each step fell
807
+ // back to the provider default, so a caller asking for a short
808
+ // per-call timeout got the default on every request.
809
+ ...(typeof options.timeout === "number"
810
+ ? {
811
+ providerOptions: {
812
+ neurolink: { timeoutMs: options.timeout },
813
+ },
814
+ }
815
+ : {}),
816
+ ...(format ? { responseFormat: format } : {}),
817
+ maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
818
+ ...(options.maxTokens ? { maxOutputTokens: options.maxTokens } : {}),
819
+ ...(options.temperature !== undefined
820
+ ? { temperature: options.temperature }
821
+ : {}),
822
+ ...(options.abortSignal ? { abortSignal: options.abortSignal } : {}),
823
+ ...(options.toolTimeoutMs !== undefined
824
+ ? { toolTimeoutMs: options.toolTimeoutMs }
825
+ : {}),
826
+ // withProviderRetry + handleProviderError are what the ai loop
827
+ // supplied around each call: without them a 429 surfaces as a raw
828
+ // upstream string instead of a RateLimitError, and a throttle is
829
+ // never retried.
830
+ runStep: (call) => withProviderRetry(call, trace.getActiveSpan() ?? undefined, `${this.providerName} generate`).catch((err) => {
831
+ throw this.handleProviderError(err);
832
+ }),
833
+ }, toolExecutionSummaries);
834
+ // Structured output rides `response_format` first. When a vendor rejects
835
+ // that outright — a tools/JSON-mode conflict, or a schema its constrained
836
+ // decoder will not accept — the recovery is to ask for the same object in
837
+ // words instead: drop `response_format` and spell the JSON Schema into the
838
+ // system prompt, letting coerceJsonToSchema recover the object from text.
839
+ //
840
+ // Ported from GenerationHandler's `promptJsonInstruction` fallback, which
841
+ // runs this on the ai-package path. That path is unreachable for every
842
+ // provider driven by this loop — GMI Cloud's MiniMax endpoint, the one it
843
+ // was written for, is a Tier-2 catalog provider on this very base class —
844
+ // so without this the recovery would simply not happen for it.
845
+ let loop;
846
+ try {
847
+ loop = await runLoop(conversation, responseFormat);
848
+ }
849
+ catch (error) {
850
+ const recoverable = responseFormat !== undefined &&
851
+ (isToolsSchemaConflictError(error) || isSchemaComplexityError(error));
852
+ if (!recoverable) {
853
+ throw error;
854
+ }
855
+ logger.warn(`[${this.providerName}] provider rejected response_format — retrying with the schema in the system prompt`, { provider: this.providerName, model: modelId });
856
+ loop = await runLoop(appendJsonSchemaInstruction(conversation, responseFormat.schema), undefined);
857
+ }
858
+ const { text, finishReason, toolsUsed } = loop;
859
+ const inputTokens = loop.inputTokens;
860
+ const outputTokens = loop.outputTokens;
861
+ const enhanced = {
862
+ content: text,
863
+ provider: this.providerName,
864
+ model: modelId,
865
+ finishReason,
866
+ usage: {
867
+ input: inputTokens,
868
+ output: outputTokens,
869
+ total: inputTokens + outputTokens,
870
+ // doGenerate reads prompt_tokens_details.cached_tokens, and the loop
871
+ // carries the counters out; discarding them here billed cached input
872
+ // at the full rate in calculateCost and made cache effectiveness
873
+ // invisible. Anthropic's native path already forwards both.
874
+ ...(loop.cacheReadTokens
875
+ ? { cacheReadTokens: loop.cacheReadTokens }
876
+ : {}),
877
+ ...(loop.cacheWriteTokens
878
+ ? { cacheCreationTokens: loop.cacheWriteTokens }
879
+ : {}),
880
+ },
881
+ responseTime: Date.now() - startTime,
882
+ toolsUsed,
883
+ toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
884
+ enhancedWithTools: toolsUsed.length > 0,
885
+ };
886
+ return this.finalizeNativeGenerate(enhanced, options, startTime);
887
+ }
710
888
  async executeStream(options, _analysisSchema) {
711
889
  this.validateStreamOptions(options);
712
890
  const startTime = Date.now();
@@ -1,6 +1,5 @@
1
1
  import type { StreamTextResult } from "../types/index.js";
2
2
  import type { LanguageModel } from "../types/index.js";
3
- import type { streamText } from "../utils/generation.js";
4
3
  /**
5
4
  * Extract the model identifier from a LanguageModel value.
6
5
  *
@@ -20,4 +19,4 @@ export declare function getModelId(model: LanguageModel, fallback?: string): str
20
19
  * `toolResults`, `toolCalls`) exists on the SDK result with compatible types.
21
20
  * This function performs the structural down-cast without `as any`.
22
21
  */
23
- export declare function toAnalyticsStreamResult(result: ReturnType<typeof streamText>): StreamTextResult;
22
+ export declare function toAnalyticsStreamResult(result: StreamTextResult | Record<string, unknown>): StreamTextResult;
@@ -32,7 +32,11 @@ export function getModelId(model, fallback = "unknown") {
32
32
  * `toolResults`, `toolCalls`) exists on the SDK result with compatible types.
33
33
  * This function performs the structural down-cast without `as any`.
34
34
  */
35
- export function toAnalyticsStreamResult(result) {
35
+ export function toAnalyticsStreamResult(
36
+ // Was `ReturnType<typeof streamText>`. Nothing calls streamText any more —
37
+ // every streaming path is native — so the parameter is the structural
38
+ // superset this narrows FROM, stated directly.
39
+ result) {
36
40
  // The AI SDK v6 result is a structural superset of our StreamTextResult.
37
41
  // Both use PromiseLike for async fields and compatible usage shapes
38
42
  // (extractTokenUsage handles both v4 and v6 field names).