@juspay/neurolink 13.6.5 → 13.8.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 (87) hide show
  1. package/CHANGELOG.md +6 -2
  2. package/dist/auth/sessionManager.js +8 -3
  3. package/dist/browser/neurolink.min.js +480 -460
  4. package/dist/cli/commands/serve.js +11 -1
  5. package/dist/cli/commands/transcribe.js +39 -3
  6. package/dist/constants/langfuse.d.ts +2 -0
  7. package/dist/constants/langfuse.js +2 -0
  8. package/dist/core/redisConversationMemoryManager.js +6 -16
  9. package/dist/factories/sttDescriptors.js +7 -0
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.js +4 -2
  12. package/dist/neurolink.js +33 -1
  13. package/dist/observability/exporters/langfuseExporter.d.ts +4 -0
  14. package/dist/observability/exporters/langfuseExporter.js +29 -8
  15. package/dist/observability/otlpEncoding.js +2 -2
  16. package/dist/server/abstract/baseServerAdapter.js +2 -0
  17. package/dist/server/adapters/expressAdapter.js +1 -1
  18. package/dist/server/adapters/fastifyAdapter.js +4 -1
  19. package/dist/server/adapters/honoAdapter.js +1 -1
  20. package/dist/server/adapters/koaAdapter.js +1 -1
  21. package/dist/server/routes/transcribeRoutes.d.ts +0 -8
  22. package/dist/server/routes/transcribeRoutes.js +130 -3
  23. package/dist/server/utils/validation.d.ts +409 -15
  24. package/dist/server/utils/validation.js +106 -4
  25. package/dist/server/websocket/frameQueue.d.ts +16 -0
  26. package/dist/server/websocket/frameQueue.js +55 -0
  27. package/dist/server/websocket/realtimeTranscription.d.ts +48 -0
  28. package/dist/server/websocket/realtimeTranscription.js +603 -0
  29. package/dist/server/websocket/transcribeWebSocket.d.ts +17 -1
  30. package/dist/server/websocket/transcribeWebSocket.js +263 -64
  31. package/dist/services/server/ai/observability/langfuseSetup.js +6 -1
  32. package/dist/telemetry/attributes.d.ts +1 -1
  33. package/dist/telemetry/attributes.js +84 -8
  34. package/dist/types/cli.d.ts +6 -0
  35. package/dist/types/exporter.d.ts +3 -1
  36. package/dist/types/server.d.ts +304 -2
  37. package/dist/types/stt.d.ts +38 -3
  38. package/dist/types/transcribe.d.ts +401 -5
  39. package/dist/utils/redis.d.ts +17 -0
  40. package/dist/utils/redis.js +31 -0
  41. package/dist/voice/audioLevel.d.ts +14 -0
  42. package/dist/voice/audioLevel.js +60 -0
  43. package/dist/voice/codeMix.d.ts +40 -0
  44. package/dist/voice/codeMix.js +282 -0
  45. package/dist/voice/correction/align.d.ts +12 -0
  46. package/dist/voice/correction/align.js +48 -0
  47. package/dist/voice/correction/guards.d.ts +39 -0
  48. package/dist/voice/correction/guards.js +122 -0
  49. package/dist/voice/correction/index.d.ts +25 -3
  50. package/dist/voice/correction/index.js +132 -4
  51. package/dist/voice/correction/rewrite.d.ts +64 -3
  52. package/dist/voice/correction/rewrite.js +295 -25
  53. package/dist/voice/eval/sttEvalLangfuse.d.ts +14 -0
  54. package/dist/voice/eval/sttEvalLangfuse.js +182 -0
  55. package/dist/voice/eval/sttEvalStore.d.ts +27 -0
  56. package/dist/voice/eval/sttEvalStore.js +397 -0
  57. package/dist/voice/index.js +3 -0
  58. package/dist/voice/languages.d.ts +34 -0
  59. package/dist/voice/languages.js +290 -0
  60. package/dist/voice/modelAssets.d.ts +28 -0
  61. package/dist/voice/modelAssets.js +113 -0
  62. package/dist/voice/output/render.d.ts +32 -0
  63. package/dist/voice/output/render.js +368 -0
  64. package/dist/voice/providers/OpenAISTT.d.ts +11 -1
  65. package/dist/voice/providers/OpenAISTT.js +57 -6
  66. package/dist/voice/session/transcriptionSession.d.ts +55 -0
  67. package/dist/voice/session/transcriptionSession.js +225 -0
  68. package/dist/voice/streaming/chunkedStreamAdapter.d.ts +7 -0
  69. package/dist/voice/streaming/chunkedStreamAdapter.js +711 -28
  70. package/dist/voice/streaming/endpointer.d.ts +50 -17
  71. package/dist/voice/streaming/endpointer.js +115 -19
  72. package/dist/voice/transcribe.d.ts +18 -1
  73. package/dist/voice/transcribe.js +459 -18
  74. package/dist/voice/transcribeStream.d.ts +18 -1
  75. package/dist/voice/transcribeStream.js +413 -16
  76. package/dist/voice/vad/assets.d.ts +36 -0
  77. package/dist/voice/vad/assets.js +142 -0
  78. package/dist/voice/vad/frameScorer.d.ts +28 -0
  79. package/dist/voice/vad/frameScorer.js +90 -0
  80. package/dist/voice/vad/sileroVad.d.ts +43 -0
  81. package/dist/voice/vad/sileroVad.js +178 -0
  82. package/dist/voice/whistle/assets.d.ts +3 -4
  83. package/dist/voice/whistle/assets.js +11 -95
  84. package/docs-site/static/search-index.json +21 -6
  85. package/models/silero-vad/README.md +19 -0
  86. package/models/silero-vad/silero_vad.onnx +0 -0
  87. package/package.json +4 -1
