diff --git a/src/dashboard/routes/chat_api_v1.py b/src/dashboard/routes/chat_api_v1.py index 9dd7cf6d..04cc2d13 100644 --- a/src/dashboard/routes/chat_api_v1.py +++ b/src/dashboard/routes/chat_api_v1.py @@ -10,22 +10,20 @@ Endpoints: GET /api/v1/status — Detailed system and model status """ +import json import logging import os import uuid -import json -import asyncio -from datetime import datetime, UTC +from datetime import UTC, datetime from pathlib import Path -from typing import Optional, List -from fastapi import APIRouter, File, HTTPException, Request, UploadFile, Query +from fastapi import APIRouter, File, HTTPException, Query, Request, UploadFile from fastapi.responses import JSONResponse, StreamingResponse -from config import settings, APP_START_TIME -from dashboard.store import message_log -from timmy.session import chat_with_tools, _get_agent +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 logger = logging.getLogger(__name__) @@ -71,10 +69,10 @@ async def api_v1_chat(request: Request): f"{datetime.now().strftime('%A, %B %d, %Y at %I:%M %p')}]\n" f"[System: iPad App client]\n" ) - + if attachments: context_prefix += f"[System: Attachments: {', '.join(attachments)}]\n" - + context_prefix += "\n" full_prompt = context_prefix + message @@ -87,7 +85,7 @@ async def api_v1_chat(request: Request): content = chunk.content if hasattr(chunk, "content") else str(chunk) if content: yield f"data: {json.dumps({'text': content})}\n\n" - + yield "data: [DONE]\n\n" except Exception as exc: logger.error("SSE stream error: %s", exc) @@ -101,14 +99,13 @@ async def api_v1_chat(request: Request): @router.get("/chat/history") async def api_v1_chat_history( - session_id: str = Query("ipad-app"), - limit: int = Query(50, ge=1, le=100) + 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() - + # In a real implementation, we'd filter by session_id if message_log supported it. # For now, we return the last 'limit' messages. history = [ @@ -120,7 +117,7 @@ async def api_v1_chat_history( } for msg in all_msgs[-limit:] ] - + return {"messages": history} @@ -155,14 +152,14 @@ async def api_v1_upload(file: UploadFile = File(...)): contents = await file.read() if len(contents) > _MAX_UPLOAD_SIZE: raise HTTPException(status_code=413, detail="File too large (max 50 MB)") - + with open(file_path, "wb") as f: f.write(contents) # Auto-detect type based on extension/mime mime_type = file.content_type or "application/octet-stream" ext = os.path.splitext(safe_name)[1].lower() - + media_type = "document" if mime_type.startswith("image/") or ext in [".jpg", ".jpeg", ".png", ".heic"]: media_type = "image" @@ -179,11 +176,7 @@ async def api_v1_upload(file: UploadFile = File(...)): "type": media_type, "summary": summary, "url": f"/uploads/{stored_name}", - "metadata": { - "fileName": safe_name, - "mimeType": mime_type, - "size": len(contents) - } + "metadata": {"fileName": safe_name, "mimeType": mime_type, "size": len(contents)}, } @@ -195,11 +188,11 @@ async def api_v1_status(): """Detailed system and model status.""" ollama_status = await _check_ollama() uptime = (datetime.now(UTC) - APP_START_TIME).total_seconds() - + return { "timmy": "online" if ollama_status.status == "healthy" else "offline", "model": settings.ollama_model, "ollama": "running" if ollama_status.status == "healthy" else "stopped", "uptime": f"{int(uptime // 3600)}h {int((uptime % 3600) // 60)}m", - "version": "2.0.0-v1-api" + "version": "2.0.0-v1-api", } diff --git a/tests/test_api_v1.py b/tests/test_api_v1.py index 8c4ae69e..4bcf325d 100644 --- a/tests/test_api_v1.py +++ b/tests/test_api_v1.py @@ -1,16 +1,15 @@ 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) -from fastapi.testclient import TestClient +from fastapi.testclient import TestClient # noqa: E402 try: - from dashboard.app import app + from dashboard.app import app # noqa: E402 + print("✓ Successfully imported dashboard.app") except ImportError as e: print(f"✗ Failed to import dashboard.app: {e}") @@ -18,6 +17,7 @@ except ImportError as e: client = TestClient(app) + def test_v1_status(): response = client.get("/api/v1/status") assert response.status_code == 200 @@ -26,17 +26,20 @@ def test_v1_status(): assert "model" in data assert "uptime" in data + def test_v1_chat_history(): response = client.get("/api/v1/chat/history") assert response.status_code == 200 data = response.json() assert "messages" in data + 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: