SDK-Sovereign / server /training_data.py
ishansurdi's picture
Add teacher-first training pipeline and deterministic play runtime
e2327c6
Raw History Blame Contribute Delete
4.69 kB
"""Teacher-trace generation and SFT export helpers."""
from __future__ import annotations
import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable, Iterable
from models import SDKAction, SDKObservation
from server.environment import SDKSovereignEnvironment
from server.policy_runtime import load_teacher_agents
from server.prompts import SYSTEM_AUDITOR, SYSTEM_LEAD
PolicyMap = dict[str, Callable[[SDKObservation], SDKAction]]
@dataclass
class TraceStep:
"""Serializable single-turn trace record."""
episode_id: str
repo_id: str
role: str
observation: dict[str, Any]
action: dict[str, Any]
reward: float
done: bool
success: bool
policy_mode: str
def serialize_model(value: Any) -> dict[str, Any]:
"""Convert dataclass-style values into plain dictionaries."""
if hasattr(value, "model_dump"):
return value.model_dump()
if hasattr(value, "__dict__"):
return dict(value.__dict__)
raise TypeError(f"Cannot serialize object of type {type(value)!r}")
def run_episode(
env: SDKSovereignEnvironment,
policies: PolicyMap,
*,
policy_mode: str,
initial_observation: SDKObservation,
) -> list[TraceStep]:
"""Run one full episode and capture all transitions."""
observation = initial_observation
trace: list[TraceStep] = []
while True:
acting_observation = observation
action = policies[acting_observation.current_role](acting_observation)
observation = env.step(action)
success = bool(observation.done and env.state.test_results and all(env.state.test_results.values()))
trace.append(
TraceStep(
episode_id=env.state.episode_id,
repo_id=env.state.repo_id,
role=action.role,
observation=serialize_model(acting_observation),
action=serialize_model(action),
reward=observation.reward,
done=observation.done,
success=success,
policy_mode=policy_mode,
)
)
if observation.done:
return trace
def generate_teacher_traces(
*,
episodes_per_repo: int = 10,
repos_root: Path | None = None,
logs_root: Path | None = None,
seed: int = 0,
) -> list[TraceStep]:
"""Generate successful teacher traces across all repos."""
env = SDKSovereignEnvironment(repos_root=repos_root, logs_root=logs_root, seed=seed)
policies = load_teacher_agents()
traces: list[TraceStep] = []
for repo_id in sorted(env.repos.keys()):
for _ in range(episodes_per_repo):
observation = env.reset(repo_id=repo_id)
traces.extend(run_episode(env, policies, policy_mode="teacher", initial_observation=observation))
return traces
def write_jsonl(records: Iterable[dict[str, Any]], output_path: Path) -> None:
"""Write records as JSONL."""
output_path.parent.mkdir(parents=True, exist_ok=True)
with output_path.open("w", encoding="utf-8") as handle:
for record in records:
handle.write(json.dumps(record) + "\n")
def export_traces_jsonl(traces: Iterable[TraceStep], output_path: Path) -> None:
"""Persist trace records for later SFT / RL preprocessing."""
write_jsonl((trace.__dict__ for trace in traces), output_path)
def build_sft_messages(role: str, observation: dict[str, Any], action: dict[str, Any]) -> list[dict[str, str]]:
"""Convert one trace item into a chat-format SFT sample."""
system_prompt = SYSTEM_AUDITOR if role == "auditor" else SYSTEM_LEAD
user_payload = json.dumps(observation, ensure_ascii=True)
assistant_payload = json.dumps(action, ensure_ascii=True)
return [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_payload},
{"role": "assistant", "content": assistant_payload},
]
def export_sft_jsonl(
traces: Iterable[TraceStep],
output_path: Path,
*,
success_only: bool = True,
) -> None:
"""Export successful teacher traces as chat-format SFT rows."""
rows = []
for trace in traces:
if success_only and not trace.success:
continue
rows.append(
{
"messages": build_sft_messages(trace.role, trace.observation, trace.action),
"role": trace.role,
"repo_id": trace.repo_id,
"episode_id": trace.episode_id,
"reward": trace.reward,
"success": trace.success,
"policy_mode": trace.policy_mode,
}
)
write_jsonl(rows, output_path)