@@ -18,7 +18,10 @@ import { tracers } from "../../telemetry/tracers.js";
18
18
  import { logger } from "../../utils/logger.js";
19
19
  import { MAX_AUDIO_BYTES } from "../../utils/sizeGuard.js";
20
20
  import { RawBodyTooLargeError } from "../utils/rawBody.js";
21
- import { createErrorResponse, TranscribeConfigSchema, TranscribeRequestSchema, validateRequest, } from "../utils/validation.js";
21
+ import { getSTTProviderDescriptor, listConfiguredSTTProviders, resolveDefaultSTTProvider, resolveSTTEndpoints, } from "../../factories/sttDescriptors.js";
22
+ import { STTProcessor } from "../../utils/sttProcessor.js";
23
+ import { STTEvalStore } from "../../voice/eval/sttEvalStore.js";
24
+ import { createErrorResponse, STTEvalBatchSchema, TranscribeConfigSchema, TranscribeRequestSchema, validateRequest, } from "../utils/validation.js";
22
25
  const FORMAT_BY_MIME = {
23
26
  "audio/mpeg": "mp3",
24
27
  "audio/mp3": "mp3",
@@ -198,6 +201,9 @@ function toTranscribeOptions(config, audio, format, maxAudioBytes) {
198
201
  ...(config.dictionary ? { dictionary: config.dictionary } : {}),
199
202
  ...(config.correction ? { correction: config.correction } : {}),
200
203
  ...(config.fallback ? { fallback: config.fallback } : {}),
204
+ ...(config.routing ? { routing: config.routing } : {}),
205
+ ...(config.chunking ? { chunking: config.chunking } : {}),
206
+ ...(config.output ? { output: config.output } : {}),
201
207
  ...(config.diarization !== undefined
202
208
  ? { diarization: config.diarization }
203
209
  : {}),
@@ -313,6 +319,7 @@ function toVerboseJson(result) {
313
319
  : {}),
314
320
  steps: result.steps,
315
321
  timings: result.timings,
322
+ ...(result.outputs ? { outputs: result.outputs } : {}),
316
323
  };
317
324
  }
