agent-hitch 0.2.7 → 0.2.8
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 +16 -2
- package/README.zh-CN.md +14 -2
- package/dist/bin/hitch.js +25 -3
- package/dist/bin/hitch.js.map +1 -1
- package/dist/scripts/canary-benchmark.js +54 -0
- package/dist/scripts/canary-benchmark.js.map +1 -0
- package/dist/scripts/canary-eval-scheduler-throughput.js +47 -0
- package/dist/scripts/canary-eval-scheduler-throughput.js.map +1 -0
- package/dist/scripts/check-architecture.js +3 -2
- package/dist/scripts/check-architecture.js.map +1 -1
- package/dist/src/adapters/catalog.js +2 -1
- package/dist/src/adapters/catalog.js.map +1 -1
- package/dist/src/adapters/providers/codex-auth.js +29 -0
- package/dist/src/adapters/providers/codex-auth.js.map +1 -0
- package/dist/src/adapters/providers/codex.js +23 -6
- package/dist/src/adapters/providers/codex.js.map +1 -1
- package/dist/src/adapters/providers/model-call.js +26 -0
- package/dist/src/adapters/providers/model-call.js.map +1 -0
- package/dist/src/artifacts/index.js +1 -1
- package/dist/src/artifacts/index.js.map +1 -1
- package/dist/src/artifacts/preparer.js +7 -2
- package/dist/src/artifacts/preparer.js.map +1 -1
- package/dist/src/backends/harbor/agent-budget.js +31 -0
- package/dist/src/backends/harbor/agent-budget.js.map +1 -0
- package/dist/src/backends/harbor/backend.js +6 -4
- package/dist/src/backends/harbor/backend.js.map +1 -1
- package/dist/src/backends/harbor/index.js +1 -0
- package/dist/src/backends/harbor/index.js.map +1 -1
- package/dist/src/backends/harbor/regrade.js +77 -0
- package/dist/src/backends/harbor/regrade.js.map +1 -0
- package/dist/src/backends/index.js +1 -0
- package/dist/src/backends/index.js.map +1 -1
- package/dist/src/benchmarks/index.js +3 -0
- package/dist/src/benchmarks/index.js.map +1 -0
- package/dist/src/benchmarks/loader.js +271 -0
- package/dist/src/benchmarks/loader.js.map +1 -0
- package/dist/src/benchmarks/metrics.js +16 -0
- package/dist/src/benchmarks/metrics.js.map +1 -0
- package/dist/src/benchmarks/toml.js +13 -0
- package/dist/src/benchmarks/toml.js.map +1 -0
- package/dist/src/benchmarks/validation.js +242 -0
- package/dist/src/benchmarks/validation.js.map +1 -0
- package/dist/src/cli/arguments.js +6 -4
- package/dist/src/cli/arguments.js.map +1 -1
- package/dist/src/cli/commands/benchmark.js +22 -0
- package/dist/src/cli/commands/benchmark.js.map +1 -0
- package/dist/src/cli/commands/capabilities.js +18 -0
- package/dist/src/cli/commands/capabilities.js.map +1 -0
- package/dist/src/cli/commands/eval.js +15 -3
- package/dist/src/cli/commands/eval.js.map +1 -1
- package/dist/src/cli/commands/run.js +37 -15
- package/dist/src/cli/commands/run.js.map +1 -1
- package/dist/src/cli/commands/trajectory.js +96 -5
- package/dist/src/cli/commands/trajectory.js.map +1 -1
- package/dist/src/cli/commands/verifier.js +36 -0
- package/dist/src/cli/commands/verifier.js.map +1 -0
- package/dist/src/cli/main.js +6 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/output.js +14 -2
- package/dist/src/cli/output.js.map +1 -1
- package/dist/src/control-plane/eval-control-work.js +5 -0
- package/dist/src/control-plane/eval-control-work.js.map +1 -1
- package/dist/src/control-plane/eval-scheduler.js +4 -1
- package/dist/src/control-plane/eval-scheduler.js.map +1 -1
- package/dist/src/control-plane/index.js +1 -1
- package/dist/src/control-plane/index.js.map +1 -1
- package/dist/src/control-plane/remote-result-transport.js +3 -0
- package/dist/src/control-plane/remote-result-transport.js.map +1 -1
- package/dist/src/control-plane/remote-work-coordinator.js +1 -0
- package/dist/src/control-plane/remote-work-coordinator.js.map +1 -1
- package/dist/src/control-plane/remote-work-item.js +62 -0
- package/dist/src/control-plane/remote-work-item.js.map +1 -0
- package/dist/src/control-plane/remote-work-recovery.js +44 -5
- package/dist/src/control-plane/remote-work-recovery.js.map +1 -1
- package/dist/src/control-plane/remote-worker-protocol.js +2 -23
- package/dist/src/control-plane/remote-worker-protocol.js.map +1 -1
- package/dist/src/control-plane/rerun-scheduler.js +51 -14
- package/dist/src/control-plane/rerun-scheduler.js.map +1 -1
- package/dist/src/control-plane/rerun-submission.js +10 -4
- package/dist/src/control-plane/rerun-submission.js.map +1 -1
- package/dist/src/control-plane/work-admission.js +2 -0
- package/dist/src/control-plane/work-admission.js.map +1 -1
- package/dist/src/control-plane/work-dispatcher.js +9 -1
- package/dist/src/control-plane/work-dispatcher.js.map +1 -1
- package/dist/src/controller-runtime/hash.js +7 -2
- package/dist/src/controller-runtime/hash.js.map +1 -1
- package/dist/src/daemon/auth.js +5 -0
- package/dist/src/daemon/auth.js.map +1 -1
- package/dist/src/domain/benchmarks.js +2 -0
- package/dist/src/domain/benchmarks.js.map +1 -0
- package/dist/src/domain/index.js +4 -0
- package/dist/src/domain/index.js.map +1 -1
- package/dist/src/domain/runs.js.map +1 -1
- package/dist/src/domain/validation.js +11 -4
- package/dist/src/domain/validation.js.map +1 -1
- package/dist/src/domain/verifier-evidence-validation.js +214 -0
- package/dist/src/domain/verifier-evidence-validation.js.map +1 -0
- package/dist/src/domain/verifier-evidence.js +3 -0
- package/dist/src/domain/verifier-evidence.js.map +1 -0
- package/dist/src/domain/verifier-score-contract.js +232 -0
- package/dist/src/domain/verifier-score-contract.js.map +1 -0
- package/dist/src/evals/benchmark-adapter-manifest.js +219 -0
- package/dist/src/evals/benchmark-adapter-manifest.js.map +1 -0
- package/dist/src/evals/benchmark-candidate.js +36 -0
- package/dist/src/evals/benchmark-candidate.js.map +1 -0
- package/dist/src/evals/benchmark-run.js +166 -0
- package/dist/src/evals/benchmark-run.js.map +1 -0
- package/dist/src/evals/collect-only-rerun.js +2 -1
- package/dist/src/evals/collect-only-rerun.js.map +1 -1
- package/dist/src/evals/directory.js +1 -1
- package/dist/src/evals/directory.js.map +1 -1
- package/dist/src/evals/duration-estimator.js +126 -0
- package/dist/src/evals/duration-estimator.js.map +1 -0
- package/dist/src/evals/eval-lifecycle-events.js +13 -0
- package/dist/src/evals/eval-lifecycle-events.js.map +1 -1
- package/dist/src/evals/eval-logical-plan.js +51 -0
- package/dist/src/evals/eval-logical-plan.js.map +1 -0
- package/dist/src/evals/eval-result-builder.js +81 -0
- package/dist/src/evals/eval-result-builder.js.map +1 -0
- package/dist/src/evals/evolution-baseline.js +191 -0
- package/dist/src/evals/evolution-baseline.js.map +1 -0
- package/dist/src/evals/execution-plan.js +39 -3
- package/dist/src/evals/execution-plan.js.map +1 -1
- package/dist/src/evals/failure-classifier.js +61 -0
- package/dist/src/evals/failure-classifier.js.map +1 -0
- package/dist/src/evals/harbor-artifact-builder.js +6 -2
- package/dist/src/evals/harbor-artifact-builder.js.map +1 -1
- package/dist/src/evals/harbor-bridge-error.js +41 -1
- package/dist/src/evals/harbor-bridge-error.js.map +1 -1
- package/dist/src/evals/harbor-node-runtime.js +146 -0
- package/dist/src/evals/harbor-node-runtime.js.map +1 -0
- package/dist/src/evals/index.js +15 -0
- package/dist/src/evals/index.js.map +1 -1
- package/dist/src/evals/infrastructure-retry.js +109 -22
- package/dist/src/evals/infrastructure-retry.js.map +1 -1
- package/dist/src/evals/native-phase-evidence.js +440 -0
- package/dist/src/evals/native-phase-evidence.js.map +1 -0
- package/dist/src/evals/physical-retry-work.js +31 -0
- package/dist/src/evals/physical-retry-work.js.map +1 -0
- package/dist/src/evals/planned-execution-support.js +22 -10
- package/dist/src/evals/planned-execution-support.js.map +1 -1
- package/dist/src/evals/planned-execution.js +120 -112
- package/dist/src/evals/planned-execution.js.map +1 -1
- package/dist/src/evals/planned-progress-publisher.js +62 -0
- package/dist/src/evals/planned-progress-publisher.js.map +1 -0
- package/dist/src/evals/planned-retry-execution.js +174 -0
- package/dist/src/evals/planned-retry-execution.js.map +1 -0
- package/dist/src/evals/planned-retry-lifecycle.js +4 -2
- package/dist/src/evals/planned-retry-lifecycle.js.map +1 -1
- package/dist/src/evals/preparation-rerun.js +225 -0
- package/dist/src/evals/preparation-rerun.js.map +1 -0
- package/dist/src/evals/progress.js +48 -8
- package/dist/src/evals/progress.js.map +1 -1
- package/dist/src/evals/recovery.js +54 -4
- package/dist/src/evals/recovery.js.map +1 -1
- package/dist/src/evals/regrade-evidence.js +77 -0
- package/dist/src/evals/regrade-evidence.js.map +1 -0
- package/dist/src/evals/remote-infrastructure-retry.js +84 -18
- package/dist/src/evals/remote-infrastructure-retry.js.map +1 -1
- package/dist/src/evals/request.js +21 -3
- package/dist/src/evals/request.js.map +1 -1
- package/dist/src/evals/rerun-inputs.js +19 -0
- package/dist/src/evals/rerun-inputs.js.map +1 -1
- package/dist/src/evals/rerun-types.js +2 -6
- package/dist/src/evals/rerun-types.js.map +1 -1
- package/dist/src/evals/rerun.js +27 -20
- package/dist/src/evals/rerun.js.map +1 -1
- package/dist/src/evals/result-helpers.js +13 -0
- package/dist/src/evals/result-helpers.js.map +1 -1
- package/dist/src/evals/retry-backoff.js +20 -0
- package/dist/src/evals/retry-backoff.js.map +1 -0
- package/dist/src/evals/retry-state.js +195 -0
- package/dist/src/evals/retry-state.js.map +1 -0
- package/dist/src/evals/scheduler-metrics.js +95 -0
- package/dist/src/evals/scheduler-metrics.js.map +1 -0
- package/dist/src/evals/scheduler-trace-replay.js +107 -0
- package/dist/src/evals/scheduler-trace-replay.js.map +1 -0
- package/dist/src/evals/service.js +87 -138
- package/dist/src/evals/service.js.map +1 -1
- package/dist/src/evals/trial-import.js +47 -50
- package/dist/src/evals/trial-import.js.map +1 -1
- package/dist/src/evals/trial-publication-recovery.js +45 -5
- package/dist/src/evals/trial-publication-recovery.js.map +1 -1
- package/dist/src/evals/trial-reference-validation.js +49 -0
- package/dist/src/evals/trial-reference-validation.js.map +1 -0
- package/dist/src/evals/verifier-artifacts.js +317 -0
- package/dist/src/evals/verifier-artifacts.js.map +1 -0
- package/dist/src/evals/verifier-eligibility.js +25 -0
- package/dist/src/evals/verifier-eligibility.js.map +1 -0
- package/dist/src/evals/verifier-only-rerun.js +210 -0
- package/dist/src/evals/verifier-only-rerun.js.map +1 -0
- package/dist/src/evals/verifier-runtime.js +61 -0
- package/dist/src/evals/verifier-runtime.js.map +1 -0
- package/dist/src/evals/verifier-score-artifacts.js +119 -0
- package/dist/src/evals/verifier-score-artifacts.js.map +1 -0
- package/dist/src/foundation/contained-file.js +85 -0
- package/dist/src/foundation/contained-file.js.map +1 -0
- package/dist/src/foundation/credential-redaction.js +26 -1
- package/dist/src/foundation/credential-redaction.js.map +1 -1
- package/dist/src/foundation/fs.js +2 -1
- package/dist/src/foundation/fs.js.map +1 -1
- package/dist/src/foundation/index.js +2 -1
- package/dist/src/foundation/index.js.map +1 -1
- package/dist/src/runs/adapter-process.js +12 -0
- package/dist/src/runs/adapter-process.js.map +1 -0
- package/dist/src/runs/executor.js +24 -30
- package/dist/src/runs/executor.js.map +1 -1
- package/dist/src/runs/finalizer.js +17 -0
- package/dist/src/runs/finalizer.js.map +1 -1
- package/dist/src/runs/index.js +5 -0
- package/dist/src/runs/index.js.map +1 -1
- package/dist/src/runs/phase-bundle.js +86 -0
- package/dist/src/runs/phase-bundle.js.map +1 -0
- package/dist/src/runs/phase-cancellation.js +107 -0
- package/dist/src/runs/phase-cancellation.js.map +1 -0
- package/dist/src/runs/phase-group.js +113 -0
- package/dist/src/runs/phase-group.js.map +1 -0
- package/dist/src/runs/query.js +1 -1
- package/dist/src/runs/query.js.map +1 -1
- package/dist/src/runs/records.js +3 -0
- package/dist/src/runs/records.js.map +1 -1
- package/dist/src/runs/request.js +4 -2
- package/dist/src/runs/request.js.map +1 -1
- package/dist/src/runs/verifier-evidence-redaction.js +61 -0
- package/dist/src/runs/verifier-evidence-redaction.js.map +1 -0
- package/dist/src/runs/verifier-evidence.js +459 -0
- package/dist/src/runs/verifier-evidence.js.map +1 -0
- package/dist/src/runs/verifier-structured-evidence.js +83 -0
- package/dist/src/runs/verifier-structured-evidence.js.map +1 -0
- package/dist/src/trajectories/analysis.js +288 -0
- package/dist/src/trajectories/analysis.js.map +1 -0
- package/dist/src/trajectories/chunk-projection.js +148 -0
- package/dist/src/trajectories/chunk-projection.js.map +1 -0
- package/dist/src/trajectories/content-projection.js +389 -0
- package/dist/src/trajectories/content-projection.js.map +1 -0
- package/dist/src/trajectories/dsh-chunk-contract.js +176 -0
- package/dist/src/trajectories/dsh-chunk-contract.js.map +1 -0
- package/dist/src/trajectories/dsh-contract.js +284 -0
- package/dist/src/trajectories/dsh-contract.js.map +1 -0
- package/dist/src/trajectories/events-chunk-drill.js +95 -0
- package/dist/src/trajectories/events-chunk-drill.js.map +1 -0
- package/dist/src/trajectories/events-page.js +367 -0
- package/dist/src/trajectories/events-page.js.map +1 -0
- package/dist/src/trajectories/index.js +3 -0
- package/dist/src/trajectories/index.js.map +1 -1
- package/dist/src/trajectories/request-attempt.js +47 -0
- package/dist/src/trajectories/request-attempt.js.map +1 -0
- package/dist/src/trajectories/stream-reader.js +311 -0
- package/dist/src/trajectories/stream-reader.js.map +1 -0
- package/dist/src/trajectories/surface-fold.js +126 -0
- package/dist/src/trajectories/surface-fold.js.map +1 -0
- package/dist/src/workers/remote-harbor-worker.js +3 -0
- package/dist/src/workers/remote-harbor-worker.js.map +1 -1
- package/docs/schemas/benchmark-hook-request-v1.schema.json +65 -0
- package/docs/schemas/benchmark-hook-response-v1.schema.json +75 -0
- package/docs/schemas/benchmark-hook-v1.schema.json +33 -0
- package/docs/schemas/benchmark-lock-v1.schema.json +351 -0
- package/docs/schemas/benchmark-metric-v1.schema.json +37 -0
- package/docs/schemas/benchmark-package-v1.schema.json +167 -0
- package/docs/schemas/benchmark-phase-group.schema.json +41 -0
- package/docs/schemas/benchmark-profile-v1.schema.json +141 -0
- package/docs/schemas/benchmark-task-v1.schema.json +384 -0
- package/docs/schemas/benchmark-tool-result-v1.schema.json +30 -0
- package/docs/schemas/error.schema.json +22 -0
- package/docs/schemas/eval-progress.schema.json +1 -15
- package/docs/schemas/eval-rerun-result.schema.json +6 -1
- package/docs/schemas/eval-rerun-submission.schema.json +3 -1
- package/docs/schemas/eval-result.schema.json +1 -15
- package/docs/schemas/eval-trial-publication.schema.json +1 -25
- package/docs/schemas/eval-trial-reference.schema.json +129 -0
- package/docs/schemas/regrade-assessment-reference.schema.json +11 -0
- package/docs/schemas/run-context.schema.json +15 -0
- package/docs/schemas/run-manifest.schema.json +4 -0
- package/docs/schemas/run-request.schema.json +4 -0
- package/docs/schemas/trajectory-analysis.schema.json +258 -0
- package/docs/schemas/trajectory-events-page.schema.json +40 -0
- package/docs/schemas/verifier-assessment.schema.json +77 -0
- package/docs/schemas/verifier-evidence.schema.json +276 -0
- package/integrations/harbor/hitch_benchmark.py +252 -0
- package/integrations/harbor/hitch_candidate_recycle.py +262 -0
- package/integrations/harbor/hitch_harbor_agent.py +541 -43
- package/integrations/harbor/hitch_harbor_environment.py +138 -0
- package/integrations/harbor/hitch_harbor_verifier.py +60 -2
- package/integrations/harbor/hitch_phase_supervisor.py +452 -0
- package/integrations/harbor/hitch_tool_client.mjs +91 -0
- package/integrations/model-call/cli.js +57 -0
- package/package.json +17 -6
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/rsi-gear/agent-hitch/blob/main/docs/schemas/verifier-evidence.schema.json",
|
|
4
|
+
"title": "Hitch Verifier Evidence V1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "kind", "run_id", "verifier"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": "1" },
|
|
10
|
+
"kind": { "const": "verifier-evidence" },
|
|
11
|
+
"run_id": { "type": "string", "pattern": "^run_[a-f0-9]{32}$" },
|
|
12
|
+
"parent": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"required": ["eval_id", "trial_id", "attempt"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"eval_id": { "type": "string", "pattern": "^eval_[a-f0-9]{32}$" },
|
|
18
|
+
"trial_id": { "type": "string", "minLength": 1 },
|
|
19
|
+
"attempt": { "type": "integer", "minimum": 1 }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"observation": { "$ref": "#/$defs/observation" },
|
|
23
|
+
"verifier": { "$ref": "#/$defs/verifier" },
|
|
24
|
+
"redactions": { "type": "array", "items": { "$ref": "#/$defs/redaction" } }
|
|
25
|
+
},
|
|
26
|
+
"$defs": {
|
|
27
|
+
"observation": {
|
|
28
|
+
"oneOf": [
|
|
29
|
+
{
|
|
30
|
+
"type": "object",
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"required": ["status", "reward", "verifier_result_ref"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"status": { "const": "valid" },
|
|
35
|
+
"reward": { "type": "number" },
|
|
36
|
+
"verifier_result_ref": { "$ref": "#/$defs/relativePath" }
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"required": ["status", "invalid_reason"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"status": { "const": "invalid" },
|
|
45
|
+
"invalid_reason": { "type": "string", "minLength": 1 },
|
|
46
|
+
"verifier_result_ref": { "$ref": "#/$defs/relativePath" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"verifier": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"required": ["status"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"status": { "enum": ["complete", "result_only", "missing", "corrupt"] },
|
|
57
|
+
"result": {},
|
|
58
|
+
"result_sha256": { "$ref": "#/$defs/sha256" },
|
|
59
|
+
"scores": { "$ref": "#/$defs/scores" },
|
|
60
|
+
"process": { "$ref": "#/$defs/process" },
|
|
61
|
+
"feedback": { "$ref": "#/$defs/feedback" },
|
|
62
|
+
"structured_artifacts": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"additionalProperties": false,
|
|
65
|
+
"minProperties": 1,
|
|
66
|
+
"properties": {
|
|
67
|
+
"process": { "$ref": "#/$defs/structuredArtifact" },
|
|
68
|
+
"feedback": { "$ref": "#/$defs/structuredArtifact" }
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"diagnostics": { "$ref": "#/$defs/diagnostics" },
|
|
72
|
+
"issues": { "type": "array", "maxItems": 16, "items": { "type": "string", "minLength": 1, "maxLength": 1024 } }
|
|
73
|
+
},
|
|
74
|
+
"allOf": [
|
|
75
|
+
{
|
|
76
|
+
"if": { "properties": { "status": { "const": "complete" } }, "required": ["status"] },
|
|
77
|
+
"then": {
|
|
78
|
+
"required": ["result", "result_sha256", "diagnostics"],
|
|
79
|
+
"properties": {
|
|
80
|
+
"diagnostics": {
|
|
81
|
+
"allOf": [
|
|
82
|
+
{ "$ref": "#/$defs/diagnostics" },
|
|
83
|
+
{ "anyOf": [{ "required": ["ctrf"] }, { "required": ["stdout"] }, { "required": ["stderr"] }] }
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"if": { "properties": { "status": { "const": "result_only" } }, "required": ["status"] },
|
|
91
|
+
"then": {
|
|
92
|
+
"required": ["result", "result_sha256"],
|
|
93
|
+
"properties": {
|
|
94
|
+
"diagnostics": {
|
|
95
|
+
"allOf": [
|
|
96
|
+
{ "$ref": "#/$defs/diagnostics" },
|
|
97
|
+
{ "not": { "anyOf": [{ "required": ["ctrf"] }, { "required": ["stdout"] }, { "required": ["stderr"] }] } }
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"if": { "properties": { "status": { "const": "missing" } }, "required": ["status"] },
|
|
105
|
+
"then": { "not": { "required": ["result"] } }
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"scores": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"additionalProperties": false,
|
|
112
|
+
"required": ["total_score", "normalization"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"total_score": { "type": "number" },
|
|
115
|
+
"process_score": { "type": "number" },
|
|
116
|
+
"normalization": { "enum": ["standard", "legacy-reward"] }
|
|
117
|
+
},
|
|
118
|
+
"allOf": [
|
|
119
|
+
{
|
|
120
|
+
"if": { "properties": { "normalization": { "const": "legacy-reward" } }, "required": ["normalization"] },
|
|
121
|
+
"then": { "not": { "required": ["process_score"] } }
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"process": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"additionalProperties": false,
|
|
128
|
+
"required": ["schema_version", "metric", "score", "detail_status"],
|
|
129
|
+
"properties": {
|
|
130
|
+
"schema_version": { "const": "1" },
|
|
131
|
+
"metric": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$" },
|
|
132
|
+
"score": { "type": "number" },
|
|
133
|
+
"detail_status": { "enum": ["components", "aggregate-only"] },
|
|
134
|
+
"passed": { "type": "integer", "minimum": 0 },
|
|
135
|
+
"total": { "type": "integer", "minimum": 0 },
|
|
136
|
+
"excluded": { "type": "integer", "minimum": 0 },
|
|
137
|
+
"components": { "type": "array", "items": { "$ref": "#/$defs/processComponent" } }
|
|
138
|
+
},
|
|
139
|
+
"oneOf": [
|
|
140
|
+
{
|
|
141
|
+
"properties": { "detail_status": { "const": "components" } },
|
|
142
|
+
"required": ["detail_status", "passed", "total", "excluded", "components"]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"properties": { "detail_status": { "const": "aggregate-only" } },
|
|
146
|
+
"required": ["detail_status"],
|
|
147
|
+
"not": { "anyOf": [{ "required": ["passed"] }, { "required": ["total"] }, { "required": ["excluded"] }, { "required": ["components"] }] }
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"processComponent": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"additionalProperties": false,
|
|
154
|
+
"required": ["id", "category", "status", "weight"],
|
|
155
|
+
"properties": {
|
|
156
|
+
"id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$" },
|
|
157
|
+
"category": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$" },
|
|
158
|
+
"status": { "enum": ["passed", "failed", "excluded"] },
|
|
159
|
+
"weight": { "type": "number", "exclusiveMinimum": 0 },
|
|
160
|
+
"code": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$" },
|
|
161
|
+
"public_details": { "type": "object" },
|
|
162
|
+
"private_details_ref": { "$ref": "#/$defs/relativePath" },
|
|
163
|
+
"trajectory_refs": { "type": "array", "items": { "$ref": "#/$defs/trajectoryRef" } }
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"feedback": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"additionalProperties": false,
|
|
169
|
+
"required": ["schema_version", "items"],
|
|
170
|
+
"properties": {
|
|
171
|
+
"schema_version": { "const": "1" },
|
|
172
|
+
"items": {
|
|
173
|
+
"type": "array",
|
|
174
|
+
"items": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"additionalProperties": false,
|
|
177
|
+
"required": ["code", "severity", "message"],
|
|
178
|
+
"properties": {
|
|
179
|
+
"code": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$" },
|
|
180
|
+
"severity": { "enum": ["info", "warning", "error"] },
|
|
181
|
+
"message": { "type": "string", "minLength": 1, "maxLength": 16384 },
|
|
182
|
+
"component_ids": { "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$" } },
|
|
183
|
+
"trajectory_refs": { "type": "array", "items": { "$ref": "#/$defs/trajectoryRef" } }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"trajectoryRef": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"additionalProperties": false,
|
|
192
|
+
"required": ["run_id"],
|
|
193
|
+
"properties": {
|
|
194
|
+
"run_id": { "type": "string", "pattern": "^run_[a-f0-9]{32}$" },
|
|
195
|
+
"seq_start": { "type": "integer", "minimum": 0 },
|
|
196
|
+
"seq_end": { "type": "integer", "minimum": 0 }
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"structuredArtifact": {
|
|
200
|
+
"type": "object",
|
|
201
|
+
"additionalProperties": false,
|
|
202
|
+
"required": ["ref", "bytes", "sha256"],
|
|
203
|
+
"properties": {
|
|
204
|
+
"ref": { "enum": ["verifier/process.json", "verifier/feedback.json"] },
|
|
205
|
+
"bytes": { "type": "integer", "minimum": 0 },
|
|
206
|
+
"sha256": { "$ref": "#/$defs/sha256" }
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"diagnostics": {
|
|
210
|
+
"type": "object",
|
|
211
|
+
"additionalProperties": false,
|
|
212
|
+
"minProperties": 1,
|
|
213
|
+
"properties": {
|
|
214
|
+
"ctrf": { "$ref": "#/$defs/artifact" },
|
|
215
|
+
"stdout": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/artifact" } },
|
|
216
|
+
"stderr": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/artifact" } },
|
|
217
|
+
"infrastructure_error": {},
|
|
218
|
+
"retry_history": { "type": "array", "minItems": 1, "items": {} }
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"artifact": {
|
|
222
|
+
"type": "object",
|
|
223
|
+
"additionalProperties": false,
|
|
224
|
+
"required": ["name", "media_type", "bytes", "sha256", "truncated"],
|
|
225
|
+
"properties": {
|
|
226
|
+
"name": { "enum": ["ctrf.json", "test-stdout.txt", "test-stderr.txt", "stdout.txt", "stderr.txt"] },
|
|
227
|
+
"media_type": { "enum": ["application/json", "text/plain"] },
|
|
228
|
+
"bytes": { "type": "integer", "minimum": 0 },
|
|
229
|
+
"sha256": { "$ref": "#/$defs/sha256" },
|
|
230
|
+
"truncated": { "type": "boolean" },
|
|
231
|
+
"json": {},
|
|
232
|
+
"text": { "type": "string" }
|
|
233
|
+
},
|
|
234
|
+
"oneOf": [
|
|
235
|
+
{ "required": ["json"], "not": { "required": ["text"] } },
|
|
236
|
+
{ "required": ["text"], "not": { "required": ["json"] } }
|
|
237
|
+
],
|
|
238
|
+
"allOf": [
|
|
239
|
+
{
|
|
240
|
+
"if": { "properties": { "name": { "const": "ctrf.json" } }, "required": ["name"] },
|
|
241
|
+
"then": { "properties": { "media_type": { "const": "application/json" } } },
|
|
242
|
+
"else": { "properties": { "media_type": { "const": "text/plain" } } }
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"if": { "properties": { "truncated": { "const": true } }, "required": ["truncated"] },
|
|
246
|
+
"then": { "required": ["text"], "not": { "required": ["json"] } }
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"if": { "required": ["json"] },
|
|
250
|
+
"then": {
|
|
251
|
+
"properties": {
|
|
252
|
+
"name": { "const": "ctrf.json" },
|
|
253
|
+
"media_type": { "const": "application/json" },
|
|
254
|
+
"truncated": { "const": false }
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
"redaction": {
|
|
261
|
+
"type": "object",
|
|
262
|
+
"additionalProperties": false,
|
|
263
|
+
"required": ["rule_id", "count"],
|
|
264
|
+
"properties": {
|
|
265
|
+
"rule_id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]{0,127}$" },
|
|
266
|
+
"count": { "type": "integer", "minimum": 1 }
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
"relativePath": {
|
|
270
|
+
"type": "string",
|
|
271
|
+
"minLength": 1,
|
|
272
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.?(?:/|$))(?!.*\\\\).+$"
|
|
273
|
+
},
|
|
274
|
+
"sha256": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Generic benchmark package hooks, tool binding and metric normalization.
|
|
2
|
+
|
|
3
|
+
Runs in the trusted Harbor worker. Package business code is never imported here.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import hashlib
|
|
9
|
+
import json
|
|
10
|
+
import math
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import shlex
|
|
13
|
+
import tempfile
|
|
14
|
+
import uuid
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def descriptor(environment_dir):
|
|
18
|
+
path = Path(environment_dir).parent / ".hitch-benchmark.json"
|
|
19
|
+
return json.loads(path.read_text()) if path.is_file() else None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BenchmarkSession:
|
|
23
|
+
def __init__(self, env, config):
|
|
24
|
+
self.env, self.config = env, config
|
|
25
|
+
self.responses = {}
|
|
26
|
+
self.started = False
|
|
27
|
+
self.stopped = False
|
|
28
|
+
self.failure = None
|
|
29
|
+
self.journal = env.trial_paths.trial_dir / "benchmark-lifecycle.json"
|
|
30
|
+
|
|
31
|
+
async def phase(self, phase):
|
|
32
|
+
if phase in self.responses:
|
|
33
|
+
return self.responses[phase]
|
|
34
|
+
hook = self.config["task"]["lifecycle"][phase]
|
|
35
|
+
labels = self.env._hitch_ownership_labels
|
|
36
|
+
request = {
|
|
37
|
+
"schema_version": "1",
|
|
38
|
+
"request_id": str(uuid.uuid5(uuid.NAMESPACE_URL, self.env.session_id + ":" + phase)),
|
|
39
|
+
"phase": phase, "task_id": self.config["task_id"],
|
|
40
|
+
"logical_trial_id": self.env.session_id, "execution_index": 0,
|
|
41
|
+
"lease_id": labels.get("io.hitch.lease-id", self.env.session_id),
|
|
42
|
+
"epoch": int(labels.get("io.hitch.lease-epoch", "1")),
|
|
43
|
+
"profile_digest": self.config["profile_digest"], "input_refs": [],
|
|
44
|
+
}
|
|
45
|
+
# Harbor's exec API accepts shell text. Quote each argv and stdin byte;
|
|
46
|
+
# no task text is interpolated as shell syntax.
|
|
47
|
+
command = "printf %s " + shlex.quote(json.dumps(request)) + " | " + shlex.join(hook["argv"])
|
|
48
|
+
try:
|
|
49
|
+
result = await self.env.service_exec(command, service=hook["target"].split(":", 1)[1], timeout_sec=math.ceil(hook["timeout_ms"] / 1000))
|
|
50
|
+
if result.return_code != 0:
|
|
51
|
+
raise RuntimeError(f"hook {phase} exited {result.return_code}: {result.stderr}")
|
|
52
|
+
if len(result.stdout or "") > 1024 * 1024:
|
|
53
|
+
raise RuntimeError("oversized hook response")
|
|
54
|
+
response = json.loads(result.stdout)
|
|
55
|
+
if set(response) != {"schema_version", "request_id", "status", "output"}:
|
|
56
|
+
raise RuntimeError(f"invalid hook response fields: {phase}")
|
|
57
|
+
if response.get("schema_version") != "1" or response.get("request_id") != request["request_id"] or response.get("status") != "ok":
|
|
58
|
+
raise RuntimeError(f"invalid/failed hook response: {phase}")
|
|
59
|
+
output = response["output"]
|
|
60
|
+
required = {"prepare": "ready", "quiesce": "quiesced", "cleanup": "cleaned"}.get(phase)
|
|
61
|
+
if required and output.get(required) is not True:
|
|
62
|
+
raise RuntimeError(f"hook {phase} did not confirm {required}")
|
|
63
|
+
if phase == "snapshot":
|
|
64
|
+
paths = self.config["task"]["submission"]["paths"]
|
|
65
|
+
artifacts = output.get("artifacts", [])
|
|
66
|
+
if not isinstance(artifacts, list) or any(type(a.get("bytes")) is not int or a["bytes"] < 0 for a in artifacts):
|
|
67
|
+
raise RuntimeError("invalid snapshot size metadata")
|
|
68
|
+
if sorted(a.get("path", "") for a in artifacts) != sorted(paths):
|
|
69
|
+
raise RuntimeError("snapshot artifact membership mismatch")
|
|
70
|
+
size = sum(a["bytes"] for a in artifacts)
|
|
71
|
+
if size > self.config["task"]["submission"]["max_bytes"] or size < 1:
|
|
72
|
+
raise RuntimeError("snapshot size outside package limit")
|
|
73
|
+
self.responses[phase] = response
|
|
74
|
+
self.write_journal()
|
|
75
|
+
return response
|
|
76
|
+
except BaseException as error:
|
|
77
|
+
self.failure = {"phase": phase, "error": type(error).__name__}
|
|
78
|
+
self.write_journal()
|
|
79
|
+
raise
|
|
80
|
+
|
|
81
|
+
def write_journal(self):
|
|
82
|
+
# Never persist tool tokens or opaque management handles.
|
|
83
|
+
phases = {p: {"request_id": r["request_id"], "status": r["status"]} for p, r in self.responses.items()}
|
|
84
|
+
self.journal.parent.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
self.journal.write_text(json.dumps({"schema_version": "1", "phases": phases, "failure": self.failure}, indent=2))
|
|
86
|
+
|
|
87
|
+
async def prepare(self):
|
|
88
|
+
self.started = True
|
|
89
|
+
if self.config["task"]["driver"]["kind"] != "tool-server":
|
|
90
|
+
self.write_journal()
|
|
91
|
+
return
|
|
92
|
+
response = await self.phase("prepare")
|
|
93
|
+
if self.config["task"]["driver"]["config"].get("native_phases"):
|
|
94
|
+
# There is no candidate identity yet. Only the phase supervisor may
|
|
95
|
+
# bind and upload a phase token after prepare/setup have completed.
|
|
96
|
+
phases = self.config["task"]["driver"]["config"]["native_phases"]
|
|
97
|
+
if (response["output"].get("native_phases_ready") is not True or response["output"].get("tool_bindings", []) != []
|
|
98
|
+
or phases["protocol"] == "hitch-native-phase-control@2" and response["output"].get("native_deadline_ready") is not True):
|
|
99
|
+
self.failure = {"phase": "prepare", "error": "NativePhasePrepareInvalid"}
|
|
100
|
+
self.write_journal()
|
|
101
|
+
raise RuntimeError("native phase prepare must confirm readiness without a static binding")
|
|
102
|
+
return
|
|
103
|
+
bindings = response["output"]["tool_bindings"]
|
|
104
|
+
expected = self.config["task"]["driver"]["config"]
|
|
105
|
+
if len(bindings) != 1 or bindings[0]["endpoint"] != expected["endpoint"] or bindings[0]["tools"] != self.config["tools"]:
|
|
106
|
+
raise RuntimeError("prepared tool binding differs from locked definition")
|
|
107
|
+
binding = bindings[0]
|
|
108
|
+
if not isinstance(binding.get("token"), str) or len(binding["token"]) < 32:
|
|
109
|
+
raise RuntimeError("missing per-trial tool authorization")
|
|
110
|
+
with tempfile.TemporaryDirectory(prefix="hitch-binding-") as temp:
|
|
111
|
+
file = Path(temp) / "binding.json"
|
|
112
|
+
file.write_text(json.dumps(binding)); file.chmod(0o600)
|
|
113
|
+
await self.env.upload_file(file, "/tmp/hitch-tool-binding.json")
|
|
114
|
+
await self.env.upload_file(Path(__file__).with_name("hitch_tool_client.mjs"), "/tmp/hitch-tools.mjs")
|
|
115
|
+
|
|
116
|
+
async def snapshot(self):
|
|
117
|
+
if self.stopped:
|
|
118
|
+
return
|
|
119
|
+
if self.config["task"]["driver"]["kind"] != "tool-server":
|
|
120
|
+
# Harbor collects the native task's declared artifacts. Shared
|
|
121
|
+
# verifiers retain upstream live-workspace semantics.
|
|
122
|
+
self.stopped = True
|
|
123
|
+
return
|
|
124
|
+
async def collect():
|
|
125
|
+
await self.phase("quiesce")
|
|
126
|
+
await self.phase("snapshot")
|
|
127
|
+
budget = self.config["profile"]["budget"]["collection_timeout_ms"]
|
|
128
|
+
await asyncio.wait_for(collect(), budget / 1000)
|
|
129
|
+
self.stopped = True
|
|
130
|
+
|
|
131
|
+
async def cleanup(self):
|
|
132
|
+
if self.started and self.config["task"]["driver"]["kind"] == "tool-server":
|
|
133
|
+
await self.phase("cleanup")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def candidate_instruction(instruction, environment):
|
|
137
|
+
config = descriptor(environment.environment_dir)
|
|
138
|
+
if config is None:
|
|
139
|
+
return instruction, None
|
|
140
|
+
if config["task"]["driver"]["kind"] != "tool-server":
|
|
141
|
+
return (json.dumps(config["candidate_input"]) if "candidate_input" in config else instruction), int(config["agent_timeout_sec"] * 1000)
|
|
142
|
+
instruction += "\n\nTools are available through the locked tool-server bridge. Run `node /tmp/hitch-tools.mjs list` to read tool descriptions and JSON schemas. Invoke a tool using `node /tmp/hitch-tools.mjs TOOL_NAME 'JSON_ARGUMENTS'` (or pass - and JSON via stdin). Complete the requested workflow with these simulated service tools.\n"
|
|
143
|
+
if "tool-result-images@1" in config["task"]["requirements"]:
|
|
144
|
+
instruction += "Image tool results contain absolute local paths. Open each image with your native image viewing tool to inspect the observation. The files contain the original image bytes; the JSON metadata is not a visual observation.\n"
|
|
145
|
+
return instruction, int(config["agent_timeout_sec"] * 1000)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async def export_final_response(environment, result):
|
|
149
|
+
config = descriptor(environment.environment_dir)
|
|
150
|
+
target = config["task"]["submission"].get("final_response") if config else None
|
|
151
|
+
if not target:
|
|
152
|
+
return
|
|
153
|
+
if not isinstance(result.get("output"), str):
|
|
154
|
+
raise RuntimeError("canonical candidate final response is unavailable")
|
|
155
|
+
with tempfile.TemporaryDirectory(prefix="hitch-response-") as temp:
|
|
156
|
+
source = Path(temp) / "response.json"
|
|
157
|
+
encoded = json.dumps({"schema_version": "1", "run_id": result["run_id"],
|
|
158
|
+
"response": result["output"], "termination": result["status"], "source": "hitch-run-result"})
|
|
159
|
+
if len(encoded.encode()) > config["task"]["submission"]["max_bytes"]:
|
|
160
|
+
raise RuntimeError("canonical response exceeds submission limit")
|
|
161
|
+
source.write_text(encoded)
|
|
162
|
+
# Keep an authoritative copy outside the candidate environment. A
|
|
163
|
+
# background process cannot replace the response used for grading.
|
|
164
|
+
(environment.trial_paths.trial_dir / "hitch-final-response.json").write_text(encoded)
|
|
165
|
+
prepared = await environment.exec("mkdir -p /hitch-evidence")
|
|
166
|
+
if prepared.return_code != 0:
|
|
167
|
+
raise RuntimeError("cannot prepare candidate response export")
|
|
168
|
+
await environment.upload_file(source, target)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
async def restore_final_response(verifier):
|
|
172
|
+
config = descriptor(verifier.task.paths.environment_dir)
|
|
173
|
+
target = config["task"]["submission"].get("final_response") if config else None
|
|
174
|
+
if not target:
|
|
175
|
+
return
|
|
176
|
+
source = verifier.trial_paths.trial_dir / "hitch-final-response.json"
|
|
177
|
+
if not source.is_file():
|
|
178
|
+
raise RuntimeError("trusted candidate response evidence is missing")
|
|
179
|
+
# Called after Harbor uploads the frozen artifacts, in the separate grader.
|
|
180
|
+
prepared = await verifier.environment.exec("rm -rf -- /hitch-evidence && mkdir -p /hitch-evidence")
|
|
181
|
+
if prepared.return_code != 0:
|
|
182
|
+
raise RuntimeError("cannot restore trusted candidate response")
|
|
183
|
+
await verifier.environment.upload_file(source, target)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def validate_collected_submission(verifier):
|
|
187
|
+
config = descriptor(verifier.task.paths.environment_dir)
|
|
188
|
+
if not config or config["task"]["driver"]["kind"] == "tool-server":
|
|
189
|
+
return
|
|
190
|
+
task = config["task"]
|
|
191
|
+
if "separate-verifier" not in task["requirements"]:
|
|
192
|
+
return
|
|
193
|
+
# Harbor 0.21 mirrors absolute sources directly under artifacts/.
|
|
194
|
+
root = verifier.trial_paths.trial_dir / "artifacts"
|
|
195
|
+
seen, size = set(), 0
|
|
196
|
+
for source in task["submission"]["paths"]:
|
|
197
|
+
file = root / source.lstrip("/")
|
|
198
|
+
for ancestor in [file, *file.parents]:
|
|
199
|
+
if ancestor == root.parent:
|
|
200
|
+
break
|
|
201
|
+
if ancestor.is_symlink():
|
|
202
|
+
raise RuntimeError("collected submission contains a symlink")
|
|
203
|
+
if not file.exists():
|
|
204
|
+
raise RuntimeError(f"submission_missing: {source}")
|
|
205
|
+
for entry in [file, *(file.rglob("*") if file.is_dir() else [])]:
|
|
206
|
+
if entry.is_symlink():
|
|
207
|
+
raise RuntimeError("collected submission contains a symlink")
|
|
208
|
+
if entry.is_file() and entry not in seen:
|
|
209
|
+
seen.add(entry)
|
|
210
|
+
size += entry.stat().st_size
|
|
211
|
+
if size > task["submission"]["max_bytes"]:
|
|
212
|
+
raise RuntimeError("collected submission exceeds package limit")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def normalize_rewards(verifier, result):
|
|
216
|
+
config = descriptor(verifier.task.paths.environment_dir)
|
|
217
|
+
if config is None:
|
|
218
|
+
return result
|
|
219
|
+
journal = json.loads((verifier.trial_paths.trial_dir / "benchmark-lifecycle.json").read_text())
|
|
220
|
+
native = config["task"]["driver"]["kind"] != "tool-server"
|
|
221
|
+
if journal["failure"] or (not native and not {"prepare", "quiesce", "snapshot"} <= journal["phases"].keys()):
|
|
222
|
+
raise RuntimeError("benchmark lifecycle did not produce a valid snapshot")
|
|
223
|
+
directory = verifier.trial_paths.verifier_dir
|
|
224
|
+
upstream = config["task"]["grading"]["kind"] == "harbor"
|
|
225
|
+
if (directory / "reward.txt").exists() and not upstream:
|
|
226
|
+
raise RuntimeError("standard packages require only reward.json")
|
|
227
|
+
# Read the original JSON before Harbor/Pydantic numeric coercion: bools and
|
|
228
|
+
# numeric strings must not become valid binary scores via the result model.
|
|
229
|
+
if (directory / "reward.json").is_file():
|
|
230
|
+
raw = json.loads((directory / "reward.json").read_text())
|
|
231
|
+
elif upstream:
|
|
232
|
+
raw = {"reward": float((directory / "reward.txt").read_text().strip())}
|
|
233
|
+
else:
|
|
234
|
+
raise RuntimeError("metric_missing: reward.json")
|
|
235
|
+
if not isinstance(raw, dict):
|
|
236
|
+
raise RuntimeError("invalid grader metric object")
|
|
237
|
+
mapped = {}
|
|
238
|
+
for name, metric in config["metrics"].items():
|
|
239
|
+
field = config["task"]["grading"]["metric_map"][name]
|
|
240
|
+
if field not in raw:
|
|
241
|
+
raise RuntimeError(f"metric_missing: {field}")
|
|
242
|
+
value = raw[field]
|
|
243
|
+
if type(value) not in (float, int) or not math.isfinite(value) or not metric["range"][0] <= value <= metric["range"][1] or (metric["type"] == "binary" and value not in (0, 1)):
|
|
244
|
+
raise RuntimeError(f"metric_invalid: {field}")
|
|
245
|
+
mapped[name] = value
|
|
246
|
+
(directory / "benchmark-rewards.json").write_text(json.dumps({"raw": raw, "metrics": mapped, "primary_metric": config["primary_metric"], "source_task_id": config["task"]["source_task_id"], "task_digest": config["task_digest"]}, indent=2))
|
|
247
|
+
contract = config.get("score_contract", {"total_score": config["primary_metric"]})
|
|
248
|
+
if not isinstance(contract, dict) or set(contract) != {"total_score"} or contract["total_score"] not in mapped:
|
|
249
|
+
raise RuntimeError("invalid standardized score contract")
|
|
250
|
+
total = mapped[contract["total_score"]]
|
|
251
|
+
auxiliary = {name: value for name, value in raw.items() if name not in {"reward", "total_score", "process_score"}}
|
|
252
|
+
return result.model_copy(update={"rewards": {**auxiliary, "reward": total, "total_score": total}})
|