Files
baketnk--frame-yap/docs/poc.md
T

7.1 KiB

FrameYap POC: implementation and validation

This is a standalone native application, not a plugin. Default builds/tests never initialize OpenVR, open a microphone, run ASR, download files or inject input.

Implemented

  • Opt-in OpenVR RGBA overlay with head/left-hand placement, status, recording timer, paginated UTF-8 preview, Record/Cancel/Insert/Enter/Quit controls.
  • Remappable SteamVR actions. Default Steam Frame grip bindings use the observed frame_controller profile. Right: short tap, then hold the second squeeze to record; release to transcribe. Left: two short taps request explicit Enter. First squeeze <=250 ms; second squeeze begins <=350 ms after first release. Activity/tracking loss cancels a held recording and requires neutral rearm.
  • SDL3 default recording device, mono float32 conversion at 16 kHz, 200 ms minimum, 20 second maximum. Microphone is closed outside actual capture. Other apps may still transmit your voice: this app does not mute VRChat or any other app.
  • Persistent local Redux worker, correlated bounded pipes, private tmpfs clips, cancellation/reaping and deadlines; exact pinned model SHA-256 verification. Model imports are lazy and loading is offline. No cloud/desktop fallback.
  • Gamescope IME v2 generated bindings, per-action short-lived lease, unavailable handling, UTF-8/control validation and explicit separate Submit action for Enter.
  • Idempotent user-local release-archive installer: SHA-256, safe extraction, atomic current-version selection, retained rollback, runtime/install lock, foreign-file refusal and explicit unregister-before-uninstall acknowledgement.

Deliberately not claimed

Review-first only. No automatic insertion or inferred commands. A transcript must be explicitly inserted into the current focused destination. We do not yet implement the proposed Xwayland focus-generation observer or safe quick typing. There remains a race with focus changes after user approval. Text delivery is reported as input queued, not application consumption or message delivery. A request is consumed once even if transport completion is uncertain; no retries. Unavailable IME acquisition leaves the preview intact.

No VAD, always-listening mode, desktop transcription service, keyboard emulation fallback, streaming-PC bridge, or automatic Enter. No general undo. Grip bindings are not guaranteed globally active in every scene/dashboard state, and the app never enables SteamVR's experimental overlay overrides on your behalf.

This is a compact prototype panel, not yet the proposed polished miniature status chip. Font coverage/complex shaping, ergonomics, compositor cost, thermal/battery impact and target application compatibility require further headset work.

Critical runtime licensing boundary

Redux weights at fad622f25f303105c20d70e201bcc477c88b620c are CC-BY-4.0. The installed kestrel-kernels 0.7.0 license is different: proprietary, requiring an M87 Labs written agreement for use; copying/redistribution depends on that agreement. PyPI availability is not permission. See third-party notes.

Do not publish a bundled Redux runtime, imply a public release is ready, or rerun inference while applicable permission is unresolved. Our adapter is implemented; that does not resolve distribution rights. An alternative runtime would be a separately scoped and independently licensed implementation—not a silent model swap.

Developer native build

Requirements: Linux, CMake/C++20, SDL3 >=3.2, Wayland client + scanner, FreeType, and a deliberately provisioned standalone OpenVR v2.15.6 SDK. No CMake fetches.

cmake -S . -B build-native -DFRAMEYAP_NATIVE=ON \
  -DOPENVR_ROOT=/absolute/path/to/openvr
cmake --build build-native -j2
ctest --test-dir build-native --output-on-failure

On ARM64, select the SDK's lib/linuxarm64/libopenvr_api.so explicitly with -DOPENVR_LIBRARY=... if necessary. Do not use an x86 library or another application's build/runtime. Installable binary has $ORIGIN/../lib RUNPATH; producer must audit and bundle its compatible dependency closure.

The device development trial built SDL 3.2.16 under a project-owned user prefix with audio enabled and video/render/GPU/joystick/haptic/sensor/camera/tests examples disabled (SDL_UNIX_CONSOLE_BUILD=ON). No OS package modifications. That is a producer workflow, not an end-user compiler requirement.

Explicit hardware checks (not CTest)

./build-native/frameyap --check-input --socket gamescope-0
./build-native/frameyap --check-overlay --assets "$PWD/assets" --font /path/font.ttf --head
./build-native/frameyap --check-controls --assets "$PWD/assets" --font /path/font.ttf --head

The first acquires/releases an IME without text/actions. The second displays a five-second inert panel. The third displays a 30-second diagnostic panel and reports gestures, without microphone or input injection. Active action handles are not proof that gestures were delivered. Checks must be explicitly launched while the user expects the panel. Normal CLI/help/version remain inert.

The ARM64 OpenVR loader's default /data/work/openvrpaths.vrpath failed on the observed device. FrameYap respects VR_PATHREG_OVERRIDE; when absent, it selects an existing standard $XDG_CONFIG_HOME/openvr/openvrpaths.vrpath (or ~/.config/openvr/openvrpaths.vrpath) before initializing OpenVR. No Steam files are edited and no runtime/session restart is performed.

Deliberate launch, once runtime permission is settled

Explicit setup downloads only the pinned, openly licensed model:

python3 scripts/fetch-model.py --destination "$HOME/.local/share/frameyap-model"

fetch-model.py is idempotent for matching hashes, rejects existing mismatched files, and is never invoked by build/tests or first utterance.

Provide your independently authorized CPU Python environment (moondream 2.4.0, kestrel 0.8.0) and local weights:

./build-native/frameyap --run --assets "$PWD/assets" --font /path/font.ttf \
  --python /authorized/runtime/bin/python3 --worker "$PWD/python/frameyap/worker.py" \
  --model "$HOME/.local/share/frameyap-model" --threads 2 --socket gamescope-0 --head

Use a disposable text destination first. --run loads the model but does not record until an explicit recording control. Click Insert only after focusing your intended text field. Quit or SIGINT/SIGTERM closes capture, invalidates delivery and terminates only the owned worker. Installer upgrades refuse an active app.

The worker API is intentionally small: a fork can replace the worker implementation or add its own model/API integration without changing overlay and delivery code. The default product remains local-only Redux; extending a fork does not authorize sending existing users' audio to a service.

Benchmarks

scripts/benchmark-worker.py accepts a supplied nonprivate PCM16/16 kHz/mono WAV; no recording, device input or automatic download. --show-text is a separate explicit disclosure of that fixture's transcript. Use only with an authorized runtime. See the dated POC record for measurements and their limits; they are not headset/performance acceptance.