@juspay/neurolink 13.7.0 → 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.
- package/CHANGELOG.md +3 -3
- package/dist/browser/neurolink.min.js +480 -460
- package/dist/cli/commands/serve.js +11 -1
- package/dist/cli/commands/transcribe.js +39 -3
- package/dist/constants/langfuse.d.ts +2 -0
- package/dist/constants/langfuse.js +2 -0
- package/dist/factories/sttDescriptors.js +7 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -2
- package/dist/neurolink.js +33 -1
- package/dist/observability/exporters/langfuseExporter.d.ts +4 -0
- package/dist/observability/exporters/langfuseExporter.js +29 -8
- package/dist/observability/otlpEncoding.js +2 -2
- package/dist/server/abstract/baseServerAdapter.js +2 -0
- package/dist/server/adapters/expressAdapter.js +1 -1
- package/dist/server/adapters/fastifyAdapter.js +4 -1
- package/dist/server/adapters/honoAdapter.js +1 -1
- package/dist/server/adapters/koaAdapter.js +1 -1
- package/dist/server/routes/transcribeRoutes.d.ts +0 -8
- package/dist/server/routes/transcribeRoutes.js +130 -3
- package/dist/server/utils/validation.d.ts +409 -15
- package/dist/server/utils/validation.js +106 -4
- package/dist/server/websocket/frameQueue.d.ts +16 -0
- package/dist/server/websocket/frameQueue.js +55 -0
- package/dist/server/websocket/realtimeTranscription.d.ts +48 -0
- package/dist/server/websocket/realtimeTranscription.js +603 -0
- package/dist/server/websocket/transcribeWebSocket.d.ts +17 -1
- package/dist/server/websocket/transcribeWebSocket.js +263 -64
- package/dist/services/server/ai/observability/langfuseSetup.js +6 -1
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/exporter.d.ts +3 -1
- package/dist/types/server.d.ts +304 -2
- package/dist/types/stt.d.ts +38 -3
- package/dist/types/transcribe.d.ts +401 -5
- package/dist/voice/audioLevel.d.ts +14 -0
- package/dist/voice/audioLevel.js +60 -0
- package/dist/voice/codeMix.d.ts +40 -0
- package/dist/voice/codeMix.js +282 -0
- package/dist/voice/correction/align.d.ts +12 -0
- package/dist/voice/correction/align.js +48 -0
- package/dist/voice/correction/guards.d.ts +39 -0
- package/dist/voice/correction/guards.js +122 -0
- package/dist/voice/correction/index.d.ts +25 -3
- package/dist/voice/correction/index.js +132 -4
- package/dist/voice/correction/rewrite.d.ts +64 -3
- package/dist/voice/correction/rewrite.js +295 -25
- package/dist/voice/eval/sttEvalLangfuse.d.ts +14 -0
- package/dist/voice/eval/sttEvalLangfuse.js +182 -0
- package/dist/voice/eval/sttEvalStore.d.ts +27 -0
- package/dist/voice/eval/sttEvalStore.js +397 -0
- package/dist/voice/index.js +3 -0
- package/dist/voice/languages.d.ts +34 -0
- package/dist/voice/languages.js +290 -0
- package/dist/voice/modelAssets.d.ts +28 -0
- package/dist/voice/modelAssets.js +113 -0
- package/dist/voice/output/render.d.ts +32 -0
- package/dist/voice/output/render.js +368 -0
- package/dist/voice/providers/OpenAISTT.d.ts +11 -1
- package/dist/voice/providers/OpenAISTT.js +57 -6
- package/dist/voice/session/transcriptionSession.d.ts +55 -0
- package/dist/voice/session/transcriptionSession.js +225 -0
- package/dist/voice/streaming/chunkedStreamAdapter.d.ts +7 -0
- package/dist/voice/streaming/chunkedStreamAdapter.js +711 -28
- package/dist/voice/streaming/endpointer.d.ts +50 -17
- package/dist/voice/streaming/endpointer.js +115 -19
- package/dist/voice/transcribe.d.ts +18 -1
- package/dist/voice/transcribe.js +459 -18
- package/dist/voice/transcribeStream.d.ts +18 -1
- package/dist/voice/transcribeStream.js +413 -16
- package/dist/voice/vad/assets.d.ts +36 -0
- package/dist/voice/vad/assets.js +142 -0
- package/dist/voice/vad/frameScorer.d.ts +28 -0
- package/dist/voice/vad/frameScorer.js +90 -0
- package/dist/voice/vad/sileroVad.d.ts +43 -0
- package/dist/voice/vad/sileroVad.js +178 -0
- package/dist/voice/whistle/assets.d.ts +3 -4
- package/dist/voice/whistle/assets.js +11 -95
- package/docs-site/static/search-index.json +21 -6
- package/models/silero-vad/README.md +19 -0
- package/models/silero-vad/silero_vad.onnx +0 -0
- package/package.json +3 -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 {
|
|
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
|
-
? {
|
|
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
|
+
}
|