tenseleyflow/documentlanguagemodel / 74d85c2

Browse files

Align preference show totals

Authored by espadonne
SHA
74d85c261a4697e033ecc41a08d8acf17c856ec1
Parents
31d74c7
Tree
6aba767

4 changed files

StatusFile+-
M src/dlm/cli/commands.py 11 5
M src/dlm/metrics/queries.py 34 0
M tests/unit/cli/test_show.py 4 0
M tests/unit/metrics/test_queries.py 10 0
src/dlm/cli/commands.pymodified
@@ -2794,6 +2794,8 @@ def show_cmd(
27942794
             payload_full["gate"] = gate
27952795
         if preference_mining is not None:
27962796
             payload_full["preference_mining"] = preference_mining
2797
+            payload_full["preference_mining_runs"] = preference_mining["run_count"]
2798
+            payload_full["total_auto_mined_pairs"] = preference_mining["total_mined_pairs"]
27972799
         if base_security is not None:
27982800
             payload_full["base_security"] = base_security
27992801
         # Write JSON to raw stdout — Rich's Console wraps lines at the
@@ -3079,15 +3081,19 @@ def _summarize_preference_mining(store_root: Path) -> dict[str, object] | None:
30793081
     """Return the latest preference-mine summary for `dlm show --json`."""
30803082
     from dlm.metrics import queries as _queries
30813083
 
3082
-    last = _queries.latest_preference_mining(store_root)
3083
-    if last is None:
3084
+    totals = _queries.preference_mining_totals(store_root)
3085
+    if totals is None:
30843086
         return None
3087
+    last = _queries.latest_preference_mining(store_root)
3088
+    assert last is not None
30853089
     rows = _queries.preference_mining_for_run(store_root, last.run_id)
30863090
     return {
3091
+        "run_count": totals.run_count,
3092
+        "event_count": totals.event_count,
3093
+        "total_mined_pairs": totals.total_mined_pairs,
3094
+        "total_skipped_prompts": totals.total_skipped_prompts,
30873095
         "last_run_id": last.run_id,
3088
-        "event_count": len(rows),
3089
-        "total_mined_pairs": sum(row.mined_pairs for row in rows),
3090
-        "total_skipped_prompts": sum(row.skipped_prompts for row in rows),
3096
+        "last_run_event_count": len(rows),
30913097
         "last_event": _queries.preference_mining_to_dict([last])[0],
30923098
     }
30933099
 
src/dlm/metrics/queries.pymodified
@@ -84,6 +84,16 @@ class PreferenceMineRow:
8484
     at: str
8585
 
8686
 
87
+@dataclass(frozen=True)
88
+class PreferenceMineTotals:
89
+    """Aggregate counts across the whole `preference_mining` table."""
90
+
91
+    run_count: int
92
+    event_count: int
93
+    total_mined_pairs: int
94
+    total_skipped_prompts: int
95
+
96
+
8797
 def recent_runs(
8898
     store_root: Path,
8999
     *,
@@ -215,6 +225,30 @@ def latest_preference_mining(store_root: Path) -> PreferenceMineRow | None:
215225
     return PreferenceMineRow(*row)
216226
 
217227
 
228
+def preference_mining_totals(store_root: Path) -> PreferenceMineTotals | None:
229
+    """Aggregate counts across all preference-mine events.
230
+
231
+    Returns None when the table is absent or empty.
232
+    """
233
+    try:
234
+        with connect(store_root) as conn:
235
+            row = conn.execute(
236
+                "SELECT COUNT(DISTINCT run_id), COUNT(*), "
237
+                "COALESCE(SUM(mined_pairs), 0), COALESCE(SUM(skipped_prompts), 0) "
238
+                "FROM preference_mining"
239
+            ).fetchone()
240
+    except sqlite3.Error:
241
+        return None
242
+    if row is None or int(row[1]) == 0:
243
+        return None
244
+    return PreferenceMineTotals(
245
+        run_count=int(row[0]),
246
+        event_count=int(row[1]),
247
+        total_mined_pairs=int(row[2]),
248
+        total_skipped_prompts=int(row[3]),
249
+    )
250
+
251
+
218252
 @dataclass(frozen=True)
219253
 class GateEventRow:
220254
     """One row of the gate_events table (per-run per-adapter)."""
tests/unit/cli/test_show.pymodified
@@ -225,11 +225,15 @@ class TestInitializedStore:
225225
         payload = json.loads(result.output)
226226
         pref = payload["preference_mining"]
227227
         assert pref["last_run_id"] == 7
228
+        assert pref["run_count"] == 1
228229
         assert pref["event_count"] == 1
230
+        assert pref["last_run_event_count"] == 1
229231
         assert pref["total_mined_pairs"] == 2
230232
         assert pref["total_skipped_prompts"] == 1
231233
         assert pref["last_event"]["judge_name"] == "sway"
232234
         assert pref["last_event"]["write_mode"] == "applied"
235
+        assert payload["preference_mining_runs"] == 1
236
+        assert payload["total_auto_mined_pairs"] == 2
233237
 
234238
 
235239
 class TestTrainingSources:
tests/unit/metrics/test_queries.pymodified
@@ -13,6 +13,7 @@ from dlm.metrics.queries import (
1313
     latest_run_id,
1414
     preference_mining_for_run,
1515
     preference_mining_to_dict,
16
+    preference_mining_totals,
1617
     recent_runs,
1718
     runs_to_dict,
1819
     steps_for_run,
@@ -145,6 +146,15 @@ class TestPreferenceMiningQueries:
145146
             pass
146147
         assert latest_preference_mining(tmp_path) is None
147148
 
149
+    def test_preference_mining_totals_aggregate_across_events(self, tmp_path: Path) -> None:
150
+        _seed(tmp_path)
151
+        totals = preference_mining_totals(tmp_path)
152
+        assert totals is not None
153
+        assert totals.run_count == 1
154
+        assert totals.event_count == 2
155
+        assert totals.total_mined_pairs == 3
156
+        assert totals.total_skipped_prompts == 3
157
+
148158
 
149159
 class TestDictSerialization:
150160
     def test_runs_to_dict_shape(self, tmp_path: Path) -> None: