update
This commit is contained in:
@@ -213,7 +213,8 @@ CREATE TABLE IF NOT EXISTS events (
|
||||
status TEXT NOT NULL DEFAULT '',
|
||||
dur_ms INTEGER,
|
||||
wait_ms INTEGER,
|
||||
meta TEXT NOT NULL DEFAULT '{}'
|
||||
meta TEXT NOT NULL DEFAULT '{}',
|
||||
run_id TEXT NOT NULL DEFAULT ''
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -354,6 +355,10 @@ async def init_db():
|
||||
await db.execute("DROP TABLE IF EXISTS vertiefungen")
|
||||
await db.execute("DROP TABLE IF EXISTS block_texte")
|
||||
await db.execute("DROP TABLE IF EXISTS guide_progress")
|
||||
try: # migration: run_id per generation run (QA groups events by it)
|
||||
await db.execute("ALTER TABLE events ADD COLUMN run_id TEXT NOT NULL DEFAULT ''")
|
||||
except aiosqlite.OperationalError:
|
||||
pass
|
||||
await db.execute(
|
||||
"UPDATE guides SET status = 'error', progress = NULL, error_msg = 'Server restart' "
|
||||
"WHERE status IN ('queued', 'generating')"
|
||||
@@ -722,14 +727,27 @@ async def kanban_advance(topic: str, board: str, card_id: str, stage: str) -> No
|
||||
await kanban_advance_many(topic, board, [(card_id, stage)])
|
||||
|
||||
|
||||
# Current generation run per topic — every event writer stamps run_id from here, so no
|
||||
# signature threading through agents/kanban is needed. Set/cleared by the flow entries
|
||||
# (board_inventory.run_boards, guide_board.run_guide_board).
|
||||
_current_run: dict[str, str] = {}
|
||||
|
||||
|
||||
def set_current_run(topic: str, run_id: str | None) -> None:
|
||||
if run_id:
|
||||
_current_run[topic] = run_id
|
||||
else:
|
||||
_current_run.pop(topic, None)
|
||||
|
||||
|
||||
async def add_event(topic: str, kind: str, key: str = "", label: str = "", status: str = "",
|
||||
dur_ms: int | None = None, wait_ms: int | None = None, meta: dict | None = None) -> None:
|
||||
"""One pipeline-history row, own commit. Callers treat this as fire-and-forget."""
|
||||
db = await get_db()
|
||||
await db.execute(
|
||||
"INSERT INTO events (topic, ts, kind, key, label, status, dur_ms, wait_ms, meta) VALUES (?,?,?,?,?,?,?,?,?)",
|
||||
"INSERT INTO events (topic, ts, kind, key, label, status, dur_ms, wait_ms, meta, run_id) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||
(topic, _now(), kind, key, label, status, dur_ms, wait_ms,
|
||||
json.dumps(meta or {}, ensure_ascii=False)))
|
||||
json.dumps(meta or {}, ensure_ascii=False), _current_run.get(topic, "")))
|
||||
await db.commit()
|
||||
|
||||
|
||||
@@ -737,9 +755,10 @@ async def _add_events_many(db, topic: str, rows: list[tuple]) -> None:
|
||||
"""Batch insert WITHOUT commit — must run inside the caller's transaction
|
||||
(kanban_advance_many) so the event batch stays atomic with the moves."""
|
||||
now = _now()
|
||||
rid = _current_run.get(topic, "")
|
||||
await db.executemany(
|
||||
"INSERT INTO events (topic, ts, kind, key, label, status, dur_ms, wait_ms, meta) VALUES (?,?,?,?,?,?,?,?,?)",
|
||||
[(topic, now, kind, key, label, status, None, None, "{}")
|
||||
"INSERT INTO events (topic, ts, kind, key, label, status, dur_ms, wait_ms, meta, run_id) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||
[(topic, now, kind, key, label, status, None, None, "{}", rid)
|
||||
for kind, key, label, status in rows])
|
||||
|
||||
|
||||
@@ -828,13 +847,39 @@ async def kanban_fail_card(topic: str, board: str, card_id: str, error: str,
|
||||
(retries, _now_plus(backoff_base * (2 ** (retries - 1))), error[:500], _now(),
|
||||
topic, board, card_id))
|
||||
await db.execute(
|
||||
"INSERT INTO events (topic, ts, kind, key, label, status, dur_ms, wait_ms, meta) VALUES (?,?,?,?,?,?,?,?,?)",
|
||||
"INSERT INTO events (topic, ts, kind, key, label, status, dur_ms, wait_ms, meta, run_id) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||
(topic, _now(), "fail", f"{board}:{card_id}", "", "dead" if dead else f"retry{retries}",
|
||||
None, None, json.dumps({"error": error[:200]}, ensure_ascii=False)))
|
||||
None, None, json.dumps({"error": error[:200]}, ensure_ascii=False), _current_run.get(topic, "")))
|
||||
await db.commit()
|
||||
return dead
|
||||
|
||||
|
||||
async def events_run_summary(topic: str, run_id: str) -> dict:
|
||||
"""Agent/token aggregate of ONE run — the numbers block of lauf-summary.json."""
|
||||
db = await get_db()
|
||||
cursor = await db.execute(
|
||||
"""SELECT status, COUNT(*), SUM(dur_ms),
|
||||
SUM(json_extract(meta,'$.tokens.input')), SUM(json_extract(meta,'$.tokens.output')),
|
||||
SUM(json_extract(meta,'$.tokens.cache_read')), SUM(json_extract(meta,'$.tokens.cache_write'))
|
||||
FROM events WHERE topic = ? AND run_id = ? AND kind = 'agent' GROUP BY status""",
|
||||
(topic, run_id))
|
||||
agents = {"gesamt": 0, "ok": 0, "timeout": 0, "cancelled": 0, "sonstige": 0, "verlorene_min": 0}
|
||||
tokens = {"input": 0, "output": 0, "cache_read": 0, "cache_write": 0}
|
||||
for status, n, dur, ti, to, cr, cw in await cursor.fetchall():
|
||||
agents["gesamt"] += n
|
||||
if status in ("ok", "timeout", "cancelled"):
|
||||
agents[status] += n
|
||||
else:
|
||||
agents["sonstige"] += n
|
||||
if status != "ok":
|
||||
agents["verlorene_min"] += round((dur or 0) / 60000)
|
||||
tokens["input"] += ti or 0
|
||||
tokens["output"] += to or 0
|
||||
tokens["cache_read"] += cr or 0
|
||||
tokens["cache_write"] += cw or 0
|
||||
return {"agents": agents, "tokens": tokens}
|
||||
|
||||
|
||||
async def kanban_dead(topic: str) -> list[dict]:
|
||||
"""Dead-letter cards across boards (for the board UI + requeue)."""
|
||||
return await kanban_cards(topic, stage="dead")
|
||||
@@ -873,6 +918,14 @@ async def kanban_stage_cards(topic: str, board: str, stage: str, limit: int = 20
|
||||
return [_card(row, cursor) for row in await cursor.fetchall()]
|
||||
|
||||
|
||||
async def kanban_delete_card(topic: str, board: str, card_id: str) -> None:
|
||||
"""Delete ONE card (repair: the merged-away/removed block's board-2 card)."""
|
||||
db = await get_db()
|
||||
await db.execute("DELETE FROM kanban_cards WHERE topic = ? AND board = ? AND card_id = ?",
|
||||
(topic, board, card_id))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def kanban_delete_cards(topic: str, board: str, kind: str | None = None) -> None:
|
||||
"""Delete derived cards (board reset) — kind=None wipes the whole board."""
|
||||
db = await get_db()
|
||||
@@ -928,11 +981,16 @@ async def upsert_guide_card(topic: str, format: str, block_norm: str, block: str
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def list_guide_cards(topic: str, format: str) -> list[dict]:
|
||||
async def list_guide_cards(topic: str, format: str | None = None) -> list[dict]:
|
||||
"""format=None: alle Formate — das Guide-QA misst den Bestand topic-weit."""
|
||||
db = await get_db()
|
||||
cursor = await db.execute(
|
||||
"SELECT * FROM guide_cards WHERE topic = ? AND format = ? ORDER BY ord, block_norm",
|
||||
(topic, format))
|
||||
if format is None:
|
||||
cursor = await db.execute(
|
||||
"SELECT * FROM guide_cards WHERE topic = ? ORDER BY format, ord, block_norm", (topic,))
|
||||
else:
|
||||
cursor = await db.execute(
|
||||
"SELECT * FROM guide_cards WHERE topic = ? AND format = ? ORDER BY ord, block_norm",
|
||||
(topic, format))
|
||||
return [_row_to_dict(row, cursor) for row in await cursor.fetchall()]
|
||||
|
||||
|
||||
@@ -1384,6 +1442,22 @@ async def subs_per_level_norm(topic: str) -> dict[str, dict[int, int]]:
|
||||
return out
|
||||
|
||||
|
||||
async def delete_artefakt_row(topic: str, block_norm: str, sub_norm: str, type: str) -> None:
|
||||
"""Remove ONE artefact row (repair: dead target — sub discarded or gone)."""
|
||||
db = await get_db()
|
||||
await db.execute("DELETE FROM sub_artefakte WHERE topic = ? AND block_norm = ? AND sub_norm = ? AND type = ?",
|
||||
(topic, block_norm, sub_norm, type))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def delete_frage_row(topic: str, block_norm: str, sub_norm: str) -> None:
|
||||
"""Remove ONE question_pattern row (repair: dead target)."""
|
||||
db = await get_db()
|
||||
await db.execute("DELETE FROM question_pattern WHERE topic = ? AND block_norm = ? AND sub_norm = ?",
|
||||
(topic, block_norm, sub_norm))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def delete_sub_artefakte(topic: str, block_norm: str | None = None) -> None:
|
||||
db = await get_db()
|
||||
if block_norm is None:
|
||||
|
||||
Reference in New Issue
Block a user