@juspay/neurolink 11.27.0 → 11.29.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,6 +85,7 @@ export * from "./video.js";
85
85
  export * from "./avatar.js";
86
86
  export * from "./music.js";
87
87
  export * from "./replicate.js";
88
+ export * from "./mediaCatalog.js";
88
89
  // Safe-fetch helper types (SSRF-hardened download)
89
90
  export * from "./safeFetch.js";
90
91
  // ModelPool — multi-provider failover with per-member cooldown (M9.x+)
@@ -96,3 +97,5 @@ export * from "./classifierRouter.js";
96
97
  // Multi-Agent orchestration types
97
98
  export * from "./agentNetwork.js";
98
99
  export * from "./localUsage.js";
100
+ // resolveRequestKind() dispatch-decision types
101
+ export * from "./dispatch.js";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Types backing the static media-handler catalog
3
+ * (src/lib/factories/mediaHandlerCatalog.ts) — the single source of truth
4
+ * for provider names/aliases across the six media-generation ecosystems
5
+ * (TTS, STT, Realtime, Video, Avatar, Music).
6
+ */
7
+ export type MediaHandlerKind = "tts" | "stt" | "realtime" | "video" | "avatar" | "music";
8
+ export type MediaHandlerDescriptor = {
9
+ readonly kind: MediaHandlerKind;
10
+ readonly name: string;
11
+ readonly aliases?: readonly string[];
12
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Types backing the static media-handler catalog
3
+ * (src/lib/factories/mediaHandlerCatalog.ts) — the single source of truth
4
+ * for provider names/aliases across the six media-generation ecosystems
5
+ * (TTS, STT, Realtime, Video, Avatar, Music).
6
+ */
7
+ export {};
@@ -13,6 +13,19 @@
13
13
  */
14
14
  import type { VideoGenerationResult, VideoOutputOptions } from "./multimodal.js";
15
15
  export type { VideoGenerationResult, VideoOutputOptions, } from "./multimodal.js";
16
+ /**
17
+ * Bag-form input to `VideoProcessor.generate()` — the primary data (image,
18
+ * prompt, region) alongside the video-specific output options, collapsed
19
+ * into a single object matching Music/Avatar's existing `generate(provider,
20
+ * options)` shape. `VideoHandler.generate()`'s own 4-positional-argument
21
+ * signature is unchanged; `VideoProcessor.generate()` translates between the
22
+ * two internally.
23
+ */
24
+ export type VideoGenerateOptions = VideoOutputOptions & {
25
+ image: Buffer;
26
+ prompt: string;
27
+ region?: string;
28
+ };
16
29
  /**
17
30
  * Director-mode transition options.
18
31
  *
@@ -11,7 +11,7 @@
11
11
  * @module utils/videoProcessor
12
12
  */
13
13
  import { VIDEO_ERROR_CODES } from "../constants/videoErrors.js";
14
- import type { VideoGenerationResult, VideoHandler, VideoOutputOptions, VideoTransitionOptions } from "../types/index.js";
14
+ import type { VideoGenerateOptions, VideoGenerationResult, VideoHandler, VideoOutputOptions, VideoTransitionOptions } from "../types/index.js";
15
15
  import { VideoError } from "../adapters/video/vertexVideoHandler.js";
16
16
  export { VideoError, VIDEO_ERROR_CODES };
17
17
  /**
@@ -45,13 +45,19 @@ export declare class VideoProcessor {
45
45
  * Generate a single video clip via the registered handler.
46
46
  *
47
47
  * @param provider - Registered provider name (e.g. "vertex", "kling")
48
- * @param image - Source image buffer
49
- * @param prompt - Text prompt describing the desired motion / content
50
- * @param options - Resolution / length / aspect-ratio / audio options
51
- * @param region - Optional region override (Vertex location, etc.)
48
+ * @param options - Bag of the source image, prompt, optional region
49
+ * override, and resolution / length / aspect-ratio / audio options.
50
+ * Translated internally into the handler-level 4-positional-argument
51
+ * call `VideoHandler.generate()`'s own signature is unchanged.
52
52
  * @throws VideoError on registry miss, handler-not-configured, or
53
53
  * generation failure
54
54
  */
55
+ static generate(provider: string, options: VideoGenerateOptions): Promise<VideoGenerationResult>;
56
+ /**
57
+ * @deprecated Positional form kept for backward compatibility with
58
+ * pre-bag callers (VideoProcessor is a public export). Use the
59
+ * options-bag overload.
60
+ */
55
61
  static generate(provider: string, image: Buffer, prompt: string, options: VideoOutputOptions, region?: string): Promise<VideoGenerationResult>;
56
62
  /**
57
63
  * Generate a transition clip via the registered handler (Director Mode).
@@ -14,6 +14,11 @@ import { ErrorCategory, ErrorSeverity } from "../constants/enums.js";
14
14
  import { VIDEO_ERROR_CODES } from "../constants/videoErrors.js";
15
15
  import { SpanSerializer, SpanStatus, SpanType, getMetricsAggregator, } from "../observability/index.js";
16
16
  import { logger } from "./logger.js";
17
+ import { withTimeout } from "./async/withTimeout.js";
18
+ // Video generation is legitimately minutes-long (Kling/Runway render queues),
19
+ // so the bound is generous — its job is to convert a wedged handler into an
20
+ // error rather than an eternal hang, not to police normal latency.
21
+ const VIDEO_GENERATION_TIMEOUT_MS = 600_000;
17
22
  // VideoError is canonical in vertexVideoHandler.ts (existing). Re-export
18
23
  // here so consumers of `VideoProcessor` can import the typed error from
19
24
  // the same module. Both throws and instanceof checks resolve to the same
@@ -69,19 +74,17 @@ export class VideoProcessor {
69
74
  "video.audio": options.audio,
70
75
  };
71
76
  }
72
- /**
73
- * Generate a single video clip via the registered handler.
74
- *
75
- * @param provider - Registered provider name (e.g. "vertex", "kling")
76
- * @param image - Source image buffer
77
- * @param prompt - Text prompt describing the desired motion / content
78
- * @param options - Resolution / length / aspect-ratio / audio options
79
- * @param region - Optional region override (Vertex location, etc.)
80
- * @throws VideoError on registry miss, handler-not-configured, or
81
- * generation failure
82
- */
83
- static async generate(provider, image, prompt, options, region) {
84
- const span = SpanSerializer.createSpan(SpanType.MEDIA_GENERATION, "video.generate", this.buildSpanAttributes(provider, options));
77
+ static async generate(provider, optionsOrImage, legacyPrompt, legacyOptions, legacyRegion) {
78
+ const bag = Buffer.isBuffer(optionsOrImage)
79
+ ? {
80
+ image: optionsOrImage,
81
+ prompt: legacyPrompt ?? "",
82
+ ...(legacyRegion !== undefined ? { region: legacyRegion } : {}),
83
+ ...(legacyOptions ?? {}),
84
+ }
85
+ : optionsOrImage;
86
+ const { image, prompt, region, ...videoOptions } = bag;
87
+ const span = SpanSerializer.createSpan(SpanType.MEDIA_GENERATION, "video.generate", this.buildSpanAttributes(provider, videoOptions));
85
88
  try {
86
89
  const handler = this.getHandler(provider);
87
90
  if (!handler) {
@@ -105,7 +108,10 @@ export class VideoProcessor {
105
108
  });
106
109
  }
107
110
  logger.debug(`[VideoProcessor] Starting video generation with provider: ${provider}`);
108
- const result = await handler.generate(image, prompt, options, region);
111
+ // Bounded per repo guideline (async provider calls wrap withTimeout):
112
+ // video generation is legitimately slow, so the deadline is generous —
113
+ // but a wedged handler must error, never hang the caller forever.
114
+ const result = await withTimeout(handler.generate(image, prompt, videoOptions, region), VIDEO_GENERATION_TIMEOUT_MS, `Video generation via "${provider}" timed out after ${VIDEO_GENERATION_TIMEOUT_MS}ms`);
109
115
  const ended = SpanSerializer.endSpan(span, SpanStatus.OK);
110
116
  getMetricsAggregator().recordSpan(ended);
111
117
  logger.info(`[VideoProcessor] Generated ${result.data.length} bytes (${provider})`);
@@ -124,7 +130,7 @@ export class VideoProcessor {
124
130
  category: ErrorCategory.EXECUTION,
125
131
  severity: ErrorSeverity.HIGH,
126
132
  retriable: true,
127
- context: { provider, options, region },
133
+ context: { provider, options: videoOptions, region },
128
134
  originalError: err instanceof Error ? err : undefined,
129
135
  });
130
136
  }
@@ -169,7 +175,8 @@ export class VideoProcessor {
169
175
  });
