from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from typing import List, Dict, Optional import os import sys from pathlib import Path from dotenv import load_dotenv load_dotenv() # Ensure we can import from project root sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from lang_agent.components.conv_store import ConversationStore app = FastAPI( title="Conversation Viewer", description="Web UI to view conversations from the database", ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Initialize conversation store try: conv_store = ConversationStore() except ValueError as e: print(f"Warning: {e}. Make sure CONN_STR environment variable is set.") conv_store = None class MessageResponse(BaseModel): message_type: str content: str sequence_number: int created_at: str class ConversationListItem(BaseModel): conversation_id: str message_count: int last_updated: Optional[str] = None @app.get("/", response_class=HTMLResponse) async def root(): """Serve the main HTML page""" html_path = Path(__file__).parent.parent / "static" / "viewer.html" if html_path.exists(): return HTMLResponse(content=html_path.read_text(encoding="utf-8")) else: return HTMLResponse(content="

Viewer HTML not found. Please create static/viewer.html

") @app.get("/api/conversations", response_model=List[ConversationListItem]) async def list_conversations(): """Get list of all conversations""" if conv_store is None: raise HTTPException(status_code=500, detail="Database connection not configured") import psycopg conn_str = os.environ.get("CONN_STR") if not conn_str: raise HTTPException(status_code=500, detail="CONN_STR not set") with psycopg.connect(conn_str) as conn: with conn.cursor(row_factory=psycopg.rows.dict_row) as cur: # Get all unique conversation IDs with message counts and last updated time cur.execute(""" SELECT conversation_id, COUNT(*) as message_count, MAX(created_at) as last_updated FROM messages GROUP BY conversation_id ORDER BY last_updated DESC """) results = cur.fetchall() return [ ConversationListItem( conversation_id=row["conversation_id"], message_count=row["message_count"], last_updated=row["last_updated"].isoformat() if row["last_updated"] else None ) for row in results ] @app.get("/api/conversations/{conversation_id}/messages", response_model=List[MessageResponse]) async def get_conversation_messages(conversation_id: str): """Get all messages for a specific conversation""" if conv_store is None: raise HTTPException(status_code=500, detail="Database connection not configured") messages = conv_store.get_conversation(conversation_id) return [ MessageResponse( message_type=msg["message_type"], content=msg["content"], sequence_number=msg["sequence_number"], created_at=msg["created_at"].isoformat() if msg["created_at"] else "" ) for msg in messages ] @app.get("/health") async def health(): return {"status": "healthy", "db_connected": conv_store is not None} if __name__ == "__main__": import uvicorn uvicorn.run( "server_viewer:app", host="0.0.0.0", port=8590, reload=True, )