agent-apprenticeship 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +261 -1
- package/bin/agent-apprenticeship.js +1 -2
- package/package.json +4 -4
- package/pyproject.toml +10 -4
- package/schemas/environment_replay.schema.json +41 -0
- package/schemas/eval_package.schema.json +37 -0
- package/schemas/experience_compilation.schema.json +53 -0
- package/schemas/runtime_training.schema.json +33 -0
- package/schemas/source_evidence_map.schema.json +25 -0
- package/schemas/trace.schema.json +904 -0
- package/schemas/trace_step.schema.json +622 -0
- package/schemas/training_rows.schema.json +29 -0
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/__init__.py +1 -1
- package/src/agent_apprenticeship/agent_env_exporter.py +53 -0
- package/src/agent_apprenticeship/agent_env_verify.py +32 -0
- package/src/agent_apprenticeship/atif.py +152 -0
- package/src/agent_apprenticeship/atif_models/LICENSE +201 -0
- package/src/agent_apprenticeship/atif_models/__init__.py +38 -0
- package/src/agent_apprenticeship/atif_models/agent.py +35 -0
- package/src/agent_apprenticeship/atif_models/content.py +159 -0
- package/src/agent_apprenticeship/atif_models/final_metrics.py +43 -0
- package/src/agent_apprenticeship/atif_models/metrics.py +47 -0
- package/src/agent_apprenticeship/atif_models/observation.py +19 -0
- package/src/agent_apprenticeship/atif_models/observation_result.py +45 -0
- package/src/agent_apprenticeship/atif_models/step.py +142 -0
- package/src/agent_apprenticeship/atif_models/subagent_trajectory_ref.py +88 -0
- package/src/agent_apprenticeship/atif_models/tool_call.py +34 -0
- package/src/agent_apprenticeship/atif_models/trajectory.py +196 -0
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/batch_runner.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/bundle_exporter.py +8 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/certification.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/cli.py +26 -7
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/codex_runner.py +17 -17
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/config.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/ecosystem_r2.py +3 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/evaluator.py +8 -27
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/grader.py +6 -5
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/integration_status.py +1 -1
- package/src/agent_apprenticeship/io.py +104 -0
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/learning.py +7 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/loop.py +5 -5
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/loop_review.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/mentor_checkpoints.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/package_exporter.py +5 -3
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/progress.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/public_run.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/public_sanitizer.py +2 -2
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/release_exporter.py +7 -4
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/rubric_generation.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/schemas.py +17 -4
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/session_events.py +4 -4
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/task_intake.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/tdo.py +5 -1
- package/src/agent_apprenticeship/trace_normalizer.py +103 -0
- package/src/agent_apprenticeship/trace_prompt.py +48 -0
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/training_signals.py +1 -1
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/validation.py +3 -3
- package/src/{agent_apprenticeship_trace → agent_apprenticeship}/verifier.py +6 -5
- package/docs/bioactive_evidence_mapping_schema.md +0 -113
- package/docs/dataset_schema.md +0 -160
- package/docs/local_usage.md +0 -191
- package/docs/scaling_to_1000_tasks.md +0 -3
- package/docs/trace_schema.md +0 -192
- package/docs/v0.2_release_notes.md +0 -63
- package/src/agent_apprenticeship_trace/io.py +0 -20
- package/src/agent_apprenticeship_trace/trace_normalizer.py +0 -1026
- package/src/agent_apprenticeship_trace/trace_prompt.py +0 -78
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/actual_outputs_normalizer.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/apprentice_adapters.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/artifact_capture.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/artifact_previews.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/artifact_resolver.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/command_discovery.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/contract_diagnostics.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/contract_verifier.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/env.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/lesson_extractor.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/llm_output_normalizer.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/openai_structured.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/recipes.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/revision.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/role_runners.py +0 -0
- /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/version.py +0 -0
|
@@ -16,8 +16,8 @@ from contextlib import contextmanager
|
|
|
16
16
|
from datetime import datetime, timezone
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
import typer
|
|
19
|
-
from .schemas import RawTaskRecord,
|
|
20
|
-
from .io import read_json, read_jsonl, write_json
|
|
19
|
+
from .schemas import RawTaskRecord, WorkflowTrace
|
|
20
|
+
from .io import read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
|
|
21
21
|
from .loop import run_task as run_one
|
|
22
22
|
from .batch_runner import run_batch as run_batch_impl
|
|
23
23
|
from .bundle_exporter import create_contribution_bundle
|
|
@@ -2357,6 +2357,8 @@ def _safe_bundle_path(path: Path) -> Path:
|
|
|
2357
2357
|
manifest = bundle / "contribution_manifest.json"
|
|
2358
2358
|
if not manifest.exists():
|
|
2359
2359
|
raise FileNotFoundError(f"Experience Compilation manifest not found: {manifest}")
|
|
2360
|
+
from .io import validate_trace_tree
|
|
2361
|
+
validate_trace_tree(bundle)
|
|
2360
2362
|
return bundle
|
|
2361
2363
|
|
|
2362
2364
|
|
|
@@ -3519,6 +3521,9 @@ def _locate_package_root(path: Path) -> Path:
|
|
|
3519
3521
|
|
|
3520
3522
|
def _copy_or_zip_package(src: Path, output: Path, *, item_id: str, source: str) -> Path:
|
|
3521
3523
|
src = _locate_package_root(src)
|
|
3524
|
+
from .agent_env_exporter import export_agent_env
|
|
3525
|
+
from .io import validate_trace_tree
|
|
3526
|
+
validate_trace_tree(src)
|
|
3522
3527
|
output = output.expanduser()
|
|
3523
3528
|
manifest = {
|
|
3524
3529
|
"export_type": "full_experience_compilation",
|
|
@@ -3547,6 +3552,7 @@ def _copy_or_zip_package(src: Path, output: Path, *, item_id: str, source: str)
|
|
|
3547
3552
|
shutil.rmtree(staging)
|
|
3548
3553
|
(staging / "package").mkdir(parents=True)
|
|
3549
3554
|
shutil.copytree(src, staging / "package", dirs_exist_ok=True)
|
|
3555
|
+
export_agent_env(staging / "package")
|
|
3550
3556
|
write_json(staging / "export_manifest.json", manifest)
|
|
3551
3557
|
_zip_dir(staging, output)
|
|
3552
3558
|
shutil.rmtree(staging)
|
|
@@ -3555,6 +3561,7 @@ def _copy_or_zip_package(src: Path, output: Path, *, item_id: str, source: str)
|
|
|
3555
3561
|
shutil.rmtree(output)
|
|
3556
3562
|
output.mkdir(parents=True)
|
|
3557
3563
|
shutil.copytree(src, output / "package", dirs_exist_ok=True)
|
|
3564
|
+
export_agent_env(output / "package")
|
|
3558
3565
|
write_json(output / "export_manifest.json", manifest)
|
|
3559
3566
|
return output
|
|
3560
3567
|
|
|
@@ -3602,6 +3609,8 @@ def ecosystem_pull(
|
|
|
3602
3609
|
typer.echo(f"Could not pull ecosystem item from R2: {redact_secrets(str(exc))}")
|
|
3603
3610
|
typer.echo("Fallback: apprentice ecosystem pull " + bundle_id + " --storage github")
|
|
3604
3611
|
raise typer.Exit(1)
|
|
3612
|
+
from .io import validate_trace_tree
|
|
3613
|
+
validate_trace_tree(dest)
|
|
3605
3614
|
typer.echo(f'pulled_bundle={dest}')
|
|
3606
3615
|
if full or mode == "full":
|
|
3607
3616
|
export_path = _copy_or_zip_package(dest, get_settings().app_home / "ecosystem" / "exports" / (_row_id(row) or bundle_id), item_id=_row_id(row) or bundle_id, source="r2_pull")
|
|
@@ -3660,6 +3669,8 @@ def ecosystem_pull(
|
|
|
3660
3669
|
typer.echo(f'Reason: {exc}')
|
|
3661
3670
|
typer.echo('Next action: set AA_HOME to a writable directory and try again.')
|
|
3662
3671
|
raise typer.Exit(1)
|
|
3672
|
+
from .io import validate_trace_tree
|
|
3673
|
+
validate_trace_tree(dest)
|
|
3663
3674
|
typer.echo(f'pulled_bundle={dest}')
|
|
3664
3675
|
return
|
|
3665
3676
|
source=Path(src).expanduser()
|
|
@@ -3677,6 +3688,8 @@ def ecosystem_pull(
|
|
|
3677
3688
|
typer.echo(f'Reason: {exc}')
|
|
3678
3689
|
typer.echo('Next action: set AA_HOME to a writable directory and try again.')
|
|
3679
3690
|
raise typer.Exit(1)
|
|
3691
|
+
from .io import validate_trace_tree
|
|
3692
|
+
validate_trace_tree(dest)
|
|
3680
3693
|
typer.echo(f'pulled_bundle={dest}')
|
|
3681
3694
|
if full or mode == "full":
|
|
3682
3695
|
export_path = _copy_or_zip_package(dest, get_settings().app_home / "ecosystem" / "exports" / bundle_id, item_id=bundle_id, source="local_pull")
|
|
@@ -3948,6 +3961,11 @@ def bundle_check(path: Path=typer.Argument(..., help='Experience Compilation fol
|
|
|
3948
3961
|
|
|
3949
3962
|
issues: list[str] = []
|
|
3950
3963
|
bundle = Path(path).expanduser()
|
|
3964
|
+
from .io import validate_trace_tree
|
|
3965
|
+
try:
|
|
3966
|
+
validate_trace_tree(bundle)
|
|
3967
|
+
except ValueError as exc:
|
|
3968
|
+
issues.append(str(exc))
|
|
3951
3969
|
manifest_candidates = [
|
|
3952
3970
|
bundle / "contribution_manifest.json",
|
|
3953
3971
|
bundle / "task_manifest.json",
|
|
@@ -4204,11 +4222,11 @@ def validate_public_release(release_root: Path=typer.Option(...)):
|
|
|
4204
4222
|
@app.command('repair-roles', hidden=True)
|
|
4205
4223
|
def repair_roles(run_root: Path=typer.Option(...), task_id: str=typer.Option(...), roles: str=typer.Option('evaluator_agent,grader_agent,verifier_agent'), attempts: str=typer.Option('baseline,revised'), rebuild_release: Path|None=typer.Option(None)):
|
|
4206
4224
|
"""Re-run model evaluation roles from existing task package artifacts/traces."""
|
|
4207
|
-
from .schemas import RubricSpec, ActualOutputs,
|
|
4225
|
+
from .schemas import RubricSpec, ActualOutputs, WorkflowTrace, GraderResult, VerifierResult
|
|
4208
4226
|
from .grader import grade_attempt, apply_score_reliability
|
|
4209
4227
|
from .verifier import verify_attempt
|
|
4210
4228
|
from .evaluator import evaluate_attempt
|
|
4211
|
-
from .io import read_json, write_json
|
|
4229
|
+
from .io import read_workflow_json as read_json, write_json
|
|
4212
4230
|
from datetime import datetime, timezone
|
|
4213
4231
|
import shutil
|
|
4214
4232
|
pkg=run_root/'packages'/task_id
|
|
@@ -4224,7 +4242,7 @@ def repair_roles(run_root: Path=typer.Option(...), task_id: str=typer.Option(...
|
|
|
4224
4242
|
typer.echo(f'skipping {attempt}: missing existing attempt outputs')
|
|
4225
4243
|
continue
|
|
4226
4244
|
actual=ActualOutputs.model_validate(read_json(actual_path))
|
|
4227
|
-
trace=
|
|
4245
|
+
trace=WorkflowTrace.model_validate(read_json(trace_path))
|
|
4228
4246
|
grade_path=pkg/'grading'/f'{attempt}_grader_result.json'
|
|
4229
4247
|
ver_path=pkg/'grading'/f'{attempt}_verifier_result.json'
|
|
4230
4248
|
grader=GraderResult.model_validate(read_json(grade_path)) if grade_path.exists() else None
|
|
@@ -4296,9 +4314,10 @@ def summarize_releases(release_root: Path=typer.Option(Path('outputs/releases'))
|
|
|
4296
4314
|
typer.echo('task_ids_publishable_with_warnings=' + ','.join(warnings))
|
|
4297
4315
|
typer.echo('task_ids_clean_publishable=' + ','.join(clean))
|
|
4298
4316
|
|
|
4299
|
-
@app.command('inspect-trace'
|
|
4317
|
+
@app.command('inspect-trace')
|
|
4300
4318
|
def inspect_trace(trace_path: Path):
|
|
4301
|
-
|
|
4319
|
+
from .atif import load_trace
|
|
4320
|
+
t=WorkflowTrace.model_validate(load_trace(trace_path)); typer.echo(f'trace_id={t.trace_id}\ntask_id={t.task_id}\nsteps={len(t.steps)}')
|
|
4302
4321
|
|
|
4303
4322
|
@app.command('codex-smoke', hidden=True)
|
|
4304
4323
|
def codex_smoke():
|
|
@@ -4,7 +4,7 @@ import signal
|
|
|
4
4
|
import shutil, subprocess
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import re
|
|
7
|
-
from .schemas import
|
|
7
|
+
from .schemas import WorkflowTrace, WorkflowTraceStep, ActualOutputs, RawTaskRecord, TaskIntakeSpec
|
|
8
8
|
from .trace_prompt import build_worker_prompt
|
|
9
9
|
from .io import write_json
|
|
10
10
|
from .config import get_settings
|
|
@@ -35,14 +35,14 @@ def _safe_agent_config(settings, runner_kind: str, attempt_kind: str) -> dict:
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def _enrich_runtime_trace(
|
|
38
|
-
trace:
|
|
38
|
+
trace: WorkflowTrace,
|
|
39
39
|
actual: ActualOutputs,
|
|
40
40
|
package_root: Path,
|
|
41
41
|
attempt_kind: str,
|
|
42
42
|
runner_kind: str,
|
|
43
43
|
started_at: str | None,
|
|
44
44
|
ended_at: str | None,
|
|
45
|
-
) ->
|
|
45
|
+
) -> WorkflowTrace:
|
|
46
46
|
settings = get_settings()
|
|
47
47
|
trace.run_id = package_root.parent.parent.name if package_root.parent.parent.name else None
|
|
48
48
|
trace.package_id = package_root.name
|
|
@@ -243,24 +243,24 @@ def codex_exec_help(command: str = "codex") -> str:
|
|
|
243
243
|
def _codex_exec_supports(flag: str, command: str = "codex") -> bool:
|
|
244
244
|
return flag in codex_exec_help(command)
|
|
245
245
|
|
|
246
|
-
def _fallback_trace(spec: TaskIntakeSpec, attempt_kind: str, prompt: str, actual: ActualOutputs, error_type: str, error_message: str, codex_sandbox: str) ->
|
|
246
|
+
def _fallback_trace(spec: TaskIntakeSpec, attempt_kind: str, prompt: str, actual: ActualOutputs, error_type: str, error_message: str, codex_sandbox: str) -> WorkflowTrace:
|
|
247
247
|
cls=classify_provider_failure(error_message)
|
|
248
248
|
attempt_id=f'{spec.task_id}_{attempt_kind}'
|
|
249
249
|
actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}'
|
|
250
250
|
safe=(public_error_summary(error_message) if cls else redact_secrets(error_message)[-3000:])
|
|
251
251
|
error_type=cls.get('error_type') or error_type
|
|
252
252
|
steps=[
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
WorkflowTraceStep(step=1, turn=1, actor='user', action='user_message', input=spec.normalized_instruction, message_role='direct_request'),
|
|
254
|
+
WorkflowTraceStep(step=2, turn=1, actor=actor, action='error', operation='other', tool='codex_cli', environment_domain='terminal', execution_mode='serial', observation='Codex attempt ended without a valid agent_trace.json or actual_outputs.json.', input='Validate required output contract for ./agent_trace.json and ./actual_outputs.json.', input_source={'source':'runtime_contract_check'}, output=safe, state_change='A minimal failure trace and failed actual_outputs.json were written by the runner.', reasoning='The package must preserve failure evidence without fabricating a successful detailed trace.', caused_by=[1], causal_type='dependency_on_tool_result', success=False, step_outcome='failed', error_type=error_type, error_message=safe, artifact_refs=[f'attempts/{attempt_kind}/prompt.md', f'attempts/{attempt_kind}/stdout.txt', f'attempts/{attempt_kind}/stderr.txt', f'attempts/{attempt_kind}/final_message.txt']),
|
|
255
255
|
]
|
|
256
|
-
return
|
|
256
|
+
return WorkflowTrace(trace_id=f'trace_{attempt_id}_fallback', collection_id=None, trace_mode='live', task=spec.normalized_instruction, task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind if attempt_kind in ['baseline','revised'] else 'other', agent_tools=['codex_cli','Bash','python','file_read','file_write'], system_prompt=prompt, system_prompt_hash=None, skills=['agent_trace_skill'], learning='When a live agent cannot write outputs, preserve logs and create a minimal failure trace.', termination_reason='agent_blocked', steps=steps, actual_outputs=actual, artifacts=[], metadata_json={'trace_valid':False,'fallback_trace_created':True,'codex_sandbox':codex_sandbox, **cls})
|
|
257
257
|
|
|
258
258
|
def _write_fallback_report(d: Path, spec: TaskIntakeSpec, attempt_kind: str, reason: str, raw_parse_error: bool=False) -> None:
|
|
259
259
|
report=TraceNormalizationReport(task_id=spec.task_id, attempt_id=f'{spec.task_id}_{attempt_kind}', attempt_kind=attempt_kind, raw_trace_ref='agent_trace.raw.json' if (d/'agent_trace.raw.json').exists() else None, normalized_trace_ref=None, canonical_trace_ref='agent_trace.json', trace_schema_valid=True, trace_normalized=False, trace_lossless=True, fallback_trace=True, raw_step_count=0, normalized_step_count=2, discarded_step_count=0, raw_trace_parse_error=raw_parse_error, validation_errors=[reason], metadata_json={'fallback_reason': reason})
|
|
260
260
|
write_json(d/'trace_normalization_report.json', report)
|
|
261
261
|
|
|
262
262
|
|
|
263
|
-
def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kind: str, prompt: str, codex_sandbox: str, validation_error: Exception | None=None) -> tuple[
|
|
263
|
+
def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kind: str, prompt: str, codex_sandbox: str, validation_error: Exception | None=None) -> tuple[WorkflowTrace, ActualOutputs, bool]:
|
|
264
264
|
d=_attempt_dir(package_root, attempt_kind)
|
|
265
265
|
recovered_contract_files = _recover_misplaced_contract_files(d)
|
|
266
266
|
actual_path=d/'actual_outputs.json'; trace_path=d/'agent_trace.json'; raw_path=d/'agent_trace.raw.json'
|
|
@@ -279,14 +279,10 @@ def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kin
|
|
|
279
279
|
context=TraceNormalizationContext(task_id=spec.task_id, attempt_id=f'{spec.task_id}_{attempt_kind}', attempt_kind=attempt_kind, task=spec.normalized_instruction, actual_outputs=actual)
|
|
280
280
|
result=repair_agent_trace_file(raw_path, context)
|
|
281
281
|
if result.normalized_trace and (result.report.raw_step_count or result.normalized_trace.get('steps')):
|
|
282
|
-
try:
|
|
283
|
-
AgentTrace.model_validate_json(raw_path.read_text())
|
|
284
|
-
except Exception:
|
|
285
|
-
(d/'agent_trace.invalid.json').write_text(raw_path.read_text())
|
|
286
282
|
write_json(d/'agent_trace.normalized.json', result.normalized_trace)
|
|
287
283
|
write_json(d/'agent_trace.json', result.normalized_trace)
|
|
288
284
|
write_json(d/'trace_normalization_report.json', result.report)
|
|
289
|
-
trace =
|
|
285
|
+
trace = WorkflowTrace.model_validate(result.normalized_trace)
|
|
290
286
|
if recovered_contract_files:
|
|
291
287
|
trace.metadata_json["contract_files_recovered_from"] = recovered_contract_files
|
|
292
288
|
actual.metadata_json.setdefault("contract_files_recovered_from", recovered_contract_files)
|
|
@@ -297,6 +293,10 @@ def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kin
|
|
|
297
293
|
msg=f'{reason}\n\nfinal_message:\n{final}\n\nstderr:\n{stderr}'
|
|
298
294
|
trace=_fallback_trace(spec, attempt_kind, prompt, actual, 'TraceNormalizationError', msg, codex_sandbox)
|
|
299
295
|
trace.metadata_json['fallback_reason']=classify_provider_failure(msg).get('fallback_reason') or ('unparseable_trace' if result.fallback_required else 'missing_steps')
|
|
296
|
+
if raw_path.exists():
|
|
297
|
+
raw_path.replace(d/'rejected_agent_response.txt')
|
|
298
|
+
write_json(d/'agent_trace.raw.json', trace)
|
|
299
|
+
write_json(d/'agent_trace.normalized.json', trace)
|
|
300
300
|
write_json(trace_path, trace); _write_fallback_report(d, spec, attempt_kind, trace.metadata_json['fallback_reason'], result.fallback_required)
|
|
301
301
|
return trace, actual, False
|
|
302
302
|
final=(d/'final_message.txt').read_text(errors='ignore') if (d/'final_message.txt').exists() else ''
|
|
@@ -326,12 +326,12 @@ def deterministic_attempt(package_root: Path, raw: RawTaskRecord, spec: TaskInta
|
|
|
326
326
|
files.append(f'attempts/{attempt_kind}/artifacts/{fname}')
|
|
327
327
|
actual=ActualOutputs(task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind, status='success', output_summary=f'Deterministic {attempt_kind} outputs for {spec.normalized_title}', primary_output_ref=files[0] if files else None, input_artifact_refs=[f'input/{name}' for name in input_files], deliverable_refs=files, final_message_ref=f'attempts/{attempt_kind}/final_message.txt', artifact_refs=files, files_created=files, files_modified=[], files_deleted=[], stdout_ref=f'attempts/{attempt_kind}/stdout.txt', stderr_ref=f'attempts/{attempt_kind}/stderr.txt', raw_log_refs=[f'attempts/{attempt_kind}/stdout.txt', f'attempts/{attempt_kind}/stderr.txt'], error_type=None, error_message=None, metadata_json={'runner':'deterministic','expected_deliverable_items':deliverables,'produced_deliverable_items':[Path(f).name for f in files]})
|
|
328
328
|
steps=[
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
329
|
+
WorkflowTraceStep(step=1, turn=1, actor='user', action='user_message', input=spec.normalized_instruction, message_role='direct_request'),
|
|
330
|
+
WorkflowTraceStep(step=2, turn=1, actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}', action='agent_step', operation='plan', tool=None, environment_domain='file', execution_mode='serial', observation='Task instructions, ./input files, and worker-visible rubric are available.', input='Review task and required deliverables.', input_source={'source':'task_packet'}, output='Plan created.', state_change='The attempt plan identified required artifacts.', reasoning='A short plan reduces missed deliverables.', caused_by=[1], causal_type='user_request', success=True, step_outcome='progress', artifact_refs=[f'input/{name}' for name in input_files]),
|
|
331
|
+
WorkflowTraceStep(step=3, turn=1, actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}', action='agent_step', operation='write', tool='file_write', environment_domain='file', execution_mode='serial', observation='Deliverable names are known.', input=', '.join(files), input_source={'source':'planned_deliverables'}, output='Artifacts written.', state_change='Required deterministic artifacts were created under ./artifacts.', reasoning='Writing explicit files satisfies the artifact contract.', caused_by=[2], causal_type='execution_of_plan', success=True, step_outcome='completed', artifact_refs=files),
|
|
332
332
|
]
|
|
333
333
|
ended_at = utc_now()
|
|
334
|
-
trace=
|
|
334
|
+
trace=WorkflowTrace(trace_id=f'trace_{attempt_id}', collection_id=None, trace_mode='live', task=spec.normalized_instruction, task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind if attempt_kind in ['baseline','revised'] else 'other', agent_tools=['deterministic_runner','file_write'], system_prompt=prompt, system_prompt_hash=None, skills=['agent_trace_skill'], learning='Materialize required artifacts and keep evaluation outside the trace.', termination_reason='task_complete', steps=steps, actual_outputs=actual, artifacts=[], metadata_json={'trace_valid': True})
|
|
335
335
|
trace = _enrich_runtime_trace(trace, actual, package_root, attempt_kind, 'deterministic', started_at, ended_at)
|
|
336
336
|
write_json(d/'actual_outputs.json', actual)
|
|
337
337
|
actual_ctx=ActualOutputsNormalizationContext(task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind, package_root=package_root, required_artifacts=deliverables)
|
|
@@ -12,7 +12,7 @@ from pydantic import BaseModel, Field
|
|
|
12
12
|
|
|
13
13
|
from .env import load_local_env
|
|
14
14
|
from .command_discovery import resolve_command, resolve_agent_command, gui_app_hint
|
|
15
|
-
from .io import read_json, write_json
|
|
15
|
+
from .io import read_workflow_json as read_json, write_json
|
|
16
16
|
from .recipes import MODEL_PROVIDER_RECIPES, WORKER_AGENT_RECIPES
|
|
17
17
|
|
|
18
18
|
|
|
@@ -22,7 +22,7 @@ from .config import (
|
|
|
22
22
|
get_settings,
|
|
23
23
|
)
|
|
24
24
|
from .env import redact_secrets
|
|
25
|
-
from .io import append_jsonl, read_json, read_jsonl, write_json
|
|
25
|
+
from .io import append_jsonl, read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
|
|
26
26
|
from .public_sanitizer import sanitize_public_obj
|
|
27
27
|
from .version import get_package_version
|
|
28
28
|
|
|
@@ -485,6 +485,8 @@ def download_r2_package(item_id: str, dest: Path, settings: Settings | None = No
|
|
|
485
485
|
if not target.resolve().is_relative_to(dest.resolve()):
|
|
486
486
|
raise RuntimeError("R2 package contains an unsafe path.")
|
|
487
487
|
zf.extractall(dest)
|
|
488
|
+
from .io import validate_trace_tree
|
|
489
|
+
validate_trace_tree(dest)
|
|
488
490
|
return dest
|
|
489
491
|
|
|
490
492
|
|
|
@@ -2,9 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
import json
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from pydantic import BaseModel
|
|
5
|
-
from .schemas import EvaluatorFeedback, GraderResult, VerifierResult,
|
|
5
|
+
from .schemas import EvaluatorFeedback, GraderResult, VerifierResult, WorkflowTrace, ActualOutputs, RevisionPlan
|
|
6
6
|
from .config import get_settings
|
|
7
|
-
from .io import read_json
|
|
7
|
+
from .io import read_workflow_json as read_json
|
|
8
8
|
from .openai_structured import get_model_provider_status, run_structured_role
|
|
9
9
|
from .public_sanitizer import sha256_text
|
|
10
10
|
from .artifact_previews import build_artifact_previews
|
|
@@ -60,30 +60,11 @@ def _latest_review_packet(package_root: Path | None, attempt_kind: str) -> dict:
|
|
|
60
60
|
return packet
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def _compact_trace_summary(trace:
|
|
63
|
+
def _compact_trace_summary(trace: WorkflowTrace | None) -> dict:
|
|
64
64
|
if trace is None:
|
|
65
65
|
return {}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
"trace_id": data.get("trace_id"),
|
|
70
|
-
"attempt_id": data.get("attempt_id"),
|
|
71
|
-
"attempt_kind": data.get("attempt_kind"),
|
|
72
|
-
"step_count": len(steps),
|
|
73
|
-
"key_steps": [
|
|
74
|
-
{
|
|
75
|
-
"step": step.get("step"),
|
|
76
|
-
"operation": step.get("operation"),
|
|
77
|
-
"interaction_surface": step.get("interaction_surface"),
|
|
78
|
-
"environment_domain": step.get("environment_domain"),
|
|
79
|
-
"step_outcome": step.get("step_outcome"),
|
|
80
|
-
"artifact_refs": (step.get("artifact_refs") or [])[:5],
|
|
81
|
-
"state_change": str(step.get("state_change") or "")[:240],
|
|
82
|
-
}
|
|
83
|
-
for step in steps[:10]
|
|
84
|
-
if isinstance(step, dict) and step.get("action") != "user_message"
|
|
85
|
-
],
|
|
86
|
-
}
|
|
66
|
+
from .atif import from_workflow
|
|
67
|
+
return from_workflow(trace.model_dump())
|
|
87
68
|
|
|
88
69
|
|
|
89
70
|
def _compact_grader(grader: GraderResult) -> dict:
|
|
@@ -142,13 +123,13 @@ def _compact_artifact_previews(bundle: dict | None) -> dict:
|
|
|
142
123
|
}
|
|
143
124
|
|
|
144
125
|
|
|
145
|
-
def _evaluator_prompt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace:
|
|
126
|
+
def _evaluator_prompt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace: WorkflowTrace | None, target_attempt_id: str, artifact_preview_bundle: dict | None=None, review_packet: dict | None=None) -> str:
|
|
146
127
|
return """Return only valid JSON. Do not include markdown. Do not add extra top-level fields; place extras under metadata_json.extra_model_fields.
|
|
147
128
|
Required skeleton: {"evaluator_feedback":{"feedback_id":"feedback_<task_id>_<attempt_kind>","task_id":"...","attempt_id":"...","target_actor":"apprentice","feedback_type":"other","failed_rubric_items":[],"evidence_refs":[],"artifact_refs":[],"feedback_summary":"...","actionable_feedback":[],"suggested_revision":"...","revision_priority":"medium","confidence":0.7,"hidden_reference_used":false,"hidden_reference_leaked":false,"failed_or_weak_rubric_items":[],"artifact_specific_comments":[],"trace_specific_comments":[],"metadata_json":{}},"revision_plan":{"revision_plan_id":"revision_plan_<task_id>","task_id":"...","source_attempt_id":"...","target_attempt_id":"...","revision_kind":"local_fix","revision_reason":"...","failed_rubric_items":[],"planned_changes":[],"risk_of_regression":"medium","uses_evaluator_feedback":true,"metadata_json":{}}}.
|
|
148
129
|
Evaluate the attempt by first reading the loop review packet when provided, then checking compact Mentor grader/verifier outputs, actual_outputs.json, artifact refs, artifact content previews, and trace evidence. Treat the review packet as the current iteration dossier: it summarizes outputs, artifacts, trace refs, rubric/verifier status, known failures, diff evidence, and recommended review focus. Produce grounded actionable feedback and a concrete revision plan whose revision guidance can be mapped to feedback_type/feedback_content in future traces. Keep the response concise. Do not rely on Apprentice Agent self-assessment or self evaluation as outcome evidence. Do not use deterministic fallback judgement; structural guardrails are integrity checks only and are not outcome judgments.
|
|
149
|
-
Target revised attempt id: """ + target_attempt_id + "\nLoop Review Packet JSON:\n" + json.dumps(review_packet or {}, sort_keys=True)[:5000] + "\nCompact GraderResult JSON:\n" + json.dumps(_compact_grader(grader), sort_keys=True) + "\nCompact VerifierResult JSON:\n" + json.dumps(_compact_verifier(verifier), sort_keys=True) + "\nCompact ActualOutputs JSON:\n" + json.dumps(_compact_actual(outputs), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(_compact_artifact_previews(artifact_preview_bundle), sort_keys=True)[:5000] + "\nCompact Trace summary JSON:\n" + json.dumps(_compact_trace_summary(trace), sort_keys=True)
|
|
130
|
+
Target revised attempt id: """ + target_attempt_id + "\nLoop Review Packet JSON:\n" + json.dumps(review_packet or {}, sort_keys=True)[:5000] + "\nCompact GraderResult JSON:\n" + json.dumps(_compact_grader(grader), sort_keys=True) + "\nCompact VerifierResult JSON:\n" + json.dumps(_compact_verifier(verifier), sort_keys=True) + "\nCompact ActualOutputs JSON:\n" + json.dumps(_compact_actual(outputs), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(_compact_artifact_previews(artifact_preview_bundle), sort_keys=True)[:5000] + "\nCompact Trace summary JSON:\n" + json.dumps(_compact_trace_summary(trace), sort_keys=True)
|
|
150
131
|
|
|
151
|
-
def evaluate_attempt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace:
|
|
132
|
+
def evaluate_attempt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace: WorkflowTrace | None, target_attempt_id: str, role_root: Path | None=None, package_root: Path | None=None) -> tuple[EvaluatorFeedback, RevisionPlan]:
|
|
152
133
|
settings=get_settings(); role_root=role_root or Path('outputs/roles')
|
|
153
134
|
artifact_preview_bundle=build_artifact_previews(package_root, grader.evidence_refs or outputs.deliverable_refs or outputs.artifact_refs or outputs.files_created)
|
|
154
135
|
review_packet=_latest_review_packet(package_root, outputs.attempt_kind)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from .atif import from_workflow
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
import json, re
|
|
4
5
|
from .schemas import GraderResult, RubricSpec, ActualOutputs, RubricItemScore
|
|
5
6
|
from .config import get_settings
|
|
6
|
-
from .schemas import
|
|
7
|
-
from .io import read_json, write_json
|
|
7
|
+
from .schemas import WorkflowTrace
|
|
8
|
+
from .io import read_workflow_json as read_json, write_json
|
|
8
9
|
from .openai_structured import get_model_provider_status, run_structured_role
|
|
9
10
|
from .public_sanitizer import sha256_text
|
|
10
11
|
from .artifact_previews import build_artifact_previews
|
|
@@ -128,13 +129,13 @@ def deterministic_grade(rubric: RubricSpec, outputs: ActualOutputs, attempt_kind
|
|
|
128
129
|
score=artifact_contract_score
|
|
129
130
|
return GraderResult(grader_result_id=f'grader_{outputs.attempt_id}', task_id=outputs.task_id, attempt_id=outputs.attempt_id, attempt_kind=attempt_kind, rubric_id=rubric.rubric_id, grader_kind='structural_guardrail', score_source='structural_guardrail', score=score, max_score=1.0, passed=artifact_contract_passed, rubric_item_scores=item_scores, failed_criteria=[s.rubric_item_id for s in item_scores if not s.passed], passed_criteria=[s.rubric_item_id for s in item_scores if s.passed], evidence_refs=evidence, confidence=0.8, reasoning_summary='Structural guardrail checked artifact presence/readiness only; Mentor grading is unavailable unless a Mentor Model or human grader runs.', limitations=['semantic_quality_not_judged','mentor_grade_unavailable'], hidden_reference_used=False, hidden_reference_leaked=False, artifact_contract_score=artifact_contract_score, semantic_score=None, final_score=score, model=None, provider=None, deterministic_precheck_ref=None, public_response_summary='Structural artifact-contract guardrail completed.', score_reliability='needs_review', metadata_json={'structural_guardrail': True, 'role_status': 'mentor_grader_unavailable', 'artifact_contract_passed': artifact_contract_passed, 'semantic_quality_not_judged': True, 'missing_artifacts': missing, **match})
|
|
130
131
|
|
|
131
|
-
def _grader_prompt(rubric: RubricSpec, outputs: ActualOutputs, trace:
|
|
132
|
+
def _grader_prompt(rubric: RubricSpec, outputs: ActualOutputs, trace: WorkflowTrace | None, precheck: GraderResult, artifact_preview_bundle: dict | None=None) -> str:
|
|
132
133
|
return """Return only valid JSON matching GraderResult. Do not include markdown.
|
|
133
134
|
You are the Mentor Model grader. Score each rubric item using the Apprentice-generated plus Mentor-audited task rubric, attempt trace, actual_outputs.json, artifact refs, and artifact content previews when available. Return a JSON object with exactly these required top-level fields: grader_result_id, task_id, attempt_id, attempt_kind, rubric_id, grader_kind, score_source, artifact_contract_score, model_score, semantic_score, final_score, score, max_score, passed, confidence, evidence_refs, rubric_item_scores, limitations, hidden_reference_used, hidden_reference_leaked, metadata_json. Each rubric_item_scores entry must include artifact_presence_ok, semantic_correctness_score, evidence_refs, reasoning_summary, confidence, failure_mode, and improvement_suggestion. Cite evidence_refs from actual_outputs/artifacts/trace, distinguish artifact presence from Mentor-judged correctness, and set score_source=model_judged and grader_kind=model. Do not rely on Apprentice Agent self-assessment or self evaluation. Do not use deterministic fallback judgement; structural artifact-contract guardrails are auxiliary only and are not grader judgments.
|
|
134
135
|
Rubric JSON:
|
|
135
|
-
""" + json.dumps(rubric.model_dump(), sort_keys=True) + "\nActualOutputs JSON:\n" + json.dumps(outputs.model_dump(), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(artifact_preview_bundle or {}, sort_keys=True)[:16000] + "\nTrace summary JSON:\n" + json.dumps((trace.model_dump() if trace else {}), sort_keys=True)
|
|
136
|
+
""" + json.dumps(rubric.model_dump(), sort_keys=True) + "\nActualOutputs JSON:\n" + json.dumps(outputs.model_dump(), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(artifact_preview_bundle or {}, sort_keys=True)[:16000] + "\nTrace summary JSON:\n" + json.dumps((from_workflow(trace.model_dump()) if trace else {}), sort_keys=True) + "\nStructural guardrail JSON:\n" + json.dumps(precheck.model_dump(), sort_keys=True)
|
|
136
137
|
|
|
137
|
-
def grade_attempt(rubric: RubricSpec, outputs: ActualOutputs, attempt_kind: str, trace:
|
|
138
|
+
def grade_attempt(rubric: RubricSpec, outputs: ActualOutputs, attempt_kind: str, trace: WorkflowTrace | None=None, role_root: Path | None=None, package_root: Path | None=None) -> GraderResult:
|
|
138
139
|
settings=get_settings()
|
|
139
140
|
pre=deterministic_grade(rubric, outputs, attempt_kind, package_root)
|
|
140
141
|
role_root=role_root or Path('outputs/roles')
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from .config import get_settings, mentor_model_provider_readiness, apprentice_agent_readiness_status
|
|
8
|
-
from .io import append_jsonl, read_jsonl
|
|
8
|
+
from .io import append_jsonl, read_workflow_jsonl as read_jsonl
|
|
9
9
|
from .recipes import MODEL_PROVIDER_RECIPES, WORKER_AGENT_RECIPES
|
|
10
10
|
|
|
11
11
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import json, hashlib
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from .atif import encode_traces, decode_traces, validate_trace, event_trace, NAMESPACE
|
|
6
|
+
|
|
7
|
+
TRACE_FILES = {'agent_trace.json', 'agent_trace.raw.json', 'agent_trace.normalized.json', 'trajectory.json', 'trace.json', 'trace.jsonl', 'agent_traces.jsonl', 'raw_agent_traces.jsonl'}
|
|
8
|
+
EVENT_FILES = {'tdo_trace.jsonl', 'compiler_trace.jsonl', 'session_events.jsonl'}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _encode(path: Path, obj):
|
|
12
|
+
data = obj.model_dump(mode='json') if isinstance(obj, BaseModel) else obj
|
|
13
|
+
data = encode_traces(data)
|
|
14
|
+
if path.name in EVENT_FILES and isinstance(data, dict) and 'agent' not in data:
|
|
15
|
+
data = event_trace(data, 'experience-compiler' if path.name != 'session_events.jsonl' else 'agent-apprenticeship')
|
|
16
|
+
if path.name in TRACE_FILES | EVENT_FILES:
|
|
17
|
+
data = validate_trace(data)
|
|
18
|
+
return data
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def write_json(path: Path, obj):
|
|
22
|
+
data = _encode(path, obj)
|
|
23
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
24
|
+
path.write_text(json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False)+'\n', encoding='utf-8')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def read_json(path: Path):
|
|
28
|
+
data = json.loads(path.read_text(encoding='utf-8'))
|
|
29
|
+
if path.name in TRACE_FILES | EVENT_FILES:
|
|
30
|
+
return validate_trace(data)
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def append_jsonl(path: Path, obj):
|
|
35
|
+
data = _encode(path, obj)
|
|
36
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
with path.open('a', encoding='utf-8') as f:
|
|
38
|
+
f.write(json.dumps(data, sort_keys=True, ensure_ascii=False)+'\n')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def read_jsonl(path: Path):
|
|
42
|
+
if not path.exists(): return []
|
|
43
|
+
rows = [json.loads(line) for line in path.read_text(encoding='utf-8').splitlines() if line.strip()]
|
|
44
|
+
if path.name in TRACE_FILES | EVENT_FILES:
|
|
45
|
+
return [validate_trace(row) for row in rows]
|
|
46
|
+
return rows
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _workflow(data, path):
|
|
50
|
+
if path.name in EVENT_FILES:
|
|
51
|
+
return data['extra'][NAMESPACE]['event']
|
|
52
|
+
return decode_traces(data)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def read_workflow_json(path: Path):
|
|
56
|
+
return _workflow(read_json(path), path)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def read_workflow_jsonl(path: Path):
|
|
60
|
+
return [_workflow(row, path) for row in read_jsonl(path)]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def sha256_file(path: Path):
|
|
64
|
+
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def validate_trace_tree(root: Path) -> int:
|
|
68
|
+
"""Validate trace documents and embedded trajectories before package exchange."""
|
|
69
|
+
count = 0
|
|
70
|
+
|
|
71
|
+
def check(value):
|
|
72
|
+
nonlocal count
|
|
73
|
+
if isinstance(value, list):
|
|
74
|
+
for item in value:
|
|
75
|
+
check(item)
|
|
76
|
+
elif isinstance(value, dict):
|
|
77
|
+
if isinstance(value.get('steps'), list) and ('agent' in value or 'trace_id' in value):
|
|
78
|
+
validate_trace(value)
|
|
79
|
+
count += 1
|
|
80
|
+
else:
|
|
81
|
+
for item in value.values():
|
|
82
|
+
check(item)
|
|
83
|
+
|
|
84
|
+
for path in root.rglob('*'):
|
|
85
|
+
if not path.is_file() or path.suffix not in {'.json', '.jsonl'}:
|
|
86
|
+
continue
|
|
87
|
+
try:
|
|
88
|
+
if path.suffix == '.jsonl':
|
|
89
|
+
for row in read_jsonl(path):
|
|
90
|
+
check(row)
|
|
91
|
+
else:
|
|
92
|
+
check(read_json(path))
|
|
93
|
+
except (ValueError, TypeError) as exc:
|
|
94
|
+
if path.name in TRACE_FILES | EVENT_FILES:
|
|
95
|
+
raise ValueError(f'{path}: invalid ATIF v1.8 trace: {exc}') from exc
|
|
96
|
+
# Non-trace artifacts can contain arbitrary or partial JSON. A parsed
|
|
97
|
+
# envelope containing a recognizable invalid trace must still fail.
|
|
98
|
+
try:
|
|
99
|
+
rows = [json.loads(line) for line in path.read_text().splitlines() if line.strip()] if path.suffix == '.jsonl' else [json.loads(path.read_text())]
|
|
100
|
+
except ValueError:
|
|
101
|
+
continue
|
|
102
|
+
for row in rows:
|
|
103
|
+
check(row)
|
|
104
|
+
return count
|
|
@@ -11,7 +11,7 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
from .config import Settings, get_settings
|
|
13
13
|
from .env import contains_secret
|
|
14
|
-
from .io import append_jsonl, read_json, read_jsonl, write_json
|
|
14
|
+
from .io import append_jsonl, read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
|
|
15
15
|
from .version import get_package_version
|
|
16
16
|
|
|
17
17
|
|
|
@@ -169,6 +169,8 @@ def _source_id(row: dict[str, Any]) -> str:
|
|
|
169
169
|
|
|
170
170
|
|
|
171
171
|
def _bundle_source(path: Path) -> dict[str, Any]:
|
|
172
|
+
from .io import validate_trace_tree
|
|
173
|
+
validate_trace_tree(path)
|
|
172
174
|
manifest_path = path / "contribution_manifest.json"
|
|
173
175
|
manifest = read_json(manifest_path) if manifest_path.exists() else {}
|
|
174
176
|
bundle_id = str(manifest.get("bundle_id") or path.name)
|
|
@@ -424,6 +426,8 @@ def compile_experience_pack(
|
|
|
424
426
|
write_json(pack_path / "source_refs.json", {"sources": source_refs})
|
|
425
427
|
(pack_path / "skill.md").write_text(skill)
|
|
426
428
|
(pack_path / "SUMMARY.md").write_text(summary)
|
|
429
|
+
from .agent_env_exporter import export_agent_env
|
|
430
|
+
export_agent_env(pack_path)
|
|
427
431
|
return pack_path
|
|
428
432
|
|
|
429
433
|
|
|
@@ -692,6 +696,8 @@ def install_runtime_training_from_bundle(
|
|
|
692
696
|
"""Install runtime-useful Experience Compiler outputs into the local registry."""
|
|
693
697
|
s = settings or get_settings()
|
|
694
698
|
bundle = _find_bundle_root(bundle_path)
|
|
699
|
+
from .io import validate_trace_tree
|
|
700
|
+
validate_trace_tree(bundle)
|
|
695
701
|
compiler = bundle / "experience_compiler"
|
|
696
702
|
skill_dir = compiler / "skill_pack"
|
|
697
703
|
runtime_dir = compiler / "runtime_learning_package"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Callable
|
|
4
|
-
from .schemas import RawTaskRecord, ActualOutputs,
|
|
4
|
+
from .schemas import RawTaskRecord, ActualOutputs, WorkflowTrace
|
|
5
5
|
from .config import get_settings
|
|
6
6
|
from .task_intake import task_intake
|
|
7
7
|
from .rubric_generation import generate_rubric
|
|
@@ -15,7 +15,7 @@ from .loop_review import reviewer_feedback_from_evaluator, write_loop_review_ite
|
|
|
15
15
|
from .lesson_extractor import extract_lesson
|
|
16
16
|
from .training_signals import process_supervision_from_trace, reward_modeling, training_signals
|
|
17
17
|
from .revision import compute_hillclimb, preference_pair
|
|
18
|
-
from .io import write_json, append_jsonl, read_json
|
|
18
|
+
from .io import write_json, append_jsonl, read_workflow_json as read_json
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def _attempt(package_root, raw, spec, kind, runner, feedback=None):
|
|
@@ -37,7 +37,7 @@ def _is_apprentice_operational_failure(actual: ActualOutputs) -> bool:
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def _write_baseline_only_signals(pkg: Path, b_actual: ActualOutputs, b_grade, b_ver, b_trace:
|
|
40
|
+
def _write_baseline_only_signals(pkg: Path, b_actual: ActualOutputs, b_grade, b_ver, b_trace: WorkflowTrace, fb) -> None:
|
|
41
41
|
lesson=extract_lesson(compute_hillclimb(b_grade, b_grade)); write_json(pkg/'signals/lesson_pack.json', lesson)
|
|
42
42
|
for ex in process_supervision_from_trace(b_trace, b_grade, b_ver, fb): append_jsonl(pkg/'signals/process_supervision.jsonl', ex)
|
|
43
43
|
append_jsonl(pkg/'signals/reward_modeling.jsonl', reward_modeling(b_actual,b_grade))
|
|
@@ -76,7 +76,7 @@ def run_task(
|
|
|
76
76
|
actual_iterations=1
|
|
77
77
|
baseline = _attempt(pkg, raw, spec, 'baseline', runner)
|
|
78
78
|
b_actual=ActualOutputs.model_validate(read_json(pkg/'attempts/baseline/actual_outputs.json'))
|
|
79
|
-
b_trace=
|
|
79
|
+
b_trace=WorkflowTrace.model_validate(read_json(pkg/'attempts/baseline/agent_trace.json'))
|
|
80
80
|
if attempt_complete_callback:
|
|
81
81
|
attempt_complete_callback(pkg, 'baseline')
|
|
82
82
|
revised_attempt_ids=[]; selected_attempt_id=b_actual.attempt_id
|
|
@@ -165,7 +165,7 @@ def run_task(
|
|
|
165
165
|
revised = _attempt(pkg, raw, spec, 'revised', runner, fb.feedback_summary)
|
|
166
166
|
actual_iterations=2
|
|
167
167
|
r_actual=ActualOutputs.model_validate(read_json(pkg/'attempts/revised/actual_outputs.json'))
|
|
168
|
-
r_trace=
|
|
168
|
+
r_trace=WorkflowTrace.model_validate(read_json(pkg/'attempts/revised/agent_trace.json'))
|
|
169
169
|
r_grade=grade_attempt(rubric, r_actual, 'revised', r_trace, role_root, pkg)
|
|
170
170
|
r_ver=verify_attempt(r_grade, r_actual, r_trace, role_root, pkg)
|
|
171
171
|
r_grade=apply_score_reliability(r_grade, r_ver)
|
|
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from .io import read_json, write_json
|
|
8
|
+
from .io import read_workflow_json as read_json, write_json
|
|
9
9
|
from .public_sanitizer import sanitize_public_obj, sanitize_public_text
|
|
10
10
|
|
|
11
11
|
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
import hashlib, mimetypes, json, shutil
|
|
4
4
|
from .schemas import *
|
|
5
|
-
from .io import write_json, append_jsonl
|
|
5
|
+
from .io import write_json, append_jsonl, read_workflow_json as read_json
|
|
6
6
|
from .rubric_generation import worker_visible_markdown
|
|
7
7
|
from .artifact_resolver import artifact_ref_candidates, normalize_artifact_ref
|
|
8
8
|
|
|
@@ -220,7 +220,7 @@ def _trace_artifact_links(package_root: Path) -> dict[str, list[dict]]:
|
|
|
220
220
|
links: dict[str, list[dict]] = {}
|
|
221
221
|
for trace_path in package_root.glob('attempts/*/agent_trace.json'):
|
|
222
222
|
try:
|
|
223
|
-
trace=
|
|
223
|
+
trace=read_json(trace_path)
|
|
224
224
|
except Exception:
|
|
225
225
|
continue
|
|
226
226
|
attempt_id=trace.get('attempt_id')
|
|
@@ -250,7 +250,7 @@ def write_artifacts_index(package_root: Path):
|
|
|
250
250
|
trace_links=_trace_artifact_links(package_root)
|
|
251
251
|
for f in package_root.rglob('*'):
|
|
252
252
|
rel=f.relative_to(package_root)
|
|
253
|
-
if is_ignored_release_path(rel):
|
|
253
|
+
if 'agent_env' in rel.parts or is_ignored_release_path(rel):
|
|
254
254
|
continue
|
|
255
255
|
if f.is_file() and f.name != 'artifacts_index.json':
|
|
256
256
|
mime=mimetypes.guess_type(f.name)[0]
|
|
@@ -300,4 +300,6 @@ def write_artifacts_index(package_root: Path):
|
|
|
300
300
|
manifest['trace_counters']=counters
|
|
301
301
|
write_json(manifest_path, manifest)
|
|
302
302
|
write_json(package_root/'artifacts_index.json', paths)
|
|
303
|
+
from .agent_env_exporter import export_agent_env
|
|
304
|
+
export_agent_env(package_root)
|
|
303
305
|
return paths
|
|
@@ -6,7 +6,7 @@ from datetime import datetime, timezone
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any, Callable, Literal
|
|
8
8
|
|
|
9
|
-
from .io import append_jsonl, read_json, read_jsonl, write_json
|
|
9
|
+
from .io import append_jsonl, read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
|
|
10
10
|
|
|
11
11
|
RUN_TERMINAL_STATUSES = {"completed", "partial", "failed"}
|
|
12
12
|
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
from .bundle_exporter import create_contribution_bundle
|
|
11
11
|
from .config import Settings, apprentice_agent_display_name, apprentice_agent_readiness_status, get_settings, settings_override
|
|
12
12
|
from .env import redact_secrets
|
|
13
|
-
from .io import read_json, write_json
|
|
13
|
+
from .io import read_workflow_json as read_json, write_json
|
|
14
14
|
from .loop import run_task
|
|
15
15
|
from .loop_review import record_followup_review_update, record_human_loop_feedback
|
|
16
16
|
from .mentor_checkpoints import write_mentor_checkpoints
|