170
176
  }
171
177
  try {
172
- return await handler.generateTransition(firstFrame, lastFrame, prompt, options, region);
178
+ // Same bound as generate(): a wedged transition must error, not hang.
179
+ return await withTimeout(handler.generateTransition(firstFrame, lastFrame, prompt, options, region), VIDEO_GENERATION_TIMEOUT_MS, `Video transition via "${provider}" timed out after ${VIDEO_GENERATION_TIMEOUT_MS}ms`);
173
180
  }
174
181
  catch (err) {
175
182
  if (err instanceof VideoError) {
@@ -8,9 +8,13 @@
8
8
  * Use STTProcessor (src/lib/utils/sttProcessor.ts) for STT.
9
9
  * Use RealtimeProcessor for realtime voice sessions.
10
10
  *
11
- * Importing this module also auto-registers every shipped TTS / STT /
12
- * Realtime handler whose backing API key is present in `process.env`.
13
- * Registration is idempotent and silently skipped on failure.
11
+ * Importing this module does NOT register any handlers as a side effect.
12
+ * Call `registerDefaultTTSHandlers()` / `registerDefaultSTTHandlers()` /
13
+ * `registerDefaultRealtimeHandlers()` explicitly (or go through
14
+ * `ProviderRegistry.registerAllProviders()`, which every documented
15
+ * `NeuroLink` entry point already calls) to register every shipped handler
16
+ * whose backing API key is present in `process.env`. Registration is
17
+ * idempotent and silently skipped on failure.
14
18
  *
15
19
  * @module voice
16
20
  */
@@ -8,12 +8,17 @@
8
8
  * Use STTProcessor (src/lib/utils/sttProcessor.ts) for STT.
9
9
  * Use RealtimeProcessor for realtime voice sessions.
10
10
  *
11
- * Importing this module also auto-registers every shipped TTS / STT /
12
- * Realtime handler whose backing API key is present in `process.env`.
13
- * Registration is idempotent and silently skipped on failure.
11
+ * Importing this module does NOT register any handlers as a side effect.
12
+ * Call `registerDefaultTTSHandlers()` / `registerDefaultSTTHandlers()` /
13
+ * `registerDefaultRealtimeHandlers()` explicitly (or go through
14
+ * `ProviderRegistry.registerAllProviders()`, which every documented
15
+ * `NeuroLink` entry point already calls) to register every shipped handler
16
+ * whose backing API key is present in `process.env`. Registration is
17
+ * idempotent and silently skipped on failure.
14
18
  *
15
19
  * @module voice
16
20
  */
21
+ import { MEDIA_HANDLER_CATALOG } from "../factories/mediaHandlerCatalog.js";
17
22
  import { logger } from "../utils/logger.js";
18
23
  import { STTProcessor } from "../utils/sttProcessor.js";
19
24
  import { TTSProcessor } from "../utils/ttsProcessor.js";
@@ -76,37 +81,48 @@ import { GoogleSTT } from "./providers/GoogleSTT.js";
76
81
  import { OpenAISTT } from "./providers/OpenAISTT.js";
77
82
  import { GeminiLive } from "./providers/GeminiLive.js";
78
83
  import { OpenAIRealtime } from "./providers/OpenAIRealtime.js";
79
- const TTS_HANDLER_CANDIDATES = [
80
- {
81
- // Google TTS doubles as both the AI Studio and Vertex TTS handler.
82
- name: "google-ai",
83
- aliases: ["vertex"],
84
- factory: () => new GoogleTTSHandler(),
85
- },
86
- { name: "openai-tts", factory: () => new OpenAITTS() },
87
- {
88
- name: "elevenlabs",
89
- aliases: ["elevenlabs-tts"],
90
- factory: () => new ElevenLabsTTS(),
91
- },
92
- { name: "azure-tts", factory: () => new AzureTTS() },
93
- { name: "fish-audio", factory: () => new FishAudioTTS() },
94
- { name: "cartesia", factory: () => new CartesiaTTS() },
95
- ];
96
- const STT_HANDLER_CANDIDATES = [
97
- {
98
- name: "whisper",
99
- aliases: ["openai-stt"],
100
- factory: () => new OpenAISTT(),
101
- },
102
- { name: "deepgram", factory: () => new DeepgramSTT() },
103
- { name: "google-stt", factory: () => new GoogleSTT() },
104
- { name: "azure-stt", factory: () => new AzureSTT() },
105
- ];
106
- const REALTIME_HANDLER_CANDIDATES = [
107
- { name: "openai-realtime", factory: () => new OpenAIRealtime() },
108
- { name: "gemini-live", factory: () => new GeminiLive() },
109
- ];
84
+ // Provider names + aliases are the Task-8 catalog's job — only the factory
85
+ // (which needs the imported handler class) stays local to this module.
86
+ const TTS_HANDLER_FACTORIES = {
87
+ // Google TTS doubles as both the AI Studio and Vertex TTS handler.
88
+ "google-ai": () => new GoogleTTSHandler(),
89
+ "openai-tts": () => new OpenAITTS(),
90
+ elevenlabs: () => new ElevenLabsTTS(),
91
+ "azure-tts": () => new AzureTTS(),
92
+ "fish-audio": () => new FishAudioTTS(),
93
+ cartesia: () => new CartesiaTTS(),
94
+ };
95
+ const TTS_HANDLER_CANDIDATES = MEDIA_HANDLER_CATALOG.filter((entry) => entry.kind === "tts").map((entry) => {
96
+ const factory = TTS_HANDLER_FACTORIES[entry.name];
97
+ if (!factory) {
98
+ throw new Error(`[voice/tts] no handler factory for catalog entry "${entry.name}"`);
99
+ }
100
+ return { name: entry.name, aliases: entry.aliases, factory };
101
+ });
102
+ const STT_HANDLER_FACTORIES = {
103
+ whisper: () => new OpenAISTT(),
104
+ deepgram: () => new DeepgramSTT(),
105
+ "google-stt": () => new GoogleSTT(),
106
+ "azure-stt": () => new AzureSTT(),
107
+ };
108
+ const STT_HANDLER_CANDIDATES = MEDIA_HANDLER_CATALOG.filter((entry) => entry.kind === "stt").map((entry) => {
109
+ const factory = STT_HANDLER_FACTORIES[entry.name];
110
+ if (!factory) {
111
+ throw new Error(`[voice/stt] no handler factory for catalog entry "${entry.name}"`);
112
+ }
113
+ return { name: entry.name, aliases: entry.aliases, factory };
114
+ });
115
+ const REALTIME_HANDLER_FACTORIES = {
116
+ "openai-realtime": () => new OpenAIRealtime(),
117
+ "gemini-live": () => new GeminiLive(),
118
+ };
119
+ const REALTIME_HANDLER_CANDIDATES = MEDIA_HANDLER_CATALOG.filter((entry) => entry.kind === "realtime").map((entry) => {
120
+ const factory = REALTIME_HANDLER_FACTORIES[entry.name];
121
+ if (!factory) {
122
+ throw new Error(`[voice/realtime] no handler factory for catalog entry "${entry.name}"`);
123
+ }
124
+ return { name: entry.name, aliases: entry.aliases, factory };
125
+ });
110
126
  function registerCandidates(candidates, supports, getRegistered, register, scope, requireConfigured) {
111
127
  for (const { name, aliases, factory } of candidates) {
112
128
  // Compute missingName / missingAliases separately so a manually-
@@ -168,9 +184,3 @@ export function registerDefaultSTTHandlers() {
168
184
  export function registerDefaultRealtimeHandlers() {
169
185
  registerCandidates(REALTIME_HANDLER_CANDIDATES, (name) => RealtimeProcessor.supports(name), (name) => RealtimeProcessor.getHandler(name), (name, handler) => RealtimeProcessor.registerHandler(name, handler), "voice/realtime", false);
170
186
  }
171
- // Run once at module import so consumers who follow the documented
172
- // `nl.generate(...)` flow get every configured handler without manually
173
- // calling `registerHandler`.
174
- registerDefaultTTSHandlers();
175
- registerDefaultSTTHandlers();
176
- registerDefaultRealtimeHandlers();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "11.27.0",
3
+ "version": "11.29.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -73,6 +73,7 @@
73
73
  "test:context": "pnpm exec tsx test/continuous-test-suite-context.ts",
74
74
  "test:evaluation": "pnpm exec tsx test/continuous-test-suite-evaluation.ts",
75
75
  "test:handler-registry": "pnpm exec tsx test/continuous-test-suite-handler-registry.ts",
76
+ "test:resolve-request-kind": "pnpm exec tsx test/continuous-test-suite-resolve-request-kind.ts",
76
77
  "test:mcp": "pnpm exec tsx test/continuous-test-suite-mcp-infra.ts",
77
78
  "test:tool-resolution": "pnpm exec tsx test/continuous-test-suite-tool-resolution.ts",
78
79
  "test:mcp:http": "pnpm exec tsx test/continuous-test-suite-mcp-http.ts",
@@ -80,6 +81,7 @@
80
81
  "test:mcp:cli": "pnpm exec tsx test/continuous-test-suite-mcp-cli.ts",
81
82
  "test:mcp:full": "pnpm run test:mcp:infra && pnpm run test:mcp:spans && pnpm run test:mcp:sdk && pnpm run test:mcp:cli && pnpm run test:mcp:http",
82
83
  "test:media": "pnpm exec tsx test/continuous-test-suite-media-gen.ts",
84
+ "test:media-registry-collisions": "pnpm exec tsx test/continuous-test-suite-media-registry-collisions.ts",
83
85
  "test:memory": "pnpm exec tsx test/continuous-test-suite-memory.ts",
84
86
  "test:openai-compat-streaming-retry": "pnpm exec tsx test/continuous-test-suite-openai-compat-streaming-retry.ts",
85
87
  "test:anthropic-streaming-retry": "pnpm exec tsx test/continuous-test-suite-anthropic-streaming-retry.ts",