@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
@@ -3504,7 +3504,7 @@
3504
3504
  {"objectID":"bec7707961c02d5123e4dceed9e13f5f5311e2f47783dd81fc290b467f50ff7a","title":"Other spans","url":"/docs/features/observability#other-spans","content":"Spans from the RAG, workflow, file, memory, context and evaluation code are exported as plain span records with their name, timing, status and their own attributes. They keep their neurolink.* names: neurolink.rag.search, neurolink.rag.rerank, neurolink.rag.chunk, neurolink.workflow.* (run, run.streaming, response.condition, judge.score, ensemble.execute, layers.execute), neurolink.file.process, neurolink.file.video.process, neurolink.context.compact, neurolink.memory.summarize and neurolink.evaluation.ragas.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Other spans","lvl3":""}},
3505
3505
  {"objectID":"ceb0e84f71b6a9deb6a57e236efa009236e933eb892ad569d59e36ba873292f3","title":"Steps and chat format","url":"/docs/features/observability#steps-and-chat-format","content":"Each iteration of the agent loop gets one step span with its one-based index in the step_index metadata. It parents that iteration's generation and the tool observations the model requested in it, so a multi-tool turn reads as step 1 (call, tools), step 2 (call, tools), and so on.\n\nGeneration input is a chat message array with the system message first. Generation output is an assistant message array: content, plus reasoning and tool_calls ({ id, type: \"function\", function: { name, arguments } }) when present. Langfuse renders both as a conversation.\n\nGeneration details:\nUsage includes input, output, total, cache read, cache creation and reasoning tokens, whatever naming the provider uses.\nCost is computed from NeuroLink's pricing table and recorded as costDetails.total.\nModel parameters are temperature, max tokens, top-p and thinking level.\nTime to first token is recorded as the generation's completion start time for streams.\nLevel is WARNING for a length or content-filter finish, an aborted call, an early break out of a stream, or an empty output, and ERROR when the call throws.\nStreams end their spans when the stream finishes, is abandoned or is aborted.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Steps and chat format","lvl3":""}},
3506
3506
  {"objectID":"7dbc08e9d130f6a8e970396c854cb6afb0be27558cdbaa21122d1df517e4006a","title":"Masking sensitive data","url":"/docs/features/observability#masking-sensitive-data","content":"mask runs on the input, output and metadata sent to Langfuse. It receives { data } and returns the value to send.\n\nmask and redactIO act on what NeuroLink sends to Langfuse, and on nothing else. They cover the trace and observation input and output (langfuse.observation.input, langfuse.observation.output, langfuse.trace.input, langfuse.trace.output); redactIO: true leaves those out of the request, and mask rewrites them instead. mask also runs on trace and observation metadata, whether or not the metadata is an object. If it throws, the value is replaced by <fully masked>.\n\nEverything else goes to Langfuse as it was recorded: the status message, exception.message, error.message, a tool's tool.error.message and a provider retry's retry.error, tool-call attributes, the proxy.body attribute of proxy body events, event attributes, and the prompt and completion attributes another library's GenAI instrumentation writes on spans that are exported with NeuroLink's. The policy does not reach any other sink either: an exporter you register with registerExporter, the spans NeuroLink sends to OTEL_EXPORTER_OTLP_ENDPOINT, and getSpans() receive the values as they were recorded. If one of them must not see content, take it out where it is recorded (NEUROLINK_RECORD_INPUTS=false) or in that sink's own processing.\n\nNeuroLink adds no credential masking of its own to error text. A failed call's status message, error.message, tool.error.message, retry.error and exception text are recorded as the provider returned them, in every sink, as the released build did, so an error that echoes a key reaches the sinks with the key. Input, output and tool arguments and results keep the credential masking they always had. To keep a credential out of error text, filter it in a custom exporter or keep it out of the provider's error.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Masking sensitive data","lvl3":""}},
3507
- {"objectID":"bc90fcd87977756303a1a998fc869c12bbd8ef1f1b88e4f17746e4bd4463d15f","title":"Langfuse exporter behaviour","url":"/docs/features/observability#langfuse-exporter-behaviour","content":"Endpoint. POST {baseUrl}/api/public/otel/v1/traces as OTLP/HTTP JSON. Requests are cut at 500 spans or 3.5 MB.\nObservation types. Agent, generation, tool, step, attempt and the other observation types reach Langfuse with their native type, model, parameters, usage, cost and first-token time.\nIdentity. User, session, trace name, tags and trace metadata are set on every observation, not only the root.\nEnvironment and release. The config field, then LANGFUSE_TRACING_ENVIRONMENT / LANGFUSE_RELEASE; they fill spans that do not carry them already. The OTLP resource's deployment.environment and service.version also read LANGFUSE_ENVIRONMENT, PUBLIC_APP_ENVIRONMENT and PUBLIC_APP_VERSION; Langfuse traces do not.\nBatching. Spans are sent when flushAt spans are queued, every flushInterval seconds, on flushOpenTelemetry() and on shutdown. exportMode: \"immediate\" sends each span as it ends.\nFailures. A request that fails with 429, 5xx or a network error is retried with exponential backoff, or after the Retry-After a 429 or 503 names, up to 60 seconds and never past a flush's budget. Other 4xx responses are not retried. Spans Langfuse reports as rejected (partialSuccess) are logged once as a warning and not re-sent; a success whose body is not an object, or whose rejected count is not a whole number between zero and the spans sent, counts as all delivered. At most 4 export requests are in flight at once. Spans that end meanwhile wait, up to 10,000 of them; when Langfuse stops answering, the oldest waiting spans are dropped, counted in failedSpans and in the errors of healthCheck(), and warned about once a minute. Spans that end during shutdown are sent within the flush budget or counted as dropped. A 401 or 403 logs one warning per process (\"Langfuse credentials rejected\") and is not retried. Delivery never throws into your code.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Langfuse exporter behaviour","lvl3":""}},
3507
+ {"objectID":"bc90fcd87977756303a1a998fc869c12bbd8ef1f1b88e4f17746e4bd4463d15f","title":"Langfuse exporter behaviour","url":"/docs/features/observability#langfuse-exporter-behaviour","content":"Endpoint. POST {baseUrl}/api/public/otel/v1/traces as OTLP/HTTP JSON. Requests are cut at 500 spans or 3.5 MB.\nObservation types. Agent, generation, tool, step, attempt and the other observation types reach Langfuse with their native type, model, parameters, usage, cost and first-token time.\nIdentity. User, session, trace name, tags and trace metadata are set on every observation, not only the root.\nEnvironment and release. The config field, then LANGFUSE_TRACING_ENVIRONMENT / LANGFUSE_RELEASE; they fill spans that do not carry them already. The OTLP resource's deployment.environment and service.version also read LANGFUSE_ENVIRONMENT, PUBLIC_APP_ENVIRONMENT and PUBLIC_APP_VERSION; Langfuse traces do not.\nBatching. Spans are sent when flushAt spans are queued, every flushInterval seconds, on flushOpenTelemetry() and on shutdown. exportMode: \"immediate\" sends each span as it ends.\nFailures. A request that fails with 429, 5xx or a network error is retried with exponential backoff, or after the Retry-After a 429 or 503 names, up to 60 seconds and never past a flush's budget. Other 4xx responses are not retried. Spans Langfuse reports as rejected (partialSuccess) are logged once as a warning and not re-sent; a success whose body is not an object, or whose rejected count is not a whole number between zero and the spans sent, counts as all delivered. At most 4 export requests are in flight at once. Spans that end meanwhile wait, up to 10,000 of them; when Langfuse stops answering, the oldest waiting spans are dropped, counted in failedSpans and in the errors of healthCheck(), and warned about once a minute. Spans that end during shutdown are sent within the flush budget or counted as dropped. A 401 or 403 is not retried; it is logged as an error (\"Langfuse credentials rejected\") with the spans dropped at most once a minute, once more when the credentials are accepted again, and for any remainder at shutdown, and a refused batch gets no Dropped N of M spans warning.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Langfuse exporter behaviour","lvl3":""}},
3508
3508
  {"objectID":"07d102a2518a59d4afc8618d87ee066c3c7e36fefe22ee00772e172d8055a720","title":"Metrics","url":"/docs/features/observability#metrics","content":"NeuroLink also records OpenTelemetry metrics, on one meter named neurolink-ai. They are recorded only when OTEL_EXPORTER_OTLP_ENDPOINT is set, when NEUROLINK_TELEMETRY_ENABLED=true, or when a global MeterProvider is registered (your application's, or NeuroLink's own); a global TracerProvider alone, NeuroLink's included, does not turn them on. They are written to the global MeterProvider. NeuroLink registers one, exporting to $OTEL_EXPORTER_OTLP_ENDPOINT/v1/metrics every 15 seconds, only when it owns the tracer provider and the endpoint is set; beside a host provider they go to the MeterProvider your application registered, and nowhere when it registered none.\n\n| Metric | Kind | Labels | Recorded for |\n| ----------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| ai_requests_total | counter | provider, model | each generate() that finishes in BaseProvider.finalizeNativeGenerate (the native generate path of providers such as Anthropic, SageMaker and the OpenAI Chat Completions family), and each proxied request whose usage the proxy captured |\n| ai_request_duration_ms | histogram,","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Metrics","lvl3":""}},
