fix: address linting and formatting issues for v1 API
All checks were successful
Tests / lint (pull_request) Successful in 5s
Tests / test (pull_request) Successful in 52s

This commit is contained in:
Manus
2026-03-18 17:51:10 -04:00
parent 55dda093c8
commit 964f28a86f
2 changed files with 24 additions and 28 deletions

View File

@@ -10,22 +10,20 @@ Endpoints:
GET /api/v1/status — Detailed system and model status GET /api/v1/status — Detailed system and model status
""" """
import json
import logging import logging
import os import os
import uuid import uuid
import json from datetime import UTC, datetime
import asyncio
from datetime import datetime, UTC
from pathlib import Path 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 fastapi.responses import JSONResponse, StreamingResponse
from config import settings, APP_START_TIME from config import APP_START_TIME, settings
from dashboard.store import message_log
from timmy.session import chat_with_tools, _get_agent
from dashboard.routes.health import _check_ollama from dashboard.routes.health import _check_ollama
from dashboard.store import message_log
from timmy.session import _get_agent
logger = logging.getLogger(__name__) 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"{datetime.now().strftime('%A, %B %d, %Y at %I:%M %p')}]\n"
f"[System: iPad App client]\n" f"[System: iPad App client]\n"
) )
if attachments: if attachments:
context_prefix += f"[System: Attachments: {', '.join(attachments)}]\n" context_prefix += f"[System: Attachments: {', '.join(attachments)}]\n"
context_prefix += "\n" context_prefix += "\n"
full_prompt = context_prefix + message 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) content = chunk.content if hasattr(chunk, "content") else str(chunk)
if content: if content:
yield f"data: {json.dumps({'text': content})}\n\n" yield f"data: {json.dumps({'text': content})}\n\n"
yield "data: [DONE]\n\n" yield "data: [DONE]\n\n"
except Exception as exc: except Exception as exc:
logger.error("SSE stream error: %s", exc) logger.error("SSE stream error: %s", exc)
@@ -101,14 +99,13 @@ async def api_v1_chat(request: Request):
@router.get("/chat/history") @router.get("/chat/history")
async def api_v1_chat_history( async def api_v1_chat_history(
session_id: str = Query("ipad-app"), session_id: str = Query("ipad-app"), limit: int = Query(50, ge=1, le=100)
limit: int = Query(50, ge=1, le=100)
): ):
"""Return recent chat history for a specific session.""" """Return recent chat history for a specific session."""
# Filter and limit the message log # Filter and limit the message log
# Note: message_log.all() returns all messages; we filter by source or just return last N # Note: message_log.all() returns all messages; we filter by source or just return last N
all_msgs = message_log.all() all_msgs = message_log.all()
# In a real implementation, we'd filter by session_id if message_log supported it. # In a real implementation, we'd filter by session_id if message_log supported it.
# For now, we return the last 'limit' messages. # For now, we return the last 'limit' messages.
history = [ history = [
@@ -120,7 +117,7 @@ async def api_v1_chat_history(
} }
for msg in all_msgs[-limit:] for msg in all_msgs[-limit:]
] ]
return {"messages": history} return {"messages": history}
@@ -155,14 +152,14 @@ async def api_v1_upload(file: UploadFile = File(...)):
contents = await file.read() contents = await file.read()
if len(contents) > _MAX_UPLOAD_SIZE: if len(contents) > _MAX_UPLOAD_SIZE:
raise HTTPException(status_code=413, detail="File too large (max 50 MB)") raise HTTPException(status_code=413, detail="File too large (max 50 MB)")
with open(file_path, "wb") as f: with open(file_path, "wb") as f:
f.write(contents) f.write(contents)
# Auto-detect type based on extension/mime # Auto-detect type based on extension/mime
mime_type = file.content_type or "application/octet-stream" mime_type = file.content_type or "application/octet-stream"
ext = os.path.splitext(safe_name)[1].lower() ext = os.path.splitext(safe_name)[1].lower()
media_type = "document" media_type = "document"
if mime_type.startswith("image/") or ext in [".jpg", ".jpeg", ".png", ".heic"]: if mime_type.startswith("image/") or ext in [".jpg", ".jpeg", ".png", ".heic"]:
media_type = "image" media_type = "image"
@@ -179,11 +176,7 @@ async def api_v1_upload(file: UploadFile = File(...)):
"type": media_type, "type": media_type,
"summary": summary, "summary": summary,
"url": f"/uploads/{stored_name}", "url": f"/uploads/{stored_name}",
"metadata": { "metadata": {"fileName": safe_name, "mimeType": mime_type, "size": len(contents)},
"fileName": safe_name,
"mimeType": mime_type,
"size": len(contents)
}
} }
@@ -195,11 +188,11 @@ async def api_v1_status():
"""Detailed system and model status.""" """Detailed system and model status."""
ollama_status = await _check_ollama() ollama_status = await _check_ollama()
uptime = (datetime.now(UTC) - APP_START_TIME).total_seconds() uptime = (datetime.now(UTC) - APP_START_TIME).total_seconds()
return { return {
"timmy": "online" if ollama_status.status == "healthy" else "offline", "timmy": "online" if ollama_status.status == "healthy" else "offline",
"model": settings.ollama_model, "model": settings.ollama_model,
"ollama": "running" if ollama_status.status == "healthy" else "stopped", "ollama": "running" if ollama_status.status == "healthy" else "stopped",
"uptime": f"{int(uptime // 3600)}h {int((uptime % 3600) // 60)}m", "uptime": f"{int(uptime // 3600)}h {int((uptime % 3600) // 60)}m",
"version": "2.0.0-v1-api" "version": "2.0.0-v1-api",
} }

View File

@@ -1,16 +1,15 @@
import sys import sys
import os
from pathlib import Path
# Absolute path to src # Absolute path to src
src_path = "/home/ubuntu/timmy-time/Timmy-time-dashboard/src" src_path = "/home/ubuntu/timmy-time/Timmy-time-dashboard/src"
if src_path not in sys.path: if src_path not in sys.path:
sys.path.insert(0, src_path) sys.path.insert(0, src_path)
from fastapi.testclient import TestClient from fastapi.testclient import TestClient # noqa: E402
try: try:
from dashboard.app import app from dashboard.app import app # noqa: E402
print("✓ Successfully imported dashboard.app") print("✓ Successfully imported dashboard.app")
except ImportError as e: except ImportError as e:
print(f"✗ Failed to import dashboard.app: {e}") print(f"✗ Failed to import dashboard.app: {e}")
@@ -18,6 +17,7 @@ except ImportError as e:
client = TestClient(app) client = TestClient(app)
def test_v1_status(): def test_v1_status():
response = client.get("/api/v1/status") response = client.get("/api/v1/status")
assert response.status_code == 200 assert response.status_code == 200
@@ -26,17 +26,20 @@ def test_v1_status():
assert "model" in data assert "model" in data
assert "uptime" in data assert "uptime" in data
def test_v1_chat_history(): def test_v1_chat_history():
response = client.get("/api/v1/chat/history") response = client.get("/api/v1/chat/history")
assert response.status_code == 200 assert response.status_code == 200
data = response.json() data = response.json()
assert "messages" in data assert "messages" in data
def test_v1_upload_fail(): def test_v1_upload_fail():
# Test without file # Test without file
response = client.post("/api/v1/upload") response = client.post("/api/v1/upload")
assert response.status_code == 422 # Unprocessable Entity (missing file) assert response.status_code == 422 # Unprocessable Entity (missing file)
if __name__ == "__main__": if __name__ == "__main__":
print("Running API v1 tests...") print("Running API v1 tests...")
try: try: