mirror of
https://github.com/PR0M3TH3AN/unredact.git
synced 2026-11-02 11:40:28 +00:00
feat: add POST /api/solve/{id}/validate endpoint
Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
1 parent
29a41e650c
commit
f7dd5a342a
2 files changed
+78
No files matched your search
@@ -442,3 +442,51 @@ async def test_cancel_clears_buffer():
|
||||
|
||||
assert fake_id not in _solve_results
|
||||
_active_solves.pop(fake_id, None)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_validate_endpoint(monkeypatch):
|
||||
"""POST /api/solve/{id}/validate should return LLM-scored results."""
|
||||
from unredact.pipeline import llm_validate
|
||||
|
||||
fake_id = "validate_test"
|
||||
_solve_results[fake_id] = [
|
||||
{"text": "Smith", "width_px": 50.0, "error_px": 0.1, "source": "names"},
|
||||
{"text": "house", "width_px": 50.0, "error_px": 0.2, "source": "words"},
|
||||
{"text": "running", "width_px": 50.0, "error_px": 0.3, "source": "words"},
|
||||
]
|
||||
|
||||
async def mock_validate(left, right, candidates):
|
||||
return [95, 10, 5]
|
||||
|
||||
monkeypatch.setattr(llm_validate, "validate_candidates", mock_validate)
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.post(f"/api/solve/{fake_id}/validate", json={
|
||||
"left_context": "Dear Mr.",
|
||||
"right_context": ", we are writing",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert len(data["results"]) == 3
|
||||
assert data["total"] == 3
|
||||
# Sorted by llm_score descending
|
||||
assert data["results"][0]["text"] == "Smith"
|
||||
assert data["results"][0]["llm_score"] == 95
|
||||
assert data["results"][1]["llm_score"] == 10
|
||||
assert data["results"][2]["llm_score"] == 5
|
||||
|
||||
_solve_results.pop(fake_id, None)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_validate_unknown_solve_id():
|
||||
"""POST /api/solve/{id}/validate with unknown id should 404."""
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.post("/api/solve/nonexistent/validate", json={
|
||||
"left_context": "test",
|
||||
"right_context": "test",
|
||||
})
|
||||
assert resp.status_code == 404
|
||||
@@ -585,6 +585,36 @@ async def get_solve_results(solve_id: str, offset: int = 0, limit: int = 200):
|
||||
}
|
||||
|
||||
|
||||
class ValidateRequest(BaseModel):
|
||||
left_context: str = ""
|
||||
right_context: str = ""
|
||||
|
||||
|
||||
@app.post("/api/solve/{solve_id}/validate")
|
||||
async def validate_solve(solve_id: str, req: ValidateRequest):
|
||||
if solve_id not in _solve_results:
|
||||
return JSONResponse({"error": "solve not found"}, status_code=404)
|
||||
|
||||
from unredact.pipeline.llm_validate import validate_candidates
|
||||
|
||||
buf = _solve_results[solve_id]
|
||||
candidates = [r["text"] for r in buf]
|
||||
|
||||
try:
|
||||
scores = await validate_candidates(
|
||||
req.left_context, req.right_context, candidates,
|
||||
)
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
|
||||
scored = []
|
||||
for i, result in enumerate(buf):
|
||||
scored.append({**result, "llm_score": scores[i] if i < len(scores) else 0})
|
||||
scored.sort(key=lambda x: x["llm_score"], reverse=True)
|
||||
|
||||
return {"results": scored, "total": len(scored)}
|
||||
|
||||
|
||||
@app.delete("/api/solve/{solve_id}")
|
||||
async def cancel_solve(solve_id: str):
|
||||
if solve_id in _active_solves:
|
||||
|
||||
Reference in new issue
Block a user