@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.
@@ -300,6 +300,7 @@ export declare class CLICommandFactory {
300
300
  };
301
301
  videoProvider: {
302
302
  type: "string";
303
+ choices: string[];
303
304
  description: string;
304
305
  };
305
306
  videoOutput: {
@@ -328,6 +329,7 @@ export declare class CLICommandFactory {
328
329
  };
329
330
  avatarProvider: {
330
331
  type: "string";
332
+ choices: string[];
331
333
  description: string;
332
334
  };
333
335
  avatarImage: {
@@ -362,6 +364,7 @@ export declare class CLICommandFactory {
362
364
  };
363
365
  musicProvider: {
364
366
  type: "string";
367
+ choices: string[];
365
368
  description: string;
366
369
  };
367
370
  musicDuration: {
@@ -1190,6 +1193,7 @@ export declare const commonOptions: {
1190
1193
  };
1191
1194
  videoProvider: {
1192
1195
  type: "string";
1196
+ choices: string[];
1193
1197
  description: string;
1194
1198
  };
1195
1199
  videoOutput: {
@@ -1218,6 +1222,7 @@ export declare const commonOptions: {
1218
1222
  };
1219
1223
  avatarProvider: {
1220
1224
  type: "string";
1225
+ choices: string[];
1221
1226
  description: string;
1222
1227
  };
1223
1228
  avatarImage: {
@@ -1252,6 +1257,7 @@ export declare const commonOptions: {
1252
1257
  };
1253
1258
  musicProvider: {
1254
1259
  type: "string";
1260
+ choices: string[];
1255
1261
  description: string;
1256
1262
  };
1257
1263
  musicDuration: {
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import chalk from "chalk";
4
4
  import ora from "ora";
5
5
  import { ModelResolver } from "../../models/modelResolver.js";
6
+ import { providerChoicesFor } from "../../factories/mediaHandlerCatalog.js";
6
7
  import { globalSession } from "../../session/globalSessionState.js";
7
8
  // Use TokenUsage from standard types - no local interface needed
8
9
  import { ContextFactory, } from "../../types/index.js";
@@ -333,7 +334,7 @@ export class CLICommandFactory {
333
334
  },
334
335
  ttsProvider: {
335
336
  type: "string",
336
- choices: ["google-ai", "vertex", "openai-tts", "elevenlabs", "azure-tts"],
337
+ choices: providerChoicesFor("tts"),
337
338
  description: "TTS provider (overrides --provider for speech synthesis)",
338
339
  },
339
340
  ttsFormat: {
@@ -381,7 +382,7 @@ export class CLICommandFactory {
381
382
  },
382
383
  sttProvider: {
383
384
  type: "string",
384
- choices: ["whisper", "deepgram", "google-stt", "azure-stt"],
385
+ choices: providerChoicesFor("stt"),
385
386
  description: "STT provider to use",
386
387
  },
387
388
  sttLanguage: {
@@ -401,6 +402,7 @@ export class CLICommandFactory {
401
402
  },
402
403
  videoProvider: {
403
404
  type: "string",
405
+ choices: providerChoicesFor("video"),
404
406
  description: "Video provider override (e.g., 'vertex' (default), 'kling', 'runway', 'replicate')",
405
407
  },
406
408
  videoOutput: {
@@ -430,6 +432,7 @@ export class CLICommandFactory {
430
432
  // Avatar Generation options (D-ID, HeyGen, MuseTalk via Replicate)
431
433
  avatarProvider: {
432
434
  type: "string",
435
+ choices: providerChoicesFor("avatar"),
433
436
  description: "Avatar provider (e.g., 'd-id' (default), 'heygen', 'replicate', 'musetalk')",
434
437
  },
435
438
  avatarImage: {
@@ -465,6 +468,7 @@ export class CLICommandFactory {
465
468
  // Music Generation options (Beatoven, ElevenLabs, Lyria, MusicGen via Replicate)
466
469
  musicProvider: {
467
470
  type: "string",
471
+ choices: providerChoicesFor("music"),
468
472
  description: "Music provider (e.g., 'beatoven' (default), 'elevenlabs-music', 'lyria', 'replicate', 'musicgen')",
469
473
  },
470
474
  musicDuration: {
@@ -1,8 +1,9 @@
1
1
  import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
2
2
  import { directAgentTools } from "../agent/directTools.js";
3
- import { isImageGenerationModel } from "./constants.js";
3
+ import { defaultProviderFor } from "../factories/mediaHandlerCatalog.js";
4
4
  import { MiddlewareFactory } from "../middleware/factory.js";
5
5
  import { modelSupports } from "../models/modelRegistry.js";
6
+ import { resolveRequestKind } from "./resolveRequestKind.js";
6
7
  import { ATTR, tracers } from "../telemetry/index.js";
7
8
  import { ERROR_CODES, isAbortError, NeuroLinkError, } from "../utils/errorHandling.js";
8
9
  import { ProviderError } from "../types/index.js";
@@ -238,14 +239,12 @@ export class BaseProvider {
238
239
  }
239
240
  // CRITICAL: Image generation models don't support real streaming
240
241
  // Force fake streaming for image models to ensure image output is yielded.
241
- // Skip this path when the caller explicitly requests non-image output (e.g.
242
- // JSON analysis) so dual-mode models like gemini-3.1-flash-image-preview
243
- // can still perform text/structured generation.
244
- const isImageModel = isImageGenerationModel(this.modelName);
245
- const requestsNonImageOutput = options.output?.format === "json" ||
246
- options.output?.format === "structured" ||
247
- options.output?.format === "text";
248
- if (isImageModel && !requestsNonImageOutput) {
242
+ // resolveRequestKind() skips this path when the caller explicitly requests
243
+ // non-image output (e.g. JSON analysis) so dual-mode models like
244
+ // gemini-3.1-flash-image-preview can still perform text/structured
245
+ // generation see its doc comment for the full precedence table.
246
+ const requestKind = resolveRequestKind(options, this.modelName);
247
+ if (requestKind === "image") {
249
248
  logger.info(`Image model detected, forcing fake streaming`, {
250
249
  provider: this.providerName,
251
250
  model: this.modelName,
@@ -1066,14 +1065,13 @@ export class BaseProvider {
1066
1065
  }
1067
1066
  async runGenerateInActiveContext(options, startTime, otelSpan, otelSpanState) {
1068
1067
  try {
1069
- if (options.output?.mode === "video") {
1068
+ // Single source of truth for "what kind of request is this" — see
1069
+ // resolveRequestKind's doc comment for the full precedence table.
1070
+ const requestKind = resolveRequestKind(options, this.modelName);
1071
+ if (requestKind === "video") {
1070
1072
  return await this.handleVideoGeneration(options, startTime);
1071
1073
  }
1072
- const isImageModel = isImageGenerationModel(this.modelName);
1073
- const requestsNonImageOutput = options.output?.format === "json" ||
1074
- options.output?.format === "structured" ||
1075
- options.output?.format === "text";
1076
- if (isImageModel && !requestsNonImageOutput) {
1074
+ if (requestKind === "image") {
1077
1075
  logger.info(`Image generation model detected, routing to executeImageGeneration`, {
1078
1076
  provider: this.providerName,
1079
1077
  model: this.modelName,
@@ -1081,7 +1079,7 @@ export class BaseProvider {
1081
1079
  const imageResult = await this.executeImageGeneration(options);
1082
1080
  return await this.enhanceResult(imageResult, options, startTime);
1083
1081
  }
1084
- if (options.tts?.enabled && !options.tts?.useAiResponse) {
1082
+ if (requestKind === "tts-direct") {
1085
1083
  return this.handleDirectTTSSynthesis(options, startTime);
1086
1084
  }
1087
1085
  const { tools, model } = await this.prepareGenerationContext(options);
@@ -2139,9 +2137,10 @@ export class BaseProvider {
2139
2137
  }
2140
2138
  // Get prompt text
2141
2139
  const prompt = options.prompt || options.input?.text || "";
2142
- // Honor output.video.provider — when omitted, fall back to "vertex"
2143
- // for backward compatibility with the original implementation.
2144
- const requestedProvider = options.output?.video?.provider ?? "vertex";
2140
+ // Honor output.video.provider — when omitted, fall back to the
2141
+ // catalog-derived default (currently "vertex") for backward
2142
+ // compatibility with the original implementation.
2143
+ const requestedProvider = options.output?.video?.provider ?? defaultProviderFor("video");
2145
2144
  if (!VideoProcessor.supports(requestedProvider)) {
2146
2145
  throw new VideoError({
2147
2146
  code: VIDEO_ERROR_CODES.PROVIDER_NOT_SUPPORTED,
@@ -2177,7 +2176,12 @@ export class BaseProvider {
2177
2176
  // shared timeout helper so standard video gen honors the caller's
2178
2177
  // timeout the same way director mode does (see above ~Line 2062).
2179
2178
  const videoTimeout = options.timeout ?? 600_000; // 10 min default
2180
- const videoResult = await this.executeWithTimeout(() => VideoProcessor.generate(requestedProvider, imageBuffer, prompt, options.output?.video ?? {}, options.region), { timeout: videoTimeout, operationType: "generate" });
2179
+ const videoResult = await this.executeWithTimeout(() => VideoProcessor.generate(requestedProvider, {
2180
+ ...(options.output?.video ?? {}),
2181
+ image: imageBuffer,
2182
+ prompt,
2183
+ region: options.region,
2184
+ }), { timeout: videoTimeout, operationType: "generate" });
2181
2185
  // Prefer the handler's own model id (more accurate — it knows the exact
2182
2186
  // checkpoint that ran). Fall back to the request-time value, and finally
2183
2187
  // to the Vertex default only when we're on the Vertex route.
@@ -0,0 +1,27 @@
1
+ import type { RequestKind, RequestKindInput } from "../types/index.js";
2
+ /**
3
+ * The dispatch decision for "what kind of request is this" — text, image,
4
+ * video, music, avatar, direct TTS synthesis, or PPT generation — at the
5
+ * CORE call sites: neurolink.ts's maybeHandleEarlyGenerateResult
6
+ * (music/avatar/ppt/workflow routing) and baseProvider.ts's
7
+ * stream()/runGenerateInActiveContext (image/video/tts-direct routing) call
8
+ * this instead of independently re-deriving the decision.
9
+ *
10
+ * NOT yet the only copy: replicate.ts's generate() override and
11
+ * googleVertex/client.ts's native dispatch (~6672-6718) still carry their
12
+ * own provider-internal versions — the Vertex one with a cruder
13
+ * startsWith() image match. Migrating those two is queued follow-up work;
14
+ * until it lands, an edit to this precedence table does not reach them.
15
+ *
16
+ * Precedence, checked in order:
17
+ * 1. output.mode (music/avatar/video/ppt) — an explicit mode always wins.
18
+ * 2. an image-generation model, unless the caller explicitly asked for a
19
+ * non-image output.format (json/structured/text) — this lets dual-mode
20
+ * models like gemini-3.1-flash-image-preview still perform text or
21
+ * structured generation when requested.
22
+ * 3. tts.enabled without tts.useAiResponse — direct synthesis, bypassing
23
+ * the LLM turn entirely (useAiResponse means the LLM's own text
24
+ * response gets synthesized afterward, which is NOT this branch).
25
+ * 4. otherwise, "text".
26
+ */
27
+ export declare function resolveRequestKind(options: RequestKindInput, modelName?: string): RequestKind;
@@ -0,0 +1,50 @@
1
+ import { isImageGenerationModel } from "./constants.js";
2
+ /**
3
+ * The dispatch decision for "what kind of request is this" — text, image,
4
+ * video, music, avatar, direct TTS synthesis, or PPT generation — at the
5
+ * CORE call sites: neurolink.ts's maybeHandleEarlyGenerateResult
6
+ * (music/avatar/ppt/workflow routing) and baseProvider.ts's
7
+ * stream()/runGenerateInActiveContext (image/video/tts-direct routing) call
8
+ * this instead of independently re-deriving the decision.
9
+ *
10
+ * NOT yet the only copy: replicate.ts's generate() override and
11
+ * googleVertex/client.ts's native dispatch (~6672-6718) still carry their
12
+ * own provider-internal versions — the Vertex one with a cruder
13
+ * startsWith() image match. Migrating those two is queued follow-up work;
14
+ * until it lands, an edit to this precedence table does not reach them.
15
+ *
16
+ * Precedence, checked in order:
17
+ * 1. output.mode (music/avatar/video/ppt) — an explicit mode always wins.
18
+ * 2. an image-generation model, unless the caller explicitly asked for a
19
+ * non-image output.format (json/structured/text) — this lets dual-mode
20
+ * models like gemini-3.1-flash-image-preview still perform text or
21
+ * structured generation when requested.
22
+ * 3. tts.enabled without tts.useAiResponse — direct synthesis, bypassing
23
+ * the LLM turn entirely (useAiResponse means the LLM's own text
24
+ * response gets synthesized afterward, which is NOT this branch).
25
+ * 4. otherwise, "text".
26
+ */
27
+ export function resolveRequestKind(options, modelName) {
28
+ if (options.output?.mode === "music") {
29
+ return "music";
30
+ }
31
+ if (options.output?.mode === "avatar") {
32
+ return "avatar";
33
+ }
34
+ if (options.output?.mode === "video") {
35
+ return "video";
36
+ }
37
+ if (options.output?.mode === "ppt") {
38
+ return "ppt";
39
+ }
40
+ const requestsNonImageOutput = options.output?.format === "json" ||
41
+ options.output?.format === "structured" ||
42
+ options.output?.format === "text";
43
+ if (isImageGenerationModel(modelName) && !requestsNonImageOutput) {
44
+ return "image";
45
+ }
46
+ if (options.tts?.enabled && !options.tts?.useAiResponse) {
47
+ return "tts-direct";
48
+ }
49
+ return "text";
50
+ }
@@ -0,0 +1,18 @@
1
+ import type { MediaHandlerDescriptor, MediaHandlerKind } from "../types/index.js";
2
+ /**
3
+ * Static catalog of every shipped media-handler provider, across all six
4
+ * ecosystems (TTS, STT, Realtime, Video, Avatar, Music). Pure data — no
5
+ * factory functions, no class imports — mirroring the
6
+ * src/lib/factories/providerDescriptors.ts pattern for text/image
7
+ * providers.
8
+ *
9
+ * Entries mirror providerRegistry.ts's hand-constructed TTS/STT/Realtime/
10
+ * Video/Avatar/Music registration blocks (registerAllProviders()) exactly —
11
+ * this file states today's truth, not an aspirational shape. Keep it in
12
+ * sync if those blocks change.
13
+ */
14
+ export declare const MEDIA_HANDLER_CATALOG: readonly MediaHandlerDescriptor[];
15
+ /** Every selectable provider name for `kind`, primaries and aliases both. */
16
+ export declare function providerChoicesFor(kind: MediaHandlerKind): string[];
17
+ /** The first-listed primary provider name for `kind` — used as a fallback default. */
18
+ export declare function defaultProviderFor(kind: MediaHandlerKind): string;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Static catalog of every shipped media-handler provider, across all six
3
+ * ecosystems (TTS, STT, Realtime, Video, Avatar, Music). Pure data — no
4
+ * factory functions, no class imports — mirroring the
5
+ * src/lib/factories/providerDescriptors.ts pattern for text/image
6
+ * providers.
7
+ *
8
+ * Entries mirror providerRegistry.ts's hand-constructed TTS/STT/Realtime/
9
+ * Video/Avatar/Music registration blocks (registerAllProviders()) exactly —
10
+ * this file states today's truth, not an aspirational shape. Keep it in
11
+ * sync if those blocks change.
12
+ */
13
+ export const MEDIA_HANDLER_CATALOG = [
14
+ // --- TTS ---
15
+ { kind: "tts", name: "google-ai", aliases: ["vertex"] },
16
+ { kind: "tts", name: "openai-tts" },
17
+ { kind: "tts", name: "elevenlabs", aliases: ["elevenlabs-tts"] },
18
+ { kind: "tts", name: "azure-tts" },
19
+ { kind: "tts", name: "fish-audio" },
20
+ { kind: "tts", name: "cartesia" },
21
+ // --- STT ---
22
+ { kind: "stt", name: "whisper", aliases: ["openai-stt"] },
23
+ { kind: "stt", name: "deepgram" },
24
+ { kind: "stt", name: "google-stt" },
25
+ { kind: "stt", name: "azure-stt" },
26
+ // --- Realtime ---
27
+ { kind: "realtime", name: "openai-realtime" },
28
+ { kind: "realtime", name: "gemini-live" },
29
+ // --- Video ---
30
+ { kind: "video", name: "vertex" },
31
+ { kind: "video", name: "kling" },
32
+ { kind: "video", name: "runway" },
33
+ { kind: "video", name: "replicate" },
34
+ // --- Avatar ---
35
+ { kind: "avatar", name: "d-id" },
36
+ { kind: "avatar", name: "replicate", aliases: ["musetalk"] },
37
+ { kind: "avatar", name: "heygen" },
38
+ // --- Music ---
39
+ { kind: "music", name: "beatoven" },
40
+ { kind: "music", name: "replicate", aliases: ["musicgen"] },
41
+ { kind: "music", name: "elevenlabs-music", aliases: ["elevenlabs-sound"] },
42
+ { kind: "music", name: "lyria" },
43
+ ];
44
+ /** Every selectable provider name for `kind`, primaries and aliases both. */
45
+ export function providerChoicesFor(kind) {
46
+ const choices = [];
47
+ for (const entry of MEDIA_HANDLER_CATALOG) {
48
+ if (entry.kind !== kind) {
49
+ continue;
50
+ }
51
+ choices.push(entry.name);
52
+ if (entry.aliases) {
53
+ choices.push(...entry.aliases);
54
+ }
55
+ }
56
+ return choices;
57
+ }
58
+ /** The first-listed primary provider name for `kind` — used as a fallback default. */
59
+ export function defaultProviderFor(kind) {
60
+ const first = MEDIA_HANDLER_CATALOG.find((entry) => entry.kind === kind);
61
+ if (!first) {
62
+ throw new Error(`No media handler catalog entries registered for kind "${kind}"`);
63
+ }
64
+ return first.name;
65
+ }