Add dark theme, richer Markdown and readable plugin results. Improve Android input evidence, response integrity and diagnostics. Unify lifecycle, guidance receipts and evidence sources across clients. Keep direct action dispatch without an added AI quota approval layer. Align version metadata at 2.2.0 and bundle Helper 1.2.2. Validation: 3063 backend tests before the version bump; 44 after. Frontend: 317 tests and production build passed.
114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
"""Read/export model diagnostics from the task's existing trace database."""
|
|
|
|
from datetime import UTC, datetime
|
|
import json
|
|
import time
|
|
|
|
from apps.admin_console.database.connection import db_session
|
|
|
|
|
|
def request_diagnostics(
|
|
session_id: str, *, db_path=None, limit: int | None = None, offset: int = 0
|
|
) -> dict:
|
|
with db_session(db_path) as conn:
|
|
session = conn.execute(
|
|
"SELECT status FROM sessions WHERE session_id = ?", (session_id,)
|
|
).fetchone()
|
|
sql = (
|
|
"SELECT payload FROM traces WHERE session_id = ? AND type = 'llm_diagnostic' "
|
|
"ORDER BY timestamp DESC"
|
|
)
|
|
params: tuple = (session_id,)
|
|
total = conn.execute(
|
|
"SELECT count(*) FROM traces WHERE session_id=? AND type='llm_diagnostic'",
|
|
params,
|
|
).fetchone()[0]
|
|
if limit is not None:
|
|
sql += " LIMIT ? OFFSET ?"
|
|
params += (limit, offset)
|
|
rows = conn.execute(sql, params).fetchall()
|
|
review_rows = conn.execute(
|
|
"SELECT payload FROM traces WHERE session_id = ? "
|
|
"AND type = 'final_review_diagnostic' ORDER BY timestamp DESC"
|
|
+ (" LIMIT ?" if limit is not None else ""),
|
|
(session_id, limit) if limit is not None else (session_id,),
|
|
).fetchall()
|
|
requests = []
|
|
seen = set()
|
|
running = bool(session and session["status"] in {"running", "resuming"})
|
|
for row in rows:
|
|
try:
|
|
value = json.loads(row["payload"])
|
|
except (ValueError, TypeError):
|
|
continue
|
|
if not isinstance(value, dict):
|
|
continue
|
|
request_id = value.get("request_id")
|
|
value["active"] = (
|
|
running
|
|
and offset == 0
|
|
and request_id not in seen
|
|
and value.get("phase")
|
|
in {
|
|
"waiting_response",
|
|
"waiting_output",
|
|
"receiving_output",
|
|
"generating_tool_arguments",
|
|
"retry_wait",
|
|
}
|
|
)
|
|
seen.add(request_id)
|
|
value["timestamps_utc"] = {
|
|
field: datetime.fromtimestamp(value[field], UTC).isoformat()
|
|
for field in (
|
|
"started_at",
|
|
"request_sent_at",
|
|
"headers_at",
|
|
"first_output_at",
|
|
"last_output_at",
|
|
"ended_at",
|
|
"retry_at",
|
|
)
|
|
if isinstance(value.get(field), (int, float))
|
|
}
|
|
requests.append(value)
|
|
final_reviews = []
|
|
for row in review_rows:
|
|
try:
|
|
value = json.loads(row["payload"])
|
|
except (ValueError, TypeError):
|
|
continue
|
|
if not isinstance(value, dict):
|
|
continue
|
|
value["active"] = (
|
|
running and offset == 0 and not final_reviews and value.get("status") == "running"
|
|
)
|
|
if value["active"] and isinstance(value.get("started_at"), (int, float)):
|
|
value["elapsed_seconds"] = round(max(0.0, time.time() - value["started_at"]), 3)
|
|
value["remaining_seconds"] = round(
|
|
max(0.0, value.get("total_seconds", 0.0) - value["elapsed_seconds"]), 3
|
|
)
|
|
value["budget_observed_at"] = time.time()
|
|
value["timestamps_utc"] = {
|
|
field: datetime.fromtimestamp(value[field], UTC).isoformat()
|
|
for field in ("started_at", "deadline_at", "ended_at")
|
|
if isinstance(value.get(field), (int, float))
|
|
}
|
|
final_reviews.append(value)
|
|
return {
|
|
"schema_version": 1,
|
|
"session_id": session_id,
|
|
"session_status": session["status"] if session else "unknown",
|
|
"collected_at": time.time(),
|
|
"requests": requests,
|
|
"final_reviews": final_reviews,
|
|
"total": total,
|
|
"offset": offset,
|
|
"next_offset": offset + limit if limit is not None and offset + limit < total else None,
|
|
"note": "时间戳为 Unix 秒;timestamps_utc 为 UTC。HTTP 200 不代表完整输出。usage=null 表示服务未报告用量。历史任务没有记录的字段无法补推。",
|
|
}
|
|
|
|
|
|
def diagnostics_json(session_id: str) -> str:
|
|
return json.dumps(request_diagnostics(session_id), ensure_ascii=False, indent=2)
|