318
325
  async function handleOpenAITranscription(ctx, options, limit, route) {
@@ -368,6 +375,11 @@ async function handleOpenAITranscription(ctx, options, limit, route) {
368
375
  const fallback = textField(form, "fallback");
369
376
  const language = textField(form, "language");
370
377
  const prompt = textField(form, "prompt");
378
+ const chunking = textField(form, "chunking");
379
+ const outputLanguages = (textField(form, "output_languages") ?? "")
380
+ .split(",")
381
+ .map((tag) => tag.trim())
382
+ .filter(Boolean);
371
383
  const candidate = {
372
384
  provider: textField(form, "provider") ?? engine.provider,
373
385
  model: engine.model,
@@ -379,12 +391,20 @@ async function handleOpenAITranscription(ctx, options, limit, route) {
379
391
  correction: {
380
392
  enabled: true,
381
393
  ...(secondOpinion
382
- ? { secondOpinion: { provider: secondOpinion } }
394
+ ? {
395
+ secondOpinion: secondOpinion === "auto"
396
+ ? "auto"
397
+ : { provider: secondOpinion },
398
+ }
383
399
  : {}),
384
400
  },
385
401
  }
386
402
  : {}),
387
403
  ...(fallback ? { fallback: { provider: fallback } } : {}),
404
+ ...(chunking ? { chunking } : {}),
405
+ ...(outputLanguages.length
406
+ ? { output: { languages: outputLanguages } }
407
+ : {}),
388
408
  ...(isTruthyField(form.get("diarize")) ? { diarization: true } : {}),
389
409
  ...(granularities.includes("word") ? { wordTimestamps: true } : {}),
390
410
  };
@@ -450,6 +470,21 @@ const TRANSCRIBE_REQUEST_JSON_SCHEMA = {
450
470
  },
451
471
  correction: { type: "object" },
452
472
  fallback: { type: "object" },
473
+ routing: { type: "object" },
474
+ chunking: { type: "string", enum: ["engine", "utterances"] },
475
+ output: {
476
+ type: "object",
477
+ description: "Renderings in other languages: languages is a list of BCP-47 tags (es, fr-CA, hi-Latn) or source",
478
+ properties: {
479
+ languages: { type: "array", items: { type: "string" } },
480
+ provider: { type: "string" },
481
+ model: { type: "string" },
482
+ timeoutMs: { type: "number" },
483
+ contextUtterances: { type: "number" },
484
+ glossary: { type: "boolean" },
485
+ },
486
+ required: ["languages"],
487
+ },
453
488
  diarization: { type: "boolean" },
454
489
  wordTimestamps: { type: "boolean" },
455
490
  timeoutMs: { type: "number" },
@@ -463,10 +498,50 @@ const TRANSCRIBE_REQUEST_JSON_SCHEMA = {
463
498
  * `maxAudioBytes` (default 50 MB) and `openaiBasePath` (default `""`, so the
464
499
  * OpenAI-compatible route is `/v1/audio/transcriptions`).
465
500
  */
501
+ /** `GET /agent/transcribe/providers`: engines, default and correction models, from the process environment. */
502
+ function describeTranscription(ctx) {
503
+ const endpoints = resolveSTTEndpoints();
504
+ const configured = new Set(listConfiguredSTTProviders());
505
+ const names = [
506
+ ...configured,
507
+ ...STTProcessor.listProviders().filter((n) => !configured.has(n)),
508
+ ];
509
+ const providers = names.map((name) => {
510
+ const endpoint = endpoints[name];
511
+ if (endpoint) {
512
+ return {
513
+ name,
514
+ kind: "endpoint",
515
+ ...(endpoint.model ? { model: endpoint.model } : {}),
516
+ ...(endpoint.lidModel ? { languageProbe: true } : {}),
517
+ ...(endpoint.priorLanguageForm ? { languagePrior: true } : {}),
518
+ };
519
+ }
520
+ return {
521
+ name,
522
+ kind: getSTTProviderDescriptor(name) ? "builtin" : "registered",
523
+ };
524
+ });
525
+ const decision = ctx.neurolink.decisionLimits();
526
+ const rewriteProvider = process.env.NEUROLINK_STT_REWRITE_PROVIDER?.trim() || null;
527
+ const rewriteModel = process.env.NEUROLINK_STT_REWRITE_MODEL?.trim() || null;
528
+ return {
529
+ default: resolveDefaultSTTProvider(),
530
+ providers,
531
+ correction: {
532
+ decision: decision
533
+ ? { provider: decision.provider, model: decision.model }
534
+ : null,
535
+ rewrite: { provider: rewriteProvider, model: rewriteModel },
536
+ },
537
+ };
538
+ }
466
539
  export function createTranscribeRoutes(basePath = "/api", options = {}) {
467
540
  const limit = options.maxAudioBytes ?? MAX_AUDIO_BYTES;
468
541
  const nativePath = `${basePath}/agent/transcribe`;
469
542
  const openaiPath = `${options.openaiBasePath ?? ""}/v1/audio/transcriptions`;
543
+ const evalPath = `${options.openaiBasePath ?? ""}/v1/evals/stt`;
544
+ const evalStore = options.evals === false ? undefined : new STTEvalStore(options.evals);
470
545
  return {
471
546
  // Empty prefix: the two routes live under different roots, and every
472
547
  // route path already starts with "", so the adapters add nothing.
@@ -494,13 +569,65 @@ export function createTranscribeRoutes(basePath = "/api", options = {}) {
494
569
  requestSchema: TRANSCRIBE_REQUEST_JSON_SCHEMA,
495
570
  tags: ["agent", "transcribe"],
496
571
  },
572
+ {
573
+ method: "GET",
574
+ path: `${nativePath}/providers`,
575
+ handler: async (ctx) => describeTranscription(ctx),
576
+ description: "The STT providers this server is configured with, the default one, and the decision and rewrite models behind correction",
577
+ tags: ["agent", "transcribe"],
578
+ },
497
579
  {
498
580
  method: "POST",
499
581
  path: openaiPath,
500
582
  handler: (ctx) => handleOpenAITranscription(ctx, options, limit, openaiPath),
501
- description: "OpenAI-compatible transcription (multipart/form-data: file, model, language, prompt, response_format; extensions: provider, diarize, dictionary, correct, fallback, second_opinion)",
583
+ description: "OpenAI-compatible transcription (multipart/form-data: file, model, language, prompt, response_format; extensions: provider, diarize, dictionary, correct, fallback, second_opinion, chunking, output_languages)",
502
584
  tags: ["transcribe", "openai-compatible"],
503
585
  },
586
+ ...(evalStore ? createEvalRoutes(evalPath, evalStore) : []),
504
587
  ],
505
588
  };
506
589
  }
590
+ /**
591
+ * The STT evaluation collector: a sender posts what two STTs wrote for the
592
+ * same call (`primary` shown, `shadow` run alongside), and reads back how far
593
+ * apart they were per speaker.
594
+ */
595
+ function createEvalRoutes(evalPath, store) {
596
+ return [
597
+ {
598
+ method: "POST",
599
+ path: evalPath,
600
+ handler: async (ctx) => {
601
+ const validation = validateRequest(STTEvalBatchSchema, ctx.body, ctx.requestId);
602
+ if (!validation.success) {
603
+ return validation.error;
604
+ }
605
+ const batch = validation.data;
606
+ return store.accept(batch);
607
+ },
608
+ description: "Accept a batch of one call's segments from two STTs (primary shown, shadow for evaluation); a resent segment id replaces the earlier one",
609
+ tags: ["transcribe", "evaluation"],
610
+ },
611
+ {
612
+ method: "GET",
613
+ path: evalPath,
614
+ handler: async () => ({ calls: await store.list() }),
615
+ description: "Calls collected for STT evaluation, most recent first",
616
+ tags: ["transcribe", "evaluation"],
617
+ },
618
+ {
619
+ method: "GET",
620
+ path: `${evalPath}/:callId`,
621
+ handler: async (ctx) => {
622
+ // Adapters already decode path params; decoding again would turn a
623
+ // literal "%" in an id into something else, or throw on "%E0%A4%A".
624
+ const callId = ctx.params.callId ?? "";
625
+ const report = await store.report(callId, ctx.query.segments === "true");
626
+ return (report ??
627
+ createErrorResponse("NOT_FOUND", `No STT evaluation for call ${callId}`, undefined, ctx.requestId, 404));
628
+ },
629
+ description: "Per-speaker comparison of the two STTs for one call (?segments=true adds the segments)",
630
+ tags: ["transcribe", "evaluation"],
631
+ },
632
+ ];
633
+ }