Add reviewed task-file flow for Codex sleep runs
This commit is contained in:
committed by
carpedkm
parent
382811ddcc
commit
05cdc26beb
+61
-14
@@ -10,6 +10,7 @@ CI use. With backend="anthropic" it spends the user's budget for real lift.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional
|
||||
|
||||
@@ -49,6 +50,11 @@ def _read(path: str) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def _progress(cfg: SleepConfig, message: str) -> None:
|
||||
if cfg.get("progress", False):
|
||||
print(f"[sleep] {message}", file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
def _render_report_md(report: SleepReport, cfg: SleepConfig) -> str:
|
||||
lines = [
|
||||
f"# SkillOpt-Sleep — night {report.night} report",
|
||||
@@ -108,6 +114,26 @@ def run_sleep_cycle(
|
||||
cfg.get("backend", "mock"),
|
||||
model=cfg.get("model", ""),
|
||||
codex_path=cfg.get("codex_path", ""),
|
||||
project_dir=project,
|
||||
)
|
||||
_progress(cfg, f"night {night}: project={project} backend={backend.name}")
|
||||
|
||||
# ── live skill/memory docs ───────────────────────────────────────────
|
||||
live_memory_path = os.path.join(project, "CLAUDE.md")
|
||||
live_skill_path = cfg.managed_skill_path()
|
||||
_progress(cfg, f"live skill: {live_skill_path}")
|
||||
raw_skill = _read(live_skill_path)
|
||||
skill = raw_skill
|
||||
memory = _read(live_memory_path)
|
||||
if not skill:
|
||||
skill = ensure_skill_scaffold(
|
||||
"", name=cfg.get("managed_skill_name", "skillopt-sleep-learned"),
|
||||
description="Preferences and procedures learned from past local agent sessions.",
|
||||
)
|
||||
target_filter = bool(
|
||||
cfg.get("target_task_filter", True)
|
||||
and cfg.get("target_skill_path", "")
|
||||
and raw_skill
|
||||
)
|
||||
|
||||
# ── 1+2. harvest + mine (unless seed_tasks injected) ─────────────────
|
||||
@@ -115,14 +141,25 @@ def run_sleep_cycle(
|
||||
if seed_tasks is not None:
|
||||
tasks = seed_tasks
|
||||
n_sessions = 0
|
||||
_progress(cfg, f"using {len(tasks)} seeded tasks")
|
||||
else:
|
||||
since = state.last_harvest_for(project)
|
||||
max_tasks = cfg.get("max_tasks_per_night", 40)
|
||||
max_sessions = cfg.get("max_sessions_per_night", 0) or max_tasks * 3
|
||||
candidate_limit = max_tasks
|
||||
if target_filter:
|
||||
candidate_limit = max(max_tasks, max_tasks * 3)
|
||||
_progress(
|
||||
cfg,
|
||||
f"harvest start: source={cfg.get('transcript_source')} max_sessions={max_sessions}",
|
||||
)
|
||||
digests = harvest_for_config(
|
||||
cfg,
|
||||
since_iso=since,
|
||||
limit=cfg.get("max_tasks_per_night", 40) * 3,
|
||||
limit=max_sessions,
|
||||
)
|
||||
n_sessions = len(digests)
|
||||
_progress(cfg, f"harvest done: sessions={n_sessions}")
|
||||
# When a real backend is configured, use it to mine checkable tasks from
|
||||
# the transcripts (rubric/rule judges); otherwise fall back to the
|
||||
# heuristic miner (no API, no checkable reference).
|
||||
@@ -130,27 +167,29 @@ def run_sleep_cycle(
|
||||
if cfg.get("backend", "mock") != "mock" and cfg.get("llm_mine", True):
|
||||
try:
|
||||
from skillopt_sleep.llm_miner import make_llm_miner
|
||||
llm_miner = make_llm_miner(backend, max_tasks=cfg.get("max_tasks_per_night", 40))
|
||||
llm_miner = make_llm_miner(
|
||||
backend,
|
||||
max_sessions=max_sessions,
|
||||
max_tasks=candidate_limit,
|
||||
)
|
||||
except Exception:
|
||||
llm_miner = None
|
||||
_progress(
|
||||
cfg,
|
||||
f"mine start: max_tasks={max_tasks} candidate_limit={candidate_limit} "
|
||||
f"llm_mine={llm_miner is not None} target_filter={target_filter}",
|
||||
)
|
||||
tasks = mine(
|
||||
digests,
|
||||
max_tasks=cfg.get("max_tasks_per_night", 40),
|
||||
max_tasks=max_tasks,
|
||||
candidate_limit=candidate_limit,
|
||||
holdout_fraction=cfg.get("holdout_fraction", 0.34),
|
||||
seed=cfg.get("seed", 42),
|
||||
llm_miner=llm_miner,
|
||||
target_skill_text=raw_skill if target_filter else "",
|
||||
target_skill_path=live_skill_path if target_filter else "",
|
||||
)
|
||||
|
||||
# ── live skill/memory docs ───────────────────────────────────────────
|
||||
live_memory_path = os.path.join(project, "CLAUDE.md")
|
||||
live_skill_path = cfg.managed_skill_path()
|
||||
skill = _read(live_skill_path)
|
||||
memory = _read(live_memory_path)
|
||||
if not skill:
|
||||
skill = ensure_skill_scaffold(
|
||||
"", name=cfg.get("managed_skill_name", "skillopt-sleep-learned"),
|
||||
description="Preferences and procedures learned from past local agent sessions.",
|
||||
)
|
||||
_progress(cfg, f"mine done: tasks={len(tasks)}")
|
||||
|
||||
report = SleepReport(
|
||||
night=night, project=project, started_at=started,
|
||||
@@ -172,6 +211,7 @@ def run_sleep_cycle(
|
||||
# / dream_factor enrich the training signal. With the defaults (recall_k=0,
|
||||
# dream_rollouts=1, dream_factor=0) this is exactly the prior single-shot
|
||||
# consolidate — behavior is unchanged unless the user opts in.
|
||||
_progress(cfg, "consolidate start")
|
||||
recall_k = int(cfg.get("recall_k", 0) or 0)
|
||||
history_tasks = []
|
||||
if recall_k > 0:
|
||||
@@ -192,12 +232,18 @@ def run_sleep_cycle(
|
||||
)
|
||||
# archive tonight's real (non-dream) tasks so future nights can recall them
|
||||
state.add_to_archive([t.to_dict() for t in tasks if t.origin != "dream"])
|
||||
_progress(
|
||||
cfg,
|
||||
f"consolidate done: gate={result.gate_action} accepted={result.accepted} "
|
||||
f"edits={len(result.applied_edits)} rejected={len(result.rejected_edits)}",
|
||||
)
|
||||
|
||||
report.n_replayed = len(tasks)
|
||||
report.baseline_score = result.baseline_score
|
||||
report.candidate_score = result.candidate_score
|
||||
report.accepted = result.accepted
|
||||
report.gate_action = result.gate_action
|
||||
report.no_edits_reason = getattr(result, "no_edits_reason", "")
|
||||
report.edits = result.applied_edits
|
||||
report.rejected_edits = result.rejected_edits
|
||||
report.tokens_used = backend.tokens_used()
|
||||
@@ -208,6 +254,7 @@ def run_sleep_cycle(
|
||||
adopted = False
|
||||
adopted_paths: List[str] = []
|
||||
if not dry_run:
|
||||
_progress(cfg, "staging start")
|
||||
report_md = _render_report_md(report, cfg)
|
||||
proposed_skill = result.new_skill if (cfg.get("evolve_skill") and result.accepted) else None
|
||||
proposed_memory = result.new_memory if (cfg.get("evolve_memory") and result.accepted) else None
|
||||
|
||||
Reference in New Issue
Block a user