feat(agent): add odoo review mode with plausibility verdict

This commit is contained in:
2026-09-17 00:29:03 +02:00
parent 7162726b66
commit ba4bf57dcf
10 changed files with 670 additions and 34 deletions
+28 -4
View File
@@ -25,7 +25,8 @@ CREATE TABLE IF NOT EXISTS interactions (
model TEXT NOT NULL,
latency_ms INTEGER NOT NULL,
n_context INTEGER NOT NULL,
regenerations INTEGER NOT NULL
regenerations INTEGER NOT NULL,
context_json TEXT
);
CREATE TABLE IF NOT EXISTS ratings (
request_id TEXT PRIMARY KEY,
@@ -62,12 +63,25 @@ class AuditStore:
self._con.execute("PRAGMA foreign_keys = ON")
self._con.execute("PRAGMA journal_mode = WAL")
self._con.executescript(SCHEMA)
self._migrate()
self._delete_expired()
def close(self) -> None:
with self._lock:
self._con.close()
def _migrate(self) -> None:
"""Idempotente Spalten-Migration für bestehende audit.db-Dateien."""
cols = {
row[1]
for row in self._con.execute("PRAGMA table_info(interactions)").fetchall()
}
if "context_json" not in cols:
self._con.execute(
"ALTER TABLE interactions ADD COLUMN context_json TEXT"
)
self._con.commit()
def _delete_expired(self) -> None:
if self.cfg.audit_retention_days <= 0:
return
@@ -76,7 +90,7 @@ class AuditStore:
self._con.execute("DELETE FROM interactions WHERE created_at < ?", (cutoff,))
self._con.commit()
def record_interaction(self, payload: dict) -> None:
def record_interaction(self, payload: dict, context: dict | None = None) -> None:
now = int(time.time())
include_content = self.cfg.audit_log_content
question = payload.get("question") if include_content else None
@@ -84,6 +98,11 @@ class AuditStore:
sources = payload.get("sources", []) if include_content else []
conflicts = payload.get("conflicts", []) if include_content else []
planned_queries = payload.get("planned_queries", []) if include_content else []
context_json = (
json.dumps(context, ensure_ascii=False)
if include_content and context
else None
)
values = (
payload["request_id"],
now,
@@ -100,14 +119,15 @@ class AuditStore:
int(payload.get("latency_ms", 0)),
int(payload.get("n_context", 0)),
int(payload.get("regenerations", 0)),
context_json,
)
with self._lock:
self._con.execute(
"INSERT INTO interactions("
"request_id, created_at, question, answer, status, verified, refused, "
"citations_json, sources_json, conflicts_json, planned_queries_json, "
"model, latency_ms, n_context, regenerations"
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
"model, latency_ms, n_context, regenerations, context_json"
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
"ON CONFLICT(request_id) DO UPDATE SET "
"created_at=excluded.created_at, question=excluded.question, "
"answer=excluded.answer, status=excluded.status, "
@@ -123,6 +143,8 @@ class AuditStore:
if self.cfg.audit_stdout:
if include_content:
event = {"event": "agent_interaction", "created_at": now, **payload}
if context:
event["context"] = context
else:
event = {
"event": "agent_interaction",
@@ -237,6 +259,8 @@ class AuditStore:
item[field.removesuffix("_json")] = json.loads(item.pop(field))
item["verified"] = bool(item["verified"])
item["refused"] = bool(item["refused"])
raw_context = item.pop("context_json", None)
item["context"] = json.loads(raw_context) if raw_context else None
item["comments"] = comments_by_request[item["request_id"]]
out.append(item)
return out