3b9e0bb1e0
Phase 1 — persona + persistent memory chat loop: - lyra/persona.py + personas/lyra.md: editable identity/voice (friend-first, honest, never invents poker math) - lyra/chat.py: turn loop assembling persona + cross-session recall + recent context, persisting both sides to SQLite - lyra/session.py, lyra/__main__.py: session lifecycle + `lyra` REPL Phase 1.25 — reuse the old web UI: - vendored the prior single-page UI into lyra/web/static, repointed to same-origin - lyra/web/server.py (FastAPI): serves the UI and backs its endpoint contract (/v1/chat/completions, session CRUD, health, inert thinking-stream) with the new chat loop + memory; SQLite stays the single source of truth - `lyra-web` console script Local backends — test for free, no OpenAI key: - llm.embed routes via EMBED_BACKEND (cloud=OpenAI, local=Ollama /api/embed) - simplified UI backend selector to Local (Ollama) / Cloud (OpenAI), default local - memory connection opened check_same_thread=False for the threaded server Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
37 lines
934 B
Python
37 lines
934 B
Python
"""`python -m lyra` (or `lyra`): a terminal REPL to talk to Lyra."""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
|
|
from lyra import chat
|
|
from lyra.session import Session
|
|
|
|
_QUIT = {"exit", "quit", ":q"}
|
|
|
|
|
|
def main() -> int:
|
|
session = Session()
|
|
print(f"Lyra — session {session.id}. Ctrl-D or 'exit' to leave.\n")
|
|
while True:
|
|
try:
|
|
user_msg = input("you > ").strip()
|
|
except (EOFError, KeyboardInterrupt):
|
|
print()
|
|
break
|
|
if not user_msg:
|
|
continue
|
|
if user_msg.lower() in _QUIT:
|
|
break
|
|
try:
|
|
reply = chat.respond(session.id, user_msg)
|
|
except Exception as exc: # keep the loop alive; surface the error
|
|
print(f"\n[error] {exc}\n", file=sys.stderr)
|
|
continue
|
|
print(f"\nlyra > {reply}\n")
|
|
print("later.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|