Files
chat/chat/web/chat.py
T

72 lines
2.6 KiB
Python

"""Chat detail (shell) page.
Renders ``/chats/<id>``: the title (host bot's name), a timeline placeholder,
the user-input form, and the drawer toggle. Turn handling lives in T19; this
module only sets up the structural shell.
"""
from __future__ import annotations
from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from chat.state.entities import get_bot
from chat.state.world import get_chat
from chat.web.bots import get_conn
from chat.web.render import render_prose
from chat.web.turns import _read_recent_dialogue
TEMPLATES = Jinja2Templates(
directory=str(Path(__file__).resolve().parent.parent / "templates")
)
# Register the prose renderer as a Jinja filter so the chat-detail
# template can use ``{{ turn.text|render_prose|safe }}`` (Task 33).
# The renderer escapes user content internally; ``|safe`` is required
# because the output contains intentional ``<p>``/``<em>``/etc. tags.
TEMPLATES.env.filters["render_prose"] = render_prose
router = APIRouter()
@router.get("/chats/{chat_id}", response_class=HTMLResponse)
async def chat_detail(chat_id: str, request: Request, conn=Depends(get_conn)):
chat = get_chat(conn, chat_id)
if chat is None:
raise HTTPException(status_code=404, detail=f"chat not found: {chat_id}")
host_bot = get_bot(conn, chat["host_bot_id"])
if host_bot is None:
# Defensive: chat row references a bot that doesn't exist. Treat as 404
# rather than crashing the template render.
raise HTTPException(
status_code=404, detail=f"host bot not found: {chat['host_bot_id']}"
)
# T19: render the timeline from event_log. We pull both user_turn and
# assistant_turn events for this chat, in chronological order. Each row
# is shaped ``{"speaker": ..., "text": ...}`` and the template
# discriminates roles via the speaker id (the literal "you" vs. a bot id).
raw_turns = _read_recent_dialogue(conn, chat_id, limit=200)
turns: list[dict] = []
for t in raw_turns:
if t["speaker"] == "you":
turns.append({"role": "you", "speaker": "you", "text": t["text"]})
else:
bot = get_bot(conn, t["speaker"])
label = bot["name"] if bot else t["speaker"]
turns.append({"role": "bot", "speaker": label, "text": t["text"]})
return TEMPLATES.TemplateResponse(
request,
"chat.html",
{
"chat": chat,
"host_bot": host_bot,
"turns": turns,
"active_nav": "chats",
},
)