3509
3509
  {"objectID":"ba32010e859f5101a6247151e93a3db0fdaa79d0afe2d47c1492c74a0e04f5f5","title":"External OpenTelemetry provider","url":"/docs/features/observability#external-opentelemetry-provider","content":"If your application registers its own global TracerProvider before tracing starts, NeuroLink creates no provider. OTEL_EXPORTER_OTLP_ENDPOINT is then ignored: NeuroLink logs one warning and exports no traces or metrics to it, so add an OTLP exporter, or any other vendor's span processor, to your own providers. neurolink.getTelemetryStatus().openTelemetry then reports usingExternalProvider: true (its enabled stays whether tracing is initialized, as before), and neurolink telemetry status and neurolink observability status print the endpoint as ignored. NeuroLink's metrics are recorded on the MeterProvider your application registers. For Langfuse choose one of two recipes.\n\nThe host provider must register a context manager, which NodeTracerProvider.register() and NodeSDK do. Without one OpenTelemetry cannot carry context between calls, so setLangfuseContext user, session and metadata never reach NeuroLink's spans and child spans lose their parents; NeuroLink logs a warning when it finds none. Shutting the host provider down shuts the shared exporters down, as createExporterSpanProcessor() always did. If the host rebuilds its provider, pass createExporterSpanProcessor(undefined, { shutdownExporters: false }): that processor only flushes the exporters on shutdown, so a rebuilt provider that adds one again keeps exporting.\n\n(a) Exporter pipeline (recommended). Add createExporterSpanProcessor() to your provider and give NeuroLink the Langfuse keys, either through new NeuroLink({ observability: { langfuse } }) or the LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY environment variables. NeuroLink registers its LangfuseExporter on the shared exporter registry, which the processor reads, so mask, batching, chunking and retry from the section above all apply. Register the processor before the first NeuroLink call; spans that end before the exporter is registered are not exported.\n\n(b) LangfuseSpanProcessor.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"External OpenTelemetry provider","lvl3":""}},
3510
3510
  {"objectID":"be39053eddf107cc2916c84cc2e34d629a925ef52f26864ff9dd628f798d49e1","title":"Initialisation","url":"/docs/features/observability#initialisation","content":"Tracing starts synchronously, once per process, at the first of these:\nnew NeuroLink({ observability }). A constructor without an observability block starts nothing.\nThe first generate, stream, decide or tryDecide call on any NeuroLink instance.\nThe first span NeuroLink's own code creates, on an entry that loads the start-up module: the main package entry, @juspay/neurolink/server and @juspay/neurolink/livekit, and the ./rag, ./workflow, ./evaluation, ./processors and ./autoresearch subpath entries. That includes direct provider calls with no NeuroLink instance, for example AIProviderFactory.createProvider(...).generate(...) imported from the main entry.\n\nThe lightweight @juspay/neurolink/client entry does not load the OpenTelemetry Node SDK and does not start tracing: its spans go to a TracerProvider your application registered, and are dropped when there is none. A Node application that relied on this entry alone to start tracing (the previous version did) adds import \"@juspay/neurolink\" once at start-up.\n\nTracing configuration is process-wide and the first start wins. An observability config passed to new NeuroLink() wins over the LANGFUSE_* variables only when that construction starts tracing. A later instance's config is ignored: if it differs from the first, NeuroLink logs a warning that names the options that differ, once per set of differing options; an identical config logs one debug line. A start before Langfuse or OTLP is configured sets nothing up, so a later config or environment can still start tracing, unless langfuse: false was passed. langfuse: true, or an options object, without both keys counts as Langfuse not configured: NeuroLink warns once that the keys are missing and, unless OTEL_EXPORTER_OTLP_ENDPOINT is set, starts nothing, so a later config with keys still starts tracing. To run beside your own OpenTelemetry setup, register your TracerProvider before tracing starts; see External OpenTelemetry provider.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Initialisation","lvl3":""}},
@@ -4059,15 +4059,30 @@
4059
4059
  {"objectID":"cfe99c9441a998e039ecbe92d247fb981326eb5e5a56e51aa7abb49fd4662663","title":"Providers and environment","url":"/docs/features/transcribe#providers-and-environment","content":"| Provider (aliases) | Configured by | Notes |\n| --------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| whisper (openai-stt, openai-compatible) | OPENAI_STT_API_KEY or OPENAI_API_KEY; OPENAI_STT_BASE_URL for another server | Any OpenAI-compatible /audio/transcriptions endpoint. |\n| deepgram | DEEPGRAM_API_KEY | Native streaming. |\n| elevenlabs-stt (scribe, elevenlabs) | ELEVENLABS_API_KEY | |\n| google-stt | GOOGLE_API_KEY, GOOGLE_AI_API_KEY, GEMINI_API_KEY or GOOGLE_APPLICATION_CREDENTIALS | |\n| azure-stt | AZURE_SPEECH_KEY | |\n| whistle (local) | nothing | Built-in, local, always available. |\n\nThe order of that table is precedence: without provider or\nNEUROLINK_STT_PROVIDER, the first configured provider is used, and Whistle\nonly when none is.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Providers and environment","lvl3":""}},
4060
4060
  {"objectID":"1c83e72d0998d73b45f4a810514691aa3fb8979166eea4883ffe4bec728140d7","title":"Dictionary","url":"/docs/features/transcribe#dictionary","content":"A dictionary teaches the pipeline the words an engine cannot know — product\nnames, people's names, internal jargon — and how engines tend to mis-hear them:\n\n| Field | Required | Use |\n| --------- | -------- | ------------------------------------------------------------------------------------------------- |\n| term | yes | The correct spelling, written exactly like this in the output. |\n| heardAs | no | Mis-hearings, in any script. Each one found in a transcript becomes a candidate for replacement. |\n| meaning | no | What the term means, given to the decision model and the rewrite so they can tell term from word. |\n\nThe dictionary is used three ways: its terms are sent to engines that take a\nprompt or vocabulary, each heardAs found in the transcript becomes a\ncandidate repair, and the whole list is given to the rewrite model.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Dictionary","lvl3":""}},
4061
4061
  {"objectID":"bb0871357646359062274d9ec5e47b41771d77b2c86ccd2d29cf96f43a538b64","title":"Correction","url":"/docs/features/transcribe#correction","content":"Correction runs on a finished transcript (or on each final utterance when\nstreaming), in two stages:\nDictionary, guarded. Each candidate is put to the decision model as one\n question — did the speaker mean the term, or the ordinary word? — so a\n product name never rewrites the everyday word it sounds like. All\n candidates go in one tryDecide() call. With no decision provider, on a\n timeout (decideTimeoutMs, default 4000) or on any error, the dictionary is\n applied as is. guard: \"none\" always applies it. The guard asks for the\n default decision provider, and it sends the first 4,000 characters of the\n transcript to it. A lone OPENAI_API_KEY counts as one (it is also Whisper's\n key), so a host that set it only for Whisper and passes a dictionary sends the\n transcript text to OpenAI's Decisions endpoint, unless OPENAI_BASE_URL,\n OPENAI_API_BASE or credentials.openai.baseURL names a host other than https://api.openai.com (a gateway's\n key is not used for the default); see\n One key, two providers.\n SCALEDOWN_API_KEY, which no other provider reads, counts too: with it as the\n only decision key, the transcript text goes to ScaleDown, the last provider in\n the order.\nRewrite. A text model fixes spelling, punctuation and capitalisation,\n writes dictionary terms correctly, reconciles a second opinion and writes\n Indic-script words in Latin letters the way people type them (Hinglish,\n Tanglish …) — never translating them. transliterate: \"native\" keeps the\n original script. rewrite: false stops after the dictionary.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Correction","lvl3":""}},
