/history endpoint added!
Browse files- src/main.py +70 -0
src/main.py
CHANGED
|
@@ -9,6 +9,7 @@ import sqlite3
|
|
| 9 |
import tempfile
|
| 10 |
import atexit
|
| 11 |
import re
|
|
|
|
| 12 |
from datetime import datetime, timedelta, timezone
|
| 13 |
from threading import Lock
|
| 14 |
from contextlib import contextmanager, asynccontextmanager
|
|
@@ -108,6 +109,18 @@ def init_database():
|
|
| 108 |
timestamp REAL NOT NULL
|
| 109 |
);
|
| 110 |
CREATE INDEX IF NOT EXISTS idx_metrics_key ON metrics(key_id, endpoint, timestamp);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
""")
|
| 112 |
|
| 113 |
init_database()
|
|
@@ -256,6 +269,16 @@ def record_metric(key_id: str, endpoint: str, success: bool, latency_ms: float):
|
|
| 256 |
(key_id, endpoint, 1 if success else 0, latency_ms, time.time())
|
| 257 |
)
|
| 258 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
def get_metrics_summary() -> dict:
|
| 260 |
import psutil
|
| 261 |
with db_transaction() as conn:
|
|
@@ -419,6 +442,16 @@ async def ask(request: Request, body: AskRequest, key_id: str = Depends(rate_lim
|
|
| 419 |
rag_service: RAGService = request.app.state.rag_service
|
| 420 |
result = rag_service.ask(body.query, subject)
|
| 421 |
success = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
return {"answer": result["answer"], "sources": result["sources"], "request_id": request.state.request_id}
|
| 423 |
except Exception as e:
|
| 424 |
logger.exception(f"RAG query failed: {e}")
|
|
@@ -548,6 +581,43 @@ async def enable_key(request: Request, target_key_id: str, key_id: str = Depends
|
|
| 548 |
logger.info(f"Key {target_key_id} enabled")
|
| 549 |
return {"status": "enabled", "key_id": target_key_id}
|
| 550 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
if __name__ == "__main__":
|
| 552 |
import uvicorn
|
| 553 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
|
| 9 |
import tempfile
|
| 10 |
import atexit
|
| 11 |
import re
|
| 12 |
+
import json
|
| 13 |
from datetime import datetime, timedelta, timezone
|
| 14 |
from threading import Lock
|
| 15 |
from contextlib import contextmanager, asynccontextmanager
|
|
|
|
| 109 |
timestamp REAL NOT NULL
|
| 110 |
);
|
| 111 |
CREATE INDEX IF NOT EXISTS idx_metrics_key ON metrics(key_id, endpoint, timestamp);
|
| 112 |
+
CREATE TABLE IF NOT EXISTS query_history (
|
| 113 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 114 |
+
key_id TEXT NOT NULL,
|
| 115 |
+
subject TEXT NOT NULL,
|
| 116 |
+
query TEXT NOT NULL,
|
| 117 |
+
answer TEXT NOT NULL,
|
| 118 |
+
sources TEXT,
|
| 119 |
+
request_id TEXT,
|
| 120 |
+
latency_ms REAL,
|
| 121 |
+
timestamp REAL NOT NULL
|
| 122 |
+
);
|
| 123 |
+
CREATE INDEX IF NOT EXISTS idx_history_timestamp ON query_history(timestamp);
|
| 124 |
""")
|
| 125 |
|
| 126 |
init_database()
|
|
|
|
| 269 |
(key_id, endpoint, 1 if success else 0, latency_ms, time.time())
|
| 270 |
)
|
| 271 |
|
| 272 |
+
def store_query_history(key_id: str, subject: str, query: str, answer: str,
|
| 273 |
+
sources: list, request_id: str, latency_ms: float):
|
| 274 |
+
with db_transaction() as conn:
|
| 275 |
+
conn.execute(
|
| 276 |
+
"""INSERT INTO query_history
|
| 277 |
+
(key_id, subject, query, answer, sources, request_id, latency_ms, timestamp)
|
| 278 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
| 279 |
+
(key_id, subject, query, answer, json.dumps(sources), request_id, latency_ms, time.time())
|
| 280 |
+
)
|
| 281 |
+
|
| 282 |
def get_metrics_summary() -> dict:
|
| 283 |
import psutil
|
| 284 |
with db_transaction() as conn:
|
|
|
|
| 442 |
rag_service: RAGService = request.app.state.rag_service
|
| 443 |
result = rag_service.ask(body.query, subject)
|
| 444 |
success = True
|
| 445 |
+
latency_ms = (time.time() - start_time) * 1000
|
| 446 |
+
store_query_history(
|
| 447 |
+
key_id=key_id,
|
| 448 |
+
subject=subject,
|
| 449 |
+
query=body.query,
|
| 450 |
+
answer=result["answer"],
|
| 451 |
+
sources=result["sources"],
|
| 452 |
+
request_id=request.state.request_id,
|
| 453 |
+
latency_ms=latency_ms
|
| 454 |
+
)
|
| 455 |
return {"answer": result["answer"], "sources": result["sources"], "request_id": request.state.request_id}
|
| 456 |
except Exception as e:
|
| 457 |
logger.exception(f"RAG query failed: {e}")
|
|
|
|
| 581 |
logger.info(f"Key {target_key_id} enabled")
|
| 582 |
return {"status": "enabled", "key_id": target_key_id}
|
| 583 |
|
| 584 |
+
@app.get("/history")
|
| 585 |
+
async def get_query_history(
|
| 586 |
+
request: Request,
|
| 587 |
+
limit: int = 100,
|
| 588 |
+
offset: int = 0,
|
| 589 |
+
key_id: str = Depends(rate_limited("default"))
|
| 590 |
+
):
|
| 591 |
+
if API_KEYS.get(key_id, {}).get("role") != "admin":
|
| 592 |
+
raise HTTPException(status_code=403, detail="Forbidden")
|
| 593 |
+
|
| 594 |
+
with db_transaction() as conn:
|
| 595 |
+
cursor = conn.execute(
|
| 596 |
+
"""SELECT id, key_id, subject, query, answer, sources, request_id,
|
| 597 |
+
latency_ms, timestamp
|
| 598 |
+
FROM query_history
|
| 599 |
+
ORDER BY timestamp DESC
|
| 600 |
+
LIMIT ? OFFSET ?""",
|
| 601 |
+
(limit, offset)
|
| 602 |
+
)
|
| 603 |
+
history = []
|
| 604 |
+
for row in cursor.fetchall():
|
| 605 |
+
history.append({
|
| 606 |
+
"id": row[0],
|
| 607 |
+
"key_id": row[1],
|
| 608 |
+
"subject": row[2],
|
| 609 |
+
"query": row[3],
|
| 610 |
+
"answer": row[4],
|
| 611 |
+
"sources": json.loads(row[5]) if row[5] else [],
|
| 612 |
+
"request_id": row[6],
|
| 613 |
+
"latency_ms": row[7],
|
| 614 |
+
"timestamp": row[8],
|
| 615 |
+
})
|
| 616 |
+
|
| 617 |
+
total = conn.execute("SELECT COUNT(*) FROM query_history").fetchone()[0]
|
| 618 |
+
|
| 619 |
+
return {"history": history, "total": total, "limit": limit, "offset": offset}
|
| 620 |
+
|
| 621 |
if __name__ == "__main__":
|
| 622 |
import uvicorn
|
| 623 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|