@moolam/bindings-speech 1.0.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/LICENSE +202 -0
- package/README.md +41 -0
- package/bin/speech-cert.mjs +11 -0
- package/certification/README.md +18 -0
- package/certification/device-profiles/mid-range-android.json +12 -0
- package/certification/profile.json +33 -0
- package/certification/prove-speech-cert.mjs +11 -0
- package/certification/reports/prove.green1.speech.cert.json +92 -0
- package/certification/reports/prove.seeded-red.speech.cert.json +70 -0
- package/certification/reports/speech.cert.json +92 -0
- package/certification/run-speech-cert.mjs +11 -0
- package/certification/schemas/speech.cert.report.schema.json +149 -0
- package/certification/voice-rtt.baseline.json +12 -0
- package/dist/edge_stt_harness.d.ts +86 -0
- package/dist/edge_stt_harness.d.ts.map +1 -0
- package/dist/edge_stt_harness.js +311 -0
- package/dist/edge_stt_harness.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/speech_certification.d.ts +149 -0
- package/dist/speech_certification.d.ts.map +1 -0
- package/dist/speech_certification.js +490 -0
- package/dist/speech_certification.js.map +1 -0
- package/dist/stt_binding.d.ts +148 -0
- package/dist/stt_binding.d.ts.map +1 -0
- package/dist/stt_binding.js +400 -0
- package/dist/stt_binding.js.map +1 -0
- package/dist/tts_binding.d.ts +187 -0
- package/dist/tts_binding.d.ts.map +1 -0
- package/dist/tts_binding.js +565 -0
- package/dist/tts_binding.js.map +1 -0
- package/dist/voice_rtt_proof.d.ts +112 -0
- package/dist/voice_rtt_proof.d.ts.map +1 -0
- package/dist/voice_rtt_proof.js +241 -0
- package/dist/voice_rtt_proof.js.map +1 -0
- package/dist/whisper_ffi.d.ts +44 -0
- package/dist/whisper_ffi.d.ts.map +1 -0
- package/dist/whisper_ffi.js +104 -0
- package/dist/whisper_ffi.js.map +1 -0
- package/fixtures/indic/audio/en-classroom.pcm +0 -0
- package/fixtures/indic/audio/hi-classroom-noise.pcm +0 -0
- package/fixtures/indic/audio/hi-en-codeswitch.pcm +0 -0
- package/fixtures/indic/audio/hi-greeting.pcm +0 -0
- package/fixtures/indic/audio/short-hi.pcm +0 -0
- package/fixtures/indic/audio/ta-greeting.pcm +0 -0
- package/fixtures/indic/catalog.json +75 -0
- package/fixtures/indic/supported-languages.json +8 -0
- package/fixtures/tts/tts-voices.model-card.json +41 -0
- package/fixtures/tts/voices.json +24 -0
- package/package.json +55 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NFR-07 voice RTT proof: final transcript timestamp → first TTS audio chunk.
|
|
3
|
+
*
|
|
4
|
+
* Uses real performance.now() wall clocks on the measurement path (not mocked
|
|
5
|
+
* clocks alone). Absolute ceiling from PRD_MATRIX (≤2500ms p95) plus relative
|
|
6
|
+
* regression vs a recorded device-profile baseline.
|
|
7
|
+
*/
|
|
8
|
+
import { type LocalTtsNativeBackend } from "./tts_binding.js";
|
|
9
|
+
export declare const NFR_07_ID: "NFR-07";
|
|
10
|
+
export declare const NFR_07_BUDGET_P95_MS = 2500;
|
|
11
|
+
export declare const DEFAULT_VOICE_RTT_DEVICE_PROFILE: string;
|
|
12
|
+
export declare const DEFAULT_VOICE_RTT_BASELINE: string;
|
|
13
|
+
export type VoiceRttDeviceProfile = {
|
|
14
|
+
schemaVersion: string;
|
|
15
|
+
profileId: string;
|
|
16
|
+
platform: string;
|
|
17
|
+
hardwareClass: string;
|
|
18
|
+
nfr: {
|
|
19
|
+
nfrId: typeof NFR_07_ID;
|
|
20
|
+
metric: "final_transcript_to_first_audio";
|
|
21
|
+
voiceRttP95Ms: number;
|
|
22
|
+
};
|
|
23
|
+
description?: string;
|
|
24
|
+
};
|
|
25
|
+
export type VoiceRttBaseline = {
|
|
26
|
+
schemaVersion: string;
|
|
27
|
+
nfrId: typeof NFR_07_ID;
|
|
28
|
+
metric: "final_transcript_to_first_audio";
|
|
29
|
+
deviceProfileId: string;
|
|
30
|
+
p95Ms: number;
|
|
31
|
+
tolerancePercent: number;
|
|
32
|
+
sampleCount: number;
|
|
33
|
+
warmupCount: number;
|
|
34
|
+
notes?: string;
|
|
35
|
+
recordedAt?: string;
|
|
36
|
+
};
|
|
37
|
+
export type VoiceRttSample = {
|
|
38
|
+
latencyMs: number;
|
|
39
|
+
utteranceId: string;
|
|
40
|
+
};
|
|
41
|
+
export type VoiceRttProofResult = {
|
|
42
|
+
nfrId: typeof NFR_07_ID;
|
|
43
|
+
metric: "final_transcript_to_first_audio";
|
|
44
|
+
outcome: "pass" | "fail";
|
|
45
|
+
ok: boolean;
|
|
46
|
+
measuredP95Ms: number;
|
|
47
|
+
budgetP95Ms: number;
|
|
48
|
+
baselineP95Ms: number;
|
|
49
|
+
allowedRelativeP95Ms: number;
|
|
50
|
+
absoluteOk: boolean;
|
|
51
|
+
relativeOk: boolean;
|
|
52
|
+
sampleCount: number;
|
|
53
|
+
samplesMs: number[];
|
|
54
|
+
deviceProfileId: string;
|
|
55
|
+
subjectId: string;
|
|
56
|
+
deviceId: string;
|
|
57
|
+
clock: "performance.now";
|
|
58
|
+
failures: string[];
|
|
59
|
+
policy: "absolute-ceiling-plus-relative-baseline";
|
|
60
|
+
};
|
|
61
|
+
export type RunVoiceRttProofOptions = {
|
|
62
|
+
subjectId: string;
|
|
63
|
+
deviceId: string;
|
|
64
|
+
deviceProfilePath?: string;
|
|
65
|
+
baselinePath?: string;
|
|
66
|
+
utteranceId?: string;
|
|
67
|
+
sampleCount?: number;
|
|
68
|
+
warmupCount?: number;
|
|
69
|
+
/** Real delay before first TTS chunk (seeded red / prove). */
|
|
70
|
+
injectFirstAudioDelayMs?: number;
|
|
71
|
+
/** Override absolute budget (tests). */
|
|
72
|
+
budgetP95Ms?: number;
|
|
73
|
+
/** Override baseline p95 (tests). */
|
|
74
|
+
baselineP95Ms?: number;
|
|
75
|
+
tolerancePercent?: number;
|
|
76
|
+
language?: string;
|
|
77
|
+
};
|
|
78
|
+
/** Percentile (nearest-rank) over ascending samples. */
|
|
79
|
+
export declare function percentileMs(samples: readonly number[], p: number): number;
|
|
80
|
+
export declare function loadVoiceRttDeviceProfile(profilePath?: string): VoiceRttDeviceProfile;
|
|
81
|
+
export declare function loadVoiceRttBaseline(baselinePath?: string): VoiceRttBaseline;
|
|
82
|
+
/**
|
|
83
|
+
* Wrap a TTS backend to delay the first audio chunk with a real timer
|
|
84
|
+
* (seeded red for NFR-07). Uses setTimeout — not a mocked clock.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createDelayedFirstAudioTtsBackend(delayMs: number, inner?: LocalTtsNativeBackend): LocalTtsNativeBackend;
|
|
87
|
+
export declare function evaluateVoiceRttGates(args: {
|
|
88
|
+
measuredP95Ms: number;
|
|
89
|
+
budgetP95Ms: number;
|
|
90
|
+
baselineP95Ms: number;
|
|
91
|
+
tolerancePercent: number;
|
|
92
|
+
}): {
|
|
93
|
+
absoluteOk: boolean;
|
|
94
|
+
relativeOk: boolean;
|
|
95
|
+
allowedRelativeP95Ms: number;
|
|
96
|
+
failures: string[];
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* One sample: STT until final → clock → TTS until first audio chunk → clock.
|
|
100
|
+
*/
|
|
101
|
+
export declare function measureOneVoiceRttSample(args: {
|
|
102
|
+
subjectId: string;
|
|
103
|
+
deviceId: string;
|
|
104
|
+
utteranceId: string;
|
|
105
|
+
language: string;
|
|
106
|
+
injectFirstAudioDelayMs?: number;
|
|
107
|
+
}): Promise<VoiceRttSample>;
|
|
108
|
+
/**
|
|
109
|
+
* Run NFR-07 voice RTT proof on the recorded mid-range device profile.
|
|
110
|
+
*/
|
|
111
|
+
export declare function runVoiceRttProof(options: RunVoiceRttProofOptions): Promise<VoiceRttProofResult>;
|
|
112
|
+
//# sourceMappingURL=voice_rtt_proof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice_rtt_proof.d.ts","sourceRoot":"","sources":["../src/voice_rtt_proof.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NFR-07 voice RTT proof: final transcript timestamp → first TTS audio chunk.
|
|
3
|
+
*
|
|
4
|
+
* Uses real performance.now() wall clocks on the measurement path (not mocked
|
|
5
|
+
* clocks alone). Absolute ceiling from PRD_MATRIX (≤2500ms p95) plus relative
|
|
6
|
+
* regression vs a recorded device-profile baseline.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { collectTranscriptSegments } from "@moolam/contract-conformance";
|
|
12
|
+
import { indicFixtureAsAudioStream, loadIndicUtteranceFixture, loadWhisperCppSpeech, } from "./stt_binding.js";
|
|
13
|
+
import { createInProcessLocalTtsBackend, loadLocalTts, } from "./tts_binding.js";
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
16
|
+
export const NFR_07_ID = "NFR-07";
|
|
17
|
+
export const NFR_07_BUDGET_P95_MS = 2500;
|
|
18
|
+
export const DEFAULT_VOICE_RTT_DEVICE_PROFILE = path.join(PACKAGE_ROOT, "certification", "device-profiles", "mid-range-android.json");
|
|
19
|
+
export const DEFAULT_VOICE_RTT_BASELINE = path.join(PACKAGE_ROOT, "certification", "voice-rtt.baseline.json");
|
|
20
|
+
function nowMs() {
|
|
21
|
+
return performance.now();
|
|
22
|
+
}
|
|
23
|
+
function sleep(ms) {
|
|
24
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
|
+
}
|
|
26
|
+
/** Percentile (nearest-rank) over ascending samples. */
|
|
27
|
+
export function percentileMs(samples, p) {
|
|
28
|
+
if (samples.length === 0)
|
|
29
|
+
return Number.NaN;
|
|
30
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
31
|
+
const rank = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1));
|
|
32
|
+
return sorted[rank];
|
|
33
|
+
}
|
|
34
|
+
export function loadVoiceRttDeviceProfile(profilePath = DEFAULT_VOICE_RTT_DEVICE_PROFILE) {
|
|
35
|
+
if (!existsSync(profilePath)) {
|
|
36
|
+
throw new Error(`voice RTT device profile missing at ${profilePath}`);
|
|
37
|
+
}
|
|
38
|
+
const raw = JSON.parse(readFileSync(profilePath, "utf8"));
|
|
39
|
+
if (typeof raw.schemaVersion !== "string" ||
|
|
40
|
+
typeof raw.profileId !== "string" ||
|
|
41
|
+
raw.nfr?.nfrId !== NFR_07_ID ||
|
|
42
|
+
raw.nfr.metric !== "final_transcript_to_first_audio" ||
|
|
43
|
+
typeof raw.nfr.voiceRttP95Ms !== "number" ||
|
|
44
|
+
!(raw.nfr.voiceRttP95Ms > 0)) {
|
|
45
|
+
throw new Error("voice RTT device profile must declare NFR-07 final_transcript_to_first_audio budget");
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
schemaVersion: raw.schemaVersion,
|
|
49
|
+
profileId: raw.profileId,
|
|
50
|
+
platform: typeof raw.platform === "string" ? raw.platform : "android",
|
|
51
|
+
hardwareClass: typeof raw.hardwareClass === "string" ? raw.hardwareClass : "mid-range",
|
|
52
|
+
nfr: {
|
|
53
|
+
nfrId: NFR_07_ID,
|
|
54
|
+
metric: "final_transcript_to_first_audio",
|
|
55
|
+
voiceRttP95Ms: raw.nfr.voiceRttP95Ms,
|
|
56
|
+
},
|
|
57
|
+
...(typeof raw.description === "string"
|
|
58
|
+
? { description: raw.description }
|
|
59
|
+
: {}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function loadVoiceRttBaseline(baselinePath = DEFAULT_VOICE_RTT_BASELINE) {
|
|
63
|
+
if (!existsSync(baselinePath)) {
|
|
64
|
+
throw new Error(`voice RTT baseline missing at ${baselinePath}`);
|
|
65
|
+
}
|
|
66
|
+
const raw = JSON.parse(readFileSync(baselinePath, "utf8"));
|
|
67
|
+
if (typeof raw.schemaVersion !== "string" ||
|
|
68
|
+
raw.nfrId !== NFR_07_ID ||
|
|
69
|
+
raw.metric !== "final_transcript_to_first_audio" ||
|
|
70
|
+
typeof raw.deviceProfileId !== "string" ||
|
|
71
|
+
typeof raw.p95Ms !== "number" ||
|
|
72
|
+
!(raw.p95Ms > 0) ||
|
|
73
|
+
typeof raw.tolerancePercent !== "number" ||
|
|
74
|
+
!(raw.tolerancePercent >= 0) ||
|
|
75
|
+
typeof raw.sampleCount !== "number" ||
|
|
76
|
+
!(raw.sampleCount > 0)) {
|
|
77
|
+
throw new Error("voice RTT baseline must declare NFR-07 p95Ms, tolerancePercent, sampleCount");
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
schemaVersion: raw.schemaVersion,
|
|
81
|
+
nfrId: NFR_07_ID,
|
|
82
|
+
metric: "final_transcript_to_first_audio",
|
|
83
|
+
deviceProfileId: raw.deviceProfileId,
|
|
84
|
+
p95Ms: raw.p95Ms,
|
|
85
|
+
tolerancePercent: raw.tolerancePercent,
|
|
86
|
+
sampleCount: Math.min(64, Math.floor(raw.sampleCount)),
|
|
87
|
+
warmupCount: typeof raw.warmupCount === "number" && raw.warmupCount >= 0
|
|
88
|
+
? Math.min(16, Math.floor(raw.warmupCount))
|
|
89
|
+
: 2,
|
|
90
|
+
...(typeof raw.notes === "string" ? { notes: raw.notes } : {}),
|
|
91
|
+
...(typeof raw.recordedAt === "string" ? { recordedAt: raw.recordedAt } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Wrap a TTS backend to delay the first audio chunk with a real timer
|
|
96
|
+
* (seeded red for NFR-07). Uses setTimeout — not a mocked clock.
|
|
97
|
+
*/
|
|
98
|
+
export function createDelayedFirstAudioTtsBackend(delayMs, inner = createInProcessLocalTtsBackend()) {
|
|
99
|
+
const ms = Math.max(0, delayMs);
|
|
100
|
+
return {
|
|
101
|
+
kind: inner.kind,
|
|
102
|
+
load: (modelId) => inner.load(modelId),
|
|
103
|
+
unload: (handle) => inner.unload(handle),
|
|
104
|
+
async synthesize(handle, params) {
|
|
105
|
+
if (ms > 0)
|
|
106
|
+
await sleep(ms);
|
|
107
|
+
return inner.synthesize(handle, params);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function evaluateVoiceRttGates(args) {
|
|
112
|
+
const failures = [];
|
|
113
|
+
const absoluteOk = Number.isFinite(args.measuredP95Ms) &&
|
|
114
|
+
args.measuredP95Ms <= args.budgetP95Ms;
|
|
115
|
+
if (!absoluteOk) {
|
|
116
|
+
failures.push(`NFR-07 absolute: p95 ${args.measuredP95Ms.toFixed(3)}ms > budget ${args.budgetP95Ms}ms`);
|
|
117
|
+
}
|
|
118
|
+
const allowedRelativeP95Ms = args.baselineP95Ms * (1 + args.tolerancePercent / 100);
|
|
119
|
+
const relativeOk = Number.isFinite(args.measuredP95Ms) &&
|
|
120
|
+
args.measuredP95Ms <= allowedRelativeP95Ms;
|
|
121
|
+
if (!relativeOk) {
|
|
122
|
+
failures.push(`NFR-07 relative: p95 ${args.measuredP95Ms.toFixed(3)}ms > allowed ${allowedRelativeP95Ms.toFixed(3)}ms (baseline ${args.baselineP95Ms}ms + ${args.tolerancePercent}%)`);
|
|
123
|
+
}
|
|
124
|
+
return { absoluteOk, relativeOk, allowedRelativeP95Ms, failures };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* One sample: STT until final → clock → TTS until first audio chunk → clock.
|
|
128
|
+
*/
|
|
129
|
+
export async function measureOneVoiceRttSample(args) {
|
|
130
|
+
const fixture = loadIndicUtteranceFixture(args.utteranceId);
|
|
131
|
+
const stt = await loadWhisperCppSpeech({
|
|
132
|
+
subjectId: args.subjectId,
|
|
133
|
+
deviceId: args.deviceId,
|
|
134
|
+
});
|
|
135
|
+
const tts = await loadLocalTts({
|
|
136
|
+
subjectId: args.subjectId,
|
|
137
|
+
deviceId: args.deviceId,
|
|
138
|
+
...(args.injectFirstAudioDelayMs && args.injectFirstAudioDelayMs > 0
|
|
139
|
+
? {
|
|
140
|
+
backend: createDelayedFirstAudioTtsBackend(args.injectFirstAudioDelayMs),
|
|
141
|
+
}
|
|
142
|
+
: {}),
|
|
143
|
+
});
|
|
144
|
+
try {
|
|
145
|
+
let tFinal;
|
|
146
|
+
let finalText = "";
|
|
147
|
+
for await (const seg of stt.transcribe(indicFixtureAsAudioStream(fixture))) {
|
|
148
|
+
if (!seg.isFinal)
|
|
149
|
+
continue;
|
|
150
|
+
tFinal = nowMs();
|
|
151
|
+
finalText = seg.text.trim() || "voice.rtt.probe";
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
if (tFinal === undefined) {
|
|
155
|
+
// Fallback collect if stream ended without early break
|
|
156
|
+
const segs = await collectTranscriptSegments(stt.transcribe(indicFixtureAsAudioStream(fixture)));
|
|
157
|
+
const final = [...segs].reverse().find((s) => s.isFinal);
|
|
158
|
+
if (!final) {
|
|
159
|
+
throw new Error(`voice RTT: no final transcript for ${args.utteranceId}`);
|
|
160
|
+
}
|
|
161
|
+
tFinal = nowMs();
|
|
162
|
+
finalText = final.text.trim() || "voice.rtt.probe";
|
|
163
|
+
}
|
|
164
|
+
let tFirst;
|
|
165
|
+
for await (const chunk of tts.synthesize(finalText, {
|
|
166
|
+
language: args.language,
|
|
167
|
+
})) {
|
|
168
|
+
if (chunk.data.byteLength > 0) {
|
|
169
|
+
tFirst = nowMs();
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (tFirst === undefined) {
|
|
174
|
+
throw new Error("voice RTT: synthesize yielded no audio chunks");
|
|
175
|
+
}
|
|
176
|
+
const latencyMs = Math.max(0, tFirst - tFinal);
|
|
177
|
+
return { latencyMs, utteranceId: args.utteranceId };
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
await Promise.all([stt.unload(), tts.unload()]);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Run NFR-07 voice RTT proof on the recorded mid-range device profile.
|
|
185
|
+
*/
|
|
186
|
+
export async function runVoiceRttProof(options) {
|
|
187
|
+
const deviceProfile = loadVoiceRttDeviceProfile(options.deviceProfilePath ?? DEFAULT_VOICE_RTT_DEVICE_PROFILE);
|
|
188
|
+
const baseline = loadVoiceRttBaseline(options.baselinePath ?? DEFAULT_VOICE_RTT_BASELINE);
|
|
189
|
+
const budgetP95Ms = options.budgetP95Ms ?? deviceProfile.nfr.voiceRttP95Ms ?? NFR_07_BUDGET_P95_MS;
|
|
190
|
+
const baselineP95Ms = options.baselineP95Ms ?? baseline.p95Ms;
|
|
191
|
+
const tolerancePercent = options.tolerancePercent ?? baseline.tolerancePercent;
|
|
192
|
+
const sampleCount = Math.min(64, Math.max(1, options.sampleCount ?? baseline.sampleCount));
|
|
193
|
+
const warmupCount = Math.min(16, Math.max(0, options.warmupCount ?? baseline.warmupCount));
|
|
194
|
+
const utteranceId = options.utteranceId ?? "hi-greeting";
|
|
195
|
+
const language = options.language ?? "hi-IN";
|
|
196
|
+
const measured = [];
|
|
197
|
+
const total = warmupCount + sampleCount;
|
|
198
|
+
for (let i = 0; i < total; i++) {
|
|
199
|
+
const sample = await measureOneVoiceRttSample({
|
|
200
|
+
subjectId: `${options.subjectId}.rtt.${i}`,
|
|
201
|
+
deviceId: options.deviceId,
|
|
202
|
+
utteranceId,
|
|
203
|
+
language,
|
|
204
|
+
...(options.injectFirstAudioDelayMs !== undefined
|
|
205
|
+
? { injectFirstAudioDelayMs: options.injectFirstAudioDelayMs }
|
|
206
|
+
: {}),
|
|
207
|
+
});
|
|
208
|
+
if (i >= warmupCount) {
|
|
209
|
+
measured.push(sample.latencyMs);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const measuredP95Ms = percentileMs(measured, 95);
|
|
213
|
+
const gates = evaluateVoiceRttGates({
|
|
214
|
+
measuredP95Ms,
|
|
215
|
+
budgetP95Ms,
|
|
216
|
+
baselineP95Ms,
|
|
217
|
+
tolerancePercent,
|
|
218
|
+
});
|
|
219
|
+
const ok = gates.absoluteOk && gates.relativeOk;
|
|
220
|
+
return {
|
|
221
|
+
nfrId: NFR_07_ID,
|
|
222
|
+
metric: "final_transcript_to_first_audio",
|
|
223
|
+
outcome: ok ? "pass" : "fail",
|
|
224
|
+
ok,
|
|
225
|
+
measuredP95Ms,
|
|
226
|
+
budgetP95Ms,
|
|
227
|
+
baselineP95Ms,
|
|
228
|
+
allowedRelativeP95Ms: gates.allowedRelativeP95Ms,
|
|
229
|
+
absoluteOk: gates.absoluteOk,
|
|
230
|
+
relativeOk: gates.relativeOk,
|
|
231
|
+
sampleCount: measured.length,
|
|
232
|
+
samplesMs: measured.map((m) => Number(m.toFixed(3))),
|
|
233
|
+
deviceProfileId: deviceProfile.profileId,
|
|
234
|
+
subjectId: options.subjectId,
|
|
235
|
+
deviceId: options.deviceId,
|
|
236
|
+
clock: "performance.now",
|
|
237
|
+
failures: gates.failures,
|
|
238
|
+
policy: "absolute-ceiling-plus-relative-baseline",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=voice_rtt_proof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice_rtt_proof.js","sourceRoot":"","sources":["../src/voice_rtt_proof.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whisper.cpp-class FFI / native addon seam.
|
|
3
|
+
*
|
|
4
|
+
* Production hosts inject a real whisper.cpp (or compatible) addon.
|
|
5
|
+
* CI uses {@link createInProcessWhisperCppBackend} so SpeechInterface
|
|
6
|
+
* stays tested without linking libwhisper on every runner.
|
|
7
|
+
*/
|
|
8
|
+
export type WhisperCppNativeHandle = {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
};
|
|
11
|
+
export type WhisperCppTranscribeNativeParams = {
|
|
12
|
+
/** Concatenated PCM (or metadata probe bytes) for the utterance. */
|
|
13
|
+
pcm: Uint8Array;
|
|
14
|
+
sampleRateHz: number;
|
|
15
|
+
/** Duration estimate in ms (from PCM length or caller). */
|
|
16
|
+
durationMs: number;
|
|
17
|
+
/** BCP-47 hint; backend may ignore when auto-detecting. */
|
|
18
|
+
languageHint?: string;
|
|
19
|
+
/** Languages the loaded model actually supports. */
|
|
20
|
+
supportedLanguages: readonly string[];
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
};
|
|
23
|
+
export type WhisperCppTranscribeNativeResult = {
|
|
24
|
+
/** Interim hypothesis text (may equal final for short utterances). */
|
|
25
|
+
partialText: string;
|
|
26
|
+
finalText: string;
|
|
27
|
+
language: string;
|
|
28
|
+
confidence: number;
|
|
29
|
+
startMs: number;
|
|
30
|
+
endMs: number;
|
|
31
|
+
};
|
|
32
|
+
export type WhisperCppNativeBackend = {
|
|
33
|
+
readonly kind: "in-process" | "native-addon";
|
|
34
|
+
load(modelId: string): Promise<WhisperCppNativeHandle>;
|
|
35
|
+
unload(handle: WhisperCppNativeHandle): Promise<void>;
|
|
36
|
+
transcribe(handle: WhisperCppNativeHandle, params: WhisperCppTranscribeNativeParams): Promise<WhisperCppTranscribeNativeResult>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* In-process whisper.cpp-class stand-in for CI / unit tests.
|
|
40
|
+
* Emits non-empty hypotheses for code-switched and short probes without
|
|
41
|
+
* crashing segmentation.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createInProcessWhisperCppBackend(): WhisperCppNativeBackend;
|
|
44
|
+
//# sourceMappingURL=whisper_ffi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whisper_ffi.d.ts","sourceRoot":"","sources":["../src/whisper_ffi.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,sBAAsB,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,oEAAoE;IACpE,GAAG,EAAE,UAAU,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG;IAC7C,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACvD,MAAM,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,UAAU,CACR,MAAM,EAAE,sBAAsB,EAC9B,MAAM,EAAE,gCAAgC,GACvC,OAAO,CAAC,gCAAgC,CAAC,CAAC;CAC9C,CAAC;AAeF;;;;GAIG;AACH,wBAAgB,gCAAgC,IAAI,uBAAuB,CAuF1E"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whisper.cpp-class FFI / native addon seam.
|
|
3
|
+
*
|
|
4
|
+
* Production hosts inject a real whisper.cpp (or compatible) addon.
|
|
5
|
+
* CI uses {@link createInProcessWhisperCppBackend} so SpeechInterface
|
|
6
|
+
* stays tested without linking libwhisper on every runner.
|
|
7
|
+
*/
|
|
8
|
+
const DEVANAGARI = /[\u0900-\u097F]/u;
|
|
9
|
+
const TAMIL = /[\u0B80-\u0BFF]/u;
|
|
10
|
+
const LATIN_WORD = /[A-Za-z]{2,}/;
|
|
11
|
+
function decodeProbeText(pcm) {
|
|
12
|
+
try {
|
|
13
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(pcm);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* In-process whisper.cpp-class stand-in for CI / unit tests.
|
|
21
|
+
* Emits non-empty hypotheses for code-switched and short probes without
|
|
22
|
+
* crashing segmentation.
|
|
23
|
+
*/
|
|
24
|
+
export function createInProcessWhisperCppBackend() {
|
|
25
|
+
let seq = 0;
|
|
26
|
+
return {
|
|
27
|
+
kind: "in-process",
|
|
28
|
+
async load(modelId) {
|
|
29
|
+
if (!modelId.trim()) {
|
|
30
|
+
throw new Error("whisper model id is required");
|
|
31
|
+
}
|
|
32
|
+
seq += 1;
|
|
33
|
+
return { id: `whisper-inproc-${seq}` };
|
|
34
|
+
},
|
|
35
|
+
async unload(_handle) {
|
|
36
|
+
/* no-op */
|
|
37
|
+
},
|
|
38
|
+
async transcribe(_handle, params) {
|
|
39
|
+
if (params.signal?.aborted) {
|
|
40
|
+
throw new Error("whisper transcribe aborted");
|
|
41
|
+
}
|
|
42
|
+
const langs = params.supportedLanguages.filter((l) => l.trim().length > 0);
|
|
43
|
+
if (langs.length === 0) {
|
|
44
|
+
throw new Error("supportedLanguages empty at native seam");
|
|
45
|
+
}
|
|
46
|
+
const fallback = langs[0];
|
|
47
|
+
const hint = params.languageHint && langs.includes(params.languageHint)
|
|
48
|
+
? params.languageHint
|
|
49
|
+
: fallback;
|
|
50
|
+
const probe = decodeProbeText(params.pcm);
|
|
51
|
+
const noisyClassroom = /NOISE:classroom/i.test(probe);
|
|
52
|
+
const hasIndic = DEVANAGARI.test(probe);
|
|
53
|
+
const hasTamil = TAMIL.test(probe);
|
|
54
|
+
const hasLatin = LATIN_WORD.test(probe);
|
|
55
|
+
const codeSwitched = (hasIndic || hasTamil) && hasLatin;
|
|
56
|
+
let language = hint;
|
|
57
|
+
if (codeSwitched) {
|
|
58
|
+
language = langs.includes("hi-IN")
|
|
59
|
+
? "hi-IN"
|
|
60
|
+
: langs.find((l) => l.startsWith("hi")) ?? hint;
|
|
61
|
+
}
|
|
62
|
+
else if (hasTamil) {
|
|
63
|
+
language =
|
|
64
|
+
langs.find((l) => l.startsWith("ta")) ?? hint;
|
|
65
|
+
}
|
|
66
|
+
else if (hasIndic) {
|
|
67
|
+
language =
|
|
68
|
+
langs.find((l) => l.startsWith("hi")) ?? hint;
|
|
69
|
+
}
|
|
70
|
+
else if (hasLatin) {
|
|
71
|
+
language =
|
|
72
|
+
langs.find((l) => l.startsWith("en")) ?? hint;
|
|
73
|
+
}
|
|
74
|
+
const durationMs = Math.max(0, params.durationMs);
|
|
75
|
+
const endMs = Math.max(durationMs, 1);
|
|
76
|
+
// Short utterances still get a hypothesis; never empty final.
|
|
77
|
+
// Strip noise marker from hypothesis text — never treat marker as learner content.
|
|
78
|
+
const cleaned = probe.replace(/NOISE:classroom\s*/gi, "").trim();
|
|
79
|
+
const base = cleaned.length > 0
|
|
80
|
+
? cleaned.slice(0, 120)
|
|
81
|
+
: durationMs < 200
|
|
82
|
+
? "…"
|
|
83
|
+
: "utterance";
|
|
84
|
+
const partialText = codeSwitched
|
|
85
|
+
? `${base.slice(0, Math.max(1, Math.floor(base.length / 2)))}…`
|
|
86
|
+
: `${base.slice(0, Math.max(1, Math.min(base.length, 24)))}`;
|
|
87
|
+
const finalText = codeSwitched ? `${base} [code-switched]` : base;
|
|
88
|
+
// Classroom ambient noise → confidence spike-down (FP-002 regression lock).
|
|
89
|
+
let confidence = codeSwitched ? 0.72 : 0.9;
|
|
90
|
+
if (noisyClassroom) {
|
|
91
|
+
confidence = Math.min(confidence, 0.35);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
partialText: partialText.length > 0 ? partialText : "…",
|
|
95
|
+
finalText: finalText.length > 0 ? finalText : "…",
|
|
96
|
+
language,
|
|
97
|
+
confidence,
|
|
98
|
+
startMs: 0,
|
|
99
|
+
endMs,
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=whisper_ffi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whisper_ffi.js","sourceRoot":"","sources":["../src/whisper_ffi.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "bindings-speech.indic-utterances.v1",
|
|
3
|
+
"sampleRateHz": 16000,
|
|
4
|
+
"pcmEncoding": "s16le-mono",
|
|
5
|
+
"description": "Committed Indic + English + code-switched Hindi/English STT audio fixtures (PCM s16le mono @ 16kHz). A short UTF-8 probe is embedded at the PCM head for the whisper.cpp-class in-process seam; production backends decode acoustics and ignore the prefix.",
|
|
6
|
+
"utterances": [
|
|
7
|
+
{
|
|
8
|
+
"id": "hi-greeting",
|
|
9
|
+
"kind": "indic-mono",
|
|
10
|
+
"language": "hi-IN",
|
|
11
|
+
"durationMs": 600,
|
|
12
|
+
"sampleRateHz": 16000,
|
|
13
|
+
"pcmRelpath": "audio/hi-greeting.pcm",
|
|
14
|
+
"byteLength": 19200,
|
|
15
|
+
"containsCodeSwitch": false,
|
|
16
|
+
"expectedLanguage": "hi-IN"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "en-classroom",
|
|
20
|
+
"kind": "english-mono",
|
|
21
|
+
"language": "en-IN",
|
|
22
|
+
"durationMs": 700,
|
|
23
|
+
"sampleRateHz": 16000,
|
|
24
|
+
"pcmRelpath": "audio/en-classroom.pcm",
|
|
25
|
+
"byteLength": 22400,
|
|
26
|
+
"containsCodeSwitch": false,
|
|
27
|
+
"expectedLanguage": "en-IN"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "ta-greeting",
|
|
31
|
+
"kind": "indic-mono",
|
|
32
|
+
"language": "ta-IN",
|
|
33
|
+
"durationMs": 650,
|
|
34
|
+
"sampleRateHz": 16000,
|
|
35
|
+
"pcmRelpath": "audio/ta-greeting.pcm",
|
|
36
|
+
"byteLength": 20800,
|
|
37
|
+
"containsCodeSwitch": false,
|
|
38
|
+
"expectedLanguage": "ta-IN"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "hi-en-codeswitch",
|
|
42
|
+
"kind": "code-switched",
|
|
43
|
+
"language": "hi-IN",
|
|
44
|
+
"durationMs": 900,
|
|
45
|
+
"sampleRateHz": 16000,
|
|
46
|
+
"pcmRelpath": "audio/hi-en-codeswitch.pcm",
|
|
47
|
+
"byteLength": 28800,
|
|
48
|
+
"containsCodeSwitch": true,
|
|
49
|
+
"expectedLanguage": "hi-IN"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "short-hi",
|
|
53
|
+
"kind": "short-utterance",
|
|
54
|
+
"language": "hi-IN",
|
|
55
|
+
"durationMs": 120,
|
|
56
|
+
"sampleRateHz": 16000,
|
|
57
|
+
"pcmRelpath": "audio/short-hi.pcm",
|
|
58
|
+
"byteLength": 3840,
|
|
59
|
+
"containsCodeSwitch": false,
|
|
60
|
+
"expectedLanguage": "hi-IN"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "hi-classroom-noise",
|
|
64
|
+
"kind": "indic-classroom-noise",
|
|
65
|
+
"language": "hi-IN",
|
|
66
|
+
"durationMs": 900,
|
|
67
|
+
"sampleRateHz": 16000,
|
|
68
|
+
"pcmRelpath": "audio/hi-classroom-noise.pcm",
|
|
69
|
+
"byteLength": 28800,
|
|
70
|
+
"containsCodeSwitch": true,
|
|
71
|
+
"expectedLanguage": "hi-IN",
|
|
72
|
+
"ambientNoise": true
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "bindings-speech.indic-languages.v1",
|
|
3
|
+
"engine": "whisper.cpp-class",
|
|
4
|
+
"pinnedRevision": "whisper.cpp-class-v1",
|
|
5
|
+
"description": "BCP-47 languages actually supported by the acoustic+language models for the Indic STT fixture pack. Load declares this set as SpeechInterface.supportedLanguages — never aspirational.",
|
|
6
|
+
"languages": ["hi-IN", "en-IN", "ta-IN"],
|
|
7
|
+
"fallbackLanguage": "en-IN"
|
|
8
|
+
}
|