4062
+ {"objectID":"b2c873c8d0abc15cbdf950fe97f97ef17d989ccf08709ef630fd83bdf0d5c57f","title":"When the rewrite runs, and on what","url":"/docs/features/transcribe#when-the-rewrite-runs-and-on-what","content":"With rewrite: \"auto\" (the default) each text is checked before the model is\ncalled, and the rewrite runs only when it has a job: a second opinion to\nreconcile, a dictionary term that was applied, non-Latin script when Latin\noutput is requested, or a sentence of four or more words with no terminal\npunctuation and punctuate on. An English sentence that arrived clean from\nthe engine therefore costs nothing after the dictionary, and steps says\nrewrite skipped · nothing to change. mode: \"always\" restores the old\nbehaviour.\n\nWith perSentence (the default) a final is split on terminal punctuation\nand the danda, and each sentence goes through the dictionary and its own\nrewrite decision, three sentences at a time. A Hindi sentence followed by an\nEnglish one is romanised and left alone respectively, the English sentence\nnever waits for the Hindi one, and steps reports per sentence (n) with\nhow many sentences were rewritten, skipped or kept. A second opinion is\nwhole-utterance by nature and keeps the single pass.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"When the rewrite runs, and on what","lvl3":""}},
4063
+ {"objectID":"b15237891f2e41a0379beb6c9d0ac198ff90257b0d263583683e8ee562c0de20","title":"The rewrite knows the language","url":"/docs/features/transcribe#the-rewrite-knows-the-language","content":"The detected language of each utterance picks the rewrite's rules. Indian\nlanguages, English and speech whose language is unknown keep the Hinglish /\nTanglish prompt above. Any other known language (Spanish, Japanese, Arabic …)\ngets a general post-editing prompt in that language: fix mis-hearings, keep\nthe language and its own script, never translate. When streaming, the last\ncontextUtterances corrected utterances travel with each rewrite as\nread-only context, so a name spelled one way stays spelled that way and the\nscript does not drift between sentences.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"The rewrite knows the language","lvl3":""}},
4064
+ {"objectID":"1862d4684a5da6ed5cda12038d6260de5fee3a97e9ac7214e2ccb6bce9cd2251","title":"Guards on what the engine said","url":"/docs/features/transcribe#guards-on-what-the-engine-said","content":"Two things a speech engine produces that were never said are caught before\nthe dictionary or the rewrite see the text, on every pass, live or file:\nA decoding loop (\"Same one. Same one. …\" for hundreds of words, which\n a long-form engine can fall into on a long or noisy chunk) is collapsed to\n two occurrences; natural repeats of up to three stay. steps reports\n repetition collapsed (n words). On a file, an utterance that was mostly\n loop is re-run as two halves, since the loop belongs to the chunk, not the\n speech.\nA prompt echo, an engine answering a pause with the vocabulary prompt\n it was given (\"Names and terms used: …\", or a list of two or more terms), is\n discarded as no text (prompt echo discarded) and the empty path takes\n over. A speaker who says a single term (\"Juspay.\") is kept. The term list is\n sent to every OpenAI-compatible server; set vocabularyPrompt: false on an\n endpoint that echoes it (OPENAI_STT_VOCABULARY_PROMPT=0 for the whisper\n slice).\nA near-silent utterance (under a quarter speech) whose whole output is a\n word or two in a script the session has not spoken is reported as silence,\n not text. With the speech detector on, so is a short utterance whose text\n would mean more than twelve words per second of detected speech.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Guards on what the engine said","lvl3":""}},
4065
+ {"objectID":"dd13b35056f05ed11abedce53e6b160e155c68375e86d164e720ac76bce9f12f","title":"Clean verbatim","url":"/docs/features/transcribe#clean-verbatim","content":"correction: { cleanup: \"clean\" } lets the rewrite remove fillers (\"um\",\na meaningless \"like\"), stutters (\"they they they\") and restarted false\nstarts, and never content; in that mode the rewrite may drop up to half the\nwords (maxDropRatio still overrides). The default, \"verbatim\", keeps\nevery word as said.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Clean verbatim","lvl3":""}},
4062
4066
  {"objectID":"7c55169273332aab63f54e94bae343516bd630e062d60a4d427d004b72ee287e","title":"Nothing is ever dropped","url":"/docs/features/transcribe#nothing-is-ever-dropped","content":"Every layer fails open, and none may lose words:\nA rewrite that drops more than maxDropRatio of the words is discarded and\n the dictionary-applied text is kept.\nA rewrite that times out, fails or returns nothing keeps the text it was\n given.\nA guard that times out or fails applies the dictionary as is.\nWhen streaming, a final pass that returns less than the live text keeps the\n live text, and an utterance no engine could read is reported as a silence\n event with its length rather than disappearing.\nraw always holds the engine's own text, so a caller can show or diff it.\n\nEach of those outcomes is written to steps, so a degraded path is visible\nrather than silent.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Nothing is ever dropped","lvl3":""}},
