@elizaos/app-core 2.0.3-beta.2 → 2.0.3-beta.4
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/agent-bridge.d.ts +5 -5
- package/api/auth/audit.d.ts +1 -1
- package/api/auth/auth-context.d.ts +1 -1
- package/api/auth/bootstrap-token.d.ts +1 -1
- package/api/auth/sessions.d.ts +1 -1
- package/api/auth-bootstrap-routes.d.ts +1 -1
- package/api/auth-pairing-routes.d.ts +1 -1
- package/api/auth-pairing-routes.d.ts.map +1 -1
- package/api/auth-pairing-routes.js +2 -3
- package/api/auth-session-routes.d.ts +2 -2
- package/api/auth.d.ts +1 -1
- package/api/automations-compat-routes.d.ts +1 -1
- package/api/background-tasks-routes.d.ts +1 -1
- package/api/catalog-routes.d.ts +1 -1
- package/api/cloud-voice-routes.d.ts +1 -1
- package/api/compat-route-shared.d.ts +12 -5
- package/api/compat-route-shared.d.ts.map +1 -1
- package/api/compat-route-shared.js +22 -187
- package/api/database-rows-compat-routes.d.ts +1 -1
- package/api/dev-compat-routes.d.ts +1 -1
- package/api/dev-route-catalog.d.ts.map +1 -1
- package/api/dev-route-catalog.js +20 -0
- package/api/drop-status-compat-route.d.ts +3 -0
- package/api/drop-status-compat-route.d.ts.map +1 -0
- package/api/drop-status-compat-route.js +12 -0
- package/api/first-run-routes.d.ts +1 -1
- package/api/internal-routes.d.ts +1 -1
- package/api/runtime-mode-routes.d.ts +1 -1
- package/api/secrets-manager-routes.d.ts.map +1 -1
- package/api/sensitive-request-routes.d.ts +2 -2
- package/api/server-config-filter.d.ts.map +1 -1
- package/api/server-config-filter.js +0 -1
- package/api/server-first-run-helpers.d.ts.map +1 -1
- package/api/server-first-run-helpers.js +0 -3
- package/api/server.d.ts +8 -8
- package/api/server.d.ts.map +1 -1
- package/api/server.js +13 -59
- package/api/training-benchmarks.d.ts +1 -1
- package/api/training-benchmarks.d.ts.map +1 -1
- package/api/training-benchmarks.js +31 -9
- package/api/workbench-compat-routes.d.ts +1 -1
- package/benchmark/server-utils.d.ts +2 -2
- package/browser.d.ts +4 -4
- package/cli/plugins-cli.d.ts.map +1 -1
- package/cli/plugins-cli.js +5 -0
- package/cli/program/register.auth.d.ts +27 -1
- package/cli/program/register.auth.d.ts.map +1 -1
- package/cli/program/register.auth.js +187 -0
- package/cli/program/register.dashboard.d.ts.map +1 -1
- package/cli/program/register.dashboard.js +11 -1
- package/cli/program.d.ts +1 -1
- package/first-run/first-run-config.d.ts +1 -1
- package/index.d.ts +46 -47
- package/index.d.ts.map +1 -1
- package/index.js +3 -7
- package/package.json +42 -36
- package/packaging/PUBLISHING_GUIDE.md +20 -20
- package/platform/elizaos-agent-browser-stub.d.ts +0 -1
- package/platform/elizaos-agent-browser-stub.d.ts.map +1 -1
- package/platform/elizaos-agent-browser-stub.js +0 -1
- package/platform/ios-runtime-bridge.d.ts.map +1 -1
- package/platform/ios-runtime-bridge.js +0 -1
- package/platform/native-plugin-entrypoints.d.ts +2 -2
- package/platforms/android/app/build.gradle +30 -7
- package/platforms/android/app/src/main/elizavoice-jni/elizavoice-jni.cpp +179 -35
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaAgentService.java +60 -32
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaBionicInferenceServer.java +405 -65
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaVoiceNative.java +35 -0
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaVoicePlugin.java +13 -6
- package/platforms/android/app/src/main/res/drawable/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/values/colors.xml +5 -1
- package/platforms/android/app/src/main/res/values/ic_launcher_background.xml +1 -1
- package/platforms/android/app/src/main/res/values/styles.xml +4 -1
- package/platforms/electrobun/electrobun.config.ts +0 -1
- package/platforms/electrobun/native/macos/window-effects.mm +4 -0
- package/platforms/electrobun/package.json +11 -9
- package/platforms/electrobun/remotes/local-model/src/bun/eliza1-catalog.ts +1 -1
- package/platforms/electrobun/remotes/runtime/src/dev/phase2-smoke.ts +10 -5
- package/platforms/electrobun/remotes/runtime/src/dev/phase3-smoke.ts +11 -15
- package/platforms/electrobun/src/application-menu.ts +0 -7
- package/platforms/electrobun/src/desktop-http-request.test.ts +30 -1
- package/platforms/electrobun/src/desktop-http-request.ts +14 -2
- package/platforms/electrobun/src/desktop-test-bridge-server.ts +28 -0
- package/platforms/electrobun/src/fatal-shutdown.test.ts +3 -7
- package/platforms/electrobun/src/index.ts +64 -13
- package/platforms/electrobun/src/lifecycle/api-base-owner.test.ts +83 -0
- package/platforms/electrobun/src/lifecycle/api-base-owner.ts +37 -16
- package/platforms/electrobun/src/native/agent.ts +28 -17
- package/platforms/electrobun/src/native/credentials.ts +6 -1
- package/platforms/electrobun/src/native/desktop-window.test.ts +142 -26
- package/platforms/electrobun/src/native/desktop.ts +8 -4
- package/platforms/electrobun/src/native/index.ts +6 -0
- package/platforms/electrobun/src/native/remote-plugin-host.ts +18 -8
- package/platforms/electrobun/src/preload.js +1 -1
- package/platforms/electrobun/src/voice/voice-live-validation.test.ts +10 -5
- package/platforms/electrobun/src/voice/voice-runtime-adapter.ts +3 -2
- package/platforms/electrobun/src/voice/voice-service.test.ts +26 -0
- package/platforms/electrobun/src/voice/voice-service.ts +57 -17
- package/platforms/electrobun/tsconfig.json +17 -1
- package/platforms/ios/App/App/Base.lproj/LaunchScreen.storyboard +4 -6
- package/registry/index.d.ts +1 -6
- package/registry/index.d.ts.map +1 -1
- package/registry/index.js +9 -101
- package/runtime/android-avf-microdroid-bridge.d.ts +1 -1
- package/runtime/bundled-fused-lib.d.ts +36 -0
- package/runtime/bundled-fused-lib.d.ts.map +1 -0
- package/runtime/bundled-fused-lib.js +77 -0
- package/runtime/channel-plugin-map.d.ts +1 -5
- package/runtime/channel-plugin-map.d.ts.map +1 -1
- package/runtime/channel-plugin-map.js +11 -31
- package/runtime/desktop/index.d.ts +5 -5
- package/runtime/dev-server.js +11 -2
- package/runtime/eliza.d.ts.map +1 -1
- package/runtime/eliza.js +18 -8
- package/runtime/mode/route-mode-guard.d.ts +1 -1
- package/runtime/mode/route-mode-matrix.d.ts +1 -1
- package/runtime/voice-warmup.d.ts +4 -19
- package/runtime/voice-warmup.d.ts.map +1 -1
- package/runtime/voice-warmup.js +4 -14
- package/scripts/aosp/README.md +10 -1
- package/scripts/aosp/compile-libllama-paths.mjs +110 -0
- package/scripts/aosp/compile-libllama-zig-pin.test.mjs +147 -0
- package/scripts/aosp/compile-libllama.mjs +377 -81
- package/scripts/aosp/compile-libllama.test.mjs +133 -0
- package/scripts/aosp/compile-shim.mjs +21 -1
- package/scripts/aosp/deploy-pixel.mjs +1 -1
- package/scripts/aosp/stage-default-models.mjs +24 -31
- package/scripts/aosp/stage-default-models.test.mjs +23 -0
- package/scripts/assert-required-bundled-packages.test.ts +22 -1
- package/scripts/build-helpers/verify-fused-symbols.mjs +33 -0
- package/scripts/build-helpers/verify-fused-symbols.test.mjs +0 -0
- package/scripts/build-image.sh +1 -3
- package/scripts/build-llama-cpp-mtp.mjs +17 -16
- package/scripts/check-i18n.mjs +0 -1
- package/scripts/copy-runtime-node-modules.ts +147 -0
- package/scripts/desktop-build.mjs +317 -4
- package/scripts/docker-ci-smoke.sh +33 -8
- package/scripts/ensure-type-package-aliases.mjs +1 -1
- package/scripts/ios-xcframework/run-physical-device-smoke.mjs +1 -1
- package/scripts/lib/agent-source-watcher.mjs +0 -1
- package/scripts/lib/artifact-staleness.d.mts +33 -0
- package/scripts/lib/artifact-staleness.mjs +122 -0
- package/scripts/lib/artifact-staleness.test.mts +98 -0
- package/scripts/lib/duet-bridge.mjs +1 -1
- package/scripts/lib/ios-plist-url-scheme.mjs +80 -0
- package/scripts/lib/mobile-build-decisions.mjs +97 -0
- package/scripts/lib/renderer-build-manifest.d.mts +65 -0
- package/scripts/lib/renderer-build-manifest.mjs +257 -0
- package/scripts/lib/renderer-build-manifest.test.mts +277 -0
- package/scripts/lib/stage-android-agent.mjs +13 -4
- package/scripts/lib/verify-ondevice-artifact.d.mts +16 -0
- package/scripts/lib/verify-ondevice-artifact.mjs +61 -0
- package/scripts/lib/verify-ondevice-artifact.test.mts +98 -0
- package/scripts/lib/websocket-pending-queue.ts +92 -0
- package/scripts/link-docker-local-app-packages.mjs +6 -0
- package/scripts/playwright-ui-live-stack.ts +430 -144
- package/scripts/playwright-ui-smoke-api-stub.mjs +160 -168
- package/scripts/run-local-plugin-live-smoke.mjs +1 -6
- package/scripts/run-mobile-build-brand-separation.test.mts +33 -0
- package/scripts/run-mobile-build-ios-engine-gate.test.mjs +85 -0
- package/scripts/run-mobile-build-mtp-staleness.test.mts +198 -0
- package/scripts/run-mobile-build.mjs +408 -86
- package/scripts/serve-real-local-agent.ts +72 -0
- package/scripts/stage-desktop-fused-lib.mjs +43 -14
- package/scripts/verify-ondevice-artifact.mjs +111 -0
- package/scripts/voice-attribution-smoke.ts +144 -38
- package/scripts/voice-duet.mjs +6 -6
- package/scripts/voice-e2e-hardware.ts +22 -16
- package/scripts/voice-interactive.mjs +16 -45
- package/security/agent-vault-id.d.ts +1 -1
- package/security/platform-secure-store-node.d.ts +1 -1
- package/services/ambient-audio/consent.d.ts +1 -1
- package/services/ambient-audio/index.d.ts +6 -6
- package/services/ambient-audio/replay-buffer.d.ts +1 -1
- package/services/ambient-audio/response-gate.d.ts +1 -1
- package/services/ambient-audio/service.d.ts +3 -3
- package/services/connector-target-catalog.d.ts +1 -1
- package/services/sensitive-requests/index.d.ts +6 -6
- package/services/steward-sidecar/wallet-setup.d.ts +1 -1
- package/services/steward-sidecar.d.ts +3 -3
- package/services/tunnel-to-mobile/index.d.ts +1 -1
- package/services/vault-bootstrap.d.ts.map +1 -1
- package/services/vault-bootstrap.js +14 -10
- package/services/voice-profiles/diarization-pipeline.d.ts +1 -1
- package/services/voice-profiles/index.d.ts +11 -11
- package/services/voice-profiles/owner-confidence.d.ts +1 -1
- package/services/voice-profiles/private-challenge.d.ts +1 -1
- package/services/voice-profiles/store.d.ts +1 -1
- package/test/helpers/live-provider.test.ts +91 -1
- package/test/helpers/live-provider.ts +195 -51
- package/test/scripts/websocket-pending-queue.test.ts +119 -0
- package/test-support/test-helpers.d.ts +0 -33
- package/test-support/test-helpers.d.ts.map +1 -1
- package/test-support/test-helpers.js +81 -189
- package/ui-compat.d.ts +1 -0
- package/ui-compat.d.ts.map +1 -1
- package/ui-compat.js +4 -0
- package/platforms/electrobun/docs/convergence-audit.json +0 -3505
- package/platforms/electrobun/docs/convergence-audit.md +0 -694
- package/platforms/electrobun/scripts/ensure-whisper-gguf.sh +0 -55
- package/platforms/electrobun/scripts/ensure-whisper-model.sh +0 -35
- package/platforms/electrobun/scripts/generate-convergence-audit.ts +0 -1203
- package/platforms/electrobun/src/native/whisper-env.test.ts +0 -71
- package/platforms/electrobun/src/native/whisper-env.ts +0 -68
- package/registry/app-registry.d.ts +0 -14
- package/registry/app-registry.d.ts.map +0 -1
- package/registry/app-registry.js +0 -29
- package/registry/entries/apps/app-polymarket.json +0 -31
- package/registry/entries/apps/clawville.json +0 -27
- package/registry/entries/apps/companion.json +0 -28
- package/registry/entries/apps/database-viewer.json +0 -27
- package/registry/entries/apps/defense-of-the-agents.json +0 -27
- package/registry/entries/apps/documents.json +0 -30
- package/registry/entries/apps/feed.json +0 -27
- package/registry/entries/apps/hyperliquid.json +0 -31
- package/registry/entries/apps/log-viewer.json +0 -27
- package/registry/entries/apps/memory-viewer.json +0 -27
- package/registry/entries/apps/model-tester.json +0 -31
- package/registry/entries/apps/plugin-viewer.json +0 -27
- package/registry/entries/apps/relationship-viewer.json +0 -27
- package/registry/entries/apps/runtime-debugger.json +0 -27
- package/registry/entries/apps/shopify.json +0 -31
- package/registry/entries/apps/skills-viewer.json +0 -27
- package/registry/entries/apps/steward.json +0 -31
- package/registry/entries/apps/training.json +0 -54
- package/registry/entries/apps/trajectory-viewer.json +0 -27
- package/registry/entries/apps/vincent.json +0 -31
- package/registry/entries/connectors/bluebubbles.json +0 -99
- package/registry/entries/connectors/bluesky.json +0 -173
- package/registry/entries/connectors/discord.json +0 -119
- package/registry/entries/connectors/farcaster.json +0 -174
- package/registry/entries/connectors/feishu.json +0 -79
- package/registry/entries/connectors/google-chat.json +0 -120
- package/registry/entries/connectors/google.json +0 -82
- package/registry/entries/connectors/imessage.json +0 -96
- package/registry/entries/connectors/instagram.json +0 -64
- package/registry/entries/connectors/line.json +0 -86
- package/registry/entries/connectors/matrix.json +0 -94
- package/registry/entries/connectors/mattermost.json +0 -110
- package/registry/entries/connectors/msteams.json +0 -104
- package/registry/entries/connectors/nextcloud-talk.json +0 -104
- package/registry/entries/connectors/nostr.json +0 -70
- package/registry/entries/connectors/signal.json +0 -81
- package/registry/entries/connectors/slack.json +0 -102
- package/registry/entries/connectors/telegram.json +0 -71
- package/registry/entries/connectors/tlon.json +0 -94
- package/registry/entries/connectors/twitch.json +0 -110
- package/registry/entries/connectors/whatsapp.json +0 -113
- package/registry/entries/connectors/x.json +0 -231
- package/registry/entries/connectors/zalo.json +0 -112
- package/registry/entries/connectors/zalouser.json +0 -122
- package/registry/entries/plugins/agent-orchestrator.json +0 -33
- package/registry/entries/plugins/agent-skills.json +0 -72
- package/registry/entries/plugins/anthropic.json +0 -73
- package/registry/entries/plugins/app-control.json +0 -23
- package/registry/entries/plugins/auto-trader.json +0 -203
- package/registry/entries/plugins/background-runner.json +0 -26
- package/registry/entries/plugins/blooio.json +0 -102
- package/registry/entries/plugins/browser.json +0 -75
- package/registry/entries/plugins/cli.json +0 -40
- package/registry/entries/plugins/clipboard.json +0 -44
- package/registry/entries/plugins/coding-tools.json +0 -71
- package/registry/entries/plugins/commands.json +0 -63
- package/registry/entries/plugins/computeruse.json +0 -74
- package/registry/entries/plugins/copilot-proxy.json +0 -93
- package/registry/entries/plugins/directives.json +0 -63
- package/registry/entries/plugins/edge-tts.json +0 -97
- package/registry/entries/plugins/elevenlabs.json +0 -169
- package/registry/entries/plugins/elizacloud.json +0 -208
- package/registry/entries/plugins/evm.json +0 -134
- package/registry/entries/plugins/experience.json +0 -34
- package/registry/entries/plugins/facewear.json +0 -131
- package/registry/entries/plugins/form.json +0 -26
- package/registry/entries/plugins/github.json +0 -93
- package/registry/entries/plugins/gmail-watch.json +0 -25
- package/registry/entries/plugins/goals.json +0 -77
- package/registry/entries/plugins/google-genai.json +0 -106
- package/registry/entries/plugins/groq.json +0 -93
- package/registry/entries/plugins/hedera.json +0 -48
- package/registry/entries/plugins/inmemorydb.json +0 -25
- package/registry/entries/plugins/linear.json +0 -51
- package/registry/entries/plugins/local-inference.json +0 -137
- package/registry/entries/plugins/local-storage.json +0 -36
- package/registry/entries/plugins/localdb.json +0 -25
- package/registry/entries/plugins/mcp.json +0 -44
- package/registry/entries/plugins/memory.json +0 -124
- package/registry/entries/plugins/minecraft.json +0 -79
- package/registry/entries/plugins/moltbook.json +0 -83
- package/registry/entries/plugins/music.json +0 -155
- package/registry/entries/plugins/mysticism.json +0 -48
- package/registry/entries/plugins/nearai.json +0 -82
- package/registry/entries/plugins/ngrok.json +0 -69
- package/registry/entries/plugins/ollama.json +0 -96
- package/registry/entries/plugins/openai.json +0 -189
- package/registry/entries/plugins/openrouter.json +0 -188
- package/registry/entries/plugins/pdf.json +0 -26
- package/registry/entries/plugins/plugin-manager.json +0 -23
- package/registry/entries/plugins/prose.json +0 -48
- package/registry/entries/plugins/rlm.json +0 -26
- package/registry/entries/plugins/roblox.json +0 -88
- package/registry/entries/plugins/rss.json +0 -64
- package/registry/entries/plugins/s3-storage.json +0 -91
- package/registry/entries/plugins/scheduling.json +0 -35
- package/registry/entries/plugins/shell.json +0 -94
- package/registry/entries/plugins/social-alpha.json +0 -72
- package/registry/entries/plugins/tailscale.json +0 -81
- package/registry/entries/plugins/tee.json +0 -53
- package/registry/entries/plugins/todos.json +0 -26
- package/registry/entries/plugins/trajectory-logger.json +0 -33
- package/registry/entries/plugins/trust.json +0 -39
- package/registry/entries/plugins/tts.json +0 -71
- package/registry/entries/plugins/tunnel.json +0 -45
- package/registry/entries/plugins/twilio.json +0 -168
- package/registry/entries/plugins/vercel-ai-gateway.json +0 -128
- package/registry/entries/plugins/video.json +0 -23
- package/registry/entries/plugins/vision.json +0 -43
- package/registry/entries/plugins/webhooks.json +0 -23
- package/registry/entries/plugins/workflow.json +0 -25
- package/registry/entries/plugins/xai.json +0 -75
- package/registry/loader.d.ts +0 -28
- package/registry/loader.d.ts.map +0 -1
- package/registry/loader.js +0 -158
- package/registry/schema.d.ts +0 -1199
- package/registry/schema.d.ts.map +0 -1
- package/registry/schema.js +0 -300
- package/scripts/patches/llama-mobile-kokoro-tts.patch +0 -480
- package/scripts/release-workflow-drift.test.ts +0 -57
- package/test/helpers/i18n.ts +0 -5
- package/test/helpers/loopback.ts +0 -33
- package/test/helpers/react-test.ts +0 -40
- package/test/helpers/skip-without.ts +0 -53
|
@@ -2,6 +2,7 @@ package ai.elizaos.app;
|
|
|
2
2
|
|
|
3
3
|
import android.net.LocalServerSocket;
|
|
4
4
|
import android.net.LocalSocket;
|
|
5
|
+
import android.system.Os;
|
|
5
6
|
import android.util.Base64;
|
|
6
7
|
import android.util.Log;
|
|
7
8
|
|
|
@@ -60,6 +61,12 @@ final class ElizaBionicInferenceServer {
|
|
|
60
61
|
private long residentCtx = 0L;
|
|
61
62
|
private long residentStream = 0L;
|
|
62
63
|
private String residentBundle = null;
|
|
64
|
+
private String residentDrafterPath = "";
|
|
65
|
+
/** The previous turn's prompt tokens — used to find the longest common prefix
|
|
66
|
+
* with the next turn's prompt so its KV can be reused (only the delta is
|
|
67
|
+
* re-prefilled). null when the stream has no reusable KV (first turn / after
|
|
68
|
+
* a reset/reopen). */
|
|
69
|
+
private int[] residentPrevTokens = null;
|
|
63
70
|
private final Object residentLock = new Object();
|
|
64
71
|
/** Hard decode ceiling for the resident stream (per-call cap is applied below). */
|
|
65
72
|
private static final int RESIDENT_STREAM_MAX_TOKENS = 2048;
|
|
@@ -70,10 +77,62 @@ final class ElizaBionicInferenceServer {
|
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
/** Bind the abstract-namespace socket and start accepting. Idempotent. */
|
|
80
|
+
/**
|
|
81
|
+
* The bionic host runs the LLM in THIS (app) process via JNI, so the fused
|
|
82
|
+
* native lib reads its tuning from the app-process environment — NOT the bun
|
|
83
|
+
* agent subprocess env (which only carries the ELIZA_LLAMA_* names). On
|
|
84
|
+
* Mali-class 8 GB phones the 2B's non-flash-attn compute + logits buffers at
|
|
85
|
+
* the upstream n_batch=512 default push peak RSS past what the device can
|
|
86
|
+
* allocate ("llm_stream_open: failed to init llama context"). FA is disabled
|
|
87
|
+
* on Android (the scalar-FA race), so the non-FA attention buffer is the
|
|
88
|
+
* dominant cost and it scales with n_batch; capping n_batch shrinks both that
|
|
89
|
+
* and the n_vocab×n_batch logits buffer ~4x, which is what lets the context
|
|
90
|
+
* fit. n_ctx is left at the model-capped default (KV is only ~0.4 GB at 8k).
|
|
91
|
+
* Only sets a value when it is not already present, so any explicit override
|
|
92
|
+
* still wins.
|
|
93
|
+
*/
|
|
94
|
+
private static void applyBionicInferenceMemoryDefaults() {
|
|
95
|
+
setEnvIfAbsent("ELIZA_LLM_N_BATCH", "128");
|
|
96
|
+
setEnvIfAbsent("ELIZA_LLM_N_CTX", "8192");
|
|
97
|
+
// The JNI bridge can default the KV cache to a fused QJL/TBQ quant
|
|
98
|
+
// (cache_type_k="qjl1_256"), but that path is a head_dim=128 sketch and
|
|
99
|
+
// is RETIRED for the shipped tiers (elizaOS/eliza#8848 / #9033 Gemma-4
|
|
100
|
+
// cutover): eliza-1 now ships Gemma-4-arch GGUFs whose KV is already
|
|
101
|
+
// minimal (MQA + windowed SWA + shared-KV; dual head dims 512 global /
|
|
102
|
+
// 256 SWA) and which run on stock f16/q8_0 KV. The head_dim=128 QJL/TBQ
|
|
103
|
+
// kernels are dimensionally inapplicable to Gemma, so keep KV quant OFF
|
|
104
|
+
// (stock f16, only ~0.4 GB at 8k ctx for the 2B). This is the intended
|
|
105
|
+
// Gemma KV path, not a fallback.
|
|
106
|
+
setEnvIfAbsent("ELIZA_BIONIC_KV_QUANT", "0");
|
|
107
|
+
// Allow MTP speculative decode when a Gemma-4 SEPARATE drafter
|
|
108
|
+
// (mtp/drafter-<tier>.gguf, loaded via "-md … --spec-type draft-mtp") —
|
|
109
|
+
// NOT a same-file NextN head embedded in the text GGUF; the JNI threads
|
|
110
|
+
// cfg.mtp_drafter_path when the TS bridge has staged one. MTP stays
|
|
111
|
+
// dormant when no drafter path is supplied; the retired same-file path
|
|
112
|
+
// is intentionally not used for Gemma bundles. The resident path uses
|
|
113
|
+
// MTP + full reset;
|
|
114
|
+
// prefix-KV reuse via reset_keep (resetAndPrefillResident →
|
|
115
|
+
// nativeLlmStreamResetKeep) stays wired for caches that support bounded
|
|
116
|
+
// partial removal (llama_memory_seq_rm).
|
|
117
|
+
setEnvIfAbsent("ELIZA_BIONIC_MTP", "1");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private static void setEnvIfAbsent(String key, String value) {
|
|
121
|
+
try {
|
|
122
|
+
if (System.getenv(key) == null) {
|
|
123
|
+
Os.setenv(key, value, true);
|
|
124
|
+
Log.i(TAG, "set " + key + "=" + value + " for in-process bionic inference");
|
|
125
|
+
}
|
|
126
|
+
} catch (Throwable t) {
|
|
127
|
+
Log.w(TAG, "could not set " + key, t);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
73
131
|
synchronized void start() {
|
|
74
132
|
if (running.get()) {
|
|
75
133
|
return;
|
|
76
134
|
}
|
|
135
|
+
applyBionicInferenceMemoryDefaults();
|
|
77
136
|
// Load the fused native engine up front so the first request doesn't pay
|
|
78
137
|
// the dlopen + Vulkan-device init; also fail fast + loud if the GPU host
|
|
79
138
|
// isn't actually usable, so the agent's refuse-and-fallback can engage.
|
|
@@ -150,6 +209,14 @@ final class ElizaBionicInferenceServer {
|
|
|
150
209
|
if (requestJson == null) {
|
|
151
210
|
break;
|
|
152
211
|
}
|
|
212
|
+
// op="generateStream" server-pushes one frame per decode step on
|
|
213
|
+
// this same connection (handled inline so it can write many
|
|
214
|
+
// frames); every other op is one-request/one-response.
|
|
215
|
+
if ("generateStream".equals(opOf(requestJson))) {
|
|
216
|
+
generateStreamRequest(requestJson, out);
|
|
217
|
+
out.flush();
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
153
220
|
String responseJson = handleRequest(requestJson);
|
|
154
221
|
writeFrame(out, responseJson);
|
|
155
222
|
out.flush();
|
|
@@ -176,13 +243,23 @@ final class ElizaBionicInferenceServer {
|
|
|
176
243
|
return tts(bundleDir, req.optString("text", ""),
|
|
177
244
|
(float) req.optDouble("speed", 1.0));
|
|
178
245
|
}
|
|
246
|
+
if ("asr".equals(op)) {
|
|
247
|
+
return asr(bundleDir, req.optString("pcmBase64", ""),
|
|
248
|
+
req.optInt("sampleRate", 16000));
|
|
249
|
+
}
|
|
250
|
+
if ("image".equals(op)) {
|
|
251
|
+
return describeImage(bundleDir, req.optString("imageBase64", ""),
|
|
252
|
+
req.optString("mmprojPath", ""), req.optString("prompt", ""));
|
|
253
|
+
}
|
|
179
254
|
if (!"generate".equals(op)) {
|
|
180
255
|
return errorJson("unsupported op: " + op);
|
|
181
256
|
}
|
|
182
257
|
String prompt = req.optString("prompt", "");
|
|
258
|
+
String drafterPath = req.optString("drafterPath", "");
|
|
183
259
|
int maxTokens = req.optInt("maxTokens", 256);
|
|
184
260
|
Log.i(TAG, "GENERATE from agent: " + prompt.length() + " prompt chars,"
|
|
185
|
-
+ " maxTokens=" + maxTokens + ", bundle=" + bundleDir
|
|
261
|
+
+ " maxTokens=" + maxTokens + ", bundle=" + bundleDir
|
|
262
|
+
+ ", drafter=" + (drafterPath.isEmpty() ? "(none)" : drafterPath));
|
|
186
263
|
// RESIDENT path (default): the model + context stay loaded across turns;
|
|
187
264
|
// only the KV cache + sampler are reset and the prompt re-prefilled per
|
|
188
265
|
// turn, so we skip the ~7-8s model RELOAD that nativeLlmSelfTest paid every
|
|
@@ -194,7 +271,7 @@ final class ElizaBionicInferenceServer {
|
|
|
194
271
|
// ELIZA_BIONIC_RESIDENT=0 to force the old path).
|
|
195
272
|
if (!"0".equals(System.getenv("ELIZA_BIONIC_RESIDENT"))) {
|
|
196
273
|
try {
|
|
197
|
-
String r = generateResident(bundleDir, prompt, maxTokens);
|
|
274
|
+
String r = generateResident(bundleDir, drafterPath, prompt, maxTokens);
|
|
198
275
|
Log.i(TAG, "GENERATE result (resident): "
|
|
199
276
|
+ (r.length() > 200 ? r.substring(0, 200) + "…" : r));
|
|
200
277
|
return r;
|
|
@@ -218,29 +295,12 @@ final class ElizaBionicInferenceServer {
|
|
|
218
295
|
* we skip the ~7-8s model reload. Greedy decode (temp=0, top_k=1), all-GPU.
|
|
219
296
|
* Returns the same {ok,tokens,ms,tokS,text} JSON as nativeLlmSelfTest.
|
|
220
297
|
*/
|
|
221
|
-
private String generateResident(String bundleDir, String prompt, int maxTokens)
|
|
298
|
+
private String generateResident(String bundleDir, String drafterPath, String prompt, int maxTokens)
|
|
222
299
|
throws org.json.JSONException {
|
|
223
300
|
synchronized (residentLock) {
|
|
224
|
-
|
|
225
|
-
resetResident();
|
|
226
|
-
residentCtx = ElizaVoiceNative.nativeContextCreate(bundleDir);
|
|
227
|
-
if (residentCtx == 0L) {
|
|
228
|
-
throw new IllegalStateException("resident contextCreate failed: " + bundleDir);
|
|
229
|
-
}
|
|
230
|
-
residentBundle = bundleDir;
|
|
231
|
-
}
|
|
232
|
-
if (residentStream == 0L) {
|
|
233
|
-
residentStream = ElizaVoiceNative.nativeLlmStreamOpen(
|
|
234
|
-
residentCtx, RESIDENT_STREAM_MAX_TOKENS, 0.0f, 1.0f, 1, -1, "");
|
|
235
|
-
if (residentStream == 0L) {
|
|
236
|
-
throw new IllegalStateException("resident streamOpen failed");
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
// Drop the previous turn's KV + sampler state for a clean re-prefill.
|
|
240
|
-
ElizaVoiceNative.nativeLlmStreamReset(residentStream);
|
|
241
|
-
int[] toks = ElizaVoiceNative.nativeTokenize(residentCtx, prompt, true, true);
|
|
301
|
+
ensureResidentCtx(bundleDir);
|
|
242
302
|
final long t0 = android.os.SystemClock.elapsedRealtime();
|
|
243
|
-
|
|
303
|
+
resetAndPrefillResident(prompt, drafterPath);
|
|
244
304
|
final StringBuilder sb = new StringBuilder();
|
|
245
305
|
int produced = 0;
|
|
246
306
|
final int cap = maxTokens > 0 ? maxTokens : 32;
|
|
@@ -266,6 +326,118 @@ final class ElizaBionicInferenceServer {
|
|
|
266
326
|
}
|
|
267
327
|
}
|
|
268
328
|
|
|
329
|
+
/** Cheap op discriminator without fully consuming the request. */
|
|
330
|
+
private static String opOf(String requestJson) {
|
|
331
|
+
try {
|
|
332
|
+
return new JSONObject(requestJson).optString("op", "generate");
|
|
333
|
+
} catch (org.json.JSONException e) {
|
|
334
|
+
return "generate";
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Parse an op="generateStream" request and run the streaming decode. */
|
|
339
|
+
private void generateStreamRequest(String requestJson, DataOutputStream out)
|
|
340
|
+
throws IOException {
|
|
341
|
+
String bundleDir = defaultBundleDir;
|
|
342
|
+
String drafterPath = "";
|
|
343
|
+
String prompt = "";
|
|
344
|
+
int maxTokens = 256;
|
|
345
|
+
try {
|
|
346
|
+
JSONObject req = new JSONObject(requestJson);
|
|
347
|
+
bundleDir = req.optString("bundleDir", "");
|
|
348
|
+
if (bundleDir.isEmpty()) {
|
|
349
|
+
bundleDir = defaultBundleDir;
|
|
350
|
+
}
|
|
351
|
+
drafterPath = req.optString("drafterPath", "");
|
|
352
|
+
prompt = req.optString("prompt", "");
|
|
353
|
+
maxTokens = req.optInt("maxTokens", 256);
|
|
354
|
+
} catch (org.json.JSONException e) {
|
|
355
|
+
writeFrame(out, errorJson(e.getMessage() == null ? e.toString() : e.getMessage()));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
generateStream(bundleDir, drafterPath, prompt, maxTokens, out);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Streaming variant of {@link #generateResident}: the identical warm decode,
|
|
363
|
+
* but it writes one length-prefixed {type:"token",text} frame per decode step
|
|
364
|
+
* to {@code out} as tokens are produced, then a terminal
|
|
365
|
+
* {type:"done",ok,tokens,ms,tokS,text} frame. This lets the agent render
|
|
366
|
+
* tokens as they decode (first paint at the first token instead of after the
|
|
367
|
+
* whole reply) and unblocks phrase-chunked LLM→TTS. The buffered op="generate"
|
|
368
|
+
* is unchanged for non-streaming callers (embed/tts/self-test).
|
|
369
|
+
*/
|
|
370
|
+
private void generateStream(String bundleDir, String drafterPath, String prompt, int maxTokens,
|
|
371
|
+
DataOutputStream out) throws IOException {
|
|
372
|
+
Log.i(TAG, "GENERATE_STREAM from agent: " + prompt.length() + " prompt chars,"
|
|
373
|
+
+ " maxTokens=" + maxTokens + ", bundle=" + bundleDir
|
|
374
|
+
+ ", drafter=" + (drafterPath.isEmpty() ? "(none)" : drafterPath));
|
|
375
|
+
final StringBuilder sb = new StringBuilder();
|
|
376
|
+
try {
|
|
377
|
+
synchronized (residentLock) {
|
|
378
|
+
ensureResidentCtx(bundleDir);
|
|
379
|
+
final long t0 = android.os.SystemClock.elapsedRealtime();
|
|
380
|
+
resetAndPrefillResident(prompt, drafterPath);
|
|
381
|
+
int produced = 0;
|
|
382
|
+
final int cap = maxTokens > 0 ? maxTokens : 32;
|
|
383
|
+
while (produced < cap) {
|
|
384
|
+
String stepJson = ElizaVoiceNative.nativeLlmStreamNext(residentStream);
|
|
385
|
+
if (stepJson == null) break;
|
|
386
|
+
JSONObject step = new JSONObject(stepJson);
|
|
387
|
+
String t = step.optString("text", "");
|
|
388
|
+
if (!t.isEmpty()) {
|
|
389
|
+
sb.append(t);
|
|
390
|
+
writeFrame(out, new JSONObject()
|
|
391
|
+
.put("type", "token").put("text", t).toString());
|
|
392
|
+
out.flush();
|
|
393
|
+
}
|
|
394
|
+
int nout = step.optInt("nout", 1);
|
|
395
|
+
produced += nout > 0 ? nout : 1;
|
|
396
|
+
if (step.optBoolean("done", false)) break;
|
|
397
|
+
}
|
|
398
|
+
final long ms = android.os.SystemClock.elapsedRealtime() - t0;
|
|
399
|
+
final double tokS = ms > 0 ? produced * 1000.0 / ms : 0.0;
|
|
400
|
+
writeFrame(out, new JSONObject()
|
|
401
|
+
.put("type", "done").put("ok", true)
|
|
402
|
+
.put("tokens", produced).put("ms", ms).put("tokS", tokS)
|
|
403
|
+
.put("text", sb.toString()).put("resident", true).toString());
|
|
404
|
+
out.flush();
|
|
405
|
+
Log.i(TAG, "GENERATE_STREAM done (resident): " + produced + " tok @ "
|
|
406
|
+
+ String.format(java.util.Locale.US, "%.2f", tokS) + " tok/s");
|
|
407
|
+
}
|
|
408
|
+
} catch (Throwable t) {
|
|
409
|
+
Log.w(TAG, "generate_stream failed", t);
|
|
410
|
+
resetResident();
|
|
411
|
+
try {
|
|
412
|
+
writeFrame(out, new JSONObject()
|
|
413
|
+
.put("type", "done").put("ok", false)
|
|
414
|
+
.put("error", t.getMessage() == null ? t.toString() : t.getMessage())
|
|
415
|
+
.toString());
|
|
416
|
+
out.flush();
|
|
417
|
+
} catch (Throwable ignored) {
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Get-or-create the shared resident inference context. ONE model load is
|
|
424
|
+
* reused by both generation (via residentStream) and embeddings (the native
|
|
425
|
+
* EliInferenceContext caches a separate non-causal embed_ctx + the causal
|
|
426
|
+
* stream within the same shared model weights), so embeds no longer reload
|
|
427
|
+
* the 1.27 GB model per call. Caller must hold residentLock.
|
|
428
|
+
*/
|
|
429
|
+
private long ensureResidentCtx(String bundleDir) {
|
|
430
|
+
if (residentCtx == 0L || !bundleDir.equals(residentBundle)) {
|
|
431
|
+
resetResident();
|
|
432
|
+
residentCtx = ElizaVoiceNative.nativeContextCreate(bundleDir);
|
|
433
|
+
if (residentCtx == 0L) {
|
|
434
|
+
throw new IllegalStateException("resident contextCreate failed: " + bundleDir);
|
|
435
|
+
}
|
|
436
|
+
residentBundle = bundleDir;
|
|
437
|
+
}
|
|
438
|
+
return residentCtx;
|
|
439
|
+
}
|
|
440
|
+
|
|
269
441
|
/** Tear down the resident model/context/stream (on bundle change, failure, or stop). */
|
|
270
442
|
private void resetResident() {
|
|
271
443
|
synchronized (residentLock) {
|
|
@@ -278,35 +450,111 @@ final class ElizaBionicInferenceServer {
|
|
|
278
450
|
residentCtx = 0L;
|
|
279
451
|
}
|
|
280
452
|
residentBundle = null;
|
|
453
|
+
residentDrafterPath = "";
|
|
454
|
+
residentPrevTokens = null;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Reset the resident stream for a new turn and prefill the prompt, REUSING
|
|
460
|
+
* the KV of the longest common token prefix with the previous turn (the
|
|
461
|
+
* system + tool-schema block is identical turn-to-turn) so only the per-turn
|
|
462
|
+
* delta is decoded. On Mali's scalar-matmul prefill the prefix is the
|
|
463
|
+
* dominant per-turn cost, so this is the single biggest latency win. Falls
|
|
464
|
+
* back to a full reset (close+reopen on failure) when there is no reusable
|
|
465
|
+
* prefix or the stream can't be trimmed (e.g. an MTP stream). Caller holds
|
|
466
|
+
* residentLock.
|
|
467
|
+
*/
|
|
468
|
+
private void resetAndPrefillResident(String prompt, String drafterPath) {
|
|
469
|
+
final String effectiveDrafterPath = drafterPath == null ? "" : drafterPath;
|
|
470
|
+
if (residentStream != 0L && !effectiveDrafterPath.equals(residentDrafterPath)) {
|
|
471
|
+
ElizaVoiceNative.nativeLlmStreamClose(residentStream);
|
|
472
|
+
residentStream = 0L;
|
|
473
|
+
residentPrevTokens = null;
|
|
474
|
+
}
|
|
475
|
+
if (residentStream == 0L) {
|
|
476
|
+
residentStream = ElizaVoiceNative.nativeLlmStreamOpen(
|
|
477
|
+
residentCtx, RESIDENT_STREAM_MAX_TOKENS, 0.0f, 1.0f, 1, -1,
|
|
478
|
+
effectiveDrafterPath);
|
|
479
|
+
if (residentStream == 0L) {
|
|
480
|
+
throw new IllegalStateException("resident streamOpen failed");
|
|
481
|
+
}
|
|
482
|
+
residentDrafterPath = effectiveDrafterPath;
|
|
483
|
+
residentPrevTokens = null;
|
|
484
|
+
}
|
|
485
|
+
final int[] toks = ElizaVoiceNative.nativeTokenize(residentCtx, prompt, true, true);
|
|
486
|
+
// Longest common token prefix with the previous turn, capped so at least
|
|
487
|
+
// one new token is prefilled (the decode samples from the last prefilled
|
|
488
|
+
// position's logits, so the suffix must be non-empty).
|
|
489
|
+
int lcp = 0;
|
|
490
|
+
if (residentPrevTokens != null) {
|
|
491
|
+
final int max = Math.min(residentPrevTokens.length, toks.length);
|
|
492
|
+
while (lcp < max && residentPrevTokens[lcp] == toks[lcp]) {
|
|
493
|
+
lcp++;
|
|
494
|
+
}
|
|
495
|
+
if (lcp >= toks.length) {
|
|
496
|
+
lcp = toks.length - 1;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
int applied = lcp > 0
|
|
500
|
+
? ElizaVoiceNative.nativeLlmStreamResetKeep(residentStream, lcp)
|
|
501
|
+
: -1;
|
|
502
|
+
if (applied < 0) {
|
|
503
|
+
// No reusable prefix (first turn / MTP / trim failure): full reset,
|
|
504
|
+
// close+reopen on failure.
|
|
505
|
+
if (ElizaVoiceNative.nativeLlmStreamReset(residentStream) != 1) {
|
|
506
|
+
ElizaVoiceNative.nativeLlmStreamClose(residentStream);
|
|
507
|
+
residentStream = ElizaVoiceNative.nativeLlmStreamOpen(
|
|
508
|
+
residentCtx, RESIDENT_STREAM_MAX_TOKENS, 0.0f, 1.0f, 1, -1,
|
|
509
|
+
effectiveDrafterPath);
|
|
510
|
+
if (residentStream == 0L) {
|
|
511
|
+
throw new IllegalStateException("resident streamReopen failed");
|
|
512
|
+
}
|
|
513
|
+
residentDrafterPath = effectiveDrafterPath;
|
|
514
|
+
}
|
|
515
|
+
applied = 0;
|
|
516
|
+
}
|
|
517
|
+
final int[] suffix = (applied <= 0)
|
|
518
|
+
? toks
|
|
519
|
+
: java.util.Arrays.copyOfRange(toks, applied, toks.length);
|
|
520
|
+
ElizaVoiceNative.nativeLlmStreamPrefill(residentStream, suffix);
|
|
521
|
+
residentPrevTokens = toks;
|
|
522
|
+
if (applied > 0) {
|
|
523
|
+
Log.i(TAG, "resident prefill reuse: kept " + applied + "/" + toks.length
|
|
524
|
+
+ " prefix tokens, prefilled " + suffix.length + " delta");
|
|
281
525
|
}
|
|
282
526
|
}
|
|
283
527
|
|
|
284
528
|
/**
|
|
285
|
-
* Embed text on the GPU via the fused model (--pooling last).
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
529
|
+
* Embed text on the GPU via the fused model (--pooling last). Reuses the
|
|
530
|
+
* shared resident context (the native side caches a non-causal embed_ctx
|
|
531
|
+
* inside it) so the 1.27 GB model is NOT reloaded per call — previously every
|
|
532
|
+
* embed did contextCreate→embed→contextDestroy (~15 s + a full model copy of
|
|
533
|
+
* memory churn each), which starved the LLM context on 8 GB devices. Single
|
|
534
|
+
* forward pass, no autoregressive decode. Returns {ok, embedding:[...], dim}.
|
|
289
535
|
*/
|
|
290
536
|
private String embed(String bundleDir, String text) throws org.json.JSONException {
|
|
291
537
|
final int POOLING_LAST = 3;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
538
|
+
synchronized (residentLock) {
|
|
539
|
+
final long ctx = ensureResidentCtx(bundleDir);
|
|
540
|
+
try {
|
|
541
|
+
float[] emb = ElizaVoiceNative.nativeEmbed(ctx, text, POOLING_LAST);
|
|
542
|
+
org.json.JSONArray arr = new org.json.JSONArray();
|
|
543
|
+
for (float v : emb) {
|
|
544
|
+
arr.put((double) v);
|
|
545
|
+
}
|
|
546
|
+
Log.i(TAG, "EMBED from agent: " + text.length() + " chars -> dim " + emb.length);
|
|
547
|
+
return new JSONObject()
|
|
548
|
+
.put("ok", true)
|
|
549
|
+
.put("embedding", arr)
|
|
550
|
+
.put("dim", emb.length)
|
|
551
|
+
.toString();
|
|
552
|
+
} catch (Throwable t) {
|
|
553
|
+
// A failed embed may leave the shared context in an unknown state;
|
|
554
|
+
// drop it so the next generate/embed rebuilds cleanly.
|
|
555
|
+
resetResident();
|
|
556
|
+
throw t;
|
|
301
557
|
}
|
|
302
|
-
Log.i(TAG, "EMBED from agent: " + text.length() + " chars -> dim " + emb.length);
|
|
303
|
-
return new JSONObject()
|
|
304
|
-
.put("ok", true)
|
|
305
|
-
.put("embedding", arr)
|
|
306
|
-
.put("dim", emb.length)
|
|
307
|
-
.toString();
|
|
308
|
-
} finally {
|
|
309
|
-
ElizaVoiceNative.nativeContextDestroy(ctx);
|
|
310
558
|
}
|
|
311
559
|
}
|
|
312
560
|
|
|
@@ -329,30 +577,122 @@ final class ElizaBionicInferenceServer {
|
|
|
329
577
|
if (gguf == null || voiceBin == null) {
|
|
330
578
|
return errorJson("tts: Kokoro GGUF + voice .bin not found under " + kokoroDir);
|
|
331
579
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
580
|
+
// Reuse the ONE resident context (the 1.27 GB model is already loaded for
|
|
581
|
+
// generation/embeddings) instead of contextCreate/Destroy per call — a
|
|
582
|
+
// fresh context reloaded the whole model every utterance. Kokoro itself
|
|
583
|
+
// is loaded once and cached on the ctx (idempotent kokoro_load), so a
|
|
584
|
+
// multi-clause reply synthesizes each clause without any reload.
|
|
585
|
+
synchronized (residentLock) {
|
|
586
|
+
final long ctx = ensureResidentCtx(bundleDir);
|
|
587
|
+
try {
|
|
588
|
+
float[] pcm = ElizaVoiceNative.nativeKokoroSynthesize(
|
|
589
|
+
ctx, gguf, voiceBin, text, speed <= 0f ? 1.0f : speed);
|
|
590
|
+
int sampleRate = ElizaVoiceNative.nativeKokoroSampleRate(ctx);
|
|
591
|
+
// Pack fp32 PCM little-endian and base64 it for the JSON frame.
|
|
592
|
+
ByteBuffer buf = ByteBuffer.allocate(pcm.length * 4).order(ByteOrder.LITTLE_ENDIAN);
|
|
593
|
+
for (float v : pcm) {
|
|
594
|
+
buf.putFloat(v);
|
|
595
|
+
}
|
|
596
|
+
String b64 = Base64.encodeToString(buf.array(), Base64.NO_WRAP);
|
|
597
|
+
Log.i(TAG, "TTS (kokoro) from agent: " + text.length() + " chars -> "
|
|
598
|
+
+ pcm.length + " samples @ " + sampleRate + " Hz");
|
|
599
|
+
return new JSONObject()
|
|
600
|
+
.put("ok", true)
|
|
601
|
+
.put("sampleRate", sampleRate)
|
|
602
|
+
.put("samples", pcm.length)
|
|
603
|
+
.put("pcmBase64", b64)
|
|
604
|
+
.toString();
|
|
605
|
+
} catch (Throwable t) {
|
|
606
|
+
// A failed synth may leave the shared ctx in an unknown state;
|
|
607
|
+
// drop it so the next generate/embed/tts rebuilds cleanly.
|
|
608
|
+
resetResident();
|
|
609
|
+
throw t;
|
|
610
|
+
}
|
|
335
611
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* On-device STT: decode the base64 little-endian fp32 PCM, run the fused
|
|
616
|
+
* local ASR batch transcribe on the resident context (it mmap-acquires the
|
|
617
|
+
* {@code asr/} weights on first use), and return {ok, text}. The agent's
|
|
618
|
+
* TRANSCRIPTION delegate routes here over UDS (op="asr"); the musl agent
|
|
619
|
+
* can't reach the fused lib itself. Reuses the ONE resident context like
|
|
620
|
+
* embed/tts so the model is not reloaded per call.
|
|
621
|
+
*/
|
|
622
|
+
private String asr(String bundleDir, String pcmBase64, int sampleRate)
|
|
623
|
+
throws org.json.JSONException {
|
|
624
|
+
if (pcmBase64.isEmpty()) {
|
|
625
|
+
return errorJson("asr: empty pcmBase64");
|
|
626
|
+
}
|
|
627
|
+
byte[] raw = Base64.decode(pcmBase64, Base64.DEFAULT);
|
|
628
|
+
final int n = raw.length / 4;
|
|
629
|
+
if (n <= 0) {
|
|
630
|
+
return errorJson("asr: pcmBase64 decoded to " + raw.length + " bytes (need fp32 PCM)");
|
|
631
|
+
}
|
|
632
|
+
float[] pcm = new float[n];
|
|
633
|
+
ByteBuffer bb = ByteBuffer.wrap(raw).order(ByteOrder.LITTLE_ENDIAN);
|
|
634
|
+
for (int i = 0; i < n; i++) {
|
|
635
|
+
pcm[i] = bb.getFloat();
|
|
636
|
+
}
|
|
637
|
+
final int sr = sampleRate > 0 ? sampleRate : 16000;
|
|
638
|
+
synchronized (residentLock) {
|
|
639
|
+
final long ctx = ensureResidentCtx(bundleDir);
|
|
640
|
+
try {
|
|
641
|
+
String text = ElizaVoiceNative.nativeAsrTranscribe(ctx, pcm, sr);
|
|
642
|
+
Log.i(TAG, "ASR from agent: " + n + " samples @ " + sr + " Hz -> \""
|
|
643
|
+
+ (text.length() > 200 ? text.substring(0, 200) + "…" : text) + "\"");
|
|
644
|
+
return new JSONObject()
|
|
645
|
+
.put("ok", true)
|
|
646
|
+
.put("text", text)
|
|
647
|
+
.toString();
|
|
648
|
+
} catch (Throwable t) {
|
|
649
|
+
// A failed transcribe may leave the shared ctx in an unknown state;
|
|
650
|
+
// drop it so the next generate/embed/tts/asr rebuilds cleanly.
|
|
651
|
+
resetResident();
|
|
652
|
+
throw t;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* On-device vision / screen-recognition: decode the base64 image bytes and
|
|
659
|
+
* run the fused mmproj describe-image on the resident TEXT model. When the
|
|
660
|
+
* caller doesn't pass an explicit {@code mmprojPath}, resolve the projector
|
|
661
|
+
* GGUF from the bundle's {@code vision/} dir. Returns {ok, text}. The agent's
|
|
662
|
+
* IMAGE_DESCRIPTION delegate routes here over UDS (op="image").
|
|
663
|
+
*/
|
|
664
|
+
private String describeImage(String bundleDir, String imageBase64,
|
|
665
|
+
String mmprojPath, String prompt) throws org.json.JSONException {
|
|
666
|
+
if (imageBase64.isEmpty()) {
|
|
667
|
+
return errorJson("image: empty imageBase64");
|
|
668
|
+
}
|
|
669
|
+
byte[] img = Base64.decode(imageBase64, Base64.DEFAULT);
|
|
670
|
+
if (img.length == 0) {
|
|
671
|
+
return errorJson("image: imageBase64 decoded to 0 bytes");
|
|
672
|
+
}
|
|
673
|
+
String mmproj = mmprojPath;
|
|
674
|
+
if (mmproj == null || mmproj.isEmpty()) {
|
|
675
|
+
File visionDir = new File(bundleDir, "vision");
|
|
676
|
+
mmproj = firstMatch(visionDir, ".gguf");
|
|
677
|
+
if (mmproj == null) {
|
|
678
|
+
return errorJson("image: mmproj GGUF not found under " + visionDir
|
|
679
|
+
+ " (stage a vision projector or pass mmprojPath)");
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
synchronized (residentLock) {
|
|
683
|
+
final long ctx = ensureResidentCtx(bundleDir);
|
|
684
|
+
try {
|
|
685
|
+
String desc = ElizaVoiceNative.nativeDescribeImage(ctx, img, mmproj, prompt);
|
|
686
|
+
Log.i(TAG, "IMAGE from agent: " + img.length + " bytes (mmproj " + mmproj + ") -> \""
|
|
687
|
+
+ (desc.length() > 200 ? desc.substring(0, 200) + "…" : desc) + "\"");
|
|
688
|
+
return new JSONObject()
|
|
689
|
+
.put("ok", true)
|
|
690
|
+
.put("text", desc)
|
|
691
|
+
.toString();
|
|
692
|
+
} catch (Throwable t) {
|
|
693
|
+
resetResident();
|
|
694
|
+
throw t;
|
|
344
695
|
}
|
|
345
|
-
String b64 = Base64.encodeToString(buf.array(), Base64.NO_WRAP);
|
|
346
|
-
Log.i(TAG, "TTS (kokoro) from agent: " + text.length() + " chars -> "
|
|
347
|
-
+ pcm.length + " samples @ " + sampleRate + " Hz");
|
|
348
|
-
return new JSONObject()
|
|
349
|
-
.put("ok", true)
|
|
350
|
-
.put("sampleRate", sampleRate)
|
|
351
|
-
.put("samples", pcm.length)
|
|
352
|
-
.put("pcmBase64", b64)
|
|
353
|
-
.toString();
|
|
354
|
-
} finally {
|
|
355
|
-
ElizaVoiceNative.nativeContextDestroy(ctx);
|
|
356
696
|
}
|
|
357
697
|
}
|
|
358
698
|
|
|
@@ -144,6 +144,9 @@ final class ElizaVoiceNative {
|
|
|
144
144
|
/** Read the diariz int8 frame labels for the i-th turn of the last call. */
|
|
145
145
|
static native byte[] nativePipelineTurnLabels(long handle, int index);
|
|
146
146
|
|
|
147
|
+
/** Read the segmented fp32 PCM for the i-th turn of the last call. */
|
|
148
|
+
static native float[] nativePipelineTurnPcm(long handle, int index);
|
|
149
|
+
|
|
147
150
|
static native void nativePipelineReset(long handle);
|
|
148
151
|
|
|
149
152
|
static native void nativePipelineClose(long handle);
|
|
@@ -198,6 +201,14 @@ final class ElizaVoiceNative {
|
|
|
198
201
|
/** Reset a persistent stream (clear KV + sampler) for warm reuse. 1=ok, 0=no. */
|
|
199
202
|
static native int nativeLlmStreamReset(long streamHandle);
|
|
200
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Prefix-preserving reset: keep the first {@code nKeep} tokens of KV cache
|
|
206
|
+
* resident and drop the rest, so the next prefill only decodes the per-turn
|
|
207
|
+
* delta. Returns the n_keep actually applied ({@code >= 0}), or a negative
|
|
208
|
+
* code on a null/MTP/unopened stream (caller falls back to a full reset).
|
|
209
|
+
*/
|
|
210
|
+
static native int nativeLlmStreamResetKeep(long streamHandle, int nKeep);
|
|
211
|
+
|
|
201
212
|
/**
|
|
202
213
|
* KEYSTONE proof: run a whole greedy text generation in one native call,
|
|
203
214
|
* in the bionic app process. With the dynamic-Vulkan lib staged, ggml-vulkan
|
|
@@ -217,4 +228,28 @@ final class ElizaVoiceNative {
|
|
|
217
228
|
*/
|
|
218
229
|
static native float[] nativeKokoroSynthesize(
|
|
219
230
|
long ctxHandle, String ggufPath, String voiceBinPath, String text, float speed);
|
|
231
|
+
|
|
232
|
+
// ── Batch ASR + mmproj vision (the agent's STT / screen-recognition path) ──
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Transcribe {@code pcm} (16 kHz mono fp32) → UTF-8 transcript via the fused
|
|
236
|
+
* local ASR head ({@code eliza_inference_asr_transcribe}). VAD-free: the
|
|
237
|
+
* resident context mmap-acquires the {@code asr/} weights on first use. The
|
|
238
|
+
* bionic host's op="asr" calls this so the agent TRANSCRIPTION delegate gets
|
|
239
|
+
* a real on-device transcript without the full attribution pipeline.
|
|
240
|
+
*/
|
|
241
|
+
static native String nativeAsrTranscribe(long ctxHandle, float[] pcm, int sampleRate);
|
|
242
|
+
|
|
243
|
+
/** {@code eliza_inference_vision_supported()} (ABI v9): 1 if mmproj vision is built. */
|
|
244
|
+
static native int nativeVisionSupported();
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Describe a raw PNG/JPEG/WebP image with the resident TEXT model + the
|
|
248
|
+
* mmproj projector at {@code mmprojPath} ({@code eliza_inference_describe_image}).
|
|
249
|
+
* {@code prompt} may be empty (a default describe prompt is used). The bionic
|
|
250
|
+
* host's op="image" calls this so the agent IMAGE_DESCRIPTION delegate runs
|
|
251
|
+
* screen/vision recognition fully on-device.
|
|
252
|
+
*/
|
|
253
|
+
static native String nativeDescribeImage(
|
|
254
|
+
long ctxHandle, byte[] imageBytes, String mmprojPath, String prompt);
|
|
220
255
|
}
|