feat: bot and you entity schemas with projector handlers
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE bots (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
persona TEXT NOT NULL,
|
||||||
|
voice_samples_json TEXT NOT NULL DEFAULT '[]',
|
||||||
|
traits_json TEXT NOT NULL DEFAULT '[]',
|
||||||
|
backstory TEXT NOT NULL DEFAULT '',
|
||||||
|
initial_relationship_to_you TEXT NOT NULL DEFAULT '',
|
||||||
|
kickoff_prose TEXT NOT NULL DEFAULT '',
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE you_entity (
|
||||||
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
pronouns TEXT NOT NULL DEFAULT '',
|
||||||
|
persona TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
from sqlite3 import Connection
|
||||||
|
from chat.eventlog.projector import on
|
||||||
|
from chat.eventlog.log import Event
|
||||||
|
|
||||||
|
|
||||||
|
@on("bot_authored")
|
||||||
|
def _apply_bot_authored(conn: Connection, e: Event) -> None:
|
||||||
|
p = e.payload
|
||||||
|
conn.execute(
|
||||||
|
"INSERT OR REPLACE INTO bots "
|
||||||
|
"(id, name, persona, voice_samples_json, traits_json, backstory, "
|
||||||
|
" initial_relationship_to_you, kickoff_prose) "
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
(p["id"], p["name"], p["persona"],
|
||||||
|
json.dumps(p.get("voice_samples", [])),
|
||||||
|
json.dumps(p.get("traits", [])),
|
||||||
|
p.get("backstory", ""),
|
||||||
|
p.get("initial_relationship_to_you", ""),
|
||||||
|
p.get("kickoff_prose", "")),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@on("you_authored")
|
||||||
|
def _apply_you_authored(conn: Connection, e: Event) -> None:
|
||||||
|
p = e.payload
|
||||||
|
conn.execute(
|
||||||
|
"INSERT OR REPLACE INTO you_entity (id, name, pronouns, persona) VALUES (1, ?, ?, ?)",
|
||||||
|
(p["name"], p.get("pronouns", ""), p.get("persona", "")),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_bot(conn: Connection, bot_id: str) -> dict | None:
|
||||||
|
row = conn.execute("SELECT * FROM bots WHERE id = ?", (bot_id,)).fetchone()
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
cols = [c[1] for c in conn.execute("PRAGMA table_info(bots)").fetchall()]
|
||||||
|
d = dict(zip(cols, row))
|
||||||
|
d["voice_samples"] = json.loads(d.pop("voice_samples_json"))
|
||||||
|
d["traits"] = json.loads(d.pop("traits_json"))
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def list_bots(conn: Connection) -> list[dict]:
|
||||||
|
cur = conn.execute("SELECT id, name FROM bots ORDER BY name")
|
||||||
|
return [{"id": r[0], "name": r[1]} for r in cur]
|
||||||
|
|
||||||
|
|
||||||
|
def get_you(conn: Connection) -> dict | None:
|
||||||
|
row = conn.execute("SELECT name, pronouns, persona FROM you_entity WHERE id = 1").fetchone()
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
return {"name": row[0], "pronouns": row[1], "persona": row[2]}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
from chat.db.migrate import apply_migrations
|
||||||
|
from chat.db.connection import open_db
|
||||||
|
from chat.eventlog.log import append_event
|
||||||
|
from chat.eventlog.projector import project
|
||||||
|
from chat.state.entities import get_bot, list_bots, get_you
|
||||||
|
import chat.state.entities # registers handlers
|
||||||
|
|
||||||
|
|
||||||
|
def test_bot_authored_creates_bot_row(tmp_path):
|
||||||
|
db = tmp_path / "t.db"
|
||||||
|
apply_migrations(db)
|
||||||
|
with open_db(db) as conn:
|
||||||
|
append_event(conn, kind="bot_authored", payload={
|
||||||
|
"id": "bot_a", "name": "BotA",
|
||||||
|
"persona": "...", "voice_samples": ["sample"], "traits": ["shy"],
|
||||||
|
"backstory": "...",
|
||||||
|
"initial_relationship_to_you": "coworker",
|
||||||
|
"kickoff_prose": "you stay late",
|
||||||
|
})
|
||||||
|
project(conn)
|
||||||
|
bot = get_bot(conn, "bot_a")
|
||||||
|
assert bot is not None
|
||||||
|
assert bot["name"] == "BotA"
|
||||||
|
assert bot["traits"] == ["shy"]
|
||||||
|
assert "bot_a" in [b["id"] for b in list_bots(conn)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_you_authored_creates_you_singleton(tmp_path):
|
||||||
|
db = tmp_path / "t.db"
|
||||||
|
apply_migrations(db)
|
||||||
|
with open_db(db) as conn:
|
||||||
|
append_event(conn, kind="you_authored", payload={
|
||||||
|
"name": "Me", "pronouns": "they/them", "persona": "engineer",
|
||||||
|
})
|
||||||
|
project(conn)
|
||||||
|
you = get_you(conn)
|
||||||
|
assert you is not None
|
||||||
|
assert you["name"] == "Me"
|
||||||
Reference in New Issue
Block a user