Local GGUF + hybrid engine, BT tether, persistent memory, the REST bridge, RPi boot.
Date: 2026-06-07 Commits: 80d5651, 4d5eb13, 35c95ea, 2a38116, f61c94e, 86e4081, 7e89c2d, 0db7b4a Theme: Make EXCAR survive losing the internet, remember across drives, expose itself to a phone, and boot itself in a car.
Report 01 built a brain that needs the cloud. A car loses signal in tunnels, underground car parks, and the countryside. This session made EXCAR keep working when that happens - and laid the device-side plumbing the mobile app would later talk to.
4d5eb13, 80d5651)What. A FastAPI server (api/server.py) that exposes the device to the mobile app over HTTP at excar.local:8000. Started on a daemon thread from app.py when the voice loop runs.
Why. The car device is headless. The only way a user configures it, checks whether it's online, or wires up services is from their phone. That needs a local API on the device.
How. FastAPI app with routers mounted per concern (status, profile, config, memory - more added in later sessions). It runs in-process alongside the voice loop on a background thread so a single excar command brings up *both* the assistant and its control surface. .gitignore was updated to keep mobile build artifacts and local data out of the repo.
Purpose / goal. Decouple the device (always-on appliance) from its UI (the phone), over a contract the mobile app can depend on. This is the seam the entire Report 03 mobile app plugs into.
35c95ea)What. A ConnectivityChecker (connectivity/bt_tether.py) that decides whether EXCAR currently has internet - typically via the driver's phone tethered over Bluetooth/USB.
Why. The hybrid brain (below) needs a fast, reliable answer to one question: *"can I reach the cloud right now?"* - to choose between the cloud model and the local model on every turn.
How. A cheap reachability check the pipeline can call per turn without adding noticeable latency.
Purpose / goal. It's the switch that makes hybrid mode possible. Without a trustworthy online/offline signal, you can't safely fall back.
f61c94e, 86e4081)What. Two new LLMEngine implementations:
LocalLlamaEngine (llm/local_engine.py) - offline inference on a local GGUF model (Atilla's), via llama.cpp bindings, streaming token deltas.
HybridLLMEngine (llm/hybrid_engine.py) - uses the cloud ClaudeEngine whenconnected, the local engine when offline.
Why. Cloud Claude is the most capable brain but useless without signal. A local model is always available but weaker. A car needs *both*: best-effort quality when connected, graceful degradation when not - with no change to the conversation experience.
How. Both satisfy the same LLMEngine Protocol (stream_reply → Iterator[str]), so the pipeline can't tell them apart - overlapped playback works identically. HybridLLMEngine composes the two plus a ConnectivityChecker and routes per turn. factory.build_hybrid_llm builds the cloud engine (falling back gracefully if the API key is missing), the local engine, and the checker. llm.provider selects claude / local / hybrid from config; llm.model_path points at the GGUF file.
Purpose / goal. This is the architectural payoff of the Protocol design from Report 00: a fundamentally different brain (local quantised model vs. cloud API) drops in with zero pipeline changes, and the two can be transparently switched mid-product.
2a38116)What. PersistentMemory (memory/persistent.py) - daily JSON conversation logs plus an evolving user-profile JSON, on top of Atilla's session-only ConversationStore.
Why. A car powers off between drives, which wipes any in-RAM history. A co-pilot that forgets you every time you park is not a co-pilot. Memory has to survive power cycles.
How. Each day's turns are appended to data/conversations/<date>.json. A user profile (name, car_brand, car_year, city, transmission, music_taste, daily_hours) lives in user_profile.json and is updated by extract_profile_facts, which regex-mines conversation text for facts ("my name is …", car brands, model years, city). get_recent_summaries(days, max_chars) returns the last N days for injection into the LLM prompt. The pipeline injects these summaries as a synthetic opening exchange before the live history (wired in Report 06).
Design note. Summaries are read if a <date>.summary.txt exists; otherwise the day is marked "(not yet summarised)". The nightly summarisation job that writes those files is intentionally deferred - the read path is ready for it.
Purpose / goal. Continuity. EXCAR can open a drive knowing who you are and what you talked about last time, which is the foundation of a relationship-feeling assistant rather than a stateless Q&A box.
7e89c2d)What. An OpenWakeWordDetector (wake/openwakeword_detector.py) - a tiny, always-on neural model that listens for the wake word and only then wakes the heavyweight Whisper STT.
Why. Running Whisper continuously on a Raspberry Pi to catch the word "EXCAR" is wasteful and hot. A purpose-built wake-word model is CPU-cheap and can run always-on; Whisper should only fire *after* it triggers.
How. activation_mode: openwakeword selects a dedicated loop in app.py (_run_openwakeword_loop): the detector blocks until the wake word, then records one utterance and hands it to pipeline.process_utterance. factory.build_wake_detector constructs it from audio.wake_words and audio.wake_model_path.
Purpose / goal. Make always-on viable on cheap hardware - the difference between a Pi that idles cool and one that pegs a core transcribing silence.
0db7b4a)What. scripts/install.sh, scripts/setup_boot.sh, scripts/setup_bt_tether.sh, and scripts/excar.service (systemd unit).
Why. In the car there's no terminal and no human to type excar. The device has to install cleanly and start itself the moment the car powers on.
How. A one-command install script, a systemd service that launches EXCAR at boot, and a BT-tether setup script so the Pi can reach the internet through the driver's phone.
Purpose / goal. Turn the laptop dev project into an *appliance* - plug the Pi into the car, power on, and EXCAR is listening. This is the operational half of "built for commercialisation".
connectivity.
The device half of EXCAR is now a real product shape. Report 03 builds its face.