diff --git a/chat/app.py b/chat/app.py index 0ac28ef..82e49ad 100644 --- a/chat/app.py +++ b/chat/app.py @@ -15,6 +15,7 @@ import chat.state.memory # noqa: F401 import chat.state.world # noqa: F401 from chat.web.bots import router as bots_router +from chat.web.settings import router as settings_router @asynccontextmanager @@ -32,6 +33,7 @@ STATIC_DIR = Path(__file__).resolve().parent / "static" app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") app.include_router(bots_router) +app.include_router(settings_router) @app.get("/health") diff --git a/chat/static/app.css b/chat/static/app.css index 96a0296..9c261a8 100644 --- a/chat/static/app.css +++ b/chat/static/app.css @@ -34,4 +34,8 @@ h1 { margin-top: 0; } padding: 8px 12px; border: 1px solid #c33; background: #fdecea; color: #a00; border-radius: 3px; } +.success { + padding: 8px 12px; border: 1px solid #2d7a3a; background: #eafaf0; + color: #1f5c2a; border-radius: 3px; +} code { font-family: ui-monospace, "SF Mono", Menlo, monospace; } diff --git a/chat/templates/settings.html b/chat/templates/settings.html new file mode 100644 index 0000000..e26fcf6 --- /dev/null +++ b/chat/templates/settings.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} +{% block title %}Settings - chat{% endblock %} +{% block content %} +

Settings

+{% if saved %} +

Settings saved.

+{% endif %} +
+ + + + + + + +
+{% endblock %} diff --git a/chat/web/settings.py b/chat/web/settings.py new file mode 100644 index 0000000..cb04d58 --- /dev/null +++ b/chat/web/settings.py @@ -0,0 +1,46 @@ +from __future__ import annotations +from pathlib import Path +from fastapi import APIRouter, Depends, Form, HTTPException, Request +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates + +from chat.eventlog.log import append_event +from chat.eventlog.projector import project +from chat.state.entities import get_you +from chat.web.bots import get_conn + +TEMPLATES = Jinja2Templates(directory=str(Path(__file__).resolve().parent.parent / "templates")) + +router = APIRouter() + + +@router.get("/settings", response_class=HTMLResponse) +async def settings_get(request: Request, conn=Depends(get_conn)): + you = get_you(conn) or {"name": "", "pronouns": "", "persona": ""} + return TEMPLATES.TemplateResponse( + request, "settings.html", {"values": you, "saved": False} + ) + + +@router.post("/settings", response_class=HTMLResponse) +async def settings_post( + request: Request, + name: str = Form(""), + pronouns: str = Form(""), + persona: str = Form(""), + conn=Depends(get_conn), +): + if not name.strip(): + raise HTTPException(status_code=400, detail="name is required") + + payload = { + "name": name.strip(), + "pronouns": pronouns.strip(), + "persona": persona.strip(), + } + append_event(conn, kind="you_authored", payload=payload) + project(conn) + + return TEMPLATES.TemplateResponse( + request, "settings.html", {"values": payload, "saved": True} + ) diff --git a/tests/test_settings.py b/tests/test_settings.py new file mode 100644 index 0000000..18d2c4b --- /dev/null +++ b/tests/test_settings.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +import pytest +from fastapi.testclient import TestClient + +from chat.app import app + + +@pytest.fixture +def client(tmp_path, monkeypatch): + config_path = tmp_path / "config.toml" + config_path.write_text('featherless_api_key = "test"\n') + monkeypatch.setenv("CHAT_CONFIG_PATH", str(config_path)) + monkeypatch.setenv("CHAT_DB_PATH", str(tmp_path / "test.db")) + with TestClient(app) as c: + yield c + + +def test_get_settings_renders_empty(client): + response = client.get("/settings") + assert response.status_code == 200 + body = response.text.lower() + assert "