@speech-sdk/core 0.32.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -10
- package/dist/apply-speed.d.ts +1 -0
- package/dist/apply-speed.d.ts.map +1 -1
- package/dist/apply-speed.js +7 -5
- package/dist/apply-speed.js.map +1 -1
- package/dist/audio-decode.js +3 -2
- package/dist/audio-decode.js.map +1 -1
- package/dist/audio-output.d.ts +1 -0
- package/dist/audio-output.d.ts.map +1 -1
- package/dist/audio-output.js +1 -1
- package/dist/audio-output.js.map +1 -1
- package/dist/conversation/attribute-timestamps.d.ts +1 -21
- package/dist/conversation/attribute-timestamps.d.ts.map +1 -1
- package/dist/conversation/attribute-timestamps.js +26 -264
- package/dist/conversation/attribute-timestamps.js.map +1 -1
- package/dist/conversation/dispatch.d.ts +3 -1
- package/dist/conversation/dispatch.d.ts.map +1 -1
- package/dist/conversation/dispatch.js +44 -18
- package/dist/conversation/dispatch.js.map +1 -1
- package/dist/conversation/errors.d.ts +9 -0
- package/dist/conversation/errors.d.ts.map +1 -1
- package/dist/conversation/errors.js +18 -0
- package/dist/conversation/errors.js.map +1 -1
- package/dist/conversation/pcm-concat.d.ts +13 -0
- package/dist/conversation/pcm-concat.d.ts.map +1 -1
- package/dist/conversation/pcm-concat.js +22 -4
- package/dist/conversation/pcm-concat.js.map +1 -1
- package/dist/conversation/split-turns.d.ts +31 -0
- package/dist/conversation/split-turns.d.ts.map +1 -0
- package/dist/conversation/split-turns.js +158 -0
- package/dist/conversation/split-turns.js.map +1 -0
- package/dist/conversation/stitch.d.ts +1 -3
- package/dist/conversation/stitch.d.ts.map +1 -1
- package/dist/conversation/stitch.js +5 -13
- package/dist/conversation/stitch.js.map +1 -1
- package/dist/conversation/types.d.ts +5 -0
- package/dist/conversation/types.d.ts.map +1 -1
- package/dist/conversation/validate.d.ts.map +1 -1
- package/dist/conversation/validate.js +3 -0
- package/dist/conversation/validate.js.map +1 -1
- package/dist/generate-conversation.d.ts +7 -3
- package/dist/generate-conversation.d.ts.map +1 -1
- package/dist/generate-conversation.js +151 -105
- package/dist/generate-conversation.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/speech-result.d.ts +20 -0
- package/dist/speech-result.d.ts.map +1 -1
- package/dist/speech-result.js.map +1 -1
- package/dist/timestamp-finalization.d.ts +1 -0
- package/dist/timestamp-finalization.d.ts.map +1 -1
- package/dist/timestamp-finalization.js +5 -5
- package/dist/timestamp-finalization.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/conversation/proportional-fill.d.ts +0 -10
- package/dist/conversation/proportional-fill.d.ts.map +0 -1
- package/dist/conversation/proportional-fill.js +0 -64
- package/dist/conversation/proportional-fill.js.map +0 -1
- package/dist/conversation/silence-detection.d.ts +0 -14
- package/dist/conversation/silence-detection.d.ts.map +0 -1
- package/dist/conversation/silence-detection.js +0 -52
- package/dist/conversation/silence-detection.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,15 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Speech SDK
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Use `google/gemini-3.8-flash-tts` for the highest fidelity or `google/gemini-3.8-flash-lite-tts` for higher volume. Both use the Interactions API. `text` is the spoken transcript; `instructions` becomes turn-level `speech_metadata.style` and is never prepended to the transcript. The Google adapter maps `[laughs]`, `[sighs]`, `[coughs]`, `[gasps]`, `[breath]`, `[short pause]`, and `[long pause]` to supported inline vocal tags. Other square-bracket directions throw; move sustained delivery cues to `instructions`.
|
|
10
|
-
|
|
11
|
-
Prebuilt voices, persistent `voice_...` IDs, and temporary `voicekey_...` keys work for single-speaker calls. Native two-speaker dialogue requires two prebuilt voices; custom voices use the SDK's local turn stitching. Unary generation returns a complete WAV file. Progressive streaming emits headerless 24 kHz mono PCM chunks. The SDK can convert unary WAV to its other advertised output formats.
|
|
12
|
-
|
|
13
|
-
Use `maxChunkWords` to target shorter audio chunks (roughly 150 spoken words per minute), `maxConcurrency` to bound SDK-created parallel calls, and `maxRetries` for isolated chunk retries. Gemini 3.8 requests retain a conservative 5,000-character hard ceiling even if `maxInputChars` is set higher. When a call is split, `metadata.chunks` reports ordered chunk indices, text spans in the normalized synthesized text, durations, retry counts, and provider diagnostics. `metadata.timestampsSource` distinguishes native, forced-aligned, and estimated timings; estimated timings do not prove transcript completeness. Google does not guarantee a maximum reliable audio duration or cross-request voice identity. Google project-wide quota enforcement belongs in the caller.
|
|
14
|
-
|
|
15
|
-
**Text-to-speech across 15 providers, one API.**
|
|
7
|
+
**The Universal SDK for text-to-speech across all major providers**
|
|
16
8
|
|
|
17
9
|
A lightweight, provider-agnostic TypeScript SDK. Zero lock-in. Runs in Node.js, Edge runtimes, and the browser.
|
|
18
10
|
|
|
@@ -174,7 +166,43 @@ const result = await generateConversation({
|
|
|
174
166
|
});
|
|
175
167
|
```
|
|
176
168
|
|
|
177
|
-
Options: `gapMs` (default 300), `volumeDbfs` (default `-20`), `maxConcurrency` (default 6), `maxRetries` (default 2), `instructions`, `timestamps`, `timestampProvider`, `apiKey`, `providerOptions`, `abortSignal`, `headers`. Per-turn overrides: `model`, `instructions`, `providerOptions` (stitch path only — throws `ConversationInputError` on native). Top-level and per-turn instructions are combined for stitched turns; native dialogue keeps them semantically separate. Native-dialogue models enforce their own voice-count and character limits; violations throw `DialogueConstraintError`.
|
|
169
|
+
Options: `gapMs` (default 300), `volumeDbfs` (default `-20`), `maxConcurrency` (default 6), `maxRetries` (default 2), `instructions`, `timestamps`, `splitTurns`, `timestampProvider`, `apiKey`, `providerOptions`, `abortSignal`, `headers`. Per-turn overrides: `model`, `instructions`, `providerOptions` (stitch path only — throws `ConversationInputError` on native). Top-level and per-turn instructions are combined for stitched turns; native dialogue keeps them semantically separate. Native-dialogue models enforce their own voice-count and character limits; violations throw `DialogueConstraintError`.
|
|
170
|
+
|
|
171
|
+
`result.metadata.path` reports how the audio was rendered: `'native'`, `'native-split'` (native dialogue in several calls, stitched), or `'stitch'`. On the stitch path `metadata.stitchReason` says why (`'mixed-models'`, `'no-native-dialogue'`, `'single-speaker'`, `'too-many-voices'`, `'custom-voice'`, `'per-turn-provider-options'`, `'per-turn-speed'`, `'max-input-chars'`, `'native-limit-exceeded'`). Match on these fields rather than on warning text.
|
|
172
|
+
|
|
173
|
+
### Per-turn audio
|
|
174
|
+
|
|
175
|
+
Pass `splitTurns: true` (with `timestamps: true`) to also get one clip per input turn — for example to show each turn as its own take, caption it, or regenerate a single turn.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { generateConversation } from '@speech-sdk/core';
|
|
179
|
+
import { createElevenLabs } from '@speech-sdk/core/providers';
|
|
180
|
+
|
|
181
|
+
const result = await generateConversation({
|
|
182
|
+
model: 'google/gemini-3.8-flash-tts',
|
|
183
|
+
turns: [
|
|
184
|
+
{ voice: 'Kore', text: '[laughs] You did not.' },
|
|
185
|
+
{ voice: 'Puck', text: 'I absolutely did.' },
|
|
186
|
+
],
|
|
187
|
+
timestamps: true,
|
|
188
|
+
// Gemini returns no word timings, so align them with a timestamp provider.
|
|
189
|
+
timestampProvider: createElevenLabs().forcedAlignment(),
|
|
190
|
+
splitTurns: true,
|
|
191
|
+
output: { format: 'wav' },
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
for (const turn of result.turns) {
|
|
195
|
+
turn.turnIndex; // index into turns[]
|
|
196
|
+
turn.audio; // GeneratedAudioFile, same format as result.audio
|
|
197
|
+
turn.startMs; // where the slice starts in result.audio
|
|
198
|
+
turn.endMs;
|
|
199
|
+
turn.timestamps; // WordTimestamp[] in seconds from the start of this slice
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The slices cover the whole conversation audio in order, with no gaps or overlaps, and are cut sample-accurately from the decoded PCM. Each boundary is cut in the middle of the first silence (at least 100 ms of 20 ms frames, each 40 dB below the audio's 90th-percentile frame level) after turn N's last word and before turn N+1's first word, so a breath, laugh, or "hmm" opening turn N+1 stays with that turn. When the gap holds no such silence, the cut goes at its quietest frame. Where the SDK joined audio itself — the stitch path's inserted gaps and the joins between native-split blocks — it cuts in the middle of that gap.
|
|
204
|
+
|
|
205
|
+
Splitting works on the native, native-split, and stitch paths. It throws `TurnSplitError` rather than cut on untrustworthy boundaries; `error.reason` is `'empty_turn'` (a turn has no attributed words), `'non_monotonic'` (a word overlaps the previous one or belongs to an earlier turn), or `'undecodable_audio'` (the native model has no PCM/WAV mode; thrown before synthesis). Callers can catch it and fall back to voicing turns separately.
|
|
178
206
|
|
|
179
207
|
## Timestamps
|
|
180
208
|
|
|
@@ -587,6 +615,7 @@ ElevenLabs Terms-of-Service content blocks use the canonical code `content_polic
|
|
|
587
615
|
| `TimestampValidationError` | Requested timestamps are empty, structurally invalid, or do not exactly cover the synthesized text |
|
|
588
616
|
| `TimestampKeyMissingError` | A legacy configured `fallbackSTT` is missing its API key |
|
|
589
617
|
| `ConversationInputError` / `DialogueConstraintError` / `StitchUnsupportedError` | `generateConversation` validation / native caps / stitch incompatibility |
|
|
618
|
+
| `TurnSplitError` | `splitTurns: true` but turn boundaries can't be trusted or the audio can't be decoded — `reason` says which |
|
|
590
619
|
| `InstructionsUnsupportedError` | Non-empty delivery instructions were supplied to a direct model without the `instructions` capability |
|
|
591
620
|
| `SpeechSDKError` | Base class |
|
|
592
621
|
|
package/dist/apply-speed.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function applySpeedToAudio(args: {
|
|
|
12
12
|
readonly audio: Uint8Array;
|
|
13
13
|
readonly mediaType: string;
|
|
14
14
|
}>;
|
|
15
|
+
export declare function outputAfterSpeed(output: AudioOutput | undefined): AudioOutput;
|
|
15
16
|
export declare function scaleTimestamps<T extends {
|
|
16
17
|
start: number;
|
|
17
18
|
end: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-speed.d.ts","sourceRoot":"","sources":["../src/apply-speed.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,WAAW,EAEjB,MAAM,mBAAmB,CAAC;AAI3B,eAAO,MAAM,SAAS,OAAO,CAAC;AAC9B,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAY7D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,CAExE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;CAC1C,GAAG,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"apply-speed.d.ts","sourceRoot":"","sources":["../src/apply-speed.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,WAAW,EAEjB,MAAM,mBAAmB,CAAC;AAI3B,eAAO,MAAM,SAAS,OAAO,CAAC;AAC9B,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAY7D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,CAExE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;CAC1C,GAAG,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBtE;AAKD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,CAE7E;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACtE,UAAU,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,EACpC,KAAK,EAAE,MAAM,GACZ,CAAC,EAAE,GAAG,SAAS,CASjB"}
|
package/dist/apply-speed.js
CHANGED
|
@@ -26,16 +26,18 @@ export async function applySpeedToAudio(args) {
|
|
|
26
26
|
});
|
|
27
27
|
const stretchedBytes = new Uint8Array(stretched.buffer, stretched.byteOffset, stretched.byteLength);
|
|
28
28
|
const wav = await wrapPcm16Mono(stretchedBytes, decoded.sampleRate);
|
|
29
|
-
// Default to mp3 when no output is specified — matches the format most providers
|
|
30
|
-
// return natively (so a caller that only sets speed gets the same container they'd
|
|
31
|
-
// get without speed, instead of a silent switch to wav).
|
|
32
|
-
const targetOutput = args.output ?? { format: "mp3" };
|
|
33
29
|
return await applyOptionalOutputConversion({
|
|
34
30
|
audio: wav,
|
|
35
31
|
mediaType: "audio/wav",
|
|
36
|
-
output:
|
|
32
|
+
output: outputAfterSpeed(args.output),
|
|
37
33
|
});
|
|
38
34
|
}
|
|
35
|
+
// Default to mp3 when no output is specified — matches the format most providers
|
|
36
|
+
// return natively (so a caller that only sets speed gets the same container they'd
|
|
37
|
+
// get without speed, instead of a silent switch to wav).
|
|
38
|
+
export function outputAfterSpeed(output) {
|
|
39
|
+
return output ?? { format: "mp3" };
|
|
40
|
+
}
|
|
39
41
|
export function scaleTimestamps(timestamps, speed) {
|
|
40
42
|
if (!timestamps) {
|
|
41
43
|
return;
|
package/dist/apply-speed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-speed.js","sourceRoot":"","sources":["../src/apply-speed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAEL,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;AAC9B,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CAAC,gCAAgC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,KAAK,GAAG,SAAS,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;QAC3C,MAAM,IAAI,UAAU,CAClB,yBAAyB,SAAS,QAAQ,SAAS,GAAG,CACvD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAKvC;IACC,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE;QACzC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,IAAI,UAAU,CACnC,SAAS,CAAC,MAAM,EAChB,SAAS,CAAC,UAAU,EACpB,SAAS,CAAC,UAAU,CACrB,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-speed.js","sourceRoot":"","sources":["../src/apply-speed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAEL,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;AAC9B,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CAAC,gCAAgC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,KAAK,GAAG,SAAS,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;QAC3C,MAAM,IAAI,UAAU,CAClB,yBAAyB,SAAS,QAAQ,SAAS,GAAG,CACvD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAKvC;IACC,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE;QACzC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,IAAI,UAAU,CACnC,SAAS,CAAC,MAAM,EAChB,SAAS,CAAC,UAAU,EACpB,SAAS,CAAC,UAAU,CACrB,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACpE,OAAO,MAAM,6BAA6B,CAAC;QACzC,KAAK,EAAE,GAAG;QACV,SAAS,EAAE,WAAW;QACtB,MAAM,EAAE,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;KACtC,CAAC,CAAC;AACL,CAAC;AAED,iFAAiF;AACjF,mFAAmF;AACnF,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,MAA+B;IAC9D,OAAO,MAAM,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,UAAoC,EACpC,KAAa;IAEb,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;IACT,CAAC;IACD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5B,GAAG,CAAC;QACJ,KAAK,EAAE,CAAC,CAAC,KAAK,GAAG,KAAK;QACtB,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,KAAK;KACnB,CAAC,CAAC,CAAC;AACN,CAAC"}
|
package/dist/audio-decode.js
CHANGED
|
@@ -21,9 +21,10 @@ function decodeRawPcm(bytes, mediaType) {
|
|
|
21
21
|
if (numberOfFrames === 0) {
|
|
22
22
|
return { pcm: new Int16Array(0), sampleRate, channels: 1 };
|
|
23
23
|
}
|
|
24
|
+
const aligned = copyAligned(bytes, bytesPerSample);
|
|
24
25
|
const data = format === "f32"
|
|
25
|
-
? new Float32Array(
|
|
26
|
-
: new Int16Array(
|
|
26
|
+
? new Float32Array(aligned.buffer, aligned.byteOffset, numberOfFrames * channels)
|
|
27
|
+
: new Int16Array(aligned.buffer, aligned.byteOffset, numberOfFrames * channels);
|
|
27
28
|
const sample = new AudioSample({
|
|
28
29
|
data,
|
|
29
30
|
format,
|
package/dist/audio-decode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-decode.js","sourceRoot":"","sources":["../src/audio-decode.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,WAAW,EACX,eAAe,EACf,UAAU,EACV,KAAK,GACN,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAQvD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAiB,EACjB,SAAiB;IAEjB,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;IAEtC,IAAI,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACrE,OAAO,YAAY,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,MAAM,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,iBAAiB,GAAG,6CAA6C,CAAC;AAExE,SAAS,YAAY,CAAC,KAAiB,EAAE,SAAiB;IACxD,MAAM,UAAU,GAAG,mBAAmB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC1D,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,oCAAoC,SAAS,6PAA6P,CAC3S,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IACtD,MAAM,cAAc,GAAG,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhD,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAC/B,KAAK,CAAC,UAAU,GAAG,cAAc,GAAG,QAAQ,CAC7C,CAAC;IACF,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,GAAG,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC7D,CAAC;IAED,MAAM,IAAI,GACR,MAAM,KAAK,KAAK;QACd,CAAC,CAAC,IAAI,YAAY,CACd,
|
|
1
|
+
{"version":3,"file":"audio-decode.js","sourceRoot":"","sources":["../src/audio-decode.ts"],"names":[],"mappings":"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"}
|
package/dist/audio-output.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare function validateOutput<T extends AudioOutput | undefined>(output
|
|
|
26
26
|
export declare function sampleRateHintFrom(output: AudioOutput | undefined): number | undefined;
|
|
27
27
|
export declare function resolveOutputForLocalConversion(output: AudioOutput): ResolvedAudioOutput;
|
|
28
28
|
export declare function mediaTypeForOutput(output: ResolvedAudioOutput): string;
|
|
29
|
+
export declare function isDecodableSourceMediaType(mediaType: string): boolean;
|
|
29
30
|
export declare function convertDecodableAudioToOutput(args: {
|
|
30
31
|
readonly audio: Uint8Array;
|
|
31
32
|
readonly mediaType: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-output.d.ts","sourceRoot":"","sources":["../src/audio-output.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IACE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEN,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEtD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IACE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEN,wBAAgB,cAAc,CAAC,CAAC,SAAS,WAAW,GAAG,SAAS,EAC9D,MAAM,EAAE,CAAC,GACR,CAAC,CAkBH;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,WAAW,GAClB,mBAAmB,CAUrB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAQtE;
|
|
1
|
+
{"version":3,"file":"audio-output.d.ts","sourceRoot":"","sources":["../src/audio-output.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IACE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEN,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEtD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IACE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEN,wBAAgB,cAAc,CAAC,CAAC,SAAS,WAAW,GAAG,SAAS,EAC9D,MAAM,EAAE,CAAC,GACR,CAAC,CAkBH;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,WAAW,GAClB,mBAAmB,CAUrB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAQtE;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAQrE;AA6BD,wBAAsB,6BAA6B,CAAC,IAAI,EAAE;IACxD,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;CACtC,GAAG,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDtE;AAED,wBAAsB,6BAA6B,CAAC,IAAI,EAAE;IACxD,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;CAC1C,GAAG,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAStE"}
|
package/dist/audio-output.js
CHANGED
|
@@ -43,7 +43,7 @@ export function mediaTypeForOutput(output) {
|
|
|
43
43
|
}
|
|
44
44
|
return `audio/pcm;rate=${output.sampleRate ?? 24_000}`;
|
|
45
45
|
}
|
|
46
|
-
function isDecodableSourceMediaType(mediaType) {
|
|
46
|
+
export function isDecodableSourceMediaType(mediaType) {
|
|
47
47
|
const lower = mediaType.toLowerCase();
|
|
48
48
|
return (lower.startsWith("audio/wav") ||
|
|
49
49
|
lower.startsWith("audio/x-wav") ||
|
package/dist/audio-output.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-output.js","sourceRoot":"","sources":["../src/audio-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAapD,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAW3C,MAAM,UAAU,cAAc,CAC5B,MAAS;IAET,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACnD,MAAM,IAAI,qBAAqB,CAC7B,qEAAqE,MAAM,CAAC,MAAM,IAAI,CACvF,CAAC;IACJ,CAAC;IACD,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;QACxD,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,qBAAqB,CAC7B,4DAA4D,CAAC,GAAG,CACjE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,MAA+B;IAE/B,OAAO,MAAM,IAAI,IAAI,IAAI,YAAY,IAAI,MAAM;QAC7C,CAAC,CAAC,MAAM,CAAC,UAAU;QACnB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,MAAmB;IAEnB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,wBAAwB;YACnD,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;SACpE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAA2B;IAC5D,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,kBAAkB,MAAM,CAAC,UAAU,IAAI,MAAM,EAAE,CAAC;AACzD,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"audio-output.js","sourceRoot":"","sources":["../src/audio-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
import type { ConversationWordTimestamp, WordTimestamp } from "../timestamps.js";
|
|
2
|
-
import type { SilenceGap } from "./silence-detection.js";
|
|
3
|
-
export interface Tier2Result {
|
|
4
|
-
readonly budgetExceeded: boolean;
|
|
5
|
-
readonly mismatches: number;
|
|
6
|
-
readonly timestamps: readonly ConversationWordTimestamp[];
|
|
7
|
-
}
|
|
8
|
-
export declare function tier2TextMatch(args: {
|
|
9
|
-
timestamps: readonly WordTimestamp[];
|
|
10
|
-
turnTexts: readonly string[];
|
|
11
|
-
}): Tier2Result;
|
|
12
|
-
export declare function tier1SilenceAnchored(args: {
|
|
13
|
-
timestamps: readonly WordTimestamp[];
|
|
14
|
-
gaps: readonly SilenceGap[];
|
|
15
|
-
turnTexts: readonly string[];
|
|
16
|
-
}): readonly ConversationWordTimestamp[] | undefined;
|
|
17
|
-
export interface AttributeTimestampsResult {
|
|
18
|
-
readonly timestamps?: readonly ConversationWordTimestamp[];
|
|
19
|
-
readonly warnings: readonly string[];
|
|
20
|
-
}
|
|
21
2
|
export declare function attributeTimestamps(args: {
|
|
22
3
|
timestamps: readonly WordTimestamp[];
|
|
23
4
|
turnTexts: readonly string[];
|
|
24
|
-
|
|
25
|
-
}): AttributeTimestampsResult;
|
|
5
|
+
}): readonly ConversationWordTimestamp[] | undefined;
|
|
26
6
|
//# sourceMappingURL=attribute-timestamps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attribute-timestamps.d.ts","sourceRoot":"","sources":["../../src/conversation/attribute-timestamps.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"attribute-timestamps.d.ts","sourceRoot":"","sources":["../../src/conversation/attribute-timestamps.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACd,MAAM,kBAAkB,CAAC;AAG1B,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IACrC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B,GAAG,SAAS,yBAAyB,EAAE,GAAG,SAAS,CAmCnD"}
|
|
@@ -1,276 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.split(WHITESPACE_SPLIT_RE)
|
|
14
|
-
.map(normalizeWord)
|
|
15
|
-
.filter((t) => t.length > 0);
|
|
16
|
-
}
|
|
17
|
-
function levenshteinAtMost1(a, b) {
|
|
18
|
-
if (a === b) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
const la = a.length;
|
|
22
|
-
const lb = b.length;
|
|
23
|
-
if (Math.abs(la - lb) > 1) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
// One substitution.
|
|
27
|
-
if (la === lb) {
|
|
28
|
-
let diffs = 0;
|
|
29
|
-
for (let i = 0; i < la; i++) {
|
|
30
|
-
if (a[i] !== b[i]) {
|
|
31
|
-
diffs++;
|
|
32
|
-
if (diffs > 1) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
// One insertion or deletion.
|
|
40
|
-
const [shorter, longer] = la < lb ? [a, b] : [b, a];
|
|
41
|
-
let i = 0;
|
|
42
|
-
let j = 0;
|
|
43
|
-
let skipped = false;
|
|
44
|
-
while (i < shorter.length && j < longer.length) {
|
|
45
|
-
if (shorter[i] === longer[j]) {
|
|
46
|
-
i++;
|
|
47
|
-
j++;
|
|
48
|
-
}
|
|
49
|
-
else if (skipped) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
skipped = true;
|
|
54
|
-
j++;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: tiered text-match is a single state machine — splitting hurts readability more than the score helps
|
|
60
|
-
export function tier2TextMatch(args) {
|
|
61
|
-
const { timestamps, turnTexts } = args;
|
|
62
|
-
if (turnTexts.length === 0) {
|
|
63
|
-
return {
|
|
64
|
-
timestamps: [],
|
|
65
|
-
mismatches: 0,
|
|
66
|
-
budgetExceeded: timestamps.some((t) => normalizeWord(t.text).length > 0),
|
|
67
|
-
};
|
|
1
|
+
import { canonicalizeTimestampText } from "../timestamp-finalization.js";
|
|
2
|
+
// Each word's canonical text must sit inside exactly one turn's canonical text, in order; undefined otherwise.
|
|
3
|
+
export function attributeTimestamps(args) {
|
|
4
|
+
const turnChars = args.turnTexts.map((t) => [
|
|
5
|
+
...canonicalizeTimestampText(t),
|
|
6
|
+
]);
|
|
7
|
+
const joined = turnChars.flat();
|
|
8
|
+
const turnEnds = [];
|
|
9
|
+
let end = 0;
|
|
10
|
+
for (const chars of turnChars) {
|
|
11
|
+
end += chars.length;
|
|
12
|
+
turnEnds.push(end);
|
|
68
13
|
}
|
|
69
|
-
const turnTokens = turnTexts.map((t) => tokenizeTurn(t));
|
|
70
|
-
const totalExpected = turnTokens.reduce((n, t) => n + t.length, 0);
|
|
71
|
-
// Per-turn budget: max(2, floor(0.2 * tokens_in_turn)).
|
|
72
|
-
const perTurnBudget = turnTokens.map((t) => Math.max(2, Math.floor(0.2 * t.length)));
|
|
73
|
-
const perTurnUsed = turnTokens.map(() => 0);
|
|
74
14
|
const out = [];
|
|
15
|
+
let position = 0;
|
|
75
16
|
let turnIndex = 0;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
totalMismatches++;
|
|
81
|
-
perTurnUsed[turnIndex] = (perTurnUsed[turnIndex] ?? 0) + 1;
|
|
82
|
-
if ((perTurnUsed[turnIndex] ?? 0) > (perTurnBudget[turnIndex] ?? 0)) {
|
|
83
|
-
budgetExceeded = true;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
for (let i = 0; i < timestamps.length; i++) {
|
|
87
|
-
const ts = timestamps[i];
|
|
88
|
-
const observed = normalizeWord(ts.text);
|
|
89
|
-
if (observed.length === 0) {
|
|
90
|
-
// Skip pure-punctuation tokens entirely.
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
// Advance turn boundary if current turn is exhausted.
|
|
94
|
-
while (turnIndex < turnTokens.length &&
|
|
95
|
-
tokenIndex >= (turnTokens[turnIndex]?.length ?? 0)) {
|
|
96
|
-
turnIndex++;
|
|
97
|
-
tokenIndex = 0;
|
|
98
|
-
}
|
|
99
|
-
if (turnIndex >= turnTokens.length) {
|
|
100
|
-
// Over-emit: provider returned more words than expected.
|
|
101
|
-
budgetExceeded = true;
|
|
102
|
-
out.push({
|
|
103
|
-
text: ts.text,
|
|
104
|
-
start: ts.start,
|
|
105
|
-
end: ts.end,
|
|
106
|
-
turnIndex: turnTokens.length - 1,
|
|
107
|
-
});
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
const expected = turnTokens[turnIndex]?.[tokenIndex] ?? "";
|
|
111
|
-
if (observed === expected || levenshteinAtMost1(observed, expected)) {
|
|
112
|
-
out.push({
|
|
113
|
-
text: ts.text,
|
|
114
|
-
start: ts.start,
|
|
115
|
-
end: ts.end,
|
|
116
|
-
turnIndex,
|
|
117
|
-
});
|
|
118
|
-
tokenIndex++;
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
// Look-ahead 1: is observed actually the NEXT expected? (provider dropped a word)
|
|
122
|
-
const expectedNext = turnTokens[turnIndex]?.[tokenIndex + 1];
|
|
123
|
-
if (expectedNext &&
|
|
124
|
-
(observed === expectedNext || levenshteinAtMost1(observed, expectedNext))) {
|
|
125
|
-
// Skip the dropped expected word. This is recovered drift, but still drift.
|
|
126
|
-
recordDrift();
|
|
127
|
-
tokenIndex++;
|
|
128
|
-
out.push({
|
|
129
|
-
text: ts.text,
|
|
130
|
-
start: ts.start,
|
|
131
|
-
end: ts.end,
|
|
132
|
-
turnIndex,
|
|
133
|
-
});
|
|
134
|
-
tokenIndex++;
|
|
135
|
-
continue;
|
|
17
|
+
for (const word of args.timestamps) {
|
|
18
|
+
const chars = [...canonicalizeTimestampText(word.text)];
|
|
19
|
+
if (chars.length === 0) {
|
|
20
|
+
return;
|
|
136
21
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (observedNext &&
|
|
142
|
-
(observedNext === expected || levenshteinAtMost1(observedNext, expected))) {
|
|
143
|
-
// Treat observed as inserted: emit at current turn, don't advance tokenIndex.
|
|
144
|
-
recordDrift();
|
|
145
|
-
out.push({
|
|
146
|
-
text: ts.text,
|
|
147
|
-
start: ts.start,
|
|
148
|
-
end: ts.end,
|
|
149
|
-
turnIndex,
|
|
150
|
-
});
|
|
151
|
-
continue;
|
|
22
|
+
for (const [offset, char] of chars.entries()) {
|
|
23
|
+
if (joined[position + offset] !== char) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
152
26
|
}
|
|
153
|
-
|
|
154
|
-
recordDrift();
|
|
155
|
-
out.push({
|
|
156
|
-
text: ts.text,
|
|
157
|
-
start: ts.start,
|
|
158
|
-
end: ts.end,
|
|
159
|
-
turnIndex,
|
|
160
|
-
});
|
|
161
|
-
tokenIndex++;
|
|
162
|
-
}
|
|
163
|
-
// End-of-stream consumption check at 95%.
|
|
164
|
-
const consumedExpected = turnTokens.slice(0, turnIndex).reduce((n, t) => n + t.length, 0) +
|
|
165
|
-
tokenIndex;
|
|
166
|
-
if (totalExpected > 0 &&
|
|
167
|
-
consumedExpected < Math.floor(totalExpected * 0.95)) {
|
|
168
|
-
budgetExceeded = true;
|
|
169
|
-
}
|
|
170
|
-
return {
|
|
171
|
-
timestamps: out,
|
|
172
|
-
mismatches: totalMismatches,
|
|
173
|
-
budgetExceeded,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
const FALLBACK_TEXT_MATCH_WARNING = "speech-sdk: timestamp attribution fell back to text-matching (silence boundaries unclear)";
|
|
177
|
-
const FALLBACK_PROPORTIONAL_WARNING = "speech-sdk: timestamp attribution fell back to proportional distribution; treat per-word turnIndex as approximate.";
|
|
178
|
-
const TIMESTAMPS_UNAVAILABLE_WARNING = "speech-sdk: timestamp attribution unavailable; provider/STT returned no word timestamps.";
|
|
179
|
-
const MIN_TIER1_TOKEN_RATIO = 0.35;
|
|
180
|
-
const MAX_TIER1_TOKEN_RATIO = 2.5;
|
|
181
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: silence-anchored partition is a single algorithm — splitting hurts readability more than the score helps
|
|
182
|
-
export function tier1SilenceAnchored(args) {
|
|
183
|
-
const { timestamps, gaps, turnTexts } = args;
|
|
184
|
-
const turnCount = turnTexts.length;
|
|
185
|
-
if (turnCount <= 1) {
|
|
186
|
-
return timestamps.map((w) => ({ ...w, turnIndex: 0 }));
|
|
187
|
-
}
|
|
188
|
-
if (timestamps.length === 0) {
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
const firstWordStartSec = timestamps[0]?.start ?? 0;
|
|
192
|
-
const lastWordEndSec = timestamps.at(-1)?.end ?? 0;
|
|
193
|
-
const candidateGaps = gaps.filter((g) => {
|
|
194
|
-
const midpointSec = (g.startMs + g.endMs) / 2 / 1000;
|
|
195
|
-
return midpointSec > firstWordStartSec && midpointSec < lastWordEndSec;
|
|
196
|
-
});
|
|
197
|
-
if (candidateGaps.length < turnCount - 1) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const selectedGaps = [...candidateGaps]
|
|
201
|
-
.sort((a, b) => b.durationMs - a.durationMs)
|
|
202
|
-
.slice(0, turnCount - 1)
|
|
203
|
-
.sort((a, b) => a.startMs - b.startMs);
|
|
204
|
-
// Boundary times in seconds (midpoint of each gap).
|
|
205
|
-
const boundariesSec = selectedGaps.map((g) => (g.startMs + g.endMs) / 2 / 1000);
|
|
206
|
-
// Partition words by which segment each word's midpoint falls into.
|
|
207
|
-
const partitions = Array.from({ length: turnCount }, () => []);
|
|
208
|
-
for (const w of timestamps) {
|
|
209
|
-
const midpoint = (w.start + w.end) / 2;
|
|
210
|
-
let turnIndex = 0;
|
|
211
|
-
while (turnIndex < boundariesSec.length &&
|
|
212
|
-
midpoint >= (boundariesSec[turnIndex] ?? Number.POSITIVE_INFINITY)) {
|
|
27
|
+
while (turnIndex < turnEnds.length && position >= turnEnds[turnIndex]) {
|
|
213
28
|
turnIndex++;
|
|
214
29
|
}
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
// Validate: no partition empty.
|
|
218
|
-
if (partitions.some((p) => p.length === 0)) {
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
const expectedCounts = turnTexts.map((t) => tokenizeTurn(t).length);
|
|
222
|
-
for (let i = 0; i < partitions.length; i++) {
|
|
223
|
-
const expected = expectedCounts[i] ?? 0;
|
|
224
|
-
if (expected === 0) {
|
|
30
|
+
if (position + chars.length > (turnEnds[turnIndex] ?? 0)) {
|
|
225
31
|
return;
|
|
226
32
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const out = [];
|
|
233
|
-
for (let i = 0; i < partitions.length; i++) {
|
|
234
|
-
for (const w of partitions[i] ?? []) {
|
|
235
|
-
out.push({ ...w, turnIndex: i });
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return out;
|
|
239
|
-
}
|
|
240
|
-
export function attributeTimestamps(args) {
|
|
241
|
-
const { timestamps, turnTexts, silenceGaps } = args;
|
|
242
|
-
const observed = timestamps.filter((w) => normalizeWord(w.text).length > 0);
|
|
243
|
-
if (observed.length === 0 || turnTexts.length === 0) {
|
|
244
|
-
return {
|
|
245
|
-
timestamps: undefined,
|
|
246
|
-
warnings: [TIMESTAMPS_UNAVAILABLE_WARNING],
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
// Tier 1: silence-anchored partitioning.
|
|
250
|
-
const tier1 = tier1SilenceAnchored({
|
|
251
|
-
timestamps: observed,
|
|
252
|
-
gaps: silenceGaps,
|
|
253
|
-
turnTexts,
|
|
254
|
-
});
|
|
255
|
-
if (tier1) {
|
|
256
|
-
return { timestamps: tier1, warnings: [] };
|
|
257
|
-
}
|
|
258
|
-
// Tier 2: improved text-match.
|
|
259
|
-
const tier2 = tier2TextMatch({ timestamps: observed, turnTexts });
|
|
260
|
-
if (!tier2.budgetExceeded) {
|
|
261
|
-
return {
|
|
262
|
-
timestamps: tier2.timestamps,
|
|
263
|
-
warnings: [
|
|
264
|
-
`${FALLBACK_TEXT_MATCH_WARNING}; ${tier2.mismatches} word(s) tolerated as mismatches.`,
|
|
265
|
-
],
|
|
266
|
-
};
|
|
33
|
+
out.push({ text: word.text, start: word.start, end: word.end, turnIndex });
|
|
34
|
+
position += chars.length;
|
|
267
35
|
}
|
|
268
|
-
|
|
269
|
-
const expectedTokensPerTurn = turnTexts.map((t) => t.split(WHITESPACE_SPLIT_RE).filter((s) => s.length > 0).length);
|
|
270
|
-
const tier3 = distributeWordsAcrossTurns(observed, expectedTokensPerTurn);
|
|
271
|
-
return {
|
|
272
|
-
timestamps: tier3,
|
|
273
|
-
warnings: [FALLBACK_PROPORTIONAL_WARNING],
|
|
274
|
-
};
|
|
36
|
+
return position === joined.length ? out : undefined;
|
|
275
37
|
}
|
|
276
38
|
//# sourceMappingURL=attribute-timestamps.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attribute-timestamps.js","sourceRoot":"","sources":["../../src/conversation/attribute-timestamps.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"attribute-timestamps.js","sourceRoot":"","sources":["../../src/conversation/attribute-timestamps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAMzE,+GAA+G;AAC/G,MAAM,UAAU,mBAAmB,CAAC,IAGnC;IACC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC1C,GAAG,yBAAyB,CAAC,CAAC,CAAC;KAChC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC;QACpB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,GAAG,GAAgC,EAAE,CAAC;IAC5C,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,CAAC,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,IAAI,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;gBACvC,OAAO;YACT,CAAC;QACH,CAAC;QACD,OAAO,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACtE,SAAS,EAAE,CAAC;QACd,CAAC;QACD,IAAI,QAAQ,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACzD,OAAO;QACT,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3E,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IACD,OAAO,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AudioOutput } from "../audio-output.js";
|
|
2
2
|
import type { ResolvedModel, StitchTurnOptions, Voice } from "../speech-provider.js";
|
|
3
|
+
import type { ConversationStitchReason } from "../speech-result.js";
|
|
3
4
|
import type { ConversationTurn } from "./types.js";
|
|
4
5
|
export type StitchFallbackReason = "fallback-from-native" | "fallback-from-native-custom-voice" | "fallback-from-native-oversized" | "fallback-from-native-voice-count" | "fallback-from-native-voice-count-exceeded";
|
|
5
6
|
export type ConversationPath = {
|
|
@@ -10,9 +11,10 @@ export type ConversationPath = {
|
|
|
10
11
|
kind: "stitch";
|
|
11
12
|
reason?: StitchFallbackReason;
|
|
12
13
|
stitchOptionsPerTurn: readonly StitchTurnOptions[];
|
|
14
|
+
stitchReason: ConversationStitchReason;
|
|
13
15
|
};
|
|
14
16
|
export declare function chooseConversationPath(input: {
|
|
15
|
-
forceStitch?:
|
|
17
|
+
forceStitch?: "per-turn-speed" | "max-input-chars";
|
|
16
18
|
resolvedPerTurn: readonly ResolvedModel<Voice>[];
|
|
17
19
|
turns: readonly ConversationTurn<Voice>[];
|
|
18
20
|
output?: AudioOutput;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/conversation/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAsB,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,KAAK,EACN,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/conversation/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAsB,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,KAAK,EACN,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,MAAM,MAAM,oBAAoB,GAC5B,sBAAsB,GACtB,mCAAmC,GACnC,gCAAgC,GAChC,kCAAkC,GAClC,2CAA2C,CAAC;AAEhD,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAI/B,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;CACzC,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,oBAAoB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACnD,YAAY,EAAE,wBAAwB,CAAC;CACxC,CAAC;AAaN,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAE5C,WAAW,CAAC,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACnD,eAAe,EAAE,SAAS,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;IACjD,KAAK,EAAE,SAAS,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;IAC1C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,gBAAgB,CA4CnB"}
|