feat: release 2.4.0 with original cases and automatic human review
This commit is contained in:
1 parent
baf069854b
commit
4dc2057d43
66 files changed
+1390
-206
No files matched your search
@@ -15,6 +15,7 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import time
|
||||
from typing import Any
|
||||
@@ -235,6 +236,47 @@ class SessionRepository:
|
||||
rows = cursor.fetchall()
|
||||
return [r[0] for r in rows if r and r[0]]
|
||||
|
||||
def get_recorded_model_info_map(self, session_ids: list[str]) -> dict[str, dict[str, str]]:
|
||||
"""Read each session's own requested model from its earliest diagnostics.
|
||||
|
||||
Prefer operator/Flash requests over auxiliary roles. Older sessions may
|
||||
have no model diagnostics; do not replace their missing data with the
|
||||
current global configuration.
|
||||
"""
|
||||
result: dict[str, dict[str, str]] = {}
|
||||
safe_payload = "CASE WHEN json_valid(t.payload) THEN t.payload ELSE '{}' END"
|
||||
valid_model = f"json_type({safe_payload}, '$.model') = 'text'"
|
||||
role = f"lower(json_extract({safe_payload}, '$.role'))"
|
||||
with db_session(self.db_path) as conn:
|
||||
for offset in range(0, len(session_ids), 500):
|
||||
batch = session_ids[offset : offset + 500]
|
||||
placeholders = ",".join("?" for _ in batch)
|
||||
rows = conn.execute(
|
||||
"SELECT s.session_id, COALESCE("
|
||||
" (SELECT t.payload FROM traces t WHERE t.session_id = s.session_id"
|
||||
f" AND t.type = 'llm_diagnostic' AND {valid_model}"
|
||||
f" AND {role} IN ('operator', 'flashrunner')"
|
||||
" ORDER BY t.timestamp ASC LIMIT 1),"
|
||||
" (SELECT t.payload FROM traces t WHERE t.session_id = s.session_id"
|
||||
f" AND t.type = 'llm_diagnostic' AND {valid_model}"
|
||||
" ORDER BY t.timestamp ASC LIMIT 1)"
|
||||
") AS model_payload FROM sessions s"
|
||||
f" WHERE s.session_id IN ({placeholders})",
|
||||
batch,
|
||||
).fetchall()
|
||||
for row in rows:
|
||||
if not row[1]:
|
||||
continue
|
||||
payload = json.loads(row[1])
|
||||
model = payload.get("model")
|
||||
if isinstance(model, str) and model.strip():
|
||||
provider = payload.get("provider")
|
||||
result[str(row[0])] = {
|
||||
"id": model,
|
||||
"provider": provider if isinstance(provider, str) else "",
|
||||
}
|
||||
return result
|
||||
|
||||
def get_llm_traces_for_profiles_map(
|
||||
self, session_ids: list[str] | None = None, limit: int = 3
|
||||
) -> dict[str, list[str]]:
|
||||
@@ -349,7 +391,22 @@ class SessionRepository:
|
||||
total += t
|
||||
cached += _int(payload.get("cached_tokens"))
|
||||
node = str(payload.get("node") or "").strip().lower()
|
||||
model_name = str(payload.get("model") or "unknown")
|
||||
model_name = payload.get("model")
|
||||
if not model_name:
|
||||
# Older gateway traces carry the model in their own endpoint key.
|
||||
source = str(payload.get("source") or "")
|
||||
source_parts = source.split(":", 2)
|
||||
if len(source_parts) == 3 and source_parts[0] == "openai":
|
||||
# Model aliases can contain colons. The HTTP(S) base URL
|
||||
# marks the boundary; an ambiguous key stays unknown.
|
||||
match = re.match(r"^openai:(.+?):https?://", source)
|
||||
model_name = match[1] if match else None
|
||||
elif len(source_parts) == 3 and source_parts[:2] in (
|
||||
["lens", "visual_transition"],
|
||||
["lens", "step_capsule"],
|
||||
):
|
||||
model_name = source_parts[2] if source_parts[2] != "fallback" else None
|
||||
model_name = str(model_name or "unknown")
|
||||
group = by_node_model.setdefault(
|
||||
(node, model_name),
|
||||
{
|
||||
|
||||
@@ -220,6 +220,41 @@ class StepRepository:
|
||||
# recovering, waiting, completed, and terminal outcomes. Other tools
|
||||
# retain the historical compact payload to avoid sending large blobs.
|
||||
res = payload_obj.get("result")
|
||||
if trace_type == "tool" and trace_name == "android_file":
|
||||
# Preserve host-written file receipts, not file contents. A file's
|
||||
# checksum is not a screenshot-store image ID.
|
||||
if isinstance(res, str):
|
||||
try:
|
||||
res = json.loads(res)
|
||||
except ValueError:
|
||||
res = None
|
||||
if isinstance(res, dict) and "outcome" in res:
|
||||
res = res["outcome"]
|
||||
if isinstance(res, str):
|
||||
try:
|
||||
res = json.loads(res)
|
||||
except ValueError:
|
||||
res = None
|
||||
if isinstance(res, dict):
|
||||
result_payload["result"] = {
|
||||
key: self._clean_value(res[key])
|
||||
for key in (
|
||||
"ok",
|
||||
"action",
|
||||
"path",
|
||||
"trace_path",
|
||||
"device_path",
|
||||
"bytes",
|
||||
"sha256",
|
||||
"verified",
|
||||
"width",
|
||||
"height",
|
||||
"captured_at",
|
||||
"device_serial",
|
||||
)
|
||||
if key in res
|
||||
}
|
||||
return result_payload
|
||||
is_video_analysis = str(trace_name or "").lower() in {
|
||||
"video_analysis",
|
||||
"video_analyzer",
|
||||
|
||||
Reference in new issue
Block a user