feat: in-app live log (SSE activity feed)

Turn the inert "Show Work" thinking panel into a real live activity log:
- lyra/logbus.py: thread-safe in-memory ring buffer other modules publish to
- chat.respond logs backend/model/embed per turn, recall counts, reply size;
  web layer logs chat errors
- server: replace the keep-alive /stream/thinking stub with /stream/logs, an
  SSE endpoint that replays the recent buffer then streams new events
- UI: repurpose the panel as a global "Live Log" — connects on load, renders
  level/time/msg/fields, drops the old per-session localStorage + dead popup

Every turn now shows its backend + model in-app, so local-vs-cloud (free vs
paid) is visible at a glance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 18:45:05 +00:00
parent 3b9e0bb1e0
commit 84c4f75e03
5 changed files with 143 additions and 160 deletions
+10 -1
View File
@@ -6,7 +6,7 @@ session, then asks the model for a reply and persists both sides.
"""
from __future__ import annotations
from lyra import llm, memory, persona
from lyra import config, llm, logbus, memory, persona
from lyra.llm import Backend, Message
RECALL_K = 5
@@ -34,6 +34,7 @@ def build_messages(session_id: str, user_msg: str) -> list[Message]:
recalled = [
ex for ex in memory.recall(user_msg, k=RECALL_K) if ex.id not in recent_ids
]
logbus.log("debug", "context built", recent=len(recent), recalled=len(recalled))
if recalled:
messages.append(_memory_note(recalled))
@@ -46,8 +47,16 @@ def build_messages(session_id: str, user_msg: str) -> list[Message]:
def respond(session_id: str, user_msg: str, backend: Backend = "cloud") -> str:
"""Produce Lyra's reply to a single user message and persist the exchange."""
cfg = config.load()
model = cfg.local_model if backend == "local" else cfg.cloud_model
logbus.log(
"info", "chat request", session=session_id, backend=backend,
model=model, embed=cfg.embed_backend,
)
messages = build_messages(session_id, user_msg)
reply = llm.complete(messages, backend=backend)
logbus.log("info", "reply", session=session_id, chars=len(reply))
memory.remember(session_id, "user", user_msg)
memory.remember(session_id, "assistant", reply)