feat(voice): on-device voice tier — Gemma 4, Moonshine and Kokoro in the browser

This commit is contained in:
Bilawal Sidhu committed 2026-09-28 23:00:00 -05:00
1 parent a19897ad15
commit b55663e3ce
65 files changed
+9257 -58

No files matched your search

+7 -1
View File
@@ -24,6 +24,10 @@ const renderer = (file) =>
/^src\/layers\/[^/]+\/(?:index|rendering|snapshotRenderer|overlay|presentation|cards|controls)\.js$/.test(
file,
);
// The runtime phonemizer loader imports a downloaded file only after its
// SHA-256 matches the pinned value; no other module may import a computed URL.
const verifiedRuntimeImport = (file) =>
file === 'src/voice/local/workers/phonemizerRuntime.js';
// Two compatibility composition entries intentionally select standalone defaults.
const compatibilityEdge = (from, to) =>
(from === 'server/providers/local.js' &&
@@ -84,7 +88,9 @@ export function checkImportDirections(root) {
}
let analysis;
try {
analysis = analyzeModule(readFileSync(resolved, 'utf8'));
analysis = analyzeModule(readFileSync(resolved, 'utf8'), {
allowComputedImport: verifiedRuntimeImport(file),
});
} catch (error) {
report(file, error.message);
return null;
+21 -2
View File
@@ -692,7 +692,7 @@
"src/sources/protocols.test.mjs",
"src/voice/actionSchemas.js",
"src/voice/actionSchemas.test.mjs",
"server/providers/openai/toolDescriptions.js",
"src/voice/actionDescriptions.js",
"src/voice/session.js",
"src/voice/sessionCommands.js",
"src/voice/realtimeSession.js",
@@ -864,5 +864,24 @@
"src/layers/traffic/motion.test.mjs",
"scripts/qa-journey-recorder.mjs",
"scripts/qa-alpr-journey.mjs",
"scripts/qa-context-arrivals.mjs"
"scripts/qa-context-arrivals.mjs",
"build/onDeviceRuntime.js",
"src/ui/styles/local-voice.css",
"src/ui/styles/voice-engine.css",
"src/voice/voiceEngine.js",
"src/voice/voiceEngineControls.js",
"src/voice/voiceEnginePresentation.js",
"src/voice/voiceEngine.test.mjs",
"src/voice/local/naturalVoice.test.mjs",
"src/voice/local/naturalVoiceSetting.js",
"src/voice/local/workers/phonemizerRuntime.js",
"src/voice/local/localVoice.test.mjs",
"src/voice/local/localVoiceUi.test.mjs",
"src/voice/local/localRuntime.test.mjs",
"src/voice/local/localWebProvider.test.mjs",
"src/voice/local/localStreaming.test.mjs",
"src/voice/local/modelCache.test.mjs",
"src/voice/local/localRadio.test.mjs",
"src/voice/local/onDeviceRuntime.test.mjs",
"src/voice/local/localWorkflow.test.mjs"
]
+9 -4
View File
@@ -15,8 +15,13 @@ const browserGlobals = new Set([
'getComputedStyle',
]);
/** Inspect literal module dependencies and browser-platform references without executing code. */
export function analyzeModule(source) {
/**
* Inspect literal module dependencies and browser-platform references without
* executing code. `allowComputedImport` admits dynamic imports of computed
* specifiers (left out of the graph) for the one loader that imports bytes it
* has already verified.
*/
export function analyzeModule(source, { allowComputedImport = false } = {}) {
const imports = [];
const browser = new Set();
function walk(node, parent, field) {
@@ -30,11 +35,11 @@ export function analyzeModule(source) {
].includes(node.type) &&
node.source
) {
if (node.source.type !== 'StringLiteral')
if (node.source.type === 'StringLiteral') imports.push(node.source.value);
else if (!(allowComputedImport && node.type === 'ImportExpression'))
throw new Error(
'Computed module imports are not allowed in runtime code',
);
imports.push(node.source.value);
}
if (node.type === 'CallExpression' && node.callee?.name === 'require')
throw new Error('Runtime modules must use ES imports');
+49 -5
View File
@@ -27,7 +27,11 @@
"vite-build": {
"runtime": "node",
"exports": ["./build/vite", "./build/html"],
"modules": ["build/vite.js", "build/application-html.js"],
"modules": [
"build/vite.js",
"build/application-html.js",
"build/onDeviceRuntime.js"
],
"external": ["vite-plugin-cesium"]
},
"live-providers": {
@@ -283,7 +287,7 @@
"src/voice/voiceCost.js",
"src/voice/actionSchemas.js",
"src/voice/layerManifest.js",
"server/providers/openai/toolDescriptions.js",
"src/voice/actionDescriptions.js",
"src/sources/rateLimit.js"
],
"external": []
@@ -1299,7 +1303,12 @@
"src/voice/narration.js",
"src/voice/earcon.js",
"src/voice/pointerContext.js",
"src/voice/pointerReticle.js"
"src/voice/pointerReticle.js",
"src/voice/voiceEngineControls.js",
"src/voice/voiceEnginePresentation.js",
"src/voice/local/modelCatalog.js",
"src/ui/chipGroup.js",
"src/ui/uiLifetime.js"
],
"external": []
},
@@ -2056,7 +2065,37 @@
"src/sources/requestDeadline.js",
"src/data/local_data/osm_military_names/names.json",
"src/layers/installations/namedMarkers.js",
"src/data/infrastructureOverlayEntry.js"
"src/data/infrastructureOverlayEntry.js",
"src/voice/voiceEngine.js",
"src/voice/voiceEngineControls.js",
"src/voice/voiceEnginePresentation.js",
"src/voice/local/gemmaToolCalls.js",
"src/voice/local/localAudio.js",
"src/voice/local/localEngine.js",
"src/voice/local/localPrompt.js",
"src/voice/local/localVoiceControls.js",
"src/voice/local/localVoicePreferences.js",
"src/voice/local/localVoicePresentation.js",
"src/voice/local/localVoiceStore.js",
"src/voice/local/localWebSession.js",
"src/voice/local/modelCatalog.js",
"src/voice/local/replies.js",
"src/voice/local/speechText.js",
"src/voice/local/toolset.js",
"src/voice/local/workers/modelCache.js",
"src/voice/local/workers/runtimeAssets.js",
"src/voice/local/workers/phonemizerRuntime.js",
"src/voice/local/workerClient.js",
"src/voice/local/localWebProvider.js",
"src/voice/local/localWebFlag.js",
"src/voice/local/localRadio.js",
"src/voice/local/speechOutput.js",
"src/voice/local/streamTee.js",
"src/voice/local/sha256.js",
"src/voice/local/naturalVoiceSetting.js",
"src/ui/styles/local-voice.css",
"src/voice/actionDescriptions.js",
"src/voice/actionSchemas.js"
],
"external": [
"hls.js",
@@ -2789,7 +2828,12 @@
"src/voice/referents.js",
"src/voice/layerManifest.js",
"src/voice/speech.js",
"src/voice/pointerReticle.js"
"src/voice/pointerReticle.js",
"src/voice/voiceEngineControls.js",
"src/voice/voiceEnginePresentation.js",
"src/voice/local/modelCatalog.js",
"src/ui/chipGroup.js",
"src/ui/uiLifetime.js"
],
"external": []
},