4063
- {"objectID":"c0207627f5f88b7914c2fbe84a641879ece05ae20f7e2194a9f0bc29c24cfd65","title":"Fallback and second opinion","url":"/docs/features/transcribe#fallback-and-second-opinion","content":"Fallback takes the request (or, when streaming, the utterance) when the\n primary engine cannot read it: when defaults to [\"unsure\", \"empty\"] — a\n low language-detection score, or no text — and \"error\" adds engine\n failures. engine.fallbackUsed says when it ran.\nSecond opinion runs a second engine on the same audio in parallel; the\n rewrite reconciles the two transcripts, taking each language's words from the\n engine that reads it better. When the primary reports an unsure language,\n or no text, the second opinion's text leads. With rewrite: false that is\n all it does — there is no reconciliation without the rewrite.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Fallback and second opinion","lvl3":""}},
4064
- {"objectID":"c903ddf3ec717cab3ff063e2bbb762f634ff09b05e5199c35d95283aba82f2f6","title":"Streaming","url":"/docs/features/transcribe#streaming","content":"Engines with a native stream use it; every other engine is streamed on the\nclient by a chunked adapter (an energy gate finds utterances, the current\nutterance is re-transcribed every intervalMs, and the prefix two passes agree\non is committed). streaming.mode: \"chunked\" forces the adapter.\n\n| Event | Carries |\n| ------------ | ---------------------------------------------------------------------------------------------- |\n| interim | The utterance's current text, split into committed (stable) and tail (may still change). |\n| language | The detected language and whether the engine was sure. |\n| final | The utterance's final text, segment and words, the engine, and whether the fallback took it. |\n| correcting | Partial text of the streamed rewrite. |\n| corrected | The corrected text, its raw, decisions, steps and timings. |\n| silence | An utterance no engine could read, with its length. |\n| error | A message, and recoverable: true when the stream carries on with the next utterance. |\n\nTuning (streaming): sampleRate (16000), endSilenceMs (750), intervalMs\n(900), softCutSeconds (16), maxUtteranceSeconds (28), onsetRms (0.006),\nprerollMs (350), minSpeechMs (250).","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Streaming","lvl3":""}},
4067
+ {"objectID":"c0207627f5f88b7914c2fbe84a641879ece05ae20f7e2194a9f0bc29c24cfd65","title":"Fallback and second opinion","url":"/docs/features/transcribe#fallback-and-second-opinion","content":"Fallback takes the request (or, when streaming, the utterance) when the\n primary engine cannot read it: when defaults to [\"unsure\", \"empty\"] — a\n low language-detection score, or no text — and \"error\" adds engine\n failures. engine.fallbackUsed says when it ran.\nSecond opinion runs a second engine on the same audio in parallel; the\n rewrite reconciles the two transcripts, taking each language's words from the\n engine that reads it better. When the primary reports an unsure language,\n or no text, the second opinion's text leads. With rewrite: false that is\n all it does — there is no reconciliation without the rewrite.\n secondOpinion: \"auto\" uses the fallback engine: with an Indic primary\n and an English-oriented fallback, every utterance the primary leads is also\n read by the fallback, and the rewrite takes Indian-language words from one\n and English words from the other. One extra engine call per utterance, run\n in parallel with the final pass.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Fallback and second opinion","lvl3":""}},
4068
+ {"objectID":"ec90cfb5585192e3852acfffcdbebb60d21a0781c27f42248a904fc24c298d7e","title":"Files through the live pipeline","url":"/docs/features/transcribe#files-through-the-live-pipeline","content":"By default a recording goes to the engine whole (chunking: \"engine\").\nWith chunking: \"utterances\" it goes through the same pipeline as live\ncaptions: the endpointer cuts it into utterances, each utterance gets the\nlanguage probe and its own engine, the dictionary with the decision guard and\nthe per-sentence correction, three utterances at a time\n(streaming.concurrency), with a failed final tried once more\n(streaming.finalRetries) and a 180 s engine timeout instead of the live\n30 s. The result has one segment per utterance with its language, and\nsteps reports how many utterances the fallback took and any the engines\ncould not read (reported, never dropped). A mixed-language recording gets\nthe right engine per utterance; an English-only one costs the same as\nbefore. Needs WAV, or ffmpeg on the server for other containers. The\nOpenAI-compatible route takes it as the chunking=utterances form field.\n\nThe same pipeline is available over the WebSocket with mode: \"file\" in the\nfirst frame: send the whole recording's PCM as fast as the socket takes it,\nthen {\"type\":\"end\"}, and receive final and corrected per utterance as\nthey finish. No request time limit applies, and up to 512 MB of audio\n(about 4.5 hours) may be queued. File-mode utterances are cut at 20 s (12 s\nat a pause) rather than the live 28 s.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Files through the live pipeline","lvl3":""}},
4069
+ {"objectID":"c45a8a1fd95d3e95bd6b505c9468e755ffa0ea88658c4e2d0a7c898ffd080e81","title":"Routing: the probe and the prior","url":"/docs/features/transcribe#routing-the-probe-and-the-prior","content":"With a fallback set, the engines are not tried one after the other:\nStreaming probe. The first pass of every utterance (from about 1.2 s of\n audio) runs the primary and the fallback in parallel. When the primary\n reports an unsure language, or nothing, and the fallback produced mostly\n Latin-script words, the fallback leads the utterance from that moment; a\n primary that is confident on two probes locks (or on one, when the\n fallback had no words to offer). By default the second pass probes again\n (lockAfterPasses: 2), so a Hindi utterance misjudged at 1.2 s is handed\n back to the Indic engine once it is sure, and words always beat an empty\n answer whatever the script. Verdicts that disagree buy one more probe: a\n language score on two or three seconds of audio jitters (an English clip\n scored −0.46, −0.16 and −0.44 on consecutive passes against a −0.18\n threshold), so one confident verdict after an unsure one never settles an\n utterance by itself. English spoken into an Indic engine shows Latin\n captions about 1.5 s after the first word instead of 5 s. The older rule\n (two unsure passes after 3 s; two confident passes rule it out for good,\n one only delays it) stays as a safety net for utterances the probe locked\n on the primary. lockAfterPasses keeps both engines running for more\n passes before locking; probe: false disables the probe.\nBatch probe. When the primary engine has a language-only model\n (lidModel on its endpoint; the IndicConformer wrapper serves\n <model>-lid), it is asked for a verdict on the first eight seconds, in\n about 200 ms, before anything is transcribed. Unsure sends the recording to\n the fallback without a full primary pass; confident runs the primary alone.\n A pinned language skips the probe.\nPrior.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Routing: the probe and the prior","lvl3":""}},
4070
+ {"objectID":"409e3d120b5efcbe74442c8871c3f5072f48e04b13c94c6d54da0a7c9707f448","title":"Output languages","url":"/docs/features/transcribe#output-languages","content":"Every utterance can also be written in other languages and scripts:\n\nEach entry is a BCP-47 tag: a language (es, ta), optionally a script\n(hi-Latn is Hindi written in Latin letters, the way people type it) and a\nregion (fr-CA). \"source\" is the transcript itself. Up to eight per\nrequest.\nOne call per utterance. A single structured call renders every\n requested language (four per call; more run in parallel). It gets both the\n native-script engine text and its corrected form, so a translation is never\n made from romanised text alone, the earlier utterances and their renderings\n as context (contextUtterances, default 3), and the dictionary terms as a\n glossary kept verbatim (glossary: false turns that off).\nNo call when there is nothing to do. \"source\", and a target in the\n transcript's own language and script, are the corrected text\n (status: \"same-language\").\nGuards. An answer that is empty, in the wrong script for its tag, or\n the source handed back untranslated is refused; a refused target gets one\n retry on its own, then status: \"rejected\" with a reason.\nNever costs the transcript. Rendering runs after the text is settled\n and fails open: text, final and corrected are what they would be\n without it. Statuses: ok, same-language, timeout, error,\n rejected, and partial for a long recording where some batches rendered.\nLong recordings are rendered per segment (20 segments per call), so\n outputs.<tag>.segments line up with result.segments and their timings.\nModel. output.provider / output.model, else\n NEUROLINK_STT_OUTPUT_PROVIDER / NEUROLINK_STT_OUTPUT_MODEL, else the\n rewrite's model. Translation quality follows the model; a stronger one than\n the rewrite's is usually worth it. timeoutMs defaults to 15000.\n\nWhen streaming, each utterance's renderings arrive as output events, one\nper language, after its corrected event (basedOn: \"corrected\") or after\nthe final when correction is off.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Output languages","lvl3":""}},
4071
+ {"objectID":"7364245a2a809591639c4eead7f5437f5db4af630a8cfdf8309791f1bd22216a","title":"Sessions: one call, many listeners","url":"/docs/features/transcribe#sessions-one-call-many-listeners","content":"Each speaker is one transcribeStream() (call platforms give one track per\nparticipant). The session renders each utterance once into the union of\nthe languages its listeners read right now: ten listeners in Spanish cost one\nrendering, a language nobody reads costs nothing. A listener receives every\nevent of the speakers it follows, tagged with speaker, and only the\noutput events of its own languages; interim: false drops live captions.\nsubscription.update({ languages }) changes them from the next utterance.\nA listener who joins late gets the backlog — the last backlog (default 50)\nutterances with every rendering already made for them; a language added\nlater applies from the next utterance on.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Sessions: one call, many listeners","lvl3":""}},
4072
+ {"objectID":"c903ddf3ec717cab3ff063e2bbb762f634ff09b05e5199c35d95283aba82f2f6","title":"Streaming","url":"/docs/features/transcribe#streaming","content":"Engines with a native stream use it; every other engine is streamed on the\nclient by a chunked adapter (a speech detector finds utterances, the current\nutterance is re-transcribed every intervalMs, and the prefix two passes agree\non is committed). streaming.mode: \"chunked\" forces the adapter.\n\n| Event | Carries |\n| ------------ | ---------------------------------------------------------------------------------------------- |\n| interim | The utterance's current text, split into committed (stable) and tail (may still change). |\n| language | The detected language and whether the engine was sure. |\n| final | The utterance's final text, segment and words, the engine, and whether the fallback took it. |\n| correcting | Partial text of the streamed rewrite. |\n| corrected | The corrected text, its raw, decisions, steps and timings. |\n| silence | An utterance no engine could read, with its length. |\n| error | A message, and recoverable: true when the stream carries on with the next utterance. |\n\nTuning (streaming): sampleRate (16000), endSilenceMs (750), intervalMs\n(900), softCutSeconds (16), maxUtteranceSeconds (28), onsetRms (0.006),\nprerollMs (350), minSpeechMs (250).\n\nThe speech detector (streaming.vad). By default the adapter runs\nSilero VAD over the stream: a 2.3 MB\nONNX model (MIT, 6000+ languages) that scores every 32 ms frame with the\nprobability that it is a human voice, which an energy gate cannot tell from a\nchair, a keyboard or a far-away TV. The model ships with the package under\nmodels/silero-vad/ and runs through the optional dependency\nonnxruntime-node (prebuilt for Linux, macOS and Windows on x64 and arm64,\nabout 0.1 ms per frame).","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Streaming","lvl3":""}},
4073
+ {"objectID":"0979959c14d3f9f8eebfdad5cbd02ba8be4456df4657de578c9b5688265c80e2","title":"Getting good results","url":"/docs/features/transcribe#getting-good-results","content":"The layers above are generic; which engine to point them at, and what to\nexpect from each, is not. The numbers below were measured on 2026-10-07 on a\nlaptop (Apple Silicon) through neurolink serve: Whistle in-process, an\nIndicConformer wrapper on the same machine, and Qwen3-ASR (\"spark\") on a GPU\nbox behind LiteLLM; the guard is a System One decision model, the rewrite an\nopen-fast chat model, both over the network. Your numbers will differ, the\nratios will not.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Getting good results","lvl3":""}},
4074
+ {"objectID":"2ef36bc87e26d52039202c127a27ca89ded6d7cfbdf461c00a2f0ecf58cba498","title":"Pick the engine by what is said","url":"/docs/features/transcribe#pick-the-engine-by-what-is-said","content":"| Engine | Good at | Weak at | Batch speed |\n| --------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | ----------------- |\n| whistle (built in, no network) | English and six European languages, clean speech, long files (5 min in 15 s) | Anything else: it will produce fluent English that is not what was said | ~18–20× real time |\n| IndicConformer (indic endpoint) | 22 Indian languages in native script, code-mixed Hindi/English, language auto-detect | English-only or non-Indian speech: it reports languageDetected: false and returns little | ~28× real time |\n| Qwen3-ASR (spark endpoint) | Mixed-language speech, Hinglish in Latin letters, long files, jargon | Slowest of the three; Tamil/Kannada phonetics are approximate | ~5–9× real time |\n| elevenlabs-stt | Hosted, 90+ languages, speaker diarization | Needs a key and the network | network bound |\n\nTwo patterns cover most rooms:\nIndian languages with English in the mix: provider: \"indic\" with\n fallback: { provider: \"spark\" }. Indic answers when it is sure; when it\n reports languageDetected: false or comes back empty (an English-only\n meeting, for instance) the fallback takes over and the result says so in\n steps (fallback spark (unsure)). Measured on a 45 s English meeting:\n Indic alone returned nothing, Indic + fallback returned all 156 words.\nEnglish, offline, no keys: whistle, which is also what you get with\n nothing configured.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Pick the engine by what is said","lvl3":""}},
4075
+ {"objectID":"279d6a296f2a30ca5d2ab6c821931a377d7703f62e75ca5f3f636a9903b807c8","title":"A file is transcribed whole; a stream is cut into utterances","url":"/docs/features/transcribe#a-file-is-transcribed-whole-a-stream-is-cut-into-utterances","content":"transcribe() and both POST routes hand the entire recording to the\nengine in one request. Whistle and the Indic wrapper split long audio on\nsilence internally; Qwen3-ASR takes it whole; none of them need you to chunk,\nand the measured 5-minute file came back complete from all three (851, 849 and\n849 words). The one limit is the upload: bodyParser.rawMaxSize (64 MB; neurolink serve --max-upload) on the\nmultipart route and the JSON body limit (10 MB) on the envelope route, so send long\nrecordings as multipart, or raise the limit. Correction then runs\nper segment line (the engine's segments, or sentences), so a dictionary\nterm found in minute four is judged on its own sentence, not on five minutes\nof context; set NEUROLINK_STT_CORRECT_WHOLE=1 to correct the whole text in\none pass instead.\n\ntranscribeStream() and the WebSocket are what the demo's Live page uses,\nand they work differently: audio arrives as 20 ms blocks, a speech detector\n(Silero VAD, or an energy gate with a noise-floor estimate when it is not\navailable) finds utterances (speech onset, 750 ms of silence to\nclose, soft cut at 16 s, hard cut at 28 s with the remainder carried over),\nthe open utterance is re-transcribed every 900 ms (interim events, with the\nprefix two passes agreed on as committed), and when it closes you get one\nfinal. The dictionary, the guard and the rewrite then run on that\nutterance and come back as one corrected event, which is why the demo\nshows a sentence appear, then change a word or two about a second later. A\nstream never waits for the whole recording, and nothing is held back: an\nutterance no engine could read is reported as silence with its length.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"A file is transcribed whole; a stream is cut into utterances","lvl3":""}},
4076
+ {"objectID":"00b2cc27d55e749e46e129f30116338a71d856054c5c64c4dab731c3cdb36b82","title":"Dictionary and correction: what they cost and when to use which","url":"/docs/features/transcribe#dictionary-and-correction-what-they-cost-and-when-to-use-which","content":"| Configuration | Adds to a 10 s clip | What you get |\n| -------------------------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------- |\n| dictionary only (correction: { rewrite: false }) | ~0.1 s per candidate | Each heardAs hit is judged by the decision model (term or literal) and replaced when a term |\n| dictionary + rewrite (correction: { enabled: true }) | +0.4–1.7 s | The judged text is then rewritten for punctuation, casing and script by a chat model |\n| correction.guard: \"none\" | ~0 s | Dictionary applied without asking; use when every alias is unambiguous |\n| transliterate: \"latin\" | inside the rewrite | Hinglish in Latin letters from a Devanagari engine (Indic → khaali meeting hai at three pm) |\n\nWrite heardAs in the script the engine actually emits: Devanagari aliases\nfor Indic (\"न्यूरोलिंक\"), Latin for Whistle and Qwen3-ASR (\"nurellink\",\n\"neural link\"). A meaning helps the guard decide between a product name and\na common word. The decision model is asked per candidate and fails open: with\nno decision provider configured the dictionary is applied as is and every\ndecision carries a note saying so.\n\nThe rewrite is the one layer that can change words, so it is bounded: a rewrite\nthat drops more than a quarter of the words, times out or errors is discarded\nand steps says kept text. With a weak engine on the wrong language the\nrewrite will \"fix\" nonsense into fluent nonsense (Whistle on Tamil becomes\n\"Now like a meeting, Iraqi Mondo Muniki…\"); the fix is the engine, not the\ncorrector.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Dictionary and correction: what they cost and when to use which","lvl3":""}},
4077
+ {"objectID":"d7ea2263d0fe38ffee58e800231f03a127969d1c73e983587d1960aefb52a32e","title":"Latency to expect","url":"/docs/features/transcribe#latency-to-expect","content":"Batch, POST /api/agent/transcribe, median of three runs, server-side\ntimings in ms, 9–11 s clips:\n\n| Engine | Clip | Engine | Guard | Rewrite | Total | × real time |\n| ------- | -------- | -----: | ----: | ------: | ----: | ----------: |\n| whistle | English | 535 | 145 | 564 | 1636 | 6.5× |\n| whistle | Hinglish | 457 | – | 507 | 962 | 9.4× |\n| indic | Hinglish | 323 | 107 | 1162 | 1607 | 5.6× |\n| indic | Tamil | 356 | – | 897 | 1282 | 8.3× |\n| spark | English | 1187 | – | 445 | 1809 | 5.9× |\n| spark | Hinglish | 1667 | – | 918 | 2447 | 3.7× |\n| spark | Tamil | 2067 | – | 444 | 2781 | 3.8× |\n\nRaw (no correction) is the Engine column alone: Whistle ≈ 0.5 s, Indic ≈ 0.35 s,\nSpark 1.2–2.1 s for ten seconds of audio. The guard column is blank when no\ndictionary alias was heard; it is ~100–150 ms when one was.\n\nStreaming, WebSocket, 100 ms frames paced in real time:\n\n| Engine | First interim | Final after end of speech | Corrected after final |\n| ------- | ------------: | ------------------------: | --------------------: |\n| whistle | ~1.0 s | ~0.5 s | ~0.5 s |\n| indic | ~0.8 s | \\<0.1 s | 0.7–1.6 s |\n| spark | ~1.1 s | 0.7–1.7 s | 0.5–0.7 s |\n\n\"First interim\" includes the 350 ms pre-roll and the first engine pass; the\ndemo shows text about a second after you start talking, the sentence settles\nwithin a second of the pause, and the corrected version replaces it under two\nseconds later.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Latency to expect","lvl3":""}},
4065
4078
  {"objectID":"336d246e949117f5c2b68c49a693582dd2395b7515979d6566e5d07a7a65fd75","title":"Finding the end of a spoken turn in a browser","url":"/docs/features/transcribe#finding-the-end-of-a-spoken-turn-in-a-browser","content":"@juspay/neurolink/voice/endpointer exports the energy gate the chunked adapter\nuses. It is a self-contained module (no Node built-ins, no other imports), so a\nbrowser app can ask where a spoken turn starts and ends without running a speech\nengine and without bundling the voice entry.\nstart is tentative: a click also starts an utterance, and a drop follows\n about endSilenceMs later. end carries a reason: silence, softCut,\n hardCut or flush.\nFeed it a continuous stream of samples. The noise floor and every timer count\n samples, so polling an AnalyserNode on a timer gives wrong timings.\nThe noise floor starts at a fixed value and rises by at most about 2x per\n second. In a loud room the first two seconds can produce an utterance made of\n noise, so keep the gate running for about two seconds before trusting events.\nA signal that never dips (a fan, a steady hum) is absorbed as noise after a\n few seconds. It measures energy, not speech: background talk keeps a turn open\n until a cut.\nsoftCutSeconds (16) ends a turn at the first gap of 320 ms or more once it is\n that long. Raise it, together with maxUtteranceSeconds, when you want one\n recording per turn.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Finding the end of a spoken turn in a browser","lvl3":""}},
