Interaction log
A structured, queryable record of every completed /speak request: what Saga heard, what it said back, which tools fired (if any), and whether a deterministic power response answered it instead of the LLM.
How to use it
Not a spoken feature — this is for *you*, after the fact. Query the SQLite table directly on whichever host runs the brain:
sqlite3 ~/.saga_assistant/memory.db
-- last 20 interactions
SELECT timestamp, command, response, stage, tools FROM interaction_log
ORDER BY id DESC LIMIT 20;
-- only the ones that used a tool (HA control, notes/reminders, memory, car status)
SELECT timestamp, command, tools FROM interaction_log
WHERE tools != '' ORDER BY id DESC;
-- only power-response answers (no LLM involved)
SELECT timestamp, command, response FROM interaction_log
WHERE stage = 'power' ORDER BY id DESC;
-- a specific session's whole thread
SELECT timestamp, command, response FROM interaction_log
WHERE session_id = '<session_id>' ORDER BY id;
How it works
- Table:
interaction_login the samememory.dbSQLite file asconversation_log(schema inpython/memory/database.py). Columns:timestamp,user_id,session_id,command,response,stage,style,model,tools(comma-joined tool names, empty if none),total_time_s. - Write path:
brain/interaction_log.py'srecord(). Never raises — a logging failure must not break a voice response. - Centralized in
brain/api.py, not in each client. Every client (run_demon.py, the voice gateway, any future client) converges on/speak, so logging there once covers all of them uniformly — this fixed a real gap where the voice-gateway path (what the always-on EMEET wake-word client actually uses) was never logging which tools fired at all, onlyrun_demon.py's own console output was. - Two log sites: inside
_generate()(covers both the single-utterance path and the staged flow's background "answer" computation —stageis"single"or"answer"respectively), and directly inspeak()'s power- response branch (stage="power",model="power",tools=""). stageis exactly what answered the request —"power"means no LLM was involved at all (see weather.md, car-parker.md for what's on that path);"single"/"answer"/"opener"mean the LLM did, withtoolsshowing what it called along the way, if anything.
Status & known limits
- This logs the brain's own request/response pairs — it doesn't capture STT confidence, audio quality, or anything upstream of the transcript reaching
/speak. - No retention/rotation policy yet — the table grows unbounded. Fine at current voice-traffic volume; revisit if that changes.
- No built-in viewer/CLI yet, just direct SQL. If this gets used often enough to want a
tail -f-style live view or a web page, that's a reasonable follow-up, not built here.