vis-agent 0.2.41.dev7619__tar.gz → 0.2.42.dev7622__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.
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/PKG-INFO +1 -1
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/pyproject.toml +1 -1
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/__init__.py +14 -9
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/bench.py +5 -4
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/harbor.py +68 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/rounds.py +43 -16
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_harbor.py +27 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_rounds.py +42 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/.gitignore +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/LICENSE +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/README.md +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-arguments.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-cases.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-copy.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-execution.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-groups.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-read-session.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-reads.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-results.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-tables.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/diff.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/live-primitives.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/session-health.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/view.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/activity.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/agents.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/automations.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/common.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/config.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/content.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/council.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/diff.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/evals.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/gateway.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/improve.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/provider.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/rooms.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/symbol.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/toggle.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/schema/view.json +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/examples/greeter/tests/test_greeter.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/hatch_build.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/__init__.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/_contracts.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/_outside.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/_wire.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/activity.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/__init__.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/_agent.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/_agents.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/_client.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/_council.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/_extensions.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/_local.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/_gateway.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/compare.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/labels.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/leaderboard.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/repeats.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/stages.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/stats.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/trajectory.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/extension.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/extension_package.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/py.typed +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/rooms.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/views.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/conftest.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_activity.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_agent.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_agent_extension_lifecycle.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_agent_extensions.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_agents.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_api_guides.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_attachments.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_authoring.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_catalog.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_catalog_recipe.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_ci_report_recipe.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_client.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_client_extensions_engine.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_code_quality_recipe.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_contracts.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_council.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_declarations.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_engine.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_engine_docstrings.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_bench.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_compare.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_gateway.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_labels.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_repeats.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_stages.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_stats.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_evals_trajectory.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_extension_package.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_extension_reference.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_gateway_performance.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_goals.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_live_primitives.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_live_wait.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_local.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_method_typing.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_monitor_recipe.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_namespace.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_outside.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_providers.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_read_session.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_record_arguments.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_registration.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_repair_hooks.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_result_sequence.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_rooms.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_scoped_settings.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_sdk_bootstrap.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_sdk_guide.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_sdk_typing.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_structured_live.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_symbol_contract.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_testing.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_text_list_arguments.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_tools.py +0 -0
- {vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vis-agent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.42.dev7622
|
|
4
4
|
Summary: The Vis Python SDK — author extensions and control local or remote engines.
|
|
5
5
|
Project-URL: Homepage, https://vis.blockether.com
|
|
6
6
|
Project-URL: Documentation, https://vis.blockether.com
|
|
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "vis-agent"
|
|
11
|
-
version = "0.2.
|
|
11
|
+
version = "0.2.42.dev7622"
|
|
12
12
|
description = "The Vis Python SDK — author extensions and control local or remote engines."
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
requires-python = ">=3.11"
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""Evaluation tools: read Vis runs and Harbor jobs, and compare their results.
|
|
2
2
|
|
|
3
|
-
Read one Vis run with `load_trajectory` or `parse_trajectory`. Read the
|
|
4
|
-
Harbor
|
|
5
|
-
`summarize`. Seal a finished round with
|
|
6
|
-
`compare_cases`, or test paired changes with
|
|
7
|
-
Score repeated attempts with `task_attempts` and
|
|
8
|
-
|
|
9
|
-
diagnose it with `bench_report` and `diagnose
|
|
10
|
-
|
|
11
|
-
causes against human labels with `agreement`, and judge stages with
|
|
3
|
+
Read one Vis run with `load_trajectory` or `parse_trajectory`. Read the cases of a
|
|
4
|
+
Harbor dataset with `read_tasks`, and the trials of a job or round with `read_trials`.
|
|
5
|
+
Count the trials with `case_results` or `summarize`. Seal a finished round with
|
|
6
|
+
`seal_round`. Compare two results with `compare_cases`, or test paired changes with
|
|
7
|
+
`paired_change` and `mcnemar_exact`. Score repeated attempts with `task_attempts` and
|
|
8
|
+
`repeat_stats`. Summarize a round from its Harbor job folders with `round_summary`,
|
|
9
|
+
then score and diagnose it with `bench_report` and `diagnose`. Keep human cause labels
|
|
10
|
+
with `save_label`. Place a round on a Harbor Hub board with `read_leaderboard`. Check
|
|
11
|
+
automatic causes against human labels with `agreement`, and judge stages with
|
|
12
|
+
`stage_agreement`. For installation and sessions, use the
|
|
12
13
|
[Python SDK guide](https://vis.blockether.com/python-sdk.html).
|
|
13
14
|
"""
|
|
14
15
|
|
|
@@ -38,9 +39,11 @@ from .bench import (
|
|
|
38
39
|
)
|
|
39
40
|
from .compare import CaseChange, CaseComparison, RouteTest, compare_cases
|
|
40
41
|
from .harbor import (
|
|
42
|
+
HarborTask,
|
|
41
43
|
HarborTrial,
|
|
42
44
|
RunSummary,
|
|
43
45
|
case_results,
|
|
46
|
+
read_tasks,
|
|
44
47
|
read_trial,
|
|
45
48
|
read_trials,
|
|
46
49
|
seal_digest,
|
|
@@ -117,6 +120,7 @@ __all__ = [
|
|
|
117
120
|
"Diagnosis",
|
|
118
121
|
"Finding",
|
|
119
122
|
"FunctionCall",
|
|
123
|
+
"HarborTask",
|
|
120
124
|
"HarborTrial",
|
|
121
125
|
"Leaderboard",
|
|
122
126
|
"LeaderboardRow",
|
|
@@ -162,6 +166,7 @@ __all__ = [
|
|
|
162
166
|
"paired_change",
|
|
163
167
|
"parse_trajectory",
|
|
164
168
|
"read_leaderboard",
|
|
169
|
+
"read_tasks",
|
|
165
170
|
"read_trial",
|
|
166
171
|
"read_trials",
|
|
167
172
|
"repeat_stats",
|
|
@@ -359,7 +359,7 @@ def attempt_cause(trial: Mapping, summary: Mapping, limits: Mapping[str, float])
|
|
|
359
359
|
"""Give the `diagnose` cause of one attempt, or `solved`.
|
|
360
360
|
|
|
361
361
|
The causes are `dead_tools`, `agent_errors`, `agent_timeouts`, `unscored`,
|
|
362
|
-
`early_stops` and `other`.
|
|
362
|
+
`early_stops` and `other`. Empty `limits` use the `agent_limits` of the summary.
|
|
363
363
|
"""
|
|
364
364
|
outcome = attempt_outcome(trial)
|
|
365
365
|
if outcome == "solved":
|
|
@@ -373,7 +373,7 @@ def attempt_cause(trial: Mapping, summary: Mapping, limits: Mapping[str, float])
|
|
|
373
373
|
if outcome == "unscored":
|
|
374
374
|
return "unscored"
|
|
375
375
|
seconds = trial.get("agent_seconds")
|
|
376
|
-
limit = limits.get(_short(trial.get("task")))
|
|
376
|
+
limit = (limits or summary.get("agent_limits") or {}).get(_short(trial.get("task")))
|
|
377
377
|
if seconds and limit and seconds / limit <= EARLY_STOP_SHARE:
|
|
378
378
|
return "early_stops"
|
|
379
379
|
return "other"
|
|
@@ -430,7 +430,8 @@ def diagnose(
|
|
|
430
430
|
"""Find where a round loses tasks, from its `round_summary` data.
|
|
431
431
|
|
|
432
432
|
`limits` gives the agent time limit of each task, from `agent_limits`. Without
|
|
433
|
-
limits,
|
|
433
|
+
limits, it uses the `agent_limits` of the summary, and without those no attempt
|
|
434
|
+
counts as an early stop. `labels` maps a trial name to a human
|
|
434
435
|
label with a `cause` value.
|
|
435
436
|
"""
|
|
436
437
|
outcomes = {
|
|
@@ -440,7 +441,7 @@ def diagnose(
|
|
|
440
441
|
scored = int(summary.get("scored_tasks") or 0)
|
|
441
442
|
solved = len(outcomes.get("solved", ()))
|
|
442
443
|
latest = latest_trials(summary)
|
|
443
|
-
limits = limits or {}
|
|
444
|
+
limits = limits or summary.get("agent_limits") or {}
|
|
444
445
|
|
|
445
446
|
def notes(outcome: str) -> list[TaskNote]:
|
|
446
447
|
return [
|
|
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
import hashlib
|
|
13
13
|
import json
|
|
14
14
|
import time
|
|
15
|
+
import tomllib
|
|
15
16
|
from collections.abc import Iterable, Mapping, Sequence
|
|
16
17
|
from dataclasses import dataclass
|
|
17
18
|
from pathlib import Path
|
|
@@ -21,9 +22,11 @@ from typing import Any
|
|
|
21
22
|
from .stats import cluster_interval
|
|
22
23
|
|
|
23
24
|
__all__ = [
|
|
25
|
+
"HarborTask",
|
|
24
26
|
"HarborTrial",
|
|
25
27
|
"RunSummary",
|
|
26
28
|
"case_results",
|
|
29
|
+
"read_tasks",
|
|
27
30
|
"read_trial",
|
|
28
31
|
"read_trials",
|
|
29
32
|
"seal_digest",
|
|
@@ -33,6 +36,8 @@ __all__ = [
|
|
|
33
36
|
]
|
|
34
37
|
|
|
35
38
|
RESULT_FILE = "result.json"
|
|
39
|
+
TASK_FILE = "task.toml"
|
|
40
|
+
INSTRUCTION_FILE = "instruction.md"
|
|
36
41
|
|
|
37
42
|
|
|
38
43
|
@dataclass(frozen=True, slots=True)
|
|
@@ -301,3 +306,66 @@ def seal_round(
|
|
|
301
306
|
}
|
|
302
307
|
body["digest"] = seal_digest(body)
|
|
303
308
|
return body
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@dataclass(frozen=True, slots=True)
|
|
312
|
+
class HarborTask:
|
|
313
|
+
"""One Harbor task, read from its `task.toml` and `instruction.md`.
|
|
314
|
+
|
|
315
|
+
`name` is the task folder name, as in `case_results`. `instruction` is the prompt
|
|
316
|
+
that the agent gets. The timeouts are in seconds, or None when `task.toml` gives
|
|
317
|
+
none. `gpus` is the GPU count that the environment needs. `metadata` holds the
|
|
318
|
+
`[metadata]` table, for example the `lang` of an e2e scenario.
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
name: str
|
|
322
|
+
instruction: str
|
|
323
|
+
agent_timeout_sec: float | None
|
|
324
|
+
verifier_timeout_sec: float | None
|
|
325
|
+
gpus: int
|
|
326
|
+
metadata: Mapping[str, Any]
|
|
327
|
+
path: Path
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _seconds(table: object) -> float | None:
|
|
331
|
+
value = table.get("timeout_sec") if isinstance(table, Mapping) else None
|
|
332
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
333
|
+
return None
|
|
334
|
+
return float(value)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def read_tasks(dataset: Path | str) -> tuple[HarborTask, ...]:
|
|
338
|
+
"""Read the tasks of a Harbor dataset directory, sorted by name.
|
|
339
|
+
|
|
340
|
+
Each task is a folder with a `task.toml`. Other folders do not count. A directory
|
|
341
|
+
that does not exist raises `FileNotFoundError`. Compile the e2e scenarios into a
|
|
342
|
+
dataset with `python -m vis_harbor.dataset --out DIR`, then read it here.
|
|
343
|
+
"""
|
|
344
|
+
root = Path(dataset)
|
|
345
|
+
if not root.is_dir():
|
|
346
|
+
raise FileNotFoundError(f"no Harbor dataset directory at {root}")
|
|
347
|
+
tasks = []
|
|
348
|
+
for config in sorted(root.glob(f"*/{TASK_FILE}")):
|
|
349
|
+
info = tomllib.loads(config.read_text(encoding="utf-8"))
|
|
350
|
+
instruction = config.parent / INSTRUCTION_FILE
|
|
351
|
+
environment = info.get("environment")
|
|
352
|
+
gpus = environment.get("gpus") if isinstance(environment, dict) else None
|
|
353
|
+
metadata = info.get("metadata")
|
|
354
|
+
tasks.append(
|
|
355
|
+
HarborTask(
|
|
356
|
+
name=config.parent.name,
|
|
357
|
+
instruction=instruction.read_text(encoding="utf-8")
|
|
358
|
+
if instruction.is_file()
|
|
359
|
+
else "",
|
|
360
|
+
agent_timeout_sec=_seconds(info.get("agent")),
|
|
361
|
+
verifier_timeout_sec=_seconds(info.get("verifier")),
|
|
362
|
+
gpus=gpus
|
|
363
|
+
if isinstance(gpus, int) and not isinstance(gpus, bool)
|
|
364
|
+
else 0,
|
|
365
|
+
metadata=MappingProxyType(
|
|
366
|
+
dict(metadata) if isinstance(metadata, dict) else {}
|
|
367
|
+
),
|
|
368
|
+
path=config.parent,
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
return tuple(tasks)
|
|
@@ -16,13 +16,12 @@ from collections import Counter
|
|
|
16
16
|
from datetime import datetime
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
|
-
from .bench import attempt_outcome
|
|
19
|
+
from .bench import agent_limits, attempt_outcome
|
|
20
20
|
from .trajectory import Step, Trajectory, load_trajectory
|
|
21
21
|
|
|
22
22
|
__all__ = ["round_summary"]
|
|
23
23
|
|
|
24
24
|
_TRAJECTORY = "agent/trajectory.json"
|
|
25
|
-
_PREFIX = "terminal-bench/"
|
|
26
25
|
_KIND = re.compile(r"[A-Za-z0-9_.:-]{1,100}")
|
|
27
26
|
|
|
28
27
|
|
|
@@ -148,8 +147,23 @@ def _tests(path: Path) -> dict | None:
|
|
|
148
147
|
return {key: summary[key] for key in keys if _count(summary.get(key))} or None
|
|
149
148
|
|
|
150
149
|
|
|
151
|
-
def
|
|
152
|
-
result
|
|
150
|
+
def _results(jobs: Path) -> list[tuple[Path, dict]]:
|
|
151
|
+
"""Read each trial `result.json` one or two levels below `jobs`, in path order.
|
|
152
|
+
|
|
153
|
+
A job writes its own `result.json` without `trial_name`. It is not a trial.
|
|
154
|
+
"""
|
|
155
|
+
found = []
|
|
156
|
+
for path in sorted({*jobs.glob("*/result.json"), *jobs.glob("*/*/result.json")}):
|
|
157
|
+
try:
|
|
158
|
+
result = json.loads(path.read_text(encoding="utf-8"))
|
|
159
|
+
except (OSError, ValueError):
|
|
160
|
+
continue
|
|
161
|
+
if isinstance(result, dict) and result.get("trial_name"):
|
|
162
|
+
found.append((path, result))
|
|
163
|
+
return found
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _trial(result_path: Path, result: dict, root: Path, model: str | None) -> dict:
|
|
153
167
|
folder = result_path.parent
|
|
154
168
|
agent = result.get("agent_result") or {}
|
|
155
169
|
verifier = result.get("verifier_result") or {}
|
|
@@ -207,6 +221,7 @@ def _trial(result_path: Path, root: Path, model: str | None) -> dict:
|
|
|
207
221
|
check = result.get("verifier") or {}
|
|
208
222
|
return {
|
|
209
223
|
"job": folder.parent.name,
|
|
224
|
+
"dataset": result.get("source"),
|
|
210
225
|
"task": result.get("task_name"),
|
|
211
226
|
"trial": result.get("trial_name"),
|
|
212
227
|
"status": status,
|
|
@@ -253,7 +268,16 @@ def _usage_totals(trials: list[dict]) -> dict:
|
|
|
253
268
|
|
|
254
269
|
|
|
255
270
|
def _name(task) -> str | None:
|
|
256
|
-
|
|
271
|
+
"""Name a task without its dataset, as `bench_report` and `diagnose` do."""
|
|
272
|
+
return task.rsplit("/", 1)[-1] if isinstance(task, str) and task else None
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _dataset(trials: list[dict], dataset_dir: Path | str | None) -> str:
|
|
276
|
+
"""Name the dataset: the most common Harbor `source` of the trials, or the folder."""
|
|
277
|
+
sources = Counter(trial["dataset"] for trial in trials if trial["dataset"])
|
|
278
|
+
if sources:
|
|
279
|
+
return sources.most_common(1)[0][0]
|
|
280
|
+
return Path(dataset_dir).name if dataset_dir is not None else "unknown"
|
|
257
281
|
|
|
258
282
|
|
|
259
283
|
def round_summary(
|
|
@@ -261,22 +285,24 @@ def round_summary(
|
|
|
261
285
|
dataset_dir: Path | str | None = None,
|
|
262
286
|
*,
|
|
263
287
|
model: str | None = None,
|
|
264
|
-
dataset: str =
|
|
288
|
+
dataset: str | None = None,
|
|
265
289
|
) -> dict:
|
|
266
290
|
"""Summarize every trial under `jobs_dir`, as `bench_report` and `diagnose` read it.
|
|
267
291
|
|
|
268
|
-
`
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
the
|
|
272
|
-
`
|
|
292
|
+
`jobs_dir` is one Harbor job folder or a folder of jobs. `dataset_dir` holds one
|
|
293
|
+
folder with a `task.toml` for each task. It gives the total, the GPU tasks, the
|
|
294
|
+
tasks without an attempt and the `agent_limits` that `diagnose` uses. Without it,
|
|
295
|
+
the total is the attempted tasks. `dataset` names the dataset. Without it, the
|
|
296
|
+
name is the `source` that Harbor wrote in the trials. `model` is the model name or
|
|
297
|
+
the `provider/model` route of the agent. An attempt of another model does not
|
|
298
|
+
count. With no `model`, any model counts. Each task counts once, by its latest
|
|
299
|
+
scored attempt.
|
|
273
300
|
"""
|
|
274
301
|
jobs = Path(jobs_dir)
|
|
275
302
|
root = jobs.parent
|
|
276
|
-
trials = [
|
|
277
|
-
_trial(path, root, model) for path in sorted(jobs.glob("*/*/result.json"))
|
|
278
|
-
]
|
|
303
|
+
trials = [_trial(path, result, root, model) for path, result in _results(jobs)]
|
|
279
304
|
attempted = {name for trial in trials if (name := _name(trial["task"]))}
|
|
305
|
+
limits = {} if dataset_dir is None else agent_limits(dataset_dir)
|
|
280
306
|
if dataset_dir is None:
|
|
281
307
|
tasks, gpu_tasks = sorted(attempted), []
|
|
282
308
|
else:
|
|
@@ -299,7 +325,7 @@ def round_summary(
|
|
|
299
325
|
(trial for trial in trials if trial["scored"] and _name(trial["task"])),
|
|
300
326
|
key=lambda trial: trial["finished_at"],
|
|
301
327
|
):
|
|
302
|
-
scored[trial["task"]
|
|
328
|
+
scored[str(_name(trial["task"]))] = trial
|
|
303
329
|
model_tasks = {
|
|
304
330
|
name
|
|
305
331
|
for trial in trials
|
|
@@ -315,10 +341,11 @@ def round_summary(
|
|
|
315
341
|
if streak and streak["iterations"] > 1:
|
|
316
342
|
repeated.append({"trial": f"{trial['job']}/{trial['trial']}", **streak})
|
|
317
343
|
return {
|
|
318
|
-
"dataset": dataset,
|
|
344
|
+
"dataset": dataset or _dataset(trials, dataset_dir),
|
|
319
345
|
"total_dataset_tasks": len(tasks),
|
|
320
346
|
"gpu_required_tasks": gpu_tasks,
|
|
321
347
|
"unattempted_tasks": [task for task in tasks if task not in attempted],
|
|
348
|
+
"agent_limits": limits,
|
|
322
349
|
"attempts": len(trials),
|
|
323
350
|
"verified_attempts": len(verified),
|
|
324
351
|
"exception_attempts": sum(trial["status"] == "exception" for trial in trials),
|
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import pytest
|
|
7
7
|
from blockether.vis.evals import (
|
|
8
8
|
case_results,
|
|
9
|
+
read_tasks,
|
|
9
10
|
read_trial,
|
|
10
11
|
read_trials,
|
|
11
12
|
seal_digest,
|
|
@@ -226,3 +227,29 @@ def test_seal_round_has_one_row_for_each_trial_and_a_digest(tmp_path):
|
|
|
226
227
|
assert seal["digest"] == seal_digest(seal) == expected
|
|
227
228
|
seal["rows"][0]["reward"] = 0.0
|
|
228
229
|
assert seal_digest(seal) != seal["digest"]
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_read_tasks_reads_the_task_folders_of_a_dataset(tmp_path):
|
|
233
|
+
task = tmp_path / "dataset" / "fix-git"
|
|
234
|
+
task.mkdir(parents=True)
|
|
235
|
+
(task / "task.toml").write_text(
|
|
236
|
+
'[metadata]\nlang = "py"\n[agent]\ntimeout_sec = 600\n'
|
|
237
|
+
"[verifier]\ntimeout_sec = 300.0\n[environment]\ngpus = 1\n"
|
|
238
|
+
)
|
|
239
|
+
(task / "instruction.md").write_text("Fix the repository.")
|
|
240
|
+
bare = tmp_path / "dataset" / "bare"
|
|
241
|
+
bare.mkdir()
|
|
242
|
+
(bare / "task.toml").write_text("")
|
|
243
|
+
(tmp_path / "dataset" / "notes").mkdir()
|
|
244
|
+
|
|
245
|
+
found = {task.name: task for task in read_tasks(tmp_path / "dataset")}
|
|
246
|
+
|
|
247
|
+
assert sorted(found) == ["bare", "fix-git"]
|
|
248
|
+
assert found["fix-git"].instruction == "Fix the repository."
|
|
249
|
+
assert found["fix-git"].agent_timeout_sec == 600.0
|
|
250
|
+
assert found["fix-git"].verifier_timeout_sec == 300.0
|
|
251
|
+
assert found["fix-git"].gpus == 1
|
|
252
|
+
assert found["fix-git"].metadata == {"lang": "py"}
|
|
253
|
+
assert (found["bare"].instruction, found["bare"].agent_timeout_sec) == ("", None)
|
|
254
|
+
with pytest.raises(FileNotFoundError):
|
|
255
|
+
read_tasks(tmp_path / "missing")
|
|
@@ -208,3 +208,45 @@ def test_round_summary_reads_the_harbor_model_without_a_trajectory(tmp_path):
|
|
|
208
208
|
assert trials["terminal-bench/silent"]["scored"] is False
|
|
209
209
|
assert trials["terminal-bench/other"]["model_attempt"] is False
|
|
210
210
|
assert summary["solved_tasks"] == ["solved"]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def test_round_summary_reads_one_e2e_job_folder(tmp_path):
|
|
214
|
+
# An e2e run writes one Harbor job. Its tasks have no dataset prefix, and the job
|
|
215
|
+
# writes its own result.json next to the trial folders.
|
|
216
|
+
job = tmp_path / "e2e-job"
|
|
217
|
+
(job / "quick__1" / "agent").mkdir(parents=True)
|
|
218
|
+
(job / "result.json").write_text(json.dumps({"id": "job", "stats": {}}))
|
|
219
|
+
result = {
|
|
220
|
+
"task_name": "quick",
|
|
221
|
+
"trial_name": "quick__1",
|
|
222
|
+
"source": "vis-e2e",
|
|
223
|
+
"started_at": "2026-01-02T00:00:00Z",
|
|
224
|
+
"finished_at": "2026-01-02T00:02:00Z",
|
|
225
|
+
"agent_execution": {
|
|
226
|
+
"started_at": "2026-01-02T00:00:00Z",
|
|
227
|
+
"finished_at": "2026-01-02T00:01:00Z",
|
|
228
|
+
},
|
|
229
|
+
"agent_info": {
|
|
230
|
+
"name": "vis",
|
|
231
|
+
"model_info": {"name": MODEL, "provider": PROVIDER},
|
|
232
|
+
},
|
|
233
|
+
"agent_result": {"n_input_tokens": 10, "n_output_tokens": 5},
|
|
234
|
+
"verifier_result": {"rewards": {"reward": 0.0}},
|
|
235
|
+
}
|
|
236
|
+
(job / "quick__1" / "result.json").write_text(json.dumps(result))
|
|
237
|
+
tasks = tmp_path / "dataset"
|
|
238
|
+
for name in ("quick", "unrun"):
|
|
239
|
+
(tasks / name).mkdir(parents=True)
|
|
240
|
+
(tasks / name / "task.toml").write_text("[agent]\ntimeout_sec = 600\n")
|
|
241
|
+
|
|
242
|
+
summary = round_summary(job, tasks)
|
|
243
|
+
|
|
244
|
+
assert summary["dataset"] == "vis-e2e"
|
|
245
|
+
assert summary["attempts"] == 1
|
|
246
|
+
assert summary["scored_tasks"] == 1
|
|
247
|
+
assert summary["unattempted_tasks"] == ["unrun"]
|
|
248
|
+
assert summary["agent_limits"] == {"quick": 600.0, "unrun": 600.0}
|
|
249
|
+
assert summary["trials"][0]["dataset"] == "vis-e2e"
|
|
250
|
+
finding = next(f for f in diagnose(summary).findings if f.kind == "early_stops")
|
|
251
|
+
assert finding.tasks[0].budget_used == 0.1
|
|
252
|
+
assert round_summary(job, dataset="scenarios")["dataset"] == "scenarios"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-arguments.json
RENAMED
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-cases.json
RENAMED
|
File without changes
|
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-execution.json
RENAMED
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-groups.json
RENAMED
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-read-session.json
RENAMED
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-reads.json
RENAMED
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-results.json
RENAMED
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/activity-tables.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/live-primitives.json
RENAMED
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/contracts/fixtures/session-health.json
RENAMED
|
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
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/examples/greeter/tests/test_greeter.py
RENAMED
|
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
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/engine/_extensions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/leaderboard.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/evals/trajectory.py
RENAMED
|
File without changes
|
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/src/blockether/vis/extension_package.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_agent_extension_lifecycle.py
RENAMED
|
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
|
{vis_agent-0.2.41.dev7619 → vis_agent-0.2.42.dev7622}/tests/test_client_extensions_engine.py
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|