Coverage for little_loops / workflow_sequence / io.py: 0%
41 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-04 12:21 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-04 12:21 -0500
1"""File I/O helpers for workflow sequence analysis."""
3from __future__ import annotations
5import json
6import sys
7from pathlib import Path
8from typing import Any
10import yaml
13def _load_messages(messages_file: Path) -> list[dict[str, Any]]:
14 """Load messages from JSONL file."""
15 messages = []
16 skipped = 0
17 with open(messages_file, encoding="utf-8") as f:
18 for line_num, raw_line in enumerate(f, 1):
19 line = raw_line.strip()
20 if not line:
21 continue
22 try:
23 messages.append(json.loads(line))
24 except json.JSONDecodeError as e:
25 skipped += 1
26 print(f"Warning: skipping malformed line {line_num}: {e}", file=sys.stderr)
27 if skipped:
28 print(f"Warning: skipped {skipped} malformed line(s) in {messages_file}", file=sys.stderr)
29 return messages
32def _load_messages_from_db(db_path: Path) -> list[dict[str, Any]]:
33 """Load user messages from the unified session DB (ENH-1621).
35 Reads ``message_events`` rows seeded by
36 :func:`little_loops.session_store._backfill_messages` and shapes them like
37 the JSONL records :func:`_load_messages` returns so the rest of
38 :func:`analyze_workflows` consumes them unchanged.
40 Returns ``[]`` when the DB is missing, the table is empty, or the schema
41 pre-dates the v2 migration — the caller treats an empty result as the
42 fallback trigger.
43 """
44 if not db_path.exists():
45 return []
46 try:
47 from little_loops.session_store import connect
49 conn = connect(db_path)
50 except Exception: # pragma: no cover - defensive
51 return []
52 try:
53 try:
54 rows = conn.execute(
55 "SELECT id, ts, session_id, content FROM message_events ORDER BY id"
56 ).fetchall()
57 except Exception:
58 return []
59 finally:
60 conn.close()
61 messages: list[dict[str, Any]] = []
62 for row in rows:
63 messages.append(
64 {
65 "uuid": f"msg-{row['id']}",
66 "timestamp": row["ts"],
67 "session_id": row["session_id"],
68 "content": row["content"] or "",
69 "git_branch": None,
70 }
71 )
72 return messages
75def _load_patterns(patterns_file: Path) -> dict[str, Any]:
76 """Load patterns from Step 1 YAML output."""
77 with open(patterns_file, encoding="utf-8") as f:
78 return yaml.safe_load(f) or {}