feat: enhance v1 API with streaming and improved history

This commit is contained in:
Manus
2026-03-19 20:23:37 -04:00
parent 964f28a86f
commit 36d1bdb521
2 changed files with 33 additions and 18 deletions

View File

@@ -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}

View File

@@ -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)