Merge nucleic/jolly-coral-egret-smoz into dev

This commit is contained in:
2026-08-04 16:15:55 -07:00
parent 1a41febf73
commit 931e180f1c
8 changed files with 1653 additions and 12 deletions
+34 -12
View File
@@ -18,7 +18,7 @@ import sys
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Sequence
from typing import Any, Callable, Sequence, TypeVar
import label_nucleic_prompts as base
from purpose_data import DataError, SOURCE_FIELDS, canonical_json, prompt_hash, validate_source_record
@@ -30,6 +30,7 @@ DEFAULT_OUTPUT = SCRIPT_DIR / ".artifacts" / "swe-chat" / "labeled-source.jsonl"
DEFAULT_MODEL = "gpt-5.6-luna"
DEFAULT_REASONING_EFFORT = "low"
STATE_SCHEMA_VERSION = 1
T = TypeVar("T")
DEFAULT_MAX_RESPONSE_CHARS = 48_000
FENCED_CODE_RE = re.compile(r"(?ms)^[ \t]*```[^\n]*\n.*?^[ \t]*```[ \t]*$")
TOOL_PAYLOAD_RE = re.compile(
@@ -57,7 +58,8 @@ class Candidate:
return base.Candidate(self.line, self.prompt, prompt_hash(self.prompt), self.session_id)
def _text(value: Any, field: str, location: str) -> str:
def required_text(value: Any, field: str, location: str) -> str:
"""A non-empty, NUL-free string field. Shared with the sibling prose labeler."""
if not isinstance(value, str) or not value.strip() or "\x00" in value:
raise DataError(f"{location}: invalid {field}")
return value
@@ -76,8 +78,8 @@ def candidates(lines: Sequence[base.SourceLine]) -> list[Candidate]:
location = f"candidate line {line.number}"
if value.get("schemaVersion") != 1:
raise DataError(f"{location}: unsupported candidate schema")
prompt = _text(value.get("prompt"), "prompt", location)
session_id = _text(value.get("sessionID"), "sessionID", location)
prompt = required_text(value.get("prompt"), "prompt", location)
session_id = required_text(value.get("sessionID"), "sessionID", location)
response = value.get("teacherResponse")
turn_ids = value.get("sourceTurnIDs")
response_hash = value.get("teacherResponseHash")
@@ -86,7 +88,7 @@ def candidates(lines: Sequence[base.SourceLine]) -> list[Candidate]:
first_hash = value.get("promptHash")
if first_hash != prompt_hash(prompt):
raise DataError(f"{location}: promptHash does not match prompt")
response_text = _text(response, "teacherResponse", location)
response_text = required_text(response, "teacherResponse", location)
if response_hash != prompt_hash(response_text):
raise DataError(f"{location}: teacherResponseHash does not match teacherResponse")
candidate = Candidate(
@@ -96,7 +98,7 @@ def candidates(lines: Sequence[base.SourceLine]) -> list[Candidate]:
session_id=session_id,
repo_id=value.get("repoID") if isinstance(value.get("repoID"), str) else None,
user_id=value.get("userID") if isinstance(value.get("userID"), str) else None,
turn_ids=tuple(_text(item, "sourceTurnID", location) for item in turn_ids), # type: ignore[arg-type]
turn_ids=tuple(required_text(item, "sourceTurnID", location) for item in turn_ids), # type: ignore[arg-type]
response_hash=response_hash,
)
digest = prompt_hash(prompt)
@@ -236,14 +238,25 @@ def validate_decisions(batch: Sequence[Candidate], response: Any) -> list[tuple[
return [(candidate, decisions[candidate.id]) for candidate in batch]
def invoke_codex(args: argparse.Namespace, batch: Sequence[Candidate]) -> list[tuple[Candidate, dict[str, Any]]]:
prompt = labeling_prompt(batch, args.max_prompt_chars, args.max_response_chars)
def run_codex(
args: argparse.Namespace,
*,
prompt: str,
schema: dict[str, Any],
validate: Callable[[Any], T],
) -> T:
"""Run one hardened, sandboxed Codex batch and return its validated decisions.
`validate` runs inside the retry loop on purpose: a response that parses but violates
the label contract is a failed attempt, not a fatal error. Sibling labelers share this
runner so the sandbox flags and retry semantics can only be changed in one place.
"""
last_error: Exception | None = None
for attempt in range(1, args.max_attempts + 1):
with tempfile.TemporaryDirectory(prefix="purpose-swe-label-") as directory:
root = Path(directory)
schema_path, response_path = root / "schema.json", root / "response.json"
schema_path.write_text(json.dumps(response_schema(batch), ensure_ascii=False), encoding="utf-8")
schema_path.write_text(json.dumps(schema, ensure_ascii=False), encoding="utf-8")
isolation = args.codex_isolation
if isolation == "auto":
isolation = "external" if os.environ.get("NUCLEIC_SESSION_ID") else "read-only"
@@ -258,13 +271,22 @@ def invoke_codex(args: argparse.Namespace, batch: Sequence[Candidate]) -> list[t
completed = subprocess.run(command, input=prompt, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=root, timeout=args.timeout_seconds, check=False)
if completed.returncode != 0:
raise DataError(f"Codex exited {completed.returncode}: {completed.stderr[-4000:].strip() or 'no stderr'}")
return validate_decisions(batch, json.loads(response_path.read_text(encoding="utf-8")))
return validate(json.loads(response_path.read_text(encoding="utf-8")))
except (DataError, OSError, subprocess.SubprocessError, json.JSONDecodeError) as error:
last_error = error
print(f"batch attempt {attempt}/{args.max_attempts} failed: {error}", file=sys.stderr, flush=True)
raise DataError(f"Codex batch failed after {args.max_attempts} attempts: {last_error}")
def invoke_codex(args: argparse.Namespace, batch: Sequence[Candidate]) -> list[tuple[Candidate, dict[str, Any]]]:
return run_codex(
args,
prompt=labeling_prompt(batch, args.max_prompt_chars, args.max_response_chars),
schema=response_schema(batch),
validate=lambda payload: validate_decisions(batch, payload),
)
def _state(candidate: Candidate, *, status: str, record: dict[str, Any] | None, reason: str | None, recoverable: bool | None) -> dict[str, Any]:
return {
"schemaVersion": STATE_SCHEMA_VERSION, "sourceLine": candidate.line.number,
@@ -296,7 +318,7 @@ def _load_state(path: Path, candidates_by_line: dict[int, Candidate]) -> dict[in
return result
def _append(path: Path, states: Sequence[dict[str, Any]]) -> None:
def append_jsonl(path: Path, states: Sequence[dict[str, Any]]) -> None:
if not states:
return
path.parent.mkdir(parents=True, exist_ok=True)
@@ -356,7 +378,7 @@ def run(args: argparse.Namespace) -> dict[str, int]:
newly.append(_state(candidate, status="labeled", record=record, reason=None, recoverable=decision["recoverableFromFirst"]))
else:
newly.append(_state(candidate, status="rejected", record=None, reason=f"semantic_junk:{decision['junkReason'].strip()}", recoverable=None))
_append(args.state, newly)
append_jsonl(args.state, newly)
states.update({state["sourceLine"]: state for state in newly})
print(f"labeled batch {number}/{len(batch_list)} ({len(batch)} sessions)", flush=True)
labeled = [state["record"] for _, state in sorted(states.items()) if state["status"] == "labeled"]