@juspay/neurolink 12.47.14 → 12.48.1
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 +2 -2
- package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
- package/dist/browser/neurolink.min.js +578 -565
- package/dist/cli/factories/commandFactory.d.ts +7 -0
- package/dist/cli/factories/commandFactory.js +26 -0
- package/dist/cli/loop/optionsSchema.js +9 -0
- package/dist/context/budgetChecker.js +54 -2
- package/dist/context/contextDecision.js +2 -0
- package/dist/context/toolPairRepair.d.ts +12 -0
- package/dist/context/toolPairRepair.js +29 -3
- package/dist/core/baseProvider.d.ts +92 -3
- package/dist/core/baseProvider.js +254 -10
- package/dist/core/conversationMemoryManager.js +17 -2
- package/dist/core/loopEngine.js +1 -1
- package/dist/core/modules/MessageBuilder.js +11 -3
- package/dist/core/modules/TelemetryHandler.d.ts +2 -2
- package/dist/core/modules/ToolsManager.d.ts +35 -0
- package/dist/core/modules/ToolsManager.js +159 -55
- package/dist/core/nativeGenerateLoop.d.ts +58 -1
- package/dist/core/nativeGenerateLoop.js +277 -3
- package/dist/core/redisConversationMemoryManager.js +10 -3
- package/dist/core/resolveRequestKind.d.ts +24 -4
- package/dist/core/resolveRequestKind.js +31 -4
- package/dist/core/toolExecutionRecorder.d.ts +6 -0
- package/dist/core/toolExecutionRecorder.js +24 -2
- package/dist/core/toolRoutingDecision.js +1 -0
- package/dist/factories/mediaHandlerCatalog.js +5 -0
- package/dist/factories/providerDescriptors.js +15 -0
- package/dist/files/fileTools.d.ts +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +9 -2
- package/dist/memory/condensationPrompt.d.ts +8 -0
- package/dist/memory/condensationPrompt.js +29 -0
- package/dist/memory/hippocampusInitializer.d.ts +14 -2
- package/dist/memory/hippocampusInitializer.js +64 -3
- package/dist/neurolink.d.ts +252 -3
- package/dist/neurolink.js +898 -43
- package/dist/providers/amazonBedrock/client.js +6 -0
- package/dist/providers/amazonSagemaker.js +14 -1
- package/dist/providers/anthropic/client.js +148 -19
- package/dist/providers/anthropic/loopAdapter.js +2 -2
- package/dist/providers/googleAiStudio/client.js +2 -1
- package/dist/providers/openaiChatCompletionsBase.js +103 -23
- package/dist/providers/systemOneDecision.d.ts +6 -0
- package/dist/providers/systemOneDecision.js +14 -63
- package/dist/providers/typesafe.d.ts +6 -0
- package/dist/providers/typesafe.js +6 -0
- package/dist/rag/retrieval/searchDecision.js +1 -0
- package/dist/routing/classifierRouter.js +6 -5
- package/dist/routing/classifierStrategies.js +8 -1
- package/dist/types/classifierRouter.d.ts +16 -3
- package/dist/types/common.d.ts +2 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/context.d.ts +8 -1
- package/dist/types/conversation.d.ts +87 -0
- package/dist/types/decision.d.ts +210 -1
- package/dist/types/decision.js +9 -1
- package/dist/types/dispatch.d.ts +11 -4
- package/dist/types/generate.d.ts +118 -54
- package/dist/types/loopEngine.d.ts +7 -2
- package/dist/types/memory.d.ts +177 -9
- package/dist/types/openaiCompatible.d.ts +11 -3
- package/dist/types/stream.d.ts +13 -16
- package/dist/types/stt.d.ts +58 -1
- package/dist/types/tools.d.ts +14 -0
- package/dist/types/tools.js +8 -0
- package/dist/types/tts.d.ts +108 -0
- package/dist/types/voice.d.ts +41 -4
- package/dist/utils/decisionAnswers.d.ts +31 -1
- package/dist/utils/decisionAnswers.js +114 -0
- package/dist/utils/decisionLimits.d.ts +39 -0
- package/dist/utils/decisionLimits.js +116 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +9 -0
- package/dist/utils/messageBuilder.d.ts +45 -1
- package/dist/utils/messageBuilder.js +393 -16
- package/dist/utils/multimodalOptionsBuilder.d.ts +1 -0
- package/dist/utils/multimodalOptionsBuilder.js +1 -0
- package/dist/utils/speechText.d.ts +41 -0
- package/dist/utils/speechText.js +364 -0
- package/dist/utils/toolResultStatus.d.ts +26 -0
- package/dist/utils/toolResultStatus.js +73 -0
- package/dist/utils/ttsProcessor.d.ts +29 -0
- package/dist/utils/ttsProcessor.js +84 -2
- package/dist/voice/index.d.ts +1 -0
- package/dist/voice/index.js +3 -0
- package/dist/voice/providers/ElevenLabsSTT.d.ts +42 -0
- package/dist/voice/providers/ElevenLabsSTT.js +351 -0
- package/dist/voice/providers/ElevenLabsTTS.d.ts +81 -1
- package/dist/voice/providers/ElevenLabsTTS.js +415 -74
- package/docs-site/static/search-index.json +48 -33
- package/package.json +5 -2
- package/dist/utils/toolEndEmitter.d.ts +0 -25
- package/dist/utils/toolEndEmitter.js +0 -64
package/dist/types/stt.d.ts
CHANGED
|
@@ -51,10 +51,25 @@ export type STTOptions = {
|
|
|
51
51
|
/**
|
|
52
52
|
* STT result from transcription
|
|
53
53
|
*/
|
|
54
|
+
/**
|
|
55
|
+
* Where a provider's `STTResult.confidence` came from, when it has to say —
|
|
56
|
+
* Scribe reports a per-word `logprob` on some responses and only a
|
|
57
|
+
* language-detection probability on others, and a caller reading the number
|
|
58
|
+
* should know which it is getting. `"none"` means the provider reported no
|
|
59
|
+
* transcript-level signal at all and the field is 0.
|
|
60
|
+
*/
|
|
61
|
+
export type STTConfidenceSource = "word_logprobs" | "language_probability" | "none";
|
|
54
62
|
export type STTResult = {
|
|
55
63
|
/** Full transcribed text */
|
|
56
64
|
text: string;
|
|
57
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Transcript confidence (0-1) — Deepgram's alternative confidence,
|
|
67
|
+
* Whisper's `exp(avg_logprob)`, Scribe's mean per-word `exp(logprob)`.
|
|
68
|
+
* Always present: a strict-TS consumer reads it without a guard. A provider
|
|
69
|
+
* with no transcript-level signal reports 0 and, where it can, says so in
|
|
70
|
+
* `metadata.confidenceSource` rather than passing off a different number
|
|
71
|
+
* (a language-detection probability, say) as this one silently.
|
|
72
|
+
*/
|
|
58
73
|
confidence: number;
|
|
59
74
|
/** Detected language code */
|
|
60
75
|
language?: string;
|
|
@@ -74,6 +89,8 @@ export type STTResult = {
|
|
|
74
89
|
provider?: string;
|
|
75
90
|
/** Model used */
|
|
76
91
|
model?: string;
|
|
92
|
+
/** What `confidence` was derived from, for providers that must choose. */
|
|
93
|
+
confidenceSource?: STTConfidenceSource;
|
|
77
94
|
/** Additional provider-specific metadata */
|
|
78
95
|
[key: string]: unknown;
|
|
79
96
|
};
|
|
@@ -435,6 +452,46 @@ export type ElevenLabsVoice = {
|
|
|
435
452
|
export type ElevenLabsVoicesResponse = {
|
|
436
453
|
voices: ElevenLabsVoice[];
|
|
437
454
|
};
|
|
455
|
+
/**
|
|
456
|
+
* Scribe model ids the batch `speech-to-text` endpoint accepts. `scribe_v2`
|
|
457
|
+
* is the current model and the default; `scribe_v2_medical` is its clinical
|
|
458
|
+
* fine-tune; `scribe_v1` (and the experimental variant) are deprecated. The
|
|
459
|
+
* `(string & {})` member keeps the union open so a newly released id can be
|
|
460
|
+
* used without a cast.
|
|
461
|
+
*/
|
|
462
|
+
export type ElevenLabsSTTModel = "scribe_v2" | "scribe_v2_medical" | "scribe_v1" | "scribe_v1_experimental" | (string & {});
|
|
463
|
+
export type ElevenLabsSTTOptions = STTOptions & {
|
|
464
|
+
/** Scribe model id. Default `scribe_v2`. */
|
|
465
|
+
model?: ElevenLabsSTTModel;
|
|
466
|
+
/**
|
|
467
|
+
* Annotate non-speech audio events ("(laughter)", "(music)") in the
|
|
468
|
+
* transcript. ElevenLabs defaults this to true; NeuroLink sends `false` so
|
|
469
|
+
* the transcript reads as plain speech unless asked otherwise.
|
|
470
|
+
* @default false
|
|
471
|
+
*/
|
|
472
|
+
tagAudioEvents?: boolean;
|
|
473
|
+
/** Per-request timeout in milliseconds. Default 60_000. */
|
|
474
|
+
timeoutMs?: number;
|
|
475
|
+
/**
|
|
476
|
+
* API base URL including the `/v1` prefix. Default
|
|
477
|
+
* `ELEVENLABS_BASE_URL` or `https://api.elevenlabs.io/v1`.
|
|
478
|
+
*/
|
|
479
|
+
baseUrl?: string;
|
|
480
|
+
};
|
|
481
|
+
export type ElevenLabsSTTWord = {
|
|
482
|
+
text: string;
|
|
483
|
+
start?: number;
|
|
484
|
+
end?: number;
|
|
485
|
+
type?: "word" | "spacing" | "audio_event" | string;
|
|
486
|
+
speaker_id?: string;
|
|
487
|
+
logprob?: number;
|
|
488
|
+
};
|
|
489
|
+
export type ElevenLabsSTTResponse = {
|
|
490
|
+
language_code?: string;
|
|
491
|
+
language_probability?: number;
|
|
492
|
+
text: string;
|
|
493
|
+
words?: ElevenLabsSTTWord[];
|
|
494
|
+
};
|
|
438
495
|
export type AzureVoiceInfo = {
|
|
439
496
|
Name: string;
|
|
440
497
|
DisplayName: string;
|
package/dist/types/tools.d.ts
CHANGED
|
@@ -358,6 +358,8 @@ export type ToolEventPayload = {
|
|
|
358
358
|
timestamp?: number;
|
|
359
359
|
duration?: number;
|
|
360
360
|
executionId?: string;
|
|
361
|
+
/** The model's tool-call id when the loop supplied one, for pairing start/end. */
|
|
362
|
+
toolCallId?: string;
|
|
361
363
|
};
|
|
362
364
|
/**
|
|
363
365
|
* Tool execution summary for completed executions
|
|
@@ -570,3 +572,15 @@ export type BashToolResult = {
|
|
|
570
572
|
stderr: string;
|
|
571
573
|
error?: string;
|
|
572
574
|
};
|
|
575
|
+
/**
|
|
576
|
+
* Own property stamped on a tool object whose `execute` already emits one
|
|
577
|
+
* `tool:start` / `tool:end` pair per execution. It lives on the OBJECT, not
|
|
578
|
+
* the function: later layers (the execution recorder, discovery) replace
|
|
579
|
+
* `execute` via `{ ...tool, execute }`, which drops a function-identity check
|
|
580
|
+
* but carries a symbol-keyed own property along.
|
|
581
|
+
*/
|
|
582
|
+
export declare const TOOL_EVENTS_WRAPPED: unique symbol;
|
|
583
|
+
/** A `Tool` that may carry the {@link TOOL_EVENTS_WRAPPED} marker. */
|
|
584
|
+
export type EventWrappedTool = Tool & {
|
|
585
|
+
[TOOL_EVENTS_WRAPPED]?: true;
|
|
586
|
+
};
|
package/dist/types/tools.js
CHANGED
|
@@ -22,3 +22,11 @@ export function isToolDefinition(value) {
|
|
|
22
22
|
typeof value.description === "string" &&
|
|
23
23
|
typeof value.execute === "function");
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Own property stamped on a tool object whose `execute` already emits one
|
|
27
|
+
* `tool:start` / `tool:end` pair per execution. It lives on the OBJECT, not
|
|
28
|
+
* the function: later layers (the execution recorder, discovery) replace
|
|
29
|
+
* `execute` via `{ ...tool, execute }`, which drops a function-identity check
|
|
30
|
+
* but carries a symbol-keyed own property along.
|
|
31
|
+
*/
|
|
32
|
+
export const TOOL_EVENTS_WRAPPED = Symbol.for("neurolink.toolEventsWrapped");
|
package/dist/types/tts.d.ts
CHANGED
|
@@ -25,12 +25,80 @@ export type TTSQuality = "standard" | "hd";
|
|
|
25
25
|
* still surfacing the built-in choices in editor autocomplete.
|
|
26
26
|
*/
|
|
27
27
|
export type TTSProviderName = "google-ai" | "vertex" | "openai-tts" | "elevenlabs" | "elevenlabs-tts" | "azure-tts" | "fish-audio" | "sixtydb" | "cartesia" | (string & {});
|
|
28
|
+
/**
|
|
29
|
+
* What `generate({ tts })` synthesizes.
|
|
30
|
+
*
|
|
31
|
+
* - `"direct"` — the input text / prompt itself, with no LLM call. This is
|
|
32
|
+
* the default, and what every documented "speak this text" caller relies
|
|
33
|
+
* on.
|
|
34
|
+
* - `"response"` — the model's generated reply, after generation completes.
|
|
35
|
+
*
|
|
36
|
+
* `stream()` always synthesizes the streamed response and ignores this.
|
|
37
|
+
*/
|
|
38
|
+
export type TTSSynthesisMode = "direct" | "response";
|
|
39
|
+
/**
|
|
40
|
+
* Options for {@link prepareTextForSpeech} — the optional cleanup pass that
|
|
41
|
+
* turns model output (markdown, URLs, emoji) into text a TTS voice can read
|
|
42
|
+
* aloud. Every default below is what `tts: { sanitize: true }` applies.
|
|
43
|
+
*/
|
|
44
|
+
export type SpeechSanitizeOptions = {
|
|
45
|
+
/**
|
|
46
|
+
* Strip markdown syntax: headings, emphasis, inline code, list bullets,
|
|
47
|
+
* blockquotes, tables (kept as comma-separated rows), links and images
|
|
48
|
+
* (kept as their text). Fenced code blocks follow `codeBlocks`.
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
markdown?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* How fenced code blocks are spoken. `"drop"` removes them entirely,
|
|
54
|
+
* `"phrase"` replaces each block with `codeBlockPhrase`, `"keep"` strips
|
|
55
|
+
* only the fence lines and reads the contents. Incremental `stream()`
|
|
56
|
+
* synthesis always uses `"keep"`: a segment may hold an unterminated fence,
|
|
57
|
+
* so the block's extent is unknowable per segment.
|
|
58
|
+
* @default "phrase"
|
|
59
|
+
*/
|
|
60
|
+
codeBlocks?: "drop" | "phrase" | "keep";
|
|
61
|
+
/**
|
|
62
|
+
* Spoken stand-in for a fenced code block when `codeBlocks` is `"phrase"`.
|
|
63
|
+
* @default "Code block omitted."
|
|
64
|
+
*/
|
|
65
|
+
codeBlockPhrase?: string;
|
|
66
|
+
/**
|
|
67
|
+
* How URLs are spoken. `"hostname"` reduces `https://docs.example.com/a/b`
|
|
68
|
+
* to `docs.example.com`; `"remove"` drops the URL altogether.
|
|
69
|
+
* @default "hostname"
|
|
70
|
+
*/
|
|
71
|
+
urls?: "hostname" | "remove";
|
|
72
|
+
/**
|
|
73
|
+
* Remove emoji and pictographs (`\p{Extended_Pictographic}`,
|
|
74
|
+
* `\p{Emoji_Presentation}`, plus modifiers and joiners).
|
|
75
|
+
* @default true
|
|
76
|
+
*/
|
|
77
|
+
emoji?: boolean;
|
|
78
|
+
};
|
|
79
|
+
/** {@link SpeechSanitizeOptions} with every default applied. */
|
|
80
|
+
export type ResolvedSpeechSanitizeOptions = Required<SpeechSanitizeOptions>;
|
|
28
81
|
/**
|
|
29
82
|
* TTS configuration options
|
|
30
83
|
*/
|
|
31
84
|
export type TTSOptions = {
|
|
32
85
|
/** Enable TTS output */
|
|
33
86
|
enabled?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* What to synthesize: the input text (`"direct"`, the default) or the
|
|
89
|
+
* model's reply (`"response"`). An explicit `mode` wins over the legacy
|
|
90
|
+
* `useAiResponse` flag. Ignored by `stream()`, which always synthesizes the
|
|
91
|
+
* streamed response.
|
|
92
|
+
*
|
|
93
|
+
* @example Speak the model's reply
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const result = await neurolink.generate({
|
|
96
|
+
* input: { text: "Tell me a joke" },
|
|
97
|
+
* tts: { enabled: true, mode: "response" },
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
mode?: TTSSynthesisMode;
|
|
34
102
|
/**
|
|
35
103
|
* Use the AI-generated response for TTS instead of the input text
|
|
36
104
|
*
|
|
@@ -41,6 +109,8 @@ export type TTSOptions = {
|
|
|
41
109
|
* When true: TTS will synthesize the AI-generated response after generation completes
|
|
42
110
|
*
|
|
43
111
|
* @default false
|
|
112
|
+
* @deprecated Use `mode: "response"` / `mode: "direct"` instead. Still
|
|
113
|
+
* honoured when `mode` is not set.
|
|
44
114
|
*
|
|
45
115
|
* @example Using input text directly (default)
|
|
46
116
|
* ```typescript
|
|
@@ -63,12 +133,37 @@ export type TTSOptions = {
|
|
|
63
133
|
* ```
|
|
64
134
|
*/
|
|
65
135
|
useAiResponse?: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Clean the text before it reaches the voice: strip markdown, reduce URLs
|
|
138
|
+
* to their hostname, drop emoji. `true` applies the defaults of
|
|
139
|
+
* {@link SpeechSanitizeOptions}; an object tunes them. Off by default so
|
|
140
|
+
* existing callers get byte-identical synthesis input. In `stream()` the
|
|
141
|
+
* pass runs per segment with fenced code kept (fence lines stripped).
|
|
142
|
+
* @default false
|
|
143
|
+
*/
|
|
144
|
+
sanitize?: boolean | SpeechSanitizeOptions;
|
|
145
|
+
/**
|
|
146
|
+
* Cancels synthesis. A handler that supports it (ElevenLabs) aborts the
|
|
147
|
+
* request in flight and makes no further retry attempt once this fires.
|
|
148
|
+
* `generate()` derives one from its own synthesis timeout and the caller's
|
|
149
|
+
* `abortSignal`, so a handler's retry loop cannot outlive the call that
|
|
150
|
+
* started it and issue a billable request nobody is waiting for.
|
|
151
|
+
*/
|
|
152
|
+
signal?: AbortSignal;
|
|
66
153
|
/** Voice identifier (e.g., "en-US-Neural2-C") */
|
|
67
154
|
voice?: string;
|
|
68
155
|
/** Audio format (default: mp3) */
|
|
69
156
|
format?: TTSAudioFormat;
|
|
70
157
|
/** Speaking rate 0.25-4.0 (default: 1.0) */
|
|
71
158
|
speed?: number;
|
|
159
|
+
/**
|
|
160
|
+
* BCP-47 language tag (e.g. "en-US", "hi") for providers that accept one.
|
|
161
|
+
* Provider-specific: ElevenLabs reduces it to the ISO 639-1 primary subtag
|
|
162
|
+
* (`en-US` → `en`) and forwards it as `language_code` on every model except
|
|
163
|
+
* `eleven_multilingual_v2`, which rejects the field; Cartesia maps it to
|
|
164
|
+
* `language`.
|
|
165
|
+
*/
|
|
166
|
+
language?: string;
|
|
72
167
|
/** Voice pitch adjustment -20.0 to 20.0 semitones (default: 0.0) */
|
|
73
168
|
pitch?: number;
|
|
74
169
|
/** Volume gain in dB -96.0 to 16.0 (default: 0.0) */
|
|
@@ -88,6 +183,19 @@ export type TTSOptions = {
|
|
|
88
183
|
*/
|
|
89
184
|
streamingBufferSize?: number;
|
|
90
185
|
};
|
|
186
|
+
/**
|
|
187
|
+
* Handler-level wall-clock knobs a provider option type may carry (today:
|
|
188
|
+
* `ElevenLabsTTSOptions`). `generate()`'s outer synthesis timeout is raised to
|
|
189
|
+
* at least `timeoutMs × (1 + retries)` plus backoff slack — with the handler
|
|
190
|
+
* defaults (30 s, one retry) standing in for a knob the caller left unset —
|
|
191
|
+
* so a handler's own retry loop is never cut short from outside.
|
|
192
|
+
*/
|
|
193
|
+
export type TTSHandlerTimeBudget = {
|
|
194
|
+
/** Per-attempt request timeout in milliseconds (default 30 000; capped at 2³¹−1). */
|
|
195
|
+
timeoutMs?: number;
|
|
196
|
+
/** Retries after the first attempt (total attempts = 1 + retries). */
|
|
197
|
+
retries?: number;
|
|
198
|
+
};
|
|
91
199
|
/**
|
|
92
200
|
* TTS audio result returned from generation
|
|
93
201
|
*/
|
package/dist/types/voice.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
export * from "./tts.js";
|
|
14
14
|
export * from "./stt.js";
|
|
15
15
|
export * from "./realtime.js";
|
|
16
|
-
import type { TTSAudioFormat, TTSOptions, TTSResult, TTSVoice } from "./tts.js";
|
|
16
|
+
import type { TTSAudioFormat, TTSHandlerTimeBudget, TTSOptions, TTSResult, TTSVoice } from "./tts.js";
|
|
17
17
|
import type { TTSHandler } from "./common.js";
|
|
18
18
|
import type { STTResult, STTHandler } from "./stt.js";
|
|
19
19
|
import type { RealtimeHandler } from "./realtime.js";
|
|
@@ -28,7 +28,7 @@ export type VoiceProviderType = "tts" | "stt" | "realtime";
|
|
|
28
28
|
/**
|
|
29
29
|
* Voice provider name union type
|
|
30
30
|
*/
|
|
31
|
-
export type VoiceProviderName = "google-tts" | "elevenlabs" | "openai-tts" | "sixtydb" | "azure-tts" | "sarvam" | "murf" | "playai" | "speechify" | "cartesia" | "deepgram" | "gladia" | "whisper" | "assemblyai" | "google-stt" | "azure-stt" | "openai-realtime" | "gemini-live";
|
|
31
|
+
export type VoiceProviderName = "google-tts" | "elevenlabs" | "openai-tts" | "sixtydb" | "azure-tts" | "sarvam" | "murf" | "playai" | "speechify" | "cartesia" | "deepgram" | "gladia" | "whisper" | "assemblyai" | "google-stt" | "azure-stt" | "elevenlabs-stt" | "openai-realtime" | "gemini-live";
|
|
32
32
|
/**
|
|
33
33
|
* Base voice provider configuration
|
|
34
34
|
*/
|
|
@@ -264,13 +264,50 @@ export type AzureTTSOptions = TTSOptions & {
|
|
|
264
264
|
*/
|
|
265
265
|
allowRawSSML?: boolean;
|
|
266
266
|
};
|
|
267
|
-
export type ElevenLabsModel = "eleven_multilingual_v2" | "eleven_turbo_v2_5" | "eleven_turbo_v2" | "eleven_monolingual_v1";
|
|
268
|
-
|
|
267
|
+
export type ElevenLabsModel = "eleven_multilingual_v2" | "eleven_turbo_v2_5" | "eleven_turbo_v2" | "eleven_flash_v2_5" | "eleven_flash_v2" | "eleven_v3" | "eleven_monolingual_v1";
|
|
268
|
+
/** Opus bitrates (kbps) ElevenLabs accepts on `opus_48000_*`. */
|
|
269
|
+
export type ElevenLabsOpusBitrate = 32 | 64 | 96 | 128 | 192;
|
|
270
|
+
/** MP3 bitrates (kbps) ElevenLabs accepts on `mp3_44100_*`. */
|
|
271
|
+
export type ElevenLabsMp3Bitrate = 32 | 64 | 96 | 128 | 192;
|
|
272
|
+
/**
|
|
273
|
+
* Every `output_format` value the ElevenLabs text-to-speech endpoint accepts.
|
|
274
|
+
* There is no `ogg_*` family — Ogg/Opus is `opus_48000_<kbps>`.
|
|
275
|
+
*/
|
|
276
|
+
export type ElevenLabsOutputFormat = "mp3_22050_32" | "mp3_44100_32" | "mp3_44100_64" | "mp3_44100_96" | "mp3_44100_128" | "mp3_44100_192" | "opus_48000_32" | "opus_48000_64" | "opus_48000_96" | "opus_48000_128" | "opus_48000_192" | "pcm_8000" | "pcm_16000" | "pcm_22050" | "pcm_24000" | "pcm_44100" | "pcm_48000" | "ulaw_8000" | "alaw_8000";
|
|
277
|
+
/** The `voice_settings` object on an ElevenLabs synthesis request. */
|
|
278
|
+
export type ElevenLabsVoiceSettings = {
|
|
279
|
+
stability?: number;
|
|
280
|
+
similarity_boost?: number;
|
|
281
|
+
style?: number;
|
|
282
|
+
use_speaker_boost?: boolean;
|
|
283
|
+
/** 0.7–1.2; `TTSOptions.speed` is clamped into this range. */
|
|
284
|
+
speed?: number;
|
|
285
|
+
};
|
|
286
|
+
export type ElevenLabsTTSOptions = TTSOptions & TTSHandlerTimeBudget & {
|
|
269
287
|
model?: ElevenLabsModel;
|
|
270
288
|
stability?: number;
|
|
271
289
|
similarityBoost?: number;
|
|
272
290
|
style?: number;
|
|
273
291
|
useSpeakerBoost?: boolean;
|
|
292
|
+
/** Bitrate for `format: "ogg" | "opus"` (→ `opus_48000_<kbps>`). Default 64; an unlisted value falls back to it with a warning. */
|
|
293
|
+
opusBitrate?: ElevenLabsOpusBitrate;
|
|
294
|
+
/** Bitrate for `format: "mp3"` (→ `mp3_44100_<kbps>`). Default 128; an unlisted value falls back to it with a warning. */
|
|
295
|
+
mp3Bitrate?: ElevenLabsMp3Bitrate;
|
|
296
|
+
/**
|
|
297
|
+
* Language sent as `language_code`, an ISO 639-1 code; a BCP-47 tag is
|
|
298
|
+
* reduced to its primary subtag (`en-US` → `en`). Sent on every model
|
|
299
|
+
* except `eleven_multilingual_v2`, which does not accept the field (the
|
|
300
|
+
* API ignores the code on any other model that cannot enforce it).
|
|
301
|
+
* Falls back to the generic `TTSOptions.language`.
|
|
302
|
+
*/
|
|
303
|
+
languageCode?: string;
|
|
304
|
+
/** Raw `voice_settings` overrides; explicit fields win over the camelCase ones. */
|
|
305
|
+
voiceSettings?: ElevenLabsVoiceSettings;
|
|
306
|
+
/**
|
|
307
|
+
* API base URL including the `/v1` prefix. Default
|
|
308
|
+
* `ELEVENLABS_BASE_URL` or `https://api.elevenlabs.io/v1`.
|
|
309
|
+
*/
|
|
310
|
+
baseUrl?: string;
|
|
274
311
|
};
|
|
275
312
|
export type GoogleVoiceType = "Standard" | "WaveNet" | "Neural2" | "Studio" | "Polyglot";
|
|
276
313
|
export type GoogleTTSOptions = TTSOptions & {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module utils/decisionAnswers
|
|
9
9
|
*/
|
|
10
|
-
import type { DecisionAnswerMap, DecisionBooleanGate, DecisionChoiceReading, DecisionScoreReading, ProviderDescriptor } from "../types/index.js";
|
|
10
|
+
import type { DecisionAnswerMap, DecisionBooleanGate, DecisionChoiceReading, DecisionQuestion, DecisionResult, DecisionScoreReading, ProviderDescriptor } from "../types/index.js";
|
|
11
11
|
/**
|
|
12
12
|
* Read a yes/no answer as a probability.
|
|
13
13
|
*
|
|
@@ -54,3 +54,33 @@ export declare function gateDecisionBoolean(answers: DecisionAnswerMap, id: stri
|
|
|
54
54
|
* after any normalisation the wire might apply.
|
|
55
55
|
*/
|
|
56
56
|
export declare function decisionKey(namespace: string, index: number): string;
|
|
57
|
+
/** Namespace for questions a host adds through `decisionHooks.extendQuestions`. */
|
|
58
|
+
export declare const HOST_DECISION_NAMESPACE = "host";
|
|
59
|
+
/** Whether a wire id is one the funnel gave a host's question (`host__N`). */
|
|
60
|
+
export declare function isHostDecisionKey(id: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* A validated copy of a question a host handed back from `extendQuestions`,
|
|
63
|
+
* or undefined when it is malformed or cannot be copied. The wire carries
|
|
64
|
+
* the copy, never the host's object: a getter, a proxy or a later edit on
|
|
65
|
+
* the host's side can then reach neither the request nor the answers keyed
|
|
66
|
+
* on it. A value `structuredClone` refuses (a proxy, a function-valued
|
|
67
|
+
* field, a throwing getter) is treated as malformed.
|
|
68
|
+
*/
|
|
69
|
+
export declare function snapshotDecisionQuestion(value: unknown): DecisionQuestion | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* `result` without the answers to a host's namespaced questions — what a
|
|
72
|
+
* consumer is handed when the funnel's host-facing steps failed after the
|
|
73
|
+
* wire call had already returned. A null-prototype map, so an own id such
|
|
74
|
+
* as `__proto__` survives the copy.
|
|
75
|
+
*/
|
|
76
|
+
export declare function stripHostAnswers(result: DecisionResult, hostIdByWireId: ReadonlyMap<string, string>): DecisionResult;
|
|
77
|
+
/**
|
|
78
|
+
* Runtime check that a value from outside the type system — a host's
|
|
79
|
+
* `extendQuestions` hook — is a well-formed question, so a malformed one
|
|
80
|
+
* is dropped on its own rather than failing the whole request that
|
|
81
|
+
* NeuroLink's own routing depends on.
|
|
82
|
+
*
|
|
83
|
+
* A `choice` needs at least two options and a `score` at least two levels;
|
|
84
|
+
* fewer carries no information, and the servers refuse it anyway.
|
|
85
|
+
*/
|
|
86
|
+
export declare function isDecisionQuestion(value: unknown): value is DecisionQuestion;
|
|
@@ -93,3 +93,117 @@ export function gateDecisionBoolean(answers, id, gate) {
|
|
|
93
93
|
export function decisionKey(namespace, index) {
|
|
94
94
|
return `${namespace}__${index}`;
|
|
95
95
|
}
|
|
96
|
+
/** Namespace for questions a host adds through `decisionHooks.extendQuestions`. */
|
|
97
|
+
export const HOST_DECISION_NAMESPACE = "host";
|
|
98
|
+
/** Whether a wire id is one the funnel gave a host's question (`host__N`). */
|
|
99
|
+
export function isHostDecisionKey(id) {
|
|
100
|
+
return id.startsWith(`${HOST_DECISION_NAMESPACE}__`);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A validated copy of a question a host handed back from `extendQuestions`,
|
|
104
|
+
* or undefined when it is malformed or cannot be copied. The wire carries
|
|
105
|
+
* the copy, never the host's object: a getter, a proxy or a later edit on
|
|
106
|
+
* the host's side can then reach neither the request nor the answers keyed
|
|
107
|
+
* on it. A value `structuredClone` refuses (a proxy, a function-valued
|
|
108
|
+
* field, a throwing getter) is treated as malformed.
|
|
109
|
+
*/
|
|
110
|
+
export function snapshotDecisionQuestion(value) {
|
|
111
|
+
let copy;
|
|
112
|
+
try {
|
|
113
|
+
copy = structuredClone(value);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
return isDecisionQuestion(copy) ? copy : undefined;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `result` without the answers to a host's namespaced questions — what a
|
|
122
|
+
* consumer is handed when the funnel's host-facing steps failed after the
|
|
123
|
+
* wire call had already returned. A null-prototype map, so an own id such
|
|
124
|
+
* as `__proto__` survives the copy.
|
|
125
|
+
*/
|
|
126
|
+
export function stripHostAnswers(result, hostIdByWireId) {
|
|
127
|
+
if (hostIdByWireId.size === 0) {
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
const answers = Object.create(null);
|
|
131
|
+
for (const [id, answer] of Object.entries(result.answers)) {
|
|
132
|
+
if (!hostIdByWireId.has(id)) {
|
|
133
|
+
answers[id] = answer;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { ...result, answers };
|
|
137
|
+
}
|
|
138
|
+
const isRecordValue = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
139
|
+
/**
|
|
140
|
+
* Whether `value` is something a decision model accepts as content: a
|
|
141
|
+
* string, number or boolean, or an array or plain object of the same.
|
|
142
|
+
* `null` and `undefined` are not — a question with no instructions is
|
|
143
|
+
* refused by every provider.
|
|
144
|
+
*/
|
|
145
|
+
function isDecisionInput(value) {
|
|
146
|
+
switch (typeof value) {
|
|
147
|
+
case "string":
|
|
148
|
+
case "boolean":
|
|
149
|
+
return true;
|
|
150
|
+
case "number":
|
|
151
|
+
// NaN and ±Infinity serialise to null and would make the provider
|
|
152
|
+
// reject the whole request, not just this question.
|
|
153
|
+
return Number.isFinite(value);
|
|
154
|
+
case "object":
|
|
155
|
+
if (value === null) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return Array.isArray(value)
|
|
159
|
+
? value.every(isDecisionInput)
|
|
160
|
+
: Object.values(value).every(isDecisionInput);
|
|
161
|
+
default:
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Runtime check that a value from outside the type system — a host's
|
|
167
|
+
* `extendQuestions` hook — is a well-formed question, so a malformed one
|
|
168
|
+
* is dropped on its own rather than failing the whole request that
|
|
169
|
+
* NeuroLink's own routing depends on.
|
|
170
|
+
*
|
|
171
|
+
* A `choice` needs at least two options and a `score` at least two levels;
|
|
172
|
+
* fewer carries no information, and the servers refuse it anyway.
|
|
173
|
+
*/
|
|
174
|
+
export function isDecisionQuestion(value) {
|
|
175
|
+
try {
|
|
176
|
+
return isWellFormedDecisionQuestion(value);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Cyclic or otherwise exotic host input: the walk below can overflow the
|
|
180
|
+
// stack. That drops this question alone rather than failing the request.
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function isWellFormedDecisionQuestion(value) {
|
|
185
|
+
if (!isRecordValue(value) || !isDecisionInput(value.instructions)) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
switch (value.type) {
|
|
189
|
+
case "boolean": {
|
|
190
|
+
if (value.criteria === undefined) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (!isRecordValue(value.criteria)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return Object.values(value.criteria).every((side) => side === null || side === undefined || isDecisionInput(side));
|
|
197
|
+
}
|
|
198
|
+
case "choice":
|
|
199
|
+
return (isRecordValue(value.criteria) &&
|
|
200
|
+
Object.keys(value.criteria).length >= 2 &&
|
|
201
|
+
Object.values(value.criteria).every((rubric) => rubric === null || isDecisionInput(rubric)));
|
|
202
|
+
case "score":
|
|
203
|
+
return (Array.isArray(value.criteria) &&
|
|
204
|
+
value.criteria.length >= 2 &&
|
|
205
|
+
value.criteria.every((level) => level === null || isDecisionInput(level)));
|
|
206
|
+
default:
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision limits — the one place a provider's `decisionLimits` are read and
|
|
3
|
+
* a state's size is estimated against them.
|
|
4
|
+
*
|
|
5
|
+
* `SystemOneDecisionProvider` refuses an over-limit request through these
|
|
6
|
+
* functions, and `NeuroLink.decisionLimits()` / `estimateDecisionStateTokens`
|
|
7
|
+
* expose the same functions to hosts, so what a host measures before the call
|
|
8
|
+
* is exactly what the pre-flight check measures inside it. Two estimators that
|
|
9
|
+
* drift apart would let a host size a state as fitting and then watch it be
|
|
10
|
+
* refused.
|
|
11
|
+
*
|
|
12
|
+
* @module utils/decisionLimits
|
|
13
|
+
*/
|
|
14
|
+
import type { DecisionLimits, DecisionLimitsReading, DecisionState, ProviderDescriptor } from "../types/index.js";
|
|
15
|
+
/**
|
|
16
|
+
* Estimate how many tokens a decision model will read from `state`.
|
|
17
|
+
*
|
|
18
|
+
* `estimateTokens` assumes ~4 characters per token. That holds for English
|
|
19
|
+
* and is several times too generous for other scripts on an English
|
|
20
|
+
* tokenizer, so when the limits declare a `nonAsciiTokensPerChar` rate,
|
|
21
|
+
* non-ASCII characters are counted at it instead. Digits, ASCII punctuation
|
|
22
|
+
* and astral characters (emoji) are each counted separately only when the
|
|
23
|
+
* limits declare a rate for that class — a tokenizer that reads each digit,
|
|
24
|
+
* and most punctuation, on its own makes a number-heavy or JSON-heavy state
|
|
25
|
+
* several times longer than the ~4-characters-per-token estimate suggests. A
|
|
26
|
+
* non-string state is serialized first, as it is on the wire.
|
|
27
|
+
*
|
|
28
|
+
* This is the estimator the pre-flight refusal uses: a state this reports at
|
|
29
|
+
* or under `maxStateTokens` is sent, one over it is refused before any
|
|
30
|
+
* network call.
|
|
31
|
+
*/
|
|
32
|
+
export declare function estimateDecisionStateTokens(state: DecisionState, limits?: Pick<DecisionLimits, "nonAsciiTokensPerChar" | "digitTokensPerChar" | "symbolTokensPerChar" | "astralTokensPerChar">): number;
|
|
33
|
+
/**
|
|
34
|
+
* Flatten a descriptor's `decisionLimits` for one model: the per-model entry
|
|
35
|
+
* overrides the base, field by field, and `advisory` becomes
|
|
36
|
+
* `enforcedLocally: false`. Returns null for a descriptor that declares no
|
|
37
|
+
* limits.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveDecisionLimitsReading(descriptor: Pick<ProviderDescriptor, "name" | "decisionLimits">, model: string): DecisionLimitsReading | null;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision limits — the one place a provider's `decisionLimits` are read and
|
|
3
|
+
* a state's size is estimated against them.
|
|
4
|
+
*
|
|
5
|
+
* `SystemOneDecisionProvider` refuses an over-limit request through these
|
|
6
|
+
* functions, and `NeuroLink.decisionLimits()` / `estimateDecisionStateTokens`
|
|
7
|
+
* expose the same functions to hosts, so what a host measures before the call
|
|
8
|
+
* is exactly what the pre-flight check measures inside it. Two estimators that
|
|
9
|
+
* drift apart would let a host size a state as fitting and then watch it be
|
|
10
|
+
* refused.
|
|
11
|
+
*
|
|
12
|
+
* @module utils/decisionLimits
|
|
13
|
+
*/
|
|
14
|
+
import { CHARS_PER_TOKEN, estimateTokens, serializeForEstimate, } from "./tokenEstimation.js";
|
|
15
|
+
/**
|
|
16
|
+
* Estimate how many tokens a decision model will read from `state`.
|
|
17
|
+
*
|
|
18
|
+
* `estimateTokens` assumes ~4 characters per token. That holds for English
|
|
19
|
+
* and is several times too generous for other scripts on an English
|
|
20
|
+
* tokenizer, so when the limits declare a `nonAsciiTokensPerChar` rate,
|
|
21
|
+
* non-ASCII characters are counted at it instead. Digits, ASCII punctuation
|
|
22
|
+
* and astral characters (emoji) are each counted separately only when the
|
|
23
|
+
* limits declare a rate for that class — a tokenizer that reads each digit,
|
|
24
|
+
* and most punctuation, on its own makes a number-heavy or JSON-heavy state
|
|
25
|
+
* several times longer than the ~4-characters-per-token estimate suggests. A
|
|
26
|
+
* non-string state is serialized first, as it is on the wire.
|
|
27
|
+
*
|
|
28
|
+
* This is the estimator the pre-flight refusal uses: a state this reports at
|
|
29
|
+
* or under `maxStateTokens` is sent, one over it is refused before any
|
|
30
|
+
* network call.
|
|
31
|
+
*/
|
|
32
|
+
export function estimateDecisionStateTokens(state, limits) {
|
|
33
|
+
const text = serializeForEstimate(state);
|
|
34
|
+
const nonAsciiTokensPerChar = limits?.nonAsciiTokensPerChar;
|
|
35
|
+
const digit = limits?.digitTokensPerChar;
|
|
36
|
+
const symbol = limits?.symbolTokensPerChar;
|
|
37
|
+
const astral = limits?.astralTokensPerChar;
|
|
38
|
+
if (nonAsciiTokensPerChar === undefined &&
|
|
39
|
+
digit === undefined &&
|
|
40
|
+
symbol === undefined &&
|
|
41
|
+
astral === undefined) {
|
|
42
|
+
return estimateTokens(text);
|
|
43
|
+
}
|
|
44
|
+
// Each class of character is counted once, at its own rate. A class with no
|
|
45
|
+
// declared rate stays in the ordinary four-characters-a-token estimate (or,
|
|
46
|
+
// for non-ASCII, at that estimate's per-character rate), so a provider that
|
|
47
|
+
// declares none of the extra rates is estimated exactly as before.
|
|
48
|
+
const isDigit = (c) => c >= "0" && c <= "9";
|
|
49
|
+
const isSymbol = (c) => /[!-/:-@[-`{-~]/.test(c);
|
|
50
|
+
let digits = 0;
|
|
51
|
+
let symbols = 0;
|
|
52
|
+
let astrals = 0;
|
|
53
|
+
let nonAscii = 0;
|
|
54
|
+
const rest = [];
|
|
55
|
+
for (const c of text) {
|
|
56
|
+
const code = c.codePointAt(0) ?? 0;
|
|
57
|
+
if (code > 0xffff && astral !== undefined) {
|
|
58
|
+
astrals += 1;
|
|
59
|
+
}
|
|
60
|
+
else if (code > 0x7f) {
|
|
61
|
+
nonAscii += 1;
|
|
62
|
+
}
|
|
63
|
+
else if (digit !== undefined && isDigit(c)) {
|
|
64
|
+
digits += 1;
|
|
65
|
+
}
|
|
66
|
+
else if (symbol !== undefined && isSymbol(c)) {
|
|
67
|
+
symbols += 1;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
rest.push(c);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return (estimateTokens(rest.join("")) +
|
|
74
|
+
Math.ceil(digits * (digit ?? 0)) +
|
|
75
|
+
Math.ceil(symbols * (symbol ?? 0)) +
|
|
76
|
+
Math.ceil(astrals * (astral ?? 0)) +
|
|
77
|
+
Math.ceil(nonAscii * (nonAsciiTokensPerChar ?? 1 / CHARS_PER_TOKEN)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Flatten a descriptor's `decisionLimits` for one model: the per-model entry
|
|
81
|
+
* overrides the base, field by field, and `advisory` becomes
|
|
82
|
+
* `enforcedLocally: false`. Returns null for a descriptor that declares no
|
|
83
|
+
* limits.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveDecisionLimitsReading(descriptor, model) {
|
|
86
|
+
const limits = descriptor.decisionLimits;
|
|
87
|
+
if (!limits) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const modelLimits = limits.models?.[model];
|
|
91
|
+
const nonAsciiTokensPerChar = modelLimits?.nonAsciiTokensPerChar ?? limits.nonAsciiTokensPerChar;
|
|
92
|
+
return {
|
|
93
|
+
provider: descriptor.name,
|
|
94
|
+
model,
|
|
95
|
+
maxStateTokens: modelLimits?.maxStateTokens ?? limits.maxStateTokens,
|
|
96
|
+
// Absent for a provider that caps by tokens or bytes rather than by
|
|
97
|
+
// question count (TypeSafe, XOR).
|
|
98
|
+
...(limits.maxQuestions !== undefined
|
|
99
|
+
? { maxQuestions: limits.maxQuestions }
|
|
100
|
+
: {}),
|
|
101
|
+
...(nonAsciiTokensPerChar !== undefined ? { nonAsciiTokensPerChar } : {}),
|
|
102
|
+
// Not model-overridable — `models` only carries `maxStateTokens` and
|
|
103
|
+
// `nonAsciiTokensPerChar` — so these three come from the base limits only.
|
|
104
|
+
...(limits.digitTokensPerChar !== undefined
|
|
105
|
+
? { digitTokensPerChar: limits.digitTokensPerChar }
|
|
106
|
+
: {}),
|
|
107
|
+
...(limits.symbolTokensPerChar !== undefined
|
|
108
|
+
? { symbolTokensPerChar: limits.symbolTokensPerChar }
|
|
109
|
+
: {}),
|
|
110
|
+
...(limits.astralTokensPerChar !== undefined
|
|
111
|
+
? { astralTokensPerChar: limits.astralTokensPerChar }
|
|
112
|
+
: {}),
|
|
113
|
+
...(limits.media !== undefined ? { media: limits.media } : {}),
|
|
114
|
+
enforcedLocally: limits.advisory !== true,
|
|
115
|
+
};
|
|
116
|
+
}
|