fix: address linting and formatting issues for v1 API
This commit is contained in:
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user