Upload 5 files
Browse files- src/api/admin.py +31 -20
- src/api/agent.py +10 -2
src/api/admin.py
CHANGED
|
@@ -865,28 +865,39 @@ async def admin_worker_health(request: Request):
|
|
| 865 |
else:
|
| 866 |
out["verdict"] = (f"Unexpected analytics status {ana.get('status')}. "
|
| 867 |
f"See probes for details.")
|
| 868 |
-
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
|
| 870 |
-
@router.get("/
|
| 871 |
-
async def
|
| 872 |
-
"""
|
| 873 |
|
| 874 |
-
|
| 875 |
-
|
| 876 |
-
|
| 877 |
-
unreachable -> no HTTP response; exception type is in 'detail'
|
| 878 |
-
rejected_401 -> CLOUDFLARE_WORKER_TOKEN does not match ANALYTICS_TOKEN
|
| 879 |
-
Buffered rows are retried automatically on later queries.
|
| 880 |
"""
|
| 881 |
_require_session(request)
|
| 882 |
-
|
| 883 |
-
from src.config import get_settings as _gs
|
| 884 |
-
st = _gs()
|
| 885 |
-
data = analytics_log_status()
|
| 886 |
-
data["worker_url_configured"] = bool(st.cloudflare_worker_url)
|
| 887 |
-
data["worker_token_configured"] = bool(st.cloudflare_worker_token)
|
| 888 |
-
if not data["recent_attempts"]:
|
| 889 |
-
data["hint"] = ("No write attempts recorded since the last restart. "
|
| 890 |
-
"Send a query through LibBee, then reload this page.")
|
| 891 |
-
return data
|
| 892 |
|
|
|
|
| 865 |
else:
|
| 866 |
out["verdict"] = (f"Unexpected analytics status {ana.get('status')}. "
|
| 867 |
f"See probes for details.")
|
| 868 |
+
return out
|
| 869 |
+
|
| 870 |
+
@router.get("/log-status")
|
| 871 |
+
async def admin_log_status(request: Request):
|
| 872 |
+
"""Outcome of recent analytics writes, without needing container logs.
|
| 873 |
+
|
| 874 |
+
'recent_attempts' is newest-first. Read it like this:
|
| 875 |
+
ok -> the write reached the Worker (HTTP 2xx)
|
| 876 |
+
throttled -> HTTP 403/429: the host is being rate-limited by Cloudflare
|
| 877 |
+
unreachable -> no HTTP response; exception type is in 'detail'
|
| 878 |
+
rejected_401 -> CLOUDFLARE_WORKER_TOKEN does not match ANALYTICS_TOKEN
|
| 879 |
+
Buffered rows are retried automatically on later queries.
|
| 880 |
+
"""
|
| 881 |
+
_require_session(request)
|
| 882 |
+
from src.agentcore.orchestrator import analytics_log_status
|
| 883 |
+
from src.config import get_settings as _gs
|
| 884 |
+
st = _gs()
|
| 885 |
+
data = analytics_log_status()
|
| 886 |
+
data["worker_url_configured"] = bool(st.cloudflare_worker_url)
|
| 887 |
+
data["worker_token_configured"] = bool(st.cloudflare_worker_token)
|
| 888 |
+
if not data["recent_attempts"]:
|
| 889 |
+
data["hint"] = ("No write attempts recorded since the last restart. "
|
| 890 |
+
"Send a query through LibBee, then reload this page.")
|
| 891 |
+
return data
|
| 892 |
|
| 893 |
+
@router.get("/canary-status")
|
| 894 |
+
async def admin_canary_status(request: Request):
|
| 895 |
+
"""Last 14 daily canary runs (the Worker's scheduled health probe).
|
| 896 |
|
| 897 |
+
Each run is one real question through the full pipeline, chosen from a
|
| 898 |
+
fixed 14-question pool rotating by day-of-year. Canary traffic is excluded
|
| 899 |
+
from analytics, so these runs never appear in the D1 usage data.
|
|
|
|
|
|
|
|
|
|
| 900 |
"""
|
| 901 |
_require_session(request)
|
| 902 |
+
return await _worker_get("/canary/status")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 903 |
|
src/api/agent.py
CHANGED
|
@@ -41,10 +41,18 @@ async def agent_query(request: Request, req: AgentRequest):
|
|
| 41 |
history = req.history
|
| 42 |
model = req.model
|
| 43 |
client_state = req.client_state
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
response = await _agent_query_inner(question, history, model, client_state, start_time)
|
| 46 |
response.answer = _sanitize_llm_response(response.answer)
|
| 47 |
-
|
|
|
|
| 48 |
_rcfg = _get_runtime_config()
|
| 49 |
_ann = _rcfg.get("announcement", "").strip()
|
| 50 |
if _ann and response.intent not in ("social_greeting", "social"):
|
|
|
|
| 41 |
history = req.history
|
| 42 |
model = req.model
|
| 43 |
client_state = req.client_state
|
| 44 |
+
# Canary requests (X-LibBee-Canary header, sent by the Worker's daily
|
| 45 |
+
# health probe) run the full pipeline but are EXCLUDED from analytics:
|
| 46 |
+
# synthetic traffic must never contaminate the D1 usage data that the
|
| 47 |
+
# research evaluation reports. Canary results live in the Worker's own
|
| 48 |
+
# canary_runs table instead.
|
| 49 |
+
is_canary = request.headers.get("x-libbee-canary") == "1"
|
| 50 |
+
if not is_canary:
|
| 51 |
+
_safe_metrics_increment("agent_requests")
|
| 52 |
response = await _agent_query_inner(question, history, model, client_state, start_time)
|
| 53 |
response.answer = _sanitize_llm_response(response.answer)
|
| 54 |
+
if not is_canary:
|
| 55 |
+
_log_agent_query(question, response, start_time)
|
| 56 |
_rcfg = _get_runtime_config()
|
| 57 |
_ann = _rcfg.get("announcement", "").strip()
|
| 58 |
if _ann and response.intent not in ("social_greeting", "social"):
|