4066
4079
  {"objectID":"dee2eadc8e2a8712fe874329eae3639423ba11bb01631e21956abde47b3c7a3f","title":"CLI","url":"/docs/features/transcribe#cli","content":"The text output is the transcript, then a dim line with the engine, the\nlanguage (detected or unsure), the audio length, each layer's time and the\nsteps. --format json prints the whole TranscribeResult (one event per line\nwith --stream); --format srt|vtt builds subtitle cues from the segments, or\nfrom the word timings. --stream decodes the file to 16 kHz mono PCM16 (WAV in\nprocess, anything else through ffmpeg) and runs it through\ntranscribeStream(). Credentials are env-only, as for every CLI command. The\nfull flag table is in the CLI reference.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"CLI","lvl3":""}},
4067
4080
  {"objectID":"3529c7278a91963a4075c1f8b880ed143b9276ea52089e84639f7c658685bae8","title":"Server routes","url":"/docs/features/transcribe#server-routes","content":"createAllRoutes() / registerAllRoutes() mount two routes on every adapter\n(Hono, Express, Fastify, Koa); createTranscribeRoutes(basePath, options)\nmounts them on their own. Neither accepts credentials: engines use the\nserver's own configuration, and a credentials field in a body is ignored.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"Server routes","lvl3":""}},
4068
4081
  {"objectID":"081502d8e5cd1a1c0f622a7d6118ac263dc88c9d07d8ef96402d71b4957a7172","title":"POST /api/agent/transcribe","url":"/docs/features/transcribe#post-apiagenttranscribe","content":"JSON body with exactly one audio source:\n\n| Source | Rules |\n| ----------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| audio | Base64, or a data:audio/...;base64, URL. Bounded by the adapter's JSON body limit (bodyParser.jsonLimit, 10 MB by default). |\n| audioUrl | An http(s) URL the server downloads through the SSRF guard (private addresses and redirects refused), capped in size. |\n| audioPath | A path on the server. Refused by default; allowed only inside the directories in transcribe.allowedAudioRoots. |\n\nThe response is the TranscribeResult, in the adapters' usual\n{ data, metadata } envelope. Errors use the usual error shape: 400 for an\ninvalid body or an unsupported provider, language or format, 403 for a refused\naudioPath, 413 for audio over the limit, 500 for an engine failure.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"POST /api/agent/transcribe","lvl3":""}},
4069
4082
  {"objectID":"32a917bb7037f51d56da847c36cba9409b8d0a8951cf84aa19822c24a74b8f3f","title":"POST /v1/audio/transcriptions (OpenAI-compatible)","url":"/docs/features/transcribe#post-v1audiotranscriptions-openai-compatible","content":"multipart/form-data, the way OpenAI's API takes it, so an OpenAI client\npointed at http://<host>:<port>/v1 works unchanged:\n\n| Field | Meaning |\n| --------------------------- | ----------------------------------------------------------------------------------------------------------- |\n| file | The audio (required). Its name or type sets the container. |\n| model | <provider> or <provider>/<model>. OpenAI's own model names and auto mean the server's default engine. |\n| language, prompt | As in the SDK. |\n| response_format | json ({ \"text\" }, the default), text (plain text) or verbose_json. |\n| timestamp_granularities[] | word requests word timings. |\n| temperature | Accepted and ignored. |\n| provider | Extension: the STT provider, overriding model. |\n| diarize | Extension: true labels speakers. |\n| dictionary | Extension: the dictionary as a JSON string. |\n| correct | Extension: true runs the correction layer. |\n| fallback | Extension: fallback STT provider.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"POST /v1/audio/transcriptions (OpenAI-compatible)","lvl3":""}},
4070
- {"objectID":"7f04ab4c7a4895ae38a057d50ee353f51a84b959feb713784f0779b9d0319798","title":"WebSocket /v1/audio/transcriptions/stream","url":"/docs/features/transcribe#websocket-v1audiotranscriptionsstream","content":"The route adapters do not handle WebSocket upgrades, so the streaming endpoint\nattaches to the Node HTTP server you own:\nThe first frame is text: a JSON config with the JSON route's fields\n minus the audio, plus streaming (for example { \"language\": \"auto\",\n\"streaming\": { \"sampleRate\": 16000 } }).\nThen binary frames of PCM16LE mono audio at that sample rate.\nA text frame { \"type\": \"end\" }, or closing the socket, ends the audio.\n\nThe server sends one JSON text frame per TranscribeStreamEvent and closes\nwith 1000 when the stream is done; an invalid config gets an error event and\nclose code 1008.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"WebSocket /v1/audio/transcriptions/stream","lvl3":""}},
4083
+ {"objectID":"7f04ab4c7a4895ae38a057d50ee353f51a84b959feb713784f0779b9d0319798","title":"WebSocket /v1/audio/transcriptions/stream","url":"/docs/features/transcribe#websocket-v1audiotranscriptionsstream","content":"The route adapters do not handle WebSocket upgrades, so the streaming endpoint\nattaches to the Node HTTP server you own:\nThe first frame is text: a JSON config with the JSON route's fields\n minus the audio, plus streaming (for example { \"language\": \"auto\",\n\"streaming\": { \"sampleRate\": 16000 } }).\nThen binary frames of PCM16LE mono audio at that sample rate.\nA text frame { \"type\": \"end\" }, or closing the socket, ends the audio.\n { \"type\": \"output\", \"languages\": [\"es\", \"fr\"] } changes the output\n languages from the next utterance on; any other text frame ends the audio.\n\nThe server sends one JSON text frame per TranscribeStreamEvent and closes\nwith 1000 when the stream is done; an invalid config gets an error event and\nclose code 1008.\n\nSessions over the socket. A speaker connects to\n/v1/audio/transcriptions/stream?session=<id>&speaker=<name> and uses the\nprotocol above. A listener connects to\n/v1/audio/transcriptions/stream/subscribe?session=<id> (the\nsubscribePath option; false turns sessions off) and sends\n{ \"type\": \"subscribe\", \"languages\": [\"es\"], \"speakers\": [\"alice\"] }, again at\nany time to change them. Listeners get the speaker's events tagged with\nspeaker, and only the output events of their languages. A session lives\nwhile a speaker or a listener is connected; the same auth token guards both\npaths.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"WebSocket /v1/audio/transcriptions/stream","lvl3":""}},
4084
+ {"objectID":"8fbe8bc1159cfcafddcfac166492577e5e8cac318dc09634d14451b4b30b9856","title":"OpenAI Realtime transcription: /v1/realtime?intent=transcription","url":"/docs/features/transcribe#openai-realtime-transcription-v1realtimeintenttranscription","content":"The same live pipeline, behind OpenAI's Realtime transcription protocol, so a\nclient that already speaks it needs no NeuroLink adapter. With LiveKit Agents\n(livekit-plugins-openai, tested on 1.3.11):\n\nWhat the client sends:\n\n| Event | Handling |\n| -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| session.update (session.type: \"transcription\", session.audio.input) or the beta transcription_session.update | Sets the rate (format.rate, default 24000; PCM16 only), the model (a profile), language and prompt. Answered with session.updated / transcription_session.updated. A running stream keeps the settings it started with. |\n| input_audio_buffer.append | Base64 PCM16 mono at the session's rate. |\n| input_audio_buffer.commit | Closes the open utterance. Not needed: an audio gap of 0.7 s (a muted mic) closes it too.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"OpenAI Realtime transcription: /v1/realtime?intent=transcription","lvl3":""}},
4085
+ {"objectID":"a5ebafcb216e0f964c4597dd78f1616a5641819180b994f22b748870d402aa4e","title":"STT evaluation: POST /v1/evals/stt","url":"/docs/features/transcribe#stt-evaluation-post-v1evalsstt","content":"A call can run two STTs on the same audio: the one people see (primary) and\none run only to be compared (shadow). The sender posts both transcripts here\nin batches, and reads back how far apart they were per speaker. The server's\nAPI key guards it like every other route.\n\nA batch may hold up to 5,000 segments, and an empty final batch is valid.\nResends are idempotent: an id already held is replaced and counted as\nupdated. GET /v1/evals/stt lists calls, most recent first.\nGET /v1/evals/stt/:callId returns the per-speaker comparison\n(?segments=true adds the segments in time order):\n\n| Field | Meaning |\n| ------------------------------------------- | ----------------------------------------------------------------------------------------- |\n| speakers_compared[].disagreement | Word edit distance between the two STTs / the primary's words, on normalized text |\n| speakers_compared[].phonetic_disagreement | The same on phonetic word keys, so Devanagari against Latin for the same words agrees |\n| speakers_compared[].primary / .shadow | model, segments, words, languages reported, mean_latency_s (final after speech) |\n| speakers_compared[].method | full, or windowed (5-minute windows) for a speaker over 4,000 words |\n| overall | Both measures summed over every speaker |\n\nNeither side is ground truth: a disagreement says the STTs differ, not which\none is right. Calls live in memory unless NEUROLINK_STT_EVAL_DIR (or the\nevals.dir route option) names a directory, where each batch is appended to\none JSON-lines file per call and replayed on start. evals: false in the\ntranscribe route options removes the routes.\n\nLangfuse.","hierarchy":{"lvl0":"Features","lvl1":"The transcribe inference type","lvl2":"STT evaluation: POST /v1/evals/stt","lvl3":""}},
4071
4086
  {"objectID":"499945493bd6604fe9de0b7511127a8f248c00e05724562e9c6c1cd6bf63aa97","title":"Text-to-Speech (TTS) Integration Guide","url":"/docs/features/tts","content":"Text-to-Speech (TTS) Integration Guide\n\nNeuroLink provides integrated Text-to-Speech (TTS) capabilities, allowing you to generate high-quality audio from text prompts or AI-generated responses. This feature is perfect for voice assistants, accessibility features, narration, podcasts, and more.\n\nOverview\n\nKey Features:\nMultiple providers - Google Cloud TTS, OpenAI TTS, ElevenLabs, Azure TTS, Fish Audio, Cartesia, and 60db\nHigh-quality voices - Neural, Wavenet, Standard, and multilingual voice types\nMultiple languages - 50+ voices across 10+ languages\nFlexible audio formats - MP3, WAV, OGG/Opus\nVoice customization - Adjust speed, pitch, and volume\nTwo synthesis modes - Direct text-to-speech OR AI response synthesis\nProduction-ready - Works with Google Cloud, OpenAI, ElevenLabs, Azure, Fish Audio, Cartesia, and 60db\n\nQuick Start\n\nInstallation\n\nTTS support is built into NeuroLink. No additional installation required.\n\nEnvironment Setup\n\nSet the appropriate environment variables for your chosen TTS provider:\n\nGoogle API Key Configuration:\n\nIf using API key authentication for Google, enable both APIs in Google Cloud Console:\nNavigate to \"APIs & Services\" > \"Credentials\"\nCreate or select your API key\nUnder \"API restrictions\", enable:\nGenerative Language API (for Gemini)\nCloud Text-to-Speech API (for TTS)\n\nBasic Usage\n\nCLI:\n\nSDK:\n\nSupported Providers\n\nTTS is available through the following providers:\n\n| Provider | Authentication | Voices / Models | Notes |\n| -------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| google-ai | Service Account (GOOGLE_APPLICATION_CREDENTIALS) | 50+ voices (Neural2, Wavenet, Standard) | Same auth as vertex (TTS uses Google Cloud Text-to-Speech client) |\n| vertex | Service Account (GOOGLE_APPLICATION_CREDENTIALS) | 50+ voices (Neural2, Wavenet, Standard) | Recommended for production |\n| openai-tts | API Key (OPENAI_API_KEY) | 6 voices: alloy, echo, fable, onyx, nova, shimmer; models: tts-1, tts-1-hd | Good default quality |\n| elevenlabs | API Key (ELEVENLABS_API_KEY) | Multilingual voices; model: eleven_multilingual_v2 | High-quality multilingual synthesis |\n| azure-tts | API Key (AZURE_SPEECH_KEY + region AZURE_SPEECH_REGION) | Neural voices with SSML support | Enterprise-grade Azure Speech |\n| fish-audio | API Key (FISH_AUDIO_API_KEY) | 14 languages, voice cloning (15 s reference); models: s1 (default), speech-1.6, speech-1.5 | Low-cost, ~80% cheaper than ElevenLabs — see provider guide |\n| cartesia | API Key (CARTESIA_API_KEY) | Cartesia voice library, English-first; models: sonic-3.6 (default) | Low-latency Sonic models — see provider guide. Synchronous /tts/bytes; the WebSocket streaming flow is exposed separately via CartesiaStream in the voice server. |\n| sixtydb | Workspace API key (SIXTYDB_API_KEY) | Quality and fast workspace voice catalogs | WAV or PCM16 at 24 kHz — see ","hierarchy":{"lvl0":"Features","lvl1":"Text-to-Speech (TTS) Integration Guide","lvl2":"","lvl3":""}},
4072
4087
  {"objectID":"9384b791f5d32ea2c126e7a7a7a8852d80e56954c061aba2d9e493b0a9c25f2b","title":"Text-to-Speech (TTS) Integration Guide","url":"/docs/features/tts#text-to-speech-tts-integration-guide","content":"NeuroLink provides integrated Text-to-Speech (TTS) capabilities, allowing you to generate high-quality audio from text prompts or AI-generated responses. This feature is perfect for voice assistants, accessibility features, narration, podcasts, and more.","hierarchy":{"lvl0":"Features","lvl1":"Text-to-Speech (TTS) Integration Guide","lvl2":"Text-to-Speech (TTS) Integration Guide","lvl3":""}},
4073
4088
  {"objectID":"9cc14837132bc1ce344a44ef8c73aa1b77fe9b81356e3f5e565115a158d47618","title":"Overview","url":"/docs/features/tts#overview","content":"Key Features:\nMultiple providers - Google Cloud TTS, OpenAI TTS, ElevenLabs, Azure TTS, Fish Audio, Cartesia, and 60db\nHigh-quality voices - Neural, Wavenet, Standard, and multilingual voice types\nMultiple languages - 50+ voices across 10+ languages\nFlexible audio formats - MP3, WAV, OGG/Opus\nVoice customization - Adjust speed, pitch, and volume\nTwo synthesis modes - Direct text-to-speech OR AI response synthesis\nProduction-ready - Works with Google Cloud, OpenAI, ElevenLabs, Azure, Fish Audio, Cartesia, and 60db","hierarchy":{"lvl0":"Features","lvl1":"Text-to-Speech (TTS) Integration Guide","lvl2":"Overview","lvl3":""}},
@@ -4421,7 +4436,7 @@
4421
4436
  {"objectID":"898a6061dd9a2d929484be906c3796feeaeeb1e71ed44ba99ad0cc02e113a3a1","title":"Engine selection","url":"/docs/getting-started/environment-variables#engine-selection","content":"Engine selection","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"Engine selection","lvl3":""}},
4422
4437
  {"objectID":"ee1457f8c17966cc8236af1a93b3f9164e8030bfc61f752930a117da9650ad18","title":"OpenAI-compatible STT (whisper)","url":"/docs/getting-started/environment-variables#openai-compatible-stt-whisper","content":"OpenAI-compatible STT (whisper)","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"OpenAI-compatible STT (whisper)","lvl3":""}},
4423
4438
  {"objectID":"77213eac36b01d5852269a6c613314d116c4a21728a962b89e0ba44daa026f55","title":"Correction layer","url":"/docs/getting-started/environment-variables#correction-layer","content":"The dictionary guard uses whichever decide provider is configured (see the\ndecision providers at the top of this page) and fails open without one.","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"Correction layer","lvl3":""}},
4424
- {"objectID":"6428bdb4cd726fdee39cb14f1f4abad548303cda9f7dd61d40034ca0cb1d3bb8","title":"Whistle (built-in local engine)","url":"/docs/getting-started/environment-variables#whistle-built-in-local-engine","content":"Whistle runs on this machine in WebAssembly. Its three files (about 17 MB,\npinned Hugging Face revisions verified by SHA-256) ship inside the package\nunder models/whistle/, so nothing is downloaded. A download happens only\nwhen that copy is missing, into ~/.neurolink/models/whistle.\n\n| Variable | Default | Description |\n| --------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------- |\n| NEUROLINK_STT_PROVIDER | first configured, else Whistle | Default STT provider, alias or named endpoint |\n| NEUROLINK_STT_MODEL | provider default | Model for the env-chosen provider |\n| NEUROLINK_STT_LANGUAGE | auto | Default language; auto lets the engine detect |\n| NEUROLINK_STT_ENDPOINTS | - | JSON map of named OpenAI-compatible STT endpoints |\n| NEUROLINK_STT_REWRITE_PROVIDER | instance default | Text provider for the correction rewrite |\n| NEUROLINK_STT_REWRITE_MODEL | provider default | Model for the correction rewrite |\n| OPENAI_STT_API_KEY | OPENAI_API_KEY (api.openai.com only) | Key for the OpenAI-compatible STT endpoint; a custom base URL never receives OPENAI_API_KEY |\n| OPENAI_STT_BASE_URL | https://api.openai.com/v1 | OpenAI-compatible /audio/transcriptions base URL","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"Whistle (built-in local engine)","lvl3":""}},
4439
+ {"objectID":"6428bdb4cd726fdee39cb14f1f4abad548303cda9f7dd61d40034ca0cb1d3bb8","title":"Whistle (built-in local engine)","url":"/docs/getting-started/environment-variables#whistle-built-in-local-engine","content":"Whistle runs on this machine in WebAssembly. Its three files (about 17 MB,\npinned Hugging Face revisions verified by SHA-256) ship inside the package\nunder models/whistle/, so nothing is downloaded. A download happens only\nwhen that copy is missing, into ~/.neurolink/models/whistle.\n\n| Variable | Default | Description |\n| ---------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------- |\n| NEUROLINK_STT_PROVIDER | first configured, else Whistle | Default STT provider, alias or named endpoint |\n| NEUROLINK_STT_MODEL | provider default | Model for the env-chosen provider |\n| NEUROLINK_STT_LANGUAGE | auto | Default language; auto lets the engine detect |\n| NEUROLINK_STT_ENDPOINTS | - | JSON map of named OpenAI-compatible STT endpoints |\n| NEUROLINK_STT_REWRITE_PROVIDER | instance default | Text provider for the correction rewrite |\n| NEUROLINK_STT_REWRITE_MODEL | provider default | Model for the correction rewrite |\n| NEUROLINK_STT_OUTPUT_PROVIDER | the rewrite's provider | Text provider for output-language renderings |\n| NEUROLINK_STT_OUTPUT_MODEL | the rewrite's model | Model for","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"Whistle (built-in local engine)","lvl3":""}},
4425
4440
  {"objectID":"e321f630560a5f6b967367da94ea47edaec5da7b8e48d2b1ae8fe2e94f3dc1a0","title":"Complete .env File Example","url":"/docs/getting-started/environment-variables#complete-env-file-example","content":"Complete .env File Example","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"Complete .env File Example","lvl3":""}},
4426
4441
  {"objectID":"93640d5dca31cd6875459e2a54309f52fa0e81dc5190aaf987e6840251eefab9","title":"Docker/Container Configuration","url":"/docs/getting-started/environment-variables#dockercontainer-configuration","content":"Docker/Container Configuration","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"Docker/Container Configuration","lvl3":""}},
4427
4442
  {"objectID":"cd706105fb9ac4dc67aa40da22162f639f4a17eebd9deb6286e9f988c5fb278d","title":"CI/CD Configuration","url":"/docs/getting-started/environment-variables#cicd-configuration","content":"CI/CD Configuration","hierarchy":{"lvl0":"Getting Started","lvl1":"🔧 Environment Variables Configuration Guide","lvl2":"CI/CD Configuration","lvl3":""}},
@@ -9861,7 +9876,7 @@
9861
9876
  {"objectID":"708cea96fded6a3d6b4ae7e87e0edd070731d631cb4d87c2684792a82addbb0c","title":"Example: Restrictive CORS","url":"/docs/reference/server-configuration#example-restrictive-cors","content":"Example: Restrictive CORS","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Example: Restrictive CORS","lvl3":""}},
9862
9877
  {"objectID":"3706f16eed022d7b144945eae7c4e22925c2d8aa62896bbc81097cab7b4875e2","title":"Rate Limit Configuration","url":"/docs/reference/server-configuration#rate-limit-configuration","content":"| Option | Type | Default | Description |\n| -------------- | ---------- | ------------------------ | ------------------------------------------ |\n| enabled | boolean | true | Enable rate limiting |\n| windowMs | number | 900000 (15 min) | Time window in milliseconds |\n| maxRequests | number | 100 | Maximum requests per window |\n| message | string | \"Too many requests...\" | Error message when limit exceeded |\n| skipPaths | string[] | [] | Paths to exclude from rate limiting |\n| keyGenerator | function | IP-based | Custom function to generate rate limit key |","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Rate Limit Configuration","lvl3":""}},
9863
9878
  {"objectID":"06f1efd322182e016ace893db5f7332ce70ca77362234b7d38a3fc3d924264b4","title":"Example: Custom Rate Limiting","url":"/docs/reference/server-configuration#example-custom-rate-limiting","content":"Example: Custom Rate Limiting","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Example: Custom Rate Limiting","lvl3":""}},
9864
- {"objectID":"7787f42e969a27e990c15b1b673f04d01ad837b5f49d95006f73e61b417d91a0","title":"Body Parser Configuration","url":"/docs/reference/server-configuration#body-parser-configuration","content":"| Option | Type | Default | Description |\n| ------------ | --------- | -------- | ------------------------------- |\n| enabled | boolean | true | Enable body parsing |\n| maxSize | string | \"10mb\" | Maximum body size |\n| jsonLimit | string | \"10mb\" | JSON body size limit |\n| urlEncoded | boolean | true | Enable URL-encoded body parsing |","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Body Parser Configuration","lvl3":""}},
9879
+ {"objectID":"7787f42e969a27e990c15b1b673f04d01ad837b5f49d95006f73e61b417d91a0","title":"Body Parser Configuration","url":"/docs/reference/server-configuration#body-parser-configuration","content":"| Option | Type | Default | Description |\n| ------------ | --------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |\n| enabled | boolean | true | Enable body parsing |\n| maxSize | string | \"10mb\" | Maximum body size |\n| jsonLimit | string | \"10mb\" | JSON body size limit |\n| rawMaxSize | string | maxSize, else \"64mb\" | Largest raw upload (multipart/form-data, audio/*, application/octet-stream); neurolink serve --max-upload 128mb |\n| urlEncoded | boolean | true | Enable URL-encoded body parsing |","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Body Parser Configuration","lvl3":""}},
9865
9880
  {"objectID":"8317d296207f703efb98d9a5476bc3fd34494aecdcada845f71f6fc775fbd343","title":"Example: Large Payload Support","url":"/docs/reference/server-configuration#example-large-payload-support","content":"Example: Large Payload Support","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Example: Large Payload Support","lvl3":""}},
9866
9881
  {"objectID":"7ad744b4dfda31a7010bd32f82e2b24e2ab09a16f27bd76033afb4836d9831b0","title":"Logging Configuration","url":"/docs/reference/server-configuration#logging-configuration","content":"| Option | Type | Default | Description |\n| ----------------- | --------- | -------- | ----------------------------- |\n| enabled | boolean | true | Enable request logging |\n| level | string | \"info\" | Log level |\n| includeBody | boolean | false | Include request body in logs |\n| includeResponse | boolean | false | Include response body in logs |","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Logging Configuration","lvl3":""}},
9867
9882
  {"objectID":"5c89db12f4ab9ca6932c30f99c85d49b34479bce5d0386d2c6d7fe6f7c3fafbe","title":"Example: Debug Logging","url":"/docs/reference/server-configuration#example-debug-logging","content":"Example: Debug Logging","hierarchy":{"lvl0":"Reference","lvl1":"Srvr Cofiguratio Rfrc []","lvl2":"Example: Debug Logging","lvl3":""}},
@@ -0,0 +1,19 @@
1
+ # Silero VAD (bundled)
2
+
3
+ The speech-detector model the `transcribe` streaming path uses to find
4
+ utterances, so that the default works without any download:
5
+
6
+ | File | Source | SHA-256 (prefix) |
7
+ | ----------------- | ------------------------------------------------------------------ | ------------------ |
8
+ | `silero_vad.onnx` | `snakers4/silero-vad` @ `bfdc0193023f121ea5b3cc7b176dbed570a68a59` | `1a153a22f4509e29` |
9
+
10
+ The upstream repository is published under the MIT License. The full hash is
11
+ pinned in `src/lib/voice/vad/assets.ts`, which verifies the file before use,
12
+ here or in any other directory. The model runs through the optional
13
+ dependency `onnxruntime-node`; without it the stream falls back to the energy
14
+ gate.
15
+
16
+ Resolution order: `streaming.vad.modelDir` / `NEUROLINK_VAD_DIR`
17
+ → `NEUROLINK_MODEL_DIR/silero-vad` → this directory
18
+ → `~/.neurolink/models/silero-vad` (where a missing file is downloaded, unless
19
+ auto-download is off).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "13.6.5",
3
+ "version": "13.8.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {
@@ -115,6 +115,7 @@
115
115
  "test:forced-tool-declared": "pnpm exec tsx test/continuous-test-suite-forced-tool-declared.ts",
116
116
  "test:span-content-redaction": "pnpm exec tsx test/continuous-test-suite-span-content-redaction.ts",
117
117
  "test:voice-turn-memory": "pnpm exec tsx test/continuous-test-suite-voice-turn-memory.ts",
118
+ "test:redis-atomic-expiry": "pnpm exec tsx test/continuous-test-suite-redis-atomic-expiry.ts",
118
119
  "test:realtime-voice": "pnpm exec tsx test/continuous-test-suite-realtime-voice.ts",
119
120
  "test:error-classification-e2e": "pnpm exec tsx test/continuous-test-suite-error-classification-e2e.ts",
120
121
  "test:error-classifier-contract": "pnpm exec tsx test/continuous-test-suite-error-classifier-contract.ts",
@@ -373,6 +374,7 @@
373
374
  "docs-site/mcp-server/*.d.ts",
374
375
  "docs-site/static/search-index.json",
375
376
  "models/whistle/*",
377
+ "models/silero-vad/*",
376
378
  "scripts/observability/*",
377
379
  "README.md",
378
380
  "CHANGELOG.md",
@@ -610,6 +612,7 @@
610
612
  "mammoth": "1.12.0",
611
613
  "mediabunny": "^1.40.1",
612
614
  "music-metadata": "^11.16.1",
615
+ "onnxruntime-node": "^1.24.3",
613
616
  "pdf-parse": "^2.4.5",
614
617
  "pptxgenjs": "^4.0.1",
615
618
  "sharp": "^0.35.3"