Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
/** Actual offline Capacitor submitLocal -> packaged CPU models -> native playback; fixture audio only. */
@RunWith(AndroidJUnit4.class)
public final class LocalSpeechBridgeInstrumentedTest {
private static void until(String expression,long timeout)throws Exception {long end=SystemClock.elapsedRealtime()+timeout;while(SystemClock.elapsedRealtime()<end){if("true".equals(WebViewTestDriver.evaluate("Boolean("+expression+")")))return;SystemClock.sleep(50);}fail("Local bridge condition timed out");}
private static void until(String expression,long timeout)throws Exception {long end=SystemClock.elapsedRealtime()+timeout;while(SystemClock.elapsedRealtime()<end){if("true".equals(WebViewTestDriver.evaluate("Boolean("+expression+")")))return;SystemClock.sleep(50);}fail("Local bridge condition timed out: "+expression+"; ended="+WebViewTestDriver.evaluate("JSON.stringify(window.__localBridgeEnded||[])")+"; failed="+WebViewTestDriver.evaluate("JSON.stringify(window.__localBridgeFailed||[])")+"; stopped="+WebViewTestDriver.evaluate("JSON.stringify(window.__localBridgeStopped||[])"));}
private static JSONObject call(String expression)throws Exception {
WebViewTestDriver.evaluate("window.__localBridgeResult=null;Promise.resolve("+expression+").then(v=>window.__localBridgeResult=JSON.stringify(v||{}),()=>window.__localBridgeResult=JSON.stringify({error:true}))");
WebViewTestDriver.evaluate("window.__localBridgeResult=null;Promise.resolve("+expression+").then(v=>window.__localBridgeResult=JSON.stringify(v||{}),e=>window.__localBridgeResult=JSON.stringify({error:true,message:String(e?.message||e),code:e?.code||null}))");
until("window.__localBridgeResult!==null",150000);
return new JSONObject((String)new JSONTokener(WebViewTestDriver.evaluate("window.__localBridgeResult")).nextValue());
}
Expand All @@ -40,17 +40,20 @@ private static JSONObject call(String expression)throws Exception {
android.media.MediaExtractor fixture=new android.media.MediaExtractor();
try{fixture.setDataSource(audio.getAbsolutePath());assertEquals("One recorder audio track",1,fixture.getTrackCount());android.media.MediaFormat format=fixture.getTrackFormat(0);assertEquals("audio/mp4a-latm",format.getString(android.media.MediaFormat.KEY_MIME));assertEquals(16000,format.getInteger(android.media.MediaFormat.KEY_SAMPLE_RATE));assertEquals(1,format.getInteger(android.media.MediaFormat.KEY_CHANNEL_COUNT));}finally{fixture.release();}
until("window.Capacitor?.Plugins?.AlphaVoiceCloud&&document.querySelector('.os')",30000);
// Resident startup changes the selected session and deliberately releases prior speech.
// The signed-out production screen exposes connection work through aria-busy.
if(!BuildConfig.ELIZA_DEV_ALLOW_TEST_MOCKS)until("document.querySelector('.alpha-auth-content')&&document.querySelector('.alpha-auth-content').getAttribute('aria-busy')!=='true'&&[...document.querySelectorAll('.alpha-auth-content button')].some(b=>b.textContent==='Sign in with Eliza Cloud'&&!b.disabled)",120000);
try {
assertFalse(call("Capacitor.Plugins.AlphaVoiceCloud.cancelRecording()").has("error"));ready("bridge-ready");
AlphaVoiceCloudPlugin nativePlugin=plugin();Object capture=field(AlphaVoiceCloudPlugin.class,"capture").get(nativePlugin);
BoundedActivityScenario.main(()->{try{field(capture.getClass(),"file").set(capture,audio);field(capture.getClass(),"id").set(capture,clip);field(capture.getClass(),"durationMs").set(capture,6625L);}catch(Exception e){throw new RuntimeException(e);}});
JSONObject text=call("Capacitor.Plugins.AlphaVoiceCloud.transcribeLocalRecording({recordingId:"+JSONObject.quote(clip)+",requestId:'bridge-asr'})");
assertFalse("Actual local transcription succeeds",text.has("error"));assertTrue(text.getBoolean("local"));assertEquals("device",text.getString("execution"));assertEquals("fused-paradee-asr",text.getString("provider"));
String lower=text.getString("text").toLowerCase(Locale.ROOT);for(String word:new String[]{"nightfall","yellow","lamps","light","quarter"})assertTrue("Known human fixture contains "+word,lower.contains(word));
WebViewTestDriver.evaluate("window.__localBridgeEnded=[];window.__localBridgeFailed=[];window.__localBridgeListeners=null;Promise.all([Capacitor.Plugins.AlphaVoiceCloud.addListener('playbackEnded',v=>window.__localBridgeEnded.push(v.playbackId)),Capacitor.Plugins.AlphaVoiceCloud.addListener('playbackFailed',v=>window.__localBridgeFailed.push(v.playbackId))]).then(h=>window.__localBridgeListeners=h)");until("window.__localBridgeListeners!==null",10000);
JSONObject speech=call("Capacitor.Plugins.AlphaVoiceCloud.synthesizeLocal({text:'The quick brown fox jumps over the lazy dog.',requestId:'bridge-tts'})");assertFalse(speech.has("error"));assertEquals("device",speech.getString("execution"));String first=speech.getString("playbackId");
assertFalse(call("Capacitor.Plugins.AlphaVoiceCloud.play({playbackId:"+JSONObject.quote(first)+"})").has("error"));until("window.__localBridgeEnded.includes("+JSONObject.quote(first)+")",30000);assertEquals("0",WebViewTestDriver.evaluate("window.__localBridgeFailed.length"));
speech=call("Capacitor.Plugins.AlphaVoiceCloud.synthesizeLocal({text:'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.',requestId:'bridge-tts-stop'})");assertFalse(speech.has("error"));String second=speech.getString("playbackId");assertNotEquals(first,second);
WebViewTestDriver.evaluate("window.__localBridgeEnded=[];window.__localBridgeFailed=[];window.__localBridgeStopped=[];window.__localBridgeListeners=null;Promise.all([Capacitor.Plugins.AlphaVoiceCloud.addListener('playbackStopped',v=>window.__localBridgeStopped.push(v)),Capacitor.Plugins.AlphaVoiceCloud.addListener('playbackEnded',v=>window.__localBridgeEnded.push(v.playbackId)),Capacitor.Plugins.AlphaVoiceCloud.addListener('playbackFailed',v=>window.__localBridgeFailed.push(v.playbackId))]).then(h=>window.__localBridgeListeners=h)");until("window.__localBridgeListeners!==null",10000);
JSONObject speech=call("Capacitor.Plugins.AlphaVoiceCloud.synthesizeLocal({text:'The quick brown fox jumps over the lazy dog.',requestId:'bridge-tts'})");assertFalse("Local synthesis failed: "+speech,speech.has("error"));assertEquals("device",speech.getString("execution"));String first=speech.getString("playbackId");
JSONObject playing=call("Capacitor.Plugins.AlphaVoiceCloud.play({playbackId:"+JSONObject.quote(first)+"})");assertFalse("Playback start failed: "+playing,playing.has("error"));until("window.__localBridgeEnded.includes("+JSONObject.quote(first)+")",30000);assertEquals("0",WebViewTestDriver.evaluate("window.__localBridgeFailed.length"));
speech=call("Capacitor.Plugins.AlphaVoiceCloud.synthesizeLocal({text:'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.',requestId:'bridge-tts-stop'})");assertFalse("Local synthesis failed: "+speech,speech.has("error"));String second=speech.getString("playbackId");assertNotEquals(first,second);
assertFalse(call("Capacitor.Plugins.AlphaVoiceCloud.play({playbackId:"+JSONObject.quote(second)+"})").has("error"));assertFalse(call("Capacitor.Plugins.AlphaVoiceCloud.stopPlayback()").has("error"));SystemClock.sleep(250);assertEquals("false",WebViewTestDriver.evaluate("window.__localBridgeEnded.includes("+JSONObject.quote(second)+")"));assertTrue(call("Capacitor.Plugins.AlphaVoiceCloud.play({playbackId:"+JSONObject.quote(second)+"})").has("error"));
// Queue a real local request behind a controlled executor gate, then cancel it before inference.
// No model output or bridge response is substituted; this deterministically exercises drain ordering.
Expand Down
15 changes: 5 additions & 10 deletions android/embedding-host/qualified-host.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"pin": "33a6d19c024a4ec130710f0808df25588266d542",
"forkPin": "6e6a4401aae473723cb164a3190eebea6526ea6e",
"pin": "b322e38de25a2c2ff26a137552e4470ec2ccc0cc",
"forkPin": "6003a10368d8a6d5fb1f70fe9c63a70866e8bcff",
"identity": "ai.elizaresearch.alphaphone",
"abi": "arm64-v8a",
"model": {
Expand All @@ -11,8 +11,8 @@
"libraries": [
{
"name": "libelizainference.so",
"sha256": "2432c1f0db8af6297a00d64bd5816a7e8665abe30e51eb4350ebcf9a9063166b",
"bytes": 8006192,
"sha256": "1aac36d40a2228f97c39baa629d1f3ad0c8388c39b18e0f2eacf3ec165669920",
"bytes": 8063904,
"alignment16KiB": true
},
{
Expand All @@ -28,7 +28,7 @@
"alignment16KiB": true
}
],
"qualificationReceiptSha256": "b876d55132ec326fd04b3673672a148712d5832530cac2f10299259c91dfdc21",
"qualificationReceiptSha256": "01cec23b19c9886094cb27ca9d9b73a74749e02bd3237c98e1b95a05e6a546ec",
"jniIdentityRelocated": true,
"inputs": [
{
Expand Down Expand Up @@ -66,10 +66,5 @@
"mode": "registered",
"className": "ai/elizaresearch/alphaphone/ElizaVoiceNative",
"subset": "full"
},
"requalification": {
"required": true,
"atPin": "f931082ebd87a0fecc3f11f8eacf58f598294d4a",
"reason": "Upstream changed the native llama.cpp fork from6e6a4401 to6003a103 and native inputs. The recorded33a6 libraries remain historical; rebuild and qualify new libraries without relabeling these bytes."
}
}
Loading
Loading