feat: add topic summarize endpoint
This commit is contained in:
@@ -9,8 +9,10 @@ from src.backend.db.database import (
|
|||||||
get_topic,
|
get_topic,
|
||||||
update_topic,
|
update_topic,
|
||||||
delete_topic,
|
delete_topic,
|
||||||
|
list_requests,
|
||||||
)
|
)
|
||||||
from src.backend.models.api import TopicCreate, TopicUpdate, ok, fail
|
from src.backend.models.api import TopicCreate, TopicUpdate, ok, fail
|
||||||
|
from src.backend.services.worker_client import WorkerClient
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/topics",
|
prefix="/topics",
|
||||||
@@ -91,3 +93,38 @@ async def delete(topic_id: str, db=Depends(_get_db)):
|
|||||||
content=fail("NOT_FOUND", "Topic not found or has active requests"),
|
content=fail("NOT_FOUND", "Topic not found or has active requests"),
|
||||||
)
|
)
|
||||||
return ok({"deleted": True})
|
return ok({"deleted": True})
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{topic_id}/summarize", summary="Regenerate topic summary")
|
||||||
|
async def summarize_topic(topic_id: str, db=Depends(_get_db)):
|
||||||
|
topic = await get_topic(db, topic_id)
|
||||||
|
if not topic:
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=404,
|
||||||
|
content=fail("NOT_FOUND", "Topic not found"),
|
||||||
|
)
|
||||||
|
|
||||||
|
completed = await list_requests(db, status="completed", topic_id=topic_id)
|
||||||
|
if not completed:
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=400,
|
||||||
|
content=fail("NO_DATA", "No completed requests to summarize"),
|
||||||
|
)
|
||||||
|
|
||||||
|
exchanges = [
|
||||||
|
{"prompt": r["prompt"], "result": r["result"]}
|
||||||
|
for r in completed
|
||||||
|
if r["result"]
|
||||||
|
]
|
||||||
|
|
||||||
|
worker = WorkerClient()
|
||||||
|
result = await worker.summarize(exchanges=exchanges, mode="complete")
|
||||||
|
|
||||||
|
if not result.get("success"):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=502,
|
||||||
|
content=fail("WORKER_ERROR", "Worker failed to generate summary"),
|
||||||
|
)
|
||||||
|
|
||||||
|
updated = await update_topic(db, topic_id, summary=result["summary"])
|
||||||
|
return ok(updated)
|
||||||
|
|||||||
@@ -148,3 +148,58 @@ async def test_delete_topic(client):
|
|||||||
|
|
||||||
resp = await client.get(f"/api/topics/{topic_id}")
|
resp = await client.get(f"/api/topics/{topic_id}")
|
||||||
assert resp.status_code == 404
|
assert resp.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_summarize_topic(client):
|
||||||
|
create_resp = await client.post(
|
||||||
|
"/api/topics",
|
||||||
|
json={"name": "summarize-test", "system_prompt": "You help."},
|
||||||
|
)
|
||||||
|
topic_id = create_resp.json()["data"]["id"]
|
||||||
|
|
||||||
|
from src.backend.db.database import get_db, create_request, complete_request
|
||||||
|
|
||||||
|
db = await get_db()
|
||||||
|
try:
|
||||||
|
req1 = await create_request(db, "Question 1", topic_id)
|
||||||
|
await complete_request(db, req1["id"], "Answer 1")
|
||||||
|
req2 = await create_request(db, "Question 2", topic_id)
|
||||||
|
await complete_request(db, req2["id"], "Answer 2")
|
||||||
|
finally:
|
||||||
|
await db.close()
|
||||||
|
|
||||||
|
from unittest.mock import patch, AsyncMock, MagicMock
|
||||||
|
|
||||||
|
mock_worker = MagicMock()
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.status_code = 200
|
||||||
|
mock_response.json.return_value = {
|
||||||
|
"success": True,
|
||||||
|
"summary": "Comprehensive summary of all exchanges.",
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_client = AsyncMock()
|
||||||
|
mock_client.post.return_value = mock_response
|
||||||
|
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
||||||
|
mock_client.__aexit__ = AsyncMock(return_value=False)
|
||||||
|
|
||||||
|
with patch("src.backend.services.worker_client.httpx.AsyncClient", return_value=mock_client):
|
||||||
|
resp = await client.post(f"/api/topics/{topic_id}/summarize")
|
||||||
|
|
||||||
|
assert resp.status_code == 200
|
||||||
|
data = resp.json()
|
||||||
|
assert data["success"] is True
|
||||||
|
assert "summary" in data["data"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_summarize_topic_no_requests(client):
|
||||||
|
create_resp = await client.post(
|
||||||
|
"/api/topics",
|
||||||
|
json={"name": "empty-topic", "system_prompt": "Empty."},
|
||||||
|
)
|
||||||
|
topic_id = create_resp.json()["data"]["id"]
|
||||||
|
|
||||||
|
resp = await client.post(f"/api/topics/{topic_id}/summarize")
|
||||||
|
assert resp.status_code == 400
|
||||||
|
|||||||
Reference in New Issue
Block a user