evolve-sdk 0.0.52.dev955__tar.gz → 0.0.52.dev965__tar.gz
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.
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/PKG-INFO +1 -1
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/__init__.py +17 -1
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/hosted.py +305 -7
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve_sdk.egg-info/PKG-INFO +1 -1
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/pyproject.toml +1 -1
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/LICENSE +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/MANIFEST.in +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/README.md +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/bridge/__init__.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/bridge/dist/bridge.bundle.cjs +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/_http.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/agent.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/bridge.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/browser_credentials.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/browser_profiles.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/config.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/integrations.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/managed_secrets.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/pipeline/__init__.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/pipeline/pipeline.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/pipeline/types.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/prompts/__init__.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/prompts/agent_md/judge.md +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/prompts/agent_md/reduce.md +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/prompts/agent_md/verify.md +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/prompts/user/judge.md +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/prompts/user/retry_feedback.md +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/prompts/user/verify.md +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/py.typed +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/results.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/retry.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/schema.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/sessions_client.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/storage_client.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/swarm/__init__.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/swarm/results.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/swarm/swarm.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/swarm/types.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve/utils.py +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve_sdk.egg-info/SOURCES.txt +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve_sdk.egg-info/dependency_links.txt +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve_sdk.egg-info/requires.txt +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve_sdk.egg-info/top_level.txt +0 -0
- {evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/setup.cfg +0 -0
|
@@ -45,6 +45,10 @@ from .hosted import (
|
|
|
45
45
|
AgentPage,
|
|
46
46
|
AgentResult,
|
|
47
47
|
AgentsClient,
|
|
48
|
+
AnalysisCheck,
|
|
49
|
+
AnalysisFailure,
|
|
50
|
+
AnalyzeConfig,
|
|
51
|
+
AnalyzeConfigInput,
|
|
48
52
|
ApiKey,
|
|
49
53
|
AttemptPhase,
|
|
50
54
|
AuthClient,
|
|
@@ -80,6 +84,7 @@ from .hosted import (
|
|
|
80
84
|
ImportTaskFailure,
|
|
81
85
|
ImportWarning,
|
|
82
86
|
Job,
|
|
87
|
+
JobAnalysisStats,
|
|
83
88
|
JobBuildExclusion,
|
|
84
89
|
JobCounts,
|
|
85
90
|
JobEvent,
|
|
@@ -119,7 +124,10 @@ from .hosted import (
|
|
|
119
124
|
TraceEventPage,
|
|
120
125
|
JobGrepGroup,
|
|
121
126
|
JobGrepPage,
|
|
127
|
+
Rubric,
|
|
128
|
+
RubricCriterion,
|
|
122
129
|
Trial,
|
|
130
|
+
TrialAnalysis,
|
|
123
131
|
TrialFile,
|
|
124
132
|
TrialFilePage,
|
|
125
133
|
TrialPage,
|
|
@@ -407,7 +415,7 @@ async def list_checkpoints(
|
|
|
407
415
|
await store.close()
|
|
408
416
|
|
|
409
417
|
|
|
410
|
-
__version__ = '0.0.52.
|
|
418
|
+
__version__ = '0.0.52.dev965'
|
|
411
419
|
|
|
412
420
|
__all__ = [
|
|
413
421
|
# Main classes
|
|
@@ -510,6 +518,7 @@ __all__ = [
|
|
|
510
518
|
'AgentArm',
|
|
511
519
|
'AgentDatasetStats',
|
|
512
520
|
'Job',
|
|
521
|
+
'JobAnalysisStats',
|
|
513
522
|
'JobBuildExclusion',
|
|
514
523
|
'JobCounts',
|
|
515
524
|
'JobFailure',
|
|
@@ -519,6 +528,13 @@ __all__ = [
|
|
|
519
528
|
'JobSecretInline',
|
|
520
529
|
'JobStats',
|
|
521
530
|
'JobStatus',
|
|
531
|
+
'AnalyzeConfig',
|
|
532
|
+
'AnalyzeConfigInput',
|
|
533
|
+
'AnalysisCheck',
|
|
534
|
+
'AnalysisFailure',
|
|
535
|
+
'Rubric',
|
|
536
|
+
'RubricCriterion',
|
|
537
|
+
'TrialAnalysis',
|
|
522
538
|
'TrialStatus',
|
|
523
539
|
'EvalSandboxProvider',
|
|
524
540
|
'SpendSource',
|
|
@@ -180,6 +180,13 @@ HostedErrorCode = Literal[
|
|
|
180
180
|
'concurrent_update',
|
|
181
181
|
'regrade_source_ineligible',
|
|
182
182
|
'no_regradable_trials',
|
|
183
|
+
# Analyze: a rubric that cannot rule an analysis (unknown keys, empty or
|
|
184
|
+
# duplicate criteria, bounds exceeded); one wave at a time (re-analysis is
|
|
185
|
+
# legal once the previous wave settles); a terminal job with no analyzable
|
|
186
|
+
# trial (every trial CANCELLED).
|
|
187
|
+
'invalid_rubric',
|
|
188
|
+
'analysis_already_running',
|
|
189
|
+
'no_analyzable_trials',
|
|
183
190
|
'import_not_found',
|
|
184
191
|
'import_too_large',
|
|
185
192
|
'invalid_archive',
|
|
@@ -1071,6 +1078,10 @@ class JobStats(TypedDict, total=False):
|
|
|
1071
1078
|
#: measured (``judge_spend_source`` ``'assumed_cap'``). 0 on jobs with no
|
|
1072
1079
|
#: judge-enabled tasks.
|
|
1073
1080
|
n_unmeasured_judge_trials: int
|
|
1081
|
+
#: Aggregate of the job's trace analyses; None when no trial of this job
|
|
1082
|
+
#: has ever been analyzed. Never a fabricated empty object — absence of
|
|
1083
|
+
#: analysis is stated as None, here and on each trial.
|
|
1084
|
+
analysis: Optional['JobAnalysisStats']
|
|
1074
1085
|
|
|
1075
1086
|
|
|
1076
1087
|
class JobRetryConfigInput(TypedDict, total=False):
|
|
@@ -1150,6 +1161,149 @@ JobSecretInline = TypedDict(
|
|
|
1150
1161
|
)
|
|
1151
1162
|
|
|
1152
1163
|
|
|
1164
|
+
class RubricCriterion(TypedDict):
|
|
1165
|
+
"""One analysis criterion — Harbor's RubricCriterion verbatim (their
|
|
1166
|
+
cli/quality_checker/models.py ``{name, description, guidance}``). The
|
|
1167
|
+
name becomes the key of the matching entry in ``checks``; the guidance is
|
|
1168
|
+
what the analyzer agent is instructed with. A plain dict at runtime, like
|
|
1169
|
+
every wire shape here.
|
|
1170
|
+
"""
|
|
1171
|
+
#: Criterion identifier, snake_case (it keys the result's ``checks``).
|
|
1172
|
+
#: Harbor's defaults are ``reward_hacking`` and ``task_specification``.
|
|
1173
|
+
name: str
|
|
1174
|
+
#: What the criterion evaluates, one sentence.
|
|
1175
|
+
description: str
|
|
1176
|
+
#: Evaluation guidance handed to the analyzer agent — what evidence to
|
|
1177
|
+
#: read and what PASS / FAIL / NOT_APPLICABLE mean for this criterion.
|
|
1178
|
+
guidance: str
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
class Rubric(TypedDict):
|
|
1182
|
+
"""An analysis rubric — Harbor's Rubric shape (``{criteria: [...]}``,
|
|
1183
|
+
their cli/quality_checker/models.py). The criteria set is FROZEN into
|
|
1184
|
+
each analysis at enqueue: the stored result is validated against exactly
|
|
1185
|
+
this set — a missing or extra criterion is a stored typed failure, never
|
|
1186
|
+
a partial pass.
|
|
1187
|
+
"""
|
|
1188
|
+
criteria: List[RubricCriterion]
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
class AnalyzeConfigInput(TypedDict, total=False):
|
|
1192
|
+
"""Trace-analysis configuration INPUT — Harbor's ``harbor analyze``
|
|
1193
|
+
vocabulary (their cli/analyze.py: ``--model``, ``--rubric``), the spec's
|
|
1194
|
+
``AnalyzeConfigInput`` schema.
|
|
1195
|
+
|
|
1196
|
+
PRESENCE of this object is the switch: on ``jobs().start(analyze=...)``
|
|
1197
|
+
it arms the embedded trigger (each trial is analyzed server-side right
|
|
1198
|
+
after it settles; CANCELLED trials are skipped); ``{}`` is legal and
|
|
1199
|
+
means "all defaults" — claude-haiku-4-5 over Harbor's default rubric
|
|
1200
|
+
(reward_hacking, task_specification). The analyzer always runs the
|
|
1201
|
+
claude-code harness in its own sealed sandbox; its spend is capped per
|
|
1202
|
+
analysis and metered as its own line, never blended into the trial's own
|
|
1203
|
+
bill.
|
|
1204
|
+
"""
|
|
1205
|
+
#: Model the analyzer agent runs — Harbor's ``--model``; the default is
|
|
1206
|
+
#: Harbor's own (claude-haiku-4-5) under its wire id on this platform's
|
|
1207
|
+
#: claude roster. Off-roster models are refused typed (``invalid_input``).
|
|
1208
|
+
model_name: str
|
|
1209
|
+
rubric: Rubric
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
class AnalyzeConfig(TypedDict):
|
|
1213
|
+
"""The RESOLVED trace-analysis policy — the caller's values or the
|
|
1214
|
+
defaults of the day, resolved at accept and stored (same law as
|
|
1215
|
+
``JobRetryConfig``). Echoed as ``Job.analyze`` when the job was created
|
|
1216
|
+
with ``analyze``; each analysis additionally carries the exact pair IT
|
|
1217
|
+
ran under (``Trial.analysis['model_name']`` / ``['rubric']``), which a
|
|
1218
|
+
later manual re-analysis may have changed.
|
|
1219
|
+
"""
|
|
1220
|
+
model_name: str
|
|
1221
|
+
rubric: Rubric
|
|
1222
|
+
|
|
1223
|
+
|
|
1224
|
+
class AnalysisCheck(TypedDict):
|
|
1225
|
+
"""One criterion's verdict — Harbor's QualityCheckModel verbatim (their
|
|
1226
|
+
cli/quality_checker/models.py ``{explanation, outcome}``)."""
|
|
1227
|
+
#: ``'pass'`` | ``'fail'`` | ``'not_applicable'``.
|
|
1228
|
+
outcome: str
|
|
1229
|
+
#: The analyzer's rationale, citing trial evidence.
|
|
1230
|
+
explanation: str
|
|
1231
|
+
|
|
1232
|
+
|
|
1233
|
+
class AnalysisFailure(TypedDict):
|
|
1234
|
+
"""Why an analysis FAILED — a stored typed failure, never a silent
|
|
1235
|
+
absence and never a fake pass."""
|
|
1236
|
+
#: Which part failed: ``invalid_result`` (the analyzer ran but its
|
|
1237
|
+
#: analysis.json failed validation — the message preserves every
|
|
1238
|
+
#: validator reason, one per line), ``inputs`` (the trial tree or task
|
|
1239
|
+
#: content could not be assembled), or an infrastructure stage of the
|
|
1240
|
+
#: analyzer run (``mint_key``, ``boot``, ``harness_install``, ``agent``,
|
|
1241
|
+
#: ``artifact_read``, ``lease_expired``, ...).
|
|
1242
|
+
phase: str
|
|
1243
|
+
message: str
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
class TrialAnalysis(TypedDict):
|
|
1247
|
+
"""One trace analysis of a trial — ``Trial.analysis``.
|
|
1248
|
+
|
|
1249
|
+
The result half is Harbor's AnalyzeResult verbatim (their
|
|
1250
|
+
analyze/models.py: ``summary``, ``checks`` keyed by criterion,
|
|
1251
|
+
``estimated_cost_usd``; the enclosing trial is Harbor's ``trial_name``);
|
|
1252
|
+
the rest is provenance — which model and rubric THIS analysis ran under,
|
|
1253
|
+
its lifecycle status, and its typed failure when it failed.
|
|
1254
|
+
|
|
1255
|
+
``estimated_cost_usd`` is the analyzer agent's OWN metered spend — its
|
|
1256
|
+
own line, never part of the trial's ``agent_result.cost_usd`` or the
|
|
1257
|
+
job's ``stats['cost_usd']``; the job aggregate is
|
|
1258
|
+
``stats['analysis']['cost_usd']``. None when nothing was measured, never
|
|
1259
|
+
a fabricated 0. A plain wire dict at runtime.
|
|
1260
|
+
"""
|
|
1261
|
+
id: str
|
|
1262
|
+
#: ``'queued'`` | ``'running'`` | ``'completed'`` | ``'failed'``. Every
|
|
1263
|
+
#: non-terminal analysis reaches ``completed`` or ``failed``; a worker
|
|
1264
|
+
#: death mid-run is reaped to a typed ``failed``.
|
|
1265
|
+
status: str
|
|
1266
|
+
model_name: str
|
|
1267
|
+
rubric: Rubric
|
|
1268
|
+
#: 3–5 sentence overview of the trial (Harbor's summary contract). None
|
|
1269
|
+
#: until completed.
|
|
1270
|
+
summary: Optional[str]
|
|
1271
|
+
#: One entry per rubric criterion, keys exactly the rubric's criterion
|
|
1272
|
+
#: names (the frozen-criteria law). None until completed.
|
|
1273
|
+
checks: Optional[Dict[str, AnalysisCheck]]
|
|
1274
|
+
estimated_cost_usd: Optional[float]
|
|
1275
|
+
#: Non-None exactly when status is ``'failed'``.
|
|
1276
|
+
failure: Optional[AnalysisFailure]
|
|
1277
|
+
#: When this analysis was enqueued.
|
|
1278
|
+
created_at: str
|
|
1279
|
+
#: When it settled; None while queued or running.
|
|
1280
|
+
finished_at: Optional[str]
|
|
1281
|
+
|
|
1282
|
+
|
|
1283
|
+
class JobAnalysisStats(TypedDict):
|
|
1284
|
+
"""The job-level analysis aggregate — ``stats['analysis']``.
|
|
1285
|
+
|
|
1286
|
+
Harbor's job ``analysis.json`` is a flat list of per-trial results
|
|
1287
|
+
(their analyze/models.py AnalyzeReport); each trial's own result rides
|
|
1288
|
+
``Trial.analysis``, and this object aggregates them. LATEST-per-trial: a
|
|
1289
|
+
re-analyzed trial contributes only its newest analysis, matching Harbor,
|
|
1290
|
+
where a re-run overwrites the trial directory's ``analysis.json``.
|
|
1291
|
+
"""
|
|
1292
|
+
#: Trials whose latest analysis produced a valid result.
|
|
1293
|
+
n_completed: int
|
|
1294
|
+
#: Trials whose latest analysis is a stored typed failure.
|
|
1295
|
+
n_failed: int
|
|
1296
|
+
#: Trials whose latest analysis is still queued or running.
|
|
1297
|
+
n_pending: int
|
|
1298
|
+
#: Measured spend of the LATEST analyses summed — the analyzer's own
|
|
1299
|
+
#: metered line, never part of ``stats['cost_usd']``. None when no
|
|
1300
|
+
#: analysis recorded measured spend.
|
|
1301
|
+
cost_usd: Optional[float]
|
|
1302
|
+
#: Per-criterion outcome tally over the completed latest analyses, keyed
|
|
1303
|
+
#: by criterion name: ``{n_pass, n_fail, n_not_applicable}`` each.
|
|
1304
|
+
checks: Dict[str, Dict[str, int]]
|
|
1305
|
+
|
|
1306
|
+
|
|
1153
1307
|
class JobRetryConfig(TypedDict):
|
|
1154
1308
|
"""The RESOLVED auto-retry policy a job runs under — the spec's
|
|
1155
1309
|
``RetryConfig`` schema, echoed on every job body as ``Job.retry``: the
|
|
@@ -1236,6 +1390,11 @@ class Job:
|
|
|
1236
1390
|
#: present: an older server that sends no policy reads as the retries-off
|
|
1237
1391
|
#: policy with Harbor's defaults, exactly how such a server behaves.
|
|
1238
1392
|
retry: JobRetryConfig
|
|
1393
|
+
#: The resolved embedded-analysis policy the job was created with; None
|
|
1394
|
+
#: when the create named none (a later manual :meth:`JobsClient.analyze`
|
|
1395
|
+
#: does not rewrite it — the job row states what the CREATE asked for,
|
|
1396
|
+
#: each analysis states what IT ran under). Always None on a regrade job.
|
|
1397
|
+
analyze: Optional[AnalyzeConfig]
|
|
1239
1398
|
#: The RESOLVED timeout multipliers this job's phases arm under —
|
|
1240
1399
|
#: Harbor's five flat JobConfig fields, echoed on every job body. The
|
|
1241
1400
|
#: global one is always a number (1.0 when the create request named
|
|
@@ -1585,6 +1744,13 @@ class Trial:
|
|
|
1585
1744
|
#: serve (:class:`evolve.results.UsageReading`). None = the meter never
|
|
1586
1745
|
#: answered, never zero.
|
|
1587
1746
|
usage: Optional[UsageReading] = None
|
|
1747
|
+
#: The trial's LATEST trace analysis (:class:`TrialAnalysis`); None when
|
|
1748
|
+
#: the trial has never been analyzed — never a fabricated empty object. A
|
|
1749
|
+
#: re-analysis (same job, different rubric or model) replaces what this
|
|
1750
|
+
#: field serves, matching Harbor, where a re-run overwrites the trial
|
|
1751
|
+
#: directory's analysis.json; earlier analyses stay stored as the audit
|
|
1752
|
+
#: record.
|
|
1753
|
+
analysis: Optional[TrialAnalysis] = None
|
|
1588
1754
|
|
|
1589
1755
|
|
|
1590
1756
|
@dataclass
|
|
@@ -2404,6 +2570,12 @@ def _map_job(data: Dict[str, Any]) -> Job:
|
|
|
2404
2570
|
# server that sends no policy reads as the retries-off policy, which
|
|
2405
2571
|
# is exactly how such a server behaves; every field always present.
|
|
2406
2572
|
retry=_map_retry_config(data.get('retry')),
|
|
2573
|
+
# The resolved embedded-analysis policy, or None: a create that named
|
|
2574
|
+
# none, and an older server that sends nothing, both mean "no
|
|
2575
|
+
# embedded analysis" — exactly what None states.
|
|
2576
|
+
analyze=(
|
|
2577
|
+
data['analyze'] if isinstance(data.get('analyze'), dict) else None
|
|
2578
|
+
),
|
|
2407
2579
|
# Timeout multipliers: an older server sends none — 1.0 / None reads
|
|
2408
2580
|
# as "every phase at 1.0", exactly how such a server behaves.
|
|
2409
2581
|
timeout_multiplier=(
|
|
@@ -2606,6 +2778,12 @@ def _map_trial(data: Dict[str, Any]) -> Trial:
|
|
|
2606
2778
|
# older servers and on every non-judge trial — None either way.
|
|
2607
2779
|
judge_result=_map_judge_result(data.get('judge_result')),
|
|
2608
2780
|
judge_spend_source=data.get('judge_spend_source'),
|
|
2781
|
+
# The trial's LATEST trace analysis. Defensive like gpu_cost: absent
|
|
2782
|
+
# (an older server, or a never-analyzed trial) and malformed both
|
|
2783
|
+
# read None — "never analyzed", never a fabricated empty object.
|
|
2784
|
+
analysis=(
|
|
2785
|
+
data['analysis'] if isinstance(data.get('analysis'), dict) else None
|
|
2786
|
+
),
|
|
2609
2787
|
environment_setup=_map_timing(data.get('environment_setup')),
|
|
2610
2788
|
agent_setup=_map_timing(data.get('agent_setup')),
|
|
2611
2789
|
agent_execution=_map_timing(data.get('agent_execution')),
|
|
@@ -4320,6 +4498,7 @@ class JobsClient:
|
|
|
4320
4498
|
max_trial_spend_usd: Optional[float] = None,
|
|
4321
4499
|
sandbox_provider: Optional[str] = None,
|
|
4322
4500
|
retry: Optional[JobRetryConfigInput] = None,
|
|
4501
|
+
analyze: Optional[AnalyzeConfigInput] = None,
|
|
4323
4502
|
timeout_multiplier: Optional[float] = None,
|
|
4324
4503
|
agent_timeout_multiplier: Optional[float] = None,
|
|
4325
4504
|
verifier_timeout_multiplier: Optional[float] = None,
|
|
@@ -4359,7 +4538,15 @@ class JobsClient:
|
|
|
4359
4538
|
``'include_exceptions'`` has no such split: None, an omitted key,
|
|
4360
4539
|
and the empty list ``[]`` all mean no filter — Harbor's include
|
|
4361
4540
|
check treats the empty set exactly like None, so ``[]`` never means
|
|
4362
|
-
"retry nothing".
|
|
4541
|
+
"retry nothing". ``analyze`` arms the EMBEDDED trace-analysis
|
|
4542
|
+
trigger (Harbor's ``harbor analyze`` vocabulary, the spec's
|
|
4543
|
+
AnalyzeConfigInput): PRESENCE is the switch — each trial is analyzed
|
|
4544
|
+
server-side right after it settles (CANCELLED trials are skipped),
|
|
4545
|
+
``{}`` means "all defaults" (claude-haiku-4-5, Harbor's default
|
|
4546
|
+
rubric), and the response echoes the RESOLVED policy as
|
|
4547
|
+
``Job.analyze`` (:class:`AnalyzeConfig`); omitted, no embedded
|
|
4548
|
+
analysis runs and :meth:`analyze` remains the manual door. The five
|
|
4549
|
+
``*timeout_multiplier`` arguments are
|
|
4363
4550
|
Harbor's timeout knobs verbatim: ``timeout_multiplier`` multiplies
|
|
4364
4551
|
every TASK-DECLARED timeout for this job's runs (default 1.0;
|
|
4365
4552
|
values below 1 shrink), and each phase-specific one —
|
|
@@ -4431,6 +4618,8 @@ class JobsClient:
|
|
|
4431
4618
|
body['sandbox_provider'] = sandbox_provider
|
|
4432
4619
|
if retry is not None:
|
|
4433
4620
|
body['retry'] = retry
|
|
4621
|
+
if analyze is not None:
|
|
4622
|
+
body['analyze'] = analyze
|
|
4434
4623
|
if timeout_multiplier is not None:
|
|
4435
4624
|
body['timeout_multiplier'] = timeout_multiplier
|
|
4436
4625
|
if agent_timeout_multiplier is not None:
|
|
@@ -4916,6 +5105,110 @@ class JobsClient:
|
|
|
4916
5105
|
)
|
|
4917
5106
|
return _map_job(raw)
|
|
4918
5107
|
|
|
5108
|
+
async def analyze(
|
|
5109
|
+
self,
|
|
5110
|
+
id: str,
|
|
5111
|
+
*,
|
|
5112
|
+
model_name: Optional[str] = None,
|
|
5113
|
+
rubric: Optional[Rubric] = None,
|
|
5114
|
+
) -> Job:
|
|
5115
|
+
"""Analyze a terminal job's trial traces (rubric-driven, Harbor's
|
|
5116
|
+
``harbor analyze``), server-side.
|
|
5117
|
+
|
|
5118
|
+
For each trial the analyzer agent reads the trial's Harbor-shape tree
|
|
5119
|
+
plus its original task and rules every rubric criterion, storing the
|
|
5120
|
+
result on the trial (``Trial.analysis``) and the aggregate on the job
|
|
5121
|
+
(``stats['analysis']``). THE RESPONSE IS THE JOB, its analyses
|
|
5122
|
+
enqueued — analyses are not a separate resource; follow them with
|
|
5123
|
+
:meth:`watch_analysis`, or poll the job's trials. This is also the
|
|
5124
|
+
RE-analysis path: calling again (same job, different rubric or
|
|
5125
|
+
model) runs a fresh wave once the previous one has settled. Both
|
|
5126
|
+
arguments omitted means the defaults: claude-haiku-4-5 over Harbor's
|
|
5127
|
+
default rubric (reward_hacking, task_specification). CANCELLED
|
|
5128
|
+
trials are never analyzed.
|
|
5129
|
+
|
|
5130
|
+
The server owns every acceptance refusal, surfaced typed:
|
|
5131
|
+
``job_not_terminal``, ``invalid_rubric`` (unknown keys named, empty
|
|
5132
|
+
or duplicate criteria, bounds), ``invalid_input`` (off-roster
|
|
5133
|
+
model), ``analysis_already_running`` (one wave at a time),
|
|
5134
|
+
``no_analyzable_trials`` (every trial CANCELLED).
|
|
5135
|
+
"""
|
|
5136
|
+
body: Dict[str, Any] = {}
|
|
5137
|
+
if model_name is not None:
|
|
5138
|
+
body['model_name'] = model_name
|
|
5139
|
+
if rubric is not None:
|
|
5140
|
+
body['rubric'] = rubric
|
|
5141
|
+
raw = await self._http.request_json(
|
|
5142
|
+
f'/api/jobs/{urllib.parse.quote(id)}/analyze', method='POST', body=body
|
|
5143
|
+
)
|
|
5144
|
+
return _map_job(raw)
|
|
5145
|
+
|
|
5146
|
+
async def watch_analysis(
|
|
5147
|
+
self,
|
|
5148
|
+
id: str,
|
|
5149
|
+
*,
|
|
5150
|
+
on_stats: Optional[Callable[[Job], None]] = None,
|
|
5151
|
+
poll_interval_s: float = 2.0,
|
|
5152
|
+
timeout_s: Optional[float] = None,
|
|
5153
|
+
) -> Job:
|
|
5154
|
+
"""Follow a job's analysis wave to its settled end.
|
|
5155
|
+
|
|
5156
|
+
Analyses have no event stream — the contract's own words: poll the
|
|
5157
|
+
job's trials to watch them settle — so this polls :meth:`get` until
|
|
5158
|
+
``stats['analysis']`` reports nothing pending, and returns the final
|
|
5159
|
+
job; the per-trial results then ride the job's trials
|
|
5160
|
+
(``Trial.analysis``). ``on_stats`` fires on every observed change of
|
|
5161
|
+
the analysis tally (including the first non-None one seen), with the
|
|
5162
|
+
job body the observation came from. A still-None tally is the
|
|
5163
|
+
enqueue race after an accepted :meth:`analyze` and is watched
|
|
5164
|
+
through, never misread as "never analyzed" — so on a job that was
|
|
5165
|
+
NEVER analyzed this polls indefinitely (until ``timeout_s``): call
|
|
5166
|
+
it after :meth:`analyze`, as the CLI always does. It is the MANUAL
|
|
5167
|
+
wave's companion, not the embedded trigger's: on a still-RUNNING job
|
|
5168
|
+
created with ``analyze``, ``n_pending`` can touch 0 between trial
|
|
5169
|
+
settles, so the watch can return before every trial has been
|
|
5170
|
+
analyzed.
|
|
5171
|
+
|
|
5172
|
+
``timeout_s`` bounds the whole watch and raises
|
|
5173
|
+
:class:`TimeoutError`. A rate limit or transient outage mid-watch is
|
|
5174
|
+
a delay, not an outcome: a 429/503 sleeps the server's
|
|
5175
|
+
``retry_after_sec`` and keeps watching.
|
|
5176
|
+
"""
|
|
5177
|
+
if poll_interval_s <= 0:
|
|
5178
|
+
raise ValueError('poll_interval_s must be positive')
|
|
5179
|
+
deadline = time.monotonic() + timeout_s if timeout_s is not None else None
|
|
5180
|
+
last_tally: Optional['tuple[int, int, int]'] = None
|
|
5181
|
+
while True:
|
|
5182
|
+
try:
|
|
5183
|
+
job = await self.get(id)
|
|
5184
|
+
except EvolveAPIError as error:
|
|
5185
|
+
if error.status not in (429, 503):
|
|
5186
|
+
raise
|
|
5187
|
+
if deadline is not None and time.monotonic() >= deadline:
|
|
5188
|
+
raise TimeoutError(
|
|
5189
|
+
f'watch_analysis({id!r}) timed out after {timeout_s}s'
|
|
5190
|
+
) from error
|
|
5191
|
+
await asyncio.sleep(
|
|
5192
|
+
max(error.retry_after_sec or 0.0, poll_interval_s)
|
|
5193
|
+
)
|
|
5194
|
+
continue
|
|
5195
|
+
analysis = job.stats.get('analysis')
|
|
5196
|
+
if isinstance(analysis, dict):
|
|
5197
|
+
tally = (
|
|
5198
|
+
int(analysis.get('n_completed', 0)),
|
|
5199
|
+
int(analysis.get('n_failed', 0)),
|
|
5200
|
+
int(analysis.get('n_pending', 0)),
|
|
5201
|
+
)
|
|
5202
|
+
if tally != last_tally:
|
|
5203
|
+
last_tally = tally
|
|
5204
|
+
if on_stats is not None:
|
|
5205
|
+
on_stats(job)
|
|
5206
|
+
if tally[2] == 0:
|
|
5207
|
+
return job
|
|
5208
|
+
if deadline is not None and time.monotonic() >= deadline:
|
|
5209
|
+
raise TimeoutError(f'watch_analysis({id!r}) timed out after {timeout_s}s')
|
|
5210
|
+
await asyncio.sleep(poll_interval_s)
|
|
5211
|
+
|
|
4919
5212
|
async def download(
|
|
4920
5213
|
self,
|
|
4921
5214
|
id: str,
|
|
@@ -4926,11 +5219,16 @@ class JobsClient:
|
|
|
4926
5219
|
standard job-directory layout (deterministic bytes).
|
|
4927
5220
|
|
|
4928
5221
|
The archive extracts to ``job-<id>/`` with ``config.json``,
|
|
4929
|
-
``result.json`` (stats incl. ``pass_at_k``)
|
|
4930
|
-
``
|
|
4931
|
-
|
|
4932
|
-
``
|
|
4933
|
-
``
|
|
5222
|
+
``lock.json``, ``result.json`` (stats incl. ``pass_at_k``) and
|
|
5223
|
+
``job.log``, and per trial its ``config.json``, ``lock.json``,
|
|
5224
|
+
``result.json`` (``step_results`` on multi-step trials),
|
|
5225
|
+
``trial.log``, ``agent/trajectory.json`` (the normalized ATIF
|
|
5226
|
+
trajectory), ``agent/{stdout,stderr}.log``, ``agent/sessions/``,
|
|
5227
|
+
``verifier/test-stdout.txt``, ``verifier/reward.json``, the raw
|
|
5228
|
+
``verifier/reward.txt`` (only when the grader wrote one),
|
|
5229
|
+
``steps/<name>/verifier/reward.json`` (multi-step trials only),
|
|
5230
|
+
``exception.txt``, and ``artifacts/`` with its always-present
|
|
5231
|
+
``manifest.json`` — absent artifacts are absent files.
|
|
4934
5232
|
|
|
4935
5233
|
Returns the archive bytes — verified against the response's
|
|
4936
5234
|
Content-Length and, when the server states one, its digest — or, when
|
|
@@ -5358,7 +5656,7 @@ class HostedEvolve:
|
|
|
5358
5656
|
|
|
5359
5657
|
@property
|
|
5360
5658
|
def jobs(self) -> JobsClient:
|
|
5361
|
-
"""Jobs: start, watch, compare, resume, retry, regrade, download."""
|
|
5659
|
+
"""Jobs: start, watch, compare, resume, retry, regrade, analyze, download."""
|
|
5362
5660
|
if self._jobs is None:
|
|
5363
5661
|
self._jobs = JobsClient(self._config)
|
|
5364
5662
|
return self._jobs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{evolve_sdk-0.0.52.dev955 → evolve_sdk-0.0.52.dev965}/evolve_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|