Files
Vijay Janapa Reddi 0afc384282 feat(vault): LLM-as-judge validator + iterative coverage loop
Two new pieces close the generation→validation→saturation feedback loop:

1. gemini_cli_llm_judge.py — multi-criteria validator. For each draft,
   judges math correctness, cell-fit (does it actually target the
   declared track/zone/level?), scenario realism, uniqueness vs canonical
   questions, and visual-asset alignment. Returns PASS/NEEDS_FIX/DROP
   per item. Batched (default 15 per call) for budget efficiency.

2. iterate_coverage_loop.py — drives the full loop:
   analyze → plan → generate → render → judge → apply → re-analyze.
   Self-paced: stops when (a) top priority gap drops below threshold,
   (b) DROP rate exceeds the saturation/hallucination threshold,
   (c) total API calls exceed budget, or (d) the same cell is top
   priority for two iterations in a row (convergence). The user no
   longer specifies "how many questions" — the loop generates until
   the corpus reaches a measurable steady state.

Plus 25 round-1 visual questions generated by the new batched generator
(5 batched calls × 5 cells each, zero failures).

The loop is the answer to "we need balance, not just volume": every
iteration's plan derives from a fresh analysis of where coverage is
weakest, so generation can never over-fill an already-saturated cell.
2026-04-25 09:18:32 -04:00

19 lines
774 B
Python

import os
import matplotlib.pyplot as plt
import numpy as np
hours = np.arange(24)
traffic = 20 + 80 * np.exp(-((hours - 14)**2) / 4)
active_gpus = np.where((hours >= 13) & (hours <= 17), 100, 20)
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(hours, traffic, label='Traffic Demand', color='#c87b2a', linewidth=2)
ax.step(hours, active_gpus, where='post', label='Active GPUs', color='#3d9e5a', linewidth=2)
ax.fill_between(hours, traffic, active_gpus, step='post', color='#d4edda', alpha=0.5, label='Idle Buffer')
ax.set_xlabel('Hour of Day')
ax.set_ylabel('GPU Count')
ax.set_title('Duty Cycling Timeline')
ax.legend()
plt.grid(True, linestyle=':', alpha=0.6)
plt.tight_layout()
plt.savefig(os.environ.get('VISUAL_OUT_PATH', 'out.svg'), format='svg', bbox_inches='tight')