diff --git a/src/dashboard/routes/chat_api_v1.py b/src/dashboard/routes/chat_api_v1.py index 04cc2d1..f656f18 100644 --- a/src/dashboard/routes/chat_api_v1.py +++ b/src/dashboard/routes/chat_api_v1.py @@ -17,13 +17,13 @@ import uuid from datetime import UTC, datetime from pathlib import Path -from fastapi import APIRouter, File, HTTPException, Query, Request, UploadFile +from fastapi import APIRouter, File, HTTPException, Request, UploadFile, Query from fastapi.responses import JSONResponse, StreamingResponse from config import APP_START_TIME, settings -from dashboard.routes.health import _check_ollama from dashboard.store import message_log from timmy.session import _get_agent +from dashboard.routes.health import _check_ollama logger = logging.getLogger(__name__) @@ -64,9 +64,11 @@ async def api_v1_chat(request: Request): return JSONResponse(status_code=400, content={"error": "message is required"}) # Prepare context for the agent + now = datetime.now() + timestamp = now.strftime("%H:%M:%S") context_prefix = ( f"[System: Current date/time is " - f"{datetime.now().strftime('%A, %B %d, %Y at %I:%M %p')}]\n" + f"{now.strftime('%A, %B %d, %Y at %I:%M %p')}]\n" f"[System: iPad App client]\n" ) @@ -76,7 +78,11 @@ async def api_v1_chat(request: Request): context_prefix += "\n" full_prompt = context_prefix + message + # Log user message + message_log.append(role="user", content=message, timestamp=timestamp, source="api-v1") + async def event_generator(): + full_response = "" try: agent = _get_agent() # Using streaming mode for SSE @@ -84,8 +90,13 @@ async def api_v1_chat(request: Request): # Agno chunks can be strings or RunOutput content = chunk.content if hasattr(chunk, "content") else str(chunk) if content: + full_response += content yield f"data: {json.dumps({'text': content})}\n\n" + # Log agent response once complete + message_log.append( + role="agent", content=full_response, timestamp=timestamp, source="api-v1" + ) yield "data: [DONE]\n\n" except Exception as exc: logger.error("SSE stream error: %s", exc) @@ -102,12 +113,9 @@ async def api_v1_chat_history( session_id: str = Query("ipad-app"), limit: int = Query(50, ge=1, le=100) ): """Return recent chat history for a specific session.""" - # Filter and limit the message log - # Note: message_log.all() returns all messages; we filter by source or just return last N - all_msgs = message_log.all() + # Using the optimized .recent() method from infrastructure.chat_store + all_msgs = message_log.recent(limit=limit) - # In a real implementation, we'd filter by session_id if message_log supported it. - # For now, we return the last 'limit' messages. history = [ { "role": msg.role, @@ -115,7 +123,7 @@ async def api_v1_chat_history( "timestamp": msg.timestamp, "source": msg.source, } - for msg in all_msgs[-limit:] + for msg in all_msgs ] return {"messages": history} diff --git a/tests/test_api_v1.py b/tests/test_api_v1.py index 4bcf325..2051101 100644 --- a/tests/test_api_v1.py +++ b/tests/test_api_v1.py @@ -1,15 +1,17 @@ import sys +import os +from pathlib import Path -# Absolute path to src -src_path = "/home/ubuntu/timmy-time/Timmy-time-dashboard/src" -if src_path not in sys.path: - sys.path.insert(0, src_path) +# Add project root to path +project_root = Path(__file__).resolve().parents[1] +src_path = project_root / "src" +if str(src_path) not in sys.path: + sys.path.insert(0, str(src_path)) from fastapi.testclient import TestClient # noqa: E402 try: from dashboard.app import app # noqa: E402 - print("✓ Successfully imported dashboard.app") except ImportError as e: print(f"✗ Failed to import dashboard.app: {e}") @@ -17,7 +19,6 @@ except ImportError as e: client = TestClient(app) - def test_v1_status(): response = client.get("/api/v1/status") assert response.status_code == 200 @@ -26,20 +27,24 @@ def test_v1_status(): assert "model" in data assert "uptime" in data - def test_v1_chat_history(): + # Append a message first to ensure history is not empty + from dashboard.store import message_log + message_log.append(role="user", content="test message", timestamp="12:00:00", source="api-v1") + response = client.get("/api/v1/chat/history") assert response.status_code == 200 data = response.json() assert "messages" in data - + assert len(data["messages"]) > 0 + # The message_log.recent() returns reversed(rows) so the last one should be our test message + assert data["messages"][-1]["content"] == "test message" def test_v1_upload_fail(): # Test without file response = client.post("/api/v1/upload") assert response.status_code == 422 # Unprocessable Entity (missing file) - if __name__ == "__main__": print("Running API v1 tests...") try: @@ -52,4 +57,6 @@ if __name__ == "__main__": print("All tests passed!") except Exception as e: print(f"Test failed: {e}") + import traceback + traceback.print_exc() sys.exit(1)