vis-agent 0.3.4.dev7992__tar.gz → 0.3.4.dev8033__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.3.4.dev7992 → vis_agent-0.3.4.dev8033}/.gitignore +3 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/PKG-INFO +1 -1
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/agents.json +88 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/config.json +29 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/gateway.json +25 -1
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/toggle.json +67 -2
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/pyproject.toml +1 -1
- vis_agent-0.3.4.dev8033/release-tag +1 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_agents.py +6 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_council.py +11 -2
- vis_agent-0.3.4.dev8033/src/blockether/vis/evals/mrcr.py +273 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/extension.py +12 -2
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agents.py +7 -2
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_client.py +1 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_contracts.py +3 -3
- vis_agent-0.3.4.dev8033/tests/test_evals_mrcr.py +101 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_scoped_settings.py +32 -0
- vis_agent-0.3.4.dev7992/release-tag +0 -1
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/LICENSE +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/README.md +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-arguments.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-cases.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-copy.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-execution.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-groups.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-read-session.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-reads.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-results.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-stream.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-tables.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/diff.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/live-primitives.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/session-health.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/view.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/activity.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/automations.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/common.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/content.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/council.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/diff.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/evals.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/improve.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/provider.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/rooms.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/symbol.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/view.json +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/examples/greeter/tests/test_greeter.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/hatch_build.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/__init__.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/_contracts.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/_outside.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/_wire.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/activity.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/__init__.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_agent.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_client.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_extensions.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_host.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_install.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_local.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_models.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/__init__.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/_gateway.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/agent.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/bench.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/check.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/compare.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/environment.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/grade.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/harbor.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/judge-rubric.toml +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/judge.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/labels.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/leaderboard.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/lic.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/longmemeval.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/repeats.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/report.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/reports.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/rounds.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/stages.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/stats.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/trajectory.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/extension_package.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/py.typed +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/rooms.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/views.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/conftest.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/evals_atif.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_activity.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agent.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agent_extension_lifecycle.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agent_extensions.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_api_guides.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_attachments.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_authoring.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_catalog.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_catalog_recipe.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_ci_report_recipe.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_client_extensions_engine.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_code_quality_recipe.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_council.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_declarations.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_engine.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_engine_docstrings.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_agent.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_bench.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_check.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_compare.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_environment.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_gateway.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_grade.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_guards.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_harbor.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_judge.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_labels.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_lic.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_longmemeval.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_repeats.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_report.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_rounds.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_stages.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_stats.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_tasks.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_trajectory.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_extension_package.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_extension_reference.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_gateway_performance.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_goals.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_install.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_live_primitives.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_live_wait.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_local.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_method_typing.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_models.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_monitor_recipe.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_namespace.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_outside.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_providers.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_read_session.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_record_arguments.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_registration.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_repair_hooks.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_result_sequence.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_rooms.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_sdk_bootstrap.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_sdk_guide.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_sdk_typing.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_shortcuts_live.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_structured_live.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_symbol_contract.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_testing.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_text_list_arguments.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_tools.py +0 -0
- {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_views.py +0 -0
|
@@ -117,3 +117,6 @@ own_your_harness.pptx
|
|
|
117
117
|
# Call journal written by the e2e schema-probe fixture when it runs from this
|
|
118
118
|
# checkout instead of a temporary workspace.
|
|
119
119
|
/schema-calls.jsonl
|
|
120
|
+
|
|
121
|
+
# uv lockfiles. The SDK and the examples are libraries; uv writes the lock locally.
|
|
122
|
+
uv.lock
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vis-agent
|
|
3
|
-
Version: 0.3.4.
|
|
3
|
+
Version: 0.3.4.dev8033
|
|
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
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"maxLength": 8192,
|
|
16
16
|
"pattern": "\\S"
|
|
17
17
|
},
|
|
18
|
+
"role": {
|
|
19
|
+
"$ref": "#/$defs/role"
|
|
20
|
+
},
|
|
18
21
|
"model": {
|
|
19
22
|
"type": "string",
|
|
20
23
|
"minLength": 1
|
|
@@ -77,6 +80,9 @@
|
|
|
77
80
|
"task": {
|
|
78
81
|
"$ref": "#/$defs/spawn/properties/task"
|
|
79
82
|
},
|
|
83
|
+
"role": {
|
|
84
|
+
"$ref": "#/$defs/spawn/properties/role"
|
|
85
|
+
},
|
|
80
86
|
"model": {
|
|
81
87
|
"$ref": "#/$defs/spawn/properties/model"
|
|
82
88
|
},
|
|
@@ -191,6 +197,27 @@
|
|
|
191
197
|
],
|
|
192
198
|
"additionalProperties": false
|
|
193
199
|
},
|
|
200
|
+
"answer": {
|
|
201
|
+
"$comment": "A person answers the open question of a subagent for its parent.",
|
|
202
|
+
"type": "object",
|
|
203
|
+
"properties": {
|
|
204
|
+
"session_id": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"minLength": 1
|
|
207
|
+
},
|
|
208
|
+
"text": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"minLength": 1,
|
|
211
|
+
"maxLength": 8192,
|
|
212
|
+
"pattern": "\\S"
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"required": [
|
|
216
|
+
"session_id",
|
|
217
|
+
"text"
|
|
218
|
+
],
|
|
219
|
+
"additionalProperties": false
|
|
220
|
+
},
|
|
194
221
|
"grant": {
|
|
195
222
|
"$comment": "Add file rules to the grant of a subagent. A child without write rules already writes where its parent writes, so a write grant changes nothing for it. A write rule under a read_only or deny rule of the child fails.",
|
|
196
223
|
"type": "object",
|
|
@@ -274,6 +301,32 @@
|
|
|
274
301
|
],
|
|
275
302
|
"additionalProperties": false
|
|
276
303
|
},
|
|
304
|
+
"role": {
|
|
305
|
+
"description": "Choose the role by the result that you need: facts, changed files, a pass or fail, or findings.",
|
|
306
|
+
"$comment": "A role is a label for people and the leader. It sets no tool, file or model limit. The TUI writes the role name. The Companion draws a Lucide icon for each role.",
|
|
307
|
+
"oneOf": [
|
|
308
|
+
{
|
|
309
|
+
"const": "research",
|
|
310
|
+
"title": "Finds facts",
|
|
311
|
+
"description": "Finds facts. Reads code, docs, logs and history, and answers one question with evidence. Use it before you decide or plan."
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"const": "implement",
|
|
315
|
+
"title": "Changes files",
|
|
316
|
+
"description": "Changes files. Writes code, tests, docs or configuration and runs the checks for its change. Use it for one bounded change."
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"const": "verify",
|
|
320
|
+
"title": "Proves it works",
|
|
321
|
+
"description": "Proves that something works. Runs tests, reproduces a bug or checks the app, and reports pass or fail with evidence."
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"const": "audit",
|
|
325
|
+
"title": "Judges the work",
|
|
326
|
+
"description": "Judges finished work. Reviews a change, plan or release for risks and mistakes, and reports findings. Give it the work, not your conclusion."
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
},
|
|
277
330
|
"model_pair": {
|
|
278
331
|
"type": "object",
|
|
279
332
|
"properties": {
|
|
@@ -342,6 +395,9 @@
|
|
|
342
395
|
"type": "string",
|
|
343
396
|
"minLength": 1
|
|
344
397
|
},
|
|
398
|
+
"role": {
|
|
399
|
+
"$ref": "#/$defs/role"
|
|
400
|
+
},
|
|
345
401
|
"status": {
|
|
346
402
|
"enum": [
|
|
347
403
|
"queued",
|
|
@@ -385,6 +441,38 @@
|
|
|
385
441
|
"type": "string",
|
|
386
442
|
"minLength": 1
|
|
387
443
|
},
|
|
444
|
+
"question_entry_id": {
|
|
445
|
+
"type": "integer",
|
|
446
|
+
"minimum": 1
|
|
447
|
+
},
|
|
448
|
+
"budget_request": {
|
|
449
|
+
"type": "object",
|
|
450
|
+
"properties": {
|
|
451
|
+
"iterations": {
|
|
452
|
+
"type": "integer",
|
|
453
|
+
"minimum": 1
|
|
454
|
+
},
|
|
455
|
+
"reason": {
|
|
456
|
+
"type": "string"
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
"files_request": {
|
|
461
|
+
"type": "object",
|
|
462
|
+
"properties": {
|
|
463
|
+
"files": {
|
|
464
|
+
"$ref": "#/$defs/files"
|
|
465
|
+
},
|
|
466
|
+
"reason": {
|
|
467
|
+
"type": "string"
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
"activity": {
|
|
472
|
+
"$comment": "The latest Activity line of a queued or running agent, such as Patched ~/src/a.clj.",
|
|
473
|
+
"type": "string",
|
|
474
|
+
"minLength": 1
|
|
475
|
+
},
|
|
388
476
|
"usage": {
|
|
389
477
|
"type": [
|
|
390
478
|
"object",
|
|
@@ -694,6 +694,32 @@
|
|
|
694
694
|
},
|
|
695
695
|
"additionalProperties": false
|
|
696
696
|
},
|
|
697
|
+
"toolList": {
|
|
698
|
+
"type": "array",
|
|
699
|
+
"maxItems": 256,
|
|
700
|
+
"uniqueItems": true,
|
|
701
|
+
"items": {
|
|
702
|
+
"type": "string",
|
|
703
|
+
"pattern": "^[A-Za-z_*][A-Za-z0-9_.*]*$",
|
|
704
|
+
"maxLength": 200
|
|
705
|
+
},
|
|
706
|
+
"description": "Tool names as python_execution binds them, such as memo.recall. * matches any characters."
|
|
707
|
+
},
|
|
708
|
+
"tools": {
|
|
709
|
+
"type": "object",
|
|
710
|
+
"properties": {
|
|
711
|
+
"allow": {
|
|
712
|
+
"$ref": "#/$defs/toolList",
|
|
713
|
+
"description": "Keep only these tools of each extension that the list names. Other tools of that extension, also later ones, are off."
|
|
714
|
+
},
|
|
715
|
+
"deny": {
|
|
716
|
+
"$ref": "#/$defs/toolList",
|
|
717
|
+
"description": "Turn these tools off."
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
"additionalProperties": false,
|
|
721
|
+
"description": "Tools of optional extensions that a session can call. Every scope applies: a narrower scope can turn tools off, never on."
|
|
722
|
+
},
|
|
697
723
|
"jail": {
|
|
698
724
|
"type": "object",
|
|
699
725
|
"properties": {
|
|
@@ -1328,6 +1354,9 @@
|
|
|
1328
1354
|
"jail": {
|
|
1329
1355
|
"$ref": "#/$defs/jail"
|
|
1330
1356
|
},
|
|
1357
|
+
"tools": {
|
|
1358
|
+
"$ref": "#/$defs/tools"
|
|
1359
|
+
},
|
|
1331
1360
|
"environment": {
|
|
1332
1361
|
"$ref": "#/$defs/environment"
|
|
1333
1362
|
},
|
|
@@ -736,6 +736,17 @@
|
|
|
736
736
|
},
|
|
737
737
|
"path": "/v1/sessions/:sid/agents"
|
|
738
738
|
},
|
|
739
|
+
{
|
|
740
|
+
"audience": "sdk",
|
|
741
|
+
"operations": {
|
|
742
|
+
"post": {
|
|
743
|
+
"request": "json",
|
|
744
|
+
"response": "json",
|
|
745
|
+
"extension": "background"
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
"path": "/v1/sessions/:sid/agents/answer"
|
|
749
|
+
},
|
|
739
750
|
{
|
|
740
751
|
"audience": "sdk",
|
|
741
752
|
"operations": {
|
|
@@ -3000,14 +3011,27 @@
|
|
|
3000
3011
|
"switch",
|
|
3001
3012
|
"select",
|
|
3002
3013
|
"text",
|
|
3014
|
+
"secret",
|
|
3003
3015
|
"multiline",
|
|
3004
3016
|
"number",
|
|
3005
3017
|
"paths",
|
|
3006
3018
|
"filesystem",
|
|
3007
3019
|
"network",
|
|
3008
|
-
"list"
|
|
3020
|
+
"list",
|
|
3021
|
+
"tools"
|
|
3009
3022
|
]
|
|
3010
3023
|
},
|
|
3024
|
+
"is_set": {
|
|
3025
|
+
"type": "boolean",
|
|
3026
|
+
"description": "Whether a secret row has a saved value. A secret row never carries the value itself."
|
|
3027
|
+
},
|
|
3028
|
+
"suggestions": {
|
|
3029
|
+
"type": "array",
|
|
3030
|
+
"description": "Values that an editor offers as completions. Other values can still be valid.",
|
|
3031
|
+
"items": {
|
|
3032
|
+
"type": "string"
|
|
3033
|
+
}
|
|
3034
|
+
},
|
|
3011
3035
|
"schema": {
|
|
3012
3036
|
"type": "string"
|
|
3013
3037
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://contract.blockether.com/vis/schema/toggle.json",
|
|
4
4
|
"title": "Feature-toggle contract",
|
|
5
|
-
"description": "Feature-toggle contributions and the boolean tokens
|
|
5
|
+
"description": "Feature-toggle contributions, and the boolean tokens and string values that settings accept.",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"boolean_true": {
|
|
8
8
|
"type": "string",
|
|
@@ -32,6 +32,12 @@
|
|
|
32
32
|
}
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
|
+
"string_value": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"maxLength": 4096,
|
|
38
|
+
"pattern": "^[^\\u0000-\\u001f\\u007f]*$",
|
|
39
|
+
"description": "The value of a string setting: one line without control characters."
|
|
40
|
+
},
|
|
35
41
|
"contribution": {
|
|
36
42
|
"type": "object",
|
|
37
43
|
"additionalProperties": true,
|
|
@@ -83,10 +89,15 @@
|
|
|
83
89
|
"type": {
|
|
84
90
|
"enum": [
|
|
85
91
|
"boolean",
|
|
86
|
-
"enum"
|
|
92
|
+
"enum",
|
|
93
|
+
"string"
|
|
87
94
|
],
|
|
88
95
|
"default": "boolean"
|
|
89
96
|
},
|
|
97
|
+
"is_secret": {
|
|
98
|
+
"type": "boolean",
|
|
99
|
+
"description": "Makes a string setting write-only. Settings show only whether it is set, never its value."
|
|
100
|
+
},
|
|
90
101
|
"choices": {
|
|
91
102
|
"type": "array",
|
|
92
103
|
"items": {},
|
|
@@ -169,6 +180,60 @@
|
|
|
169
180
|
}
|
|
170
181
|
]
|
|
171
182
|
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"if": {
|
|
186
|
+
"properties": {
|
|
187
|
+
"type": {
|
|
188
|
+
"const": "string"
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"required": [
|
|
192
|
+
"type"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
"then": {
|
|
196
|
+
"properties": {
|
|
197
|
+
"default": {
|
|
198
|
+
"$ref": "#/$defs/string_value"
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"not": {
|
|
202
|
+
"required": [
|
|
203
|
+
"choices"
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"if": {
|
|
210
|
+
"properties": {
|
|
211
|
+
"is_secret": {
|
|
212
|
+
"const": true
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"required": [
|
|
216
|
+
"is_secret"
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
"then": {
|
|
220
|
+
"properties": {
|
|
221
|
+
"type": {
|
|
222
|
+
"const": "string"
|
|
223
|
+
},
|
|
224
|
+
"default": {
|
|
225
|
+
"const": ""
|
|
226
|
+
},
|
|
227
|
+
"scopes": {
|
|
228
|
+
"const": [
|
|
229
|
+
"global"
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"required": [
|
|
234
|
+
"type"
|
|
235
|
+
]
|
|
236
|
+
}
|
|
172
237
|
}
|
|
173
238
|
]
|
|
174
239
|
},
|
|
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "vis-agent"
|
|
11
|
-
version = "0.3.4.
|
|
11
|
+
version = "0.3.4.dev8033"
|
|
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.12"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
beta-8906d28bc9e78c61bfe556c52e15d3045f046731
|
|
@@ -18,10 +18,16 @@ class Subagent:
|
|
|
18
18
|
status: str
|
|
19
19
|
iteration_budget: int
|
|
20
20
|
iterations_used: int
|
|
21
|
+
role: str | None = None
|
|
21
22
|
provider: str | None = None
|
|
22
23
|
model: str | None = None
|
|
23
24
|
routing_locked: bool = False
|
|
24
25
|
pending_input: bool = False
|
|
26
|
+
question: str | None = None
|
|
27
|
+
question_entry_id: int | None = None
|
|
28
|
+
budget_request: dict[str, Any] | None = None
|
|
29
|
+
files_request: dict[str, Any] | None = None
|
|
30
|
+
activity: str | None = None
|
|
25
31
|
usage: dict[str, Any] | None = None
|
|
26
32
|
files: dict[str, list[str]] | None = None
|
|
27
33
|
|
|
@@ -231,6 +231,7 @@ class Council:
|
|
|
231
231
|
self,
|
|
232
232
|
task: str,
|
|
233
233
|
*,
|
|
234
|
+
role: str | None = None,
|
|
234
235
|
provider: str | None = None,
|
|
235
236
|
model: str | None = None,
|
|
236
237
|
iteration_budget: int | None = None,
|
|
@@ -247,12 +248,15 @@ class Council:
|
|
|
247
248
|
it with different input is rejected. This is not an ordinary independent fork,
|
|
248
249
|
and it does not grant broader permissions. ``files`` narrows file access with
|
|
249
250
|
``write``, ``read_only`` and ``deny`` rules: paths or globs, also for future files.
|
|
251
|
+
``role`` is ``research``, ``implement``, ``verify`` or ``audit``: a label for people
|
|
252
|
+
that sets no limit.
|
|
250
253
|
"""
|
|
251
254
|
body = {"task": task}
|
|
252
255
|
body.update(
|
|
253
256
|
{
|
|
254
257
|
name: value
|
|
255
258
|
for name, value in {
|
|
259
|
+
"role": role,
|
|
256
260
|
"provider": provider,
|
|
257
261
|
"iteration_budget": iteration_budget,
|
|
258
262
|
"model": model,
|
|
@@ -291,8 +295,8 @@ class Council:
|
|
|
291
295
|
) -> tuple[SubagentRecord, ...]:
|
|
292
296
|
"""Spawn one subagent for each task, wait for all of them and return their records.
|
|
293
297
|
|
|
294
|
-
A task is a string, or a dict with ``task`` and optional ``
|
|
295
|
-
``iteration_budget`` and ``files``. The wait ends when every child answers, asks
|
|
298
|
+
A task is a string, or a dict with ``task`` and optional ``role``, ``model``,
|
|
299
|
+
``provider``, ``iteration_budget`` and ``files``. The wait ends when every child answers, asks
|
|
296
300
|
or stops, or after ``timeout_secs`` (at most 1800). A running child stays running.
|
|
297
301
|
"""
|
|
298
302
|
body = {"tasks": tasks, "timeout_secs": timeout_secs}
|
|
@@ -352,6 +356,11 @@ class Council:
|
|
|
352
356
|
body = {"session_id": session_id, "reason": reason}
|
|
353
357
|
return self._records("/deny", body, "deny")[0]
|
|
354
358
|
|
|
359
|
+
def answer(self, session_id: str, text: str) -> SubagentRecord:
|
|
360
|
+
"""Answer the open question of a subagent for its parent; the child continues."""
|
|
361
|
+
body = {"session_id": session_id, "text": text}
|
|
362
|
+
return self._records("/answer", body, "answer")[0]
|
|
363
|
+
|
|
355
364
|
def grant(self, session_id: str, files: dict[str, list[str]]) -> SubagentRecord:
|
|
356
365
|
"""Add ``write``, ``read_only`` or ``deny`` file rules to the grant of a subagent.
|
|
357
366
|
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Write OpenAI MRCR conversations as long multi-turn Harbor tasks.
|
|
2
|
+
|
|
3
|
+
[MRCR](https://huggingface.co/datasets/openai/mrcr) (multi-round coreference resolution)
|
|
4
|
+
hides several similar requests in one long conversation, for example two requests for
|
|
5
|
+
a poem about tapirs. The last request asks for one of the earlier answers, for example
|
|
6
|
+
the second poem about tapirs, word for word, with a random string at the start. The
|
|
7
|
+
model must find the correct answer among similar ones and repeat it exactly.
|
|
8
|
+
|
|
9
|
+
A task pastes the earlier conversation in user turns of about `turn_tokens` tokens
|
|
10
|
+
each, and the agent gives a short reply. The last turn gives the request. Only the
|
|
11
|
+
answer to that request counts. When the conversation is larger than the context budget
|
|
12
|
+
of the agent, the agent must fold old turns. A fold note seldom keeps a whole answer
|
|
13
|
+
word for word, so MRCR shows what a fold loses. Compare it with
|
|
14
|
+
`blockether.vis.evals.longmemeval`, which asks only for short facts.
|
|
15
|
+
|
|
16
|
+
The grader follows the MRCR grader: the answer must start with the random string, and
|
|
17
|
+
`difflib.SequenceMatcher` compares the rest with the expected answer. The MRCR score is
|
|
18
|
+
that ratio. A Harbor reward is a pass or a failure, so a task passes when the ratio is
|
|
19
|
+
at least `MIN_RATIO`. The protocol differs from MRCR in two ways:
|
|
20
|
+
|
|
21
|
+
- The conversation arrives as pasted text in user turns, not as one prompt with
|
|
22
|
+
assistant messages.
|
|
23
|
+
- The score is a pass at `MIN_RATIO`, not the mean ratio.
|
|
24
|
+
|
|
25
|
+
Importing this module makes no network calls. `load_rows` downloads only the rows that
|
|
26
|
+
it needs, about 1 MB for each long row. Run `python -m blockether.vis.evals.mrcr DATASET
|
|
27
|
+
--cache FILE` to write the tasks.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import json
|
|
34
|
+
import urllib.request
|
|
35
|
+
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Any
|
|
38
|
+
|
|
39
|
+
from .harbor import HarborTask, write_task
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"MIN_RATIO",
|
|
43
|
+
"NEEDLES",
|
|
44
|
+
"fetch_row",
|
|
45
|
+
"load_rows",
|
|
46
|
+
"mrcr_task_name",
|
|
47
|
+
"task_turns",
|
|
48
|
+
"write_mrcr_tasks",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
ROWS_URL = (
|
|
52
|
+
"https://datasets-server.huggingface.co/rows"
|
|
53
|
+
"?dataset=openai/mrcr&config=default&split=train&offset={index}&length=1"
|
|
54
|
+
)
|
|
55
|
+
NEEDLES = {2: 0, 4: 800, 8: 1600}
|
|
56
|
+
"""The first row of each needle count. Each needle count has `ROWS_PER_NEEDLES` rows."""
|
|
57
|
+
ROWS_PER_NEEDLES = 800
|
|
58
|
+
PROGRAM_FILE = "check_answer.py"
|
|
59
|
+
EXPECTED_FILE = "expected.json"
|
|
60
|
+
CHARS_PER_TOKEN = 4
|
|
61
|
+
TURN_TOKENS = 10_000
|
|
62
|
+
TURN_TIMEOUT_SEC = 300.0
|
|
63
|
+
MIN_RATIO = 0.9
|
|
64
|
+
MIN_CHARS = 600_000
|
|
65
|
+
MAX_CHARS = 1_300_000
|
|
66
|
+
|
|
67
|
+
_FIRST = (
|
|
68
|
+
"Over the next messages I will paste an earlier conversation between a user and "
|
|
69
|
+
"an AI assistant. Later I will ask you about it. Do not create or edit files: keep "
|
|
70
|
+
"what you need in this conversation. Until I give my request, a short reply is "
|
|
71
|
+
"enough."
|
|
72
|
+
)
|
|
73
|
+
_MORE = "More of the earlier conversation."
|
|
74
|
+
_LAST = (
|
|
75
|
+
"That is the whole earlier conversation. Answer my request about it. Give only the "
|
|
76
|
+
"requested text, without a code block or other words: {request}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
_PROGRAM = r"""# Grade an MRCR answer like the MRCR grader. The answer is on stdin.
|
|
80
|
+
import json
|
|
81
|
+
import sys
|
|
82
|
+
from difflib import SequenceMatcher
|
|
83
|
+
from pathlib import Path
|
|
84
|
+
|
|
85
|
+
expected = json.loads(Path(__file__).with_name("expected.json").read_text())
|
|
86
|
+
prefix = expected["prefix"]
|
|
87
|
+
answer = sys.stdin.read().strip()
|
|
88
|
+
if not answer.startswith(prefix):
|
|
89
|
+
sys.exit(f"the answer does not start with {prefix!r}: {answer[:80]!r}")
|
|
90
|
+
ratio = SequenceMatcher(
|
|
91
|
+
None, answer.removeprefix(prefix), expected["answer"].removeprefix(prefix)
|
|
92
|
+
).ratio()
|
|
93
|
+
if ratio < expected["min_ratio"]:
|
|
94
|
+
sys.exit(f"the answer matches {ratio:.3f} of the expected text, less than {expected['min_ratio']}")
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def fetch_row(index: int) -> dict[str, Any]:
|
|
99
|
+
"""Download the MRCR row `index` from the Hugging Face dataset server."""
|
|
100
|
+
request = urllib.request.Request(
|
|
101
|
+
ROWS_URL.format(index=index), headers={"User-Agent": "vis-evals"}
|
|
102
|
+
)
|
|
103
|
+
with urllib.request.urlopen(request, timeout=300) as response:
|
|
104
|
+
rows = json.load(response)["rows"]
|
|
105
|
+
if len(rows) != 1:
|
|
106
|
+
raise ValueError(f"MRCR row {index} does not exist")
|
|
107
|
+
return {"row_idx": index, **rows[0]["row"]}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def load_rows(
|
|
111
|
+
cache: Path | str,
|
|
112
|
+
*,
|
|
113
|
+
needles: int = 2,
|
|
114
|
+
min_chars: int = MIN_CHARS,
|
|
115
|
+
max_chars: int = MAX_CHARS,
|
|
116
|
+
limit: int = 5,
|
|
117
|
+
fetch: Callable[[int], Mapping[str, Any]] = fetch_row,
|
|
118
|
+
) -> list[dict[str, Any]]:
|
|
119
|
+
"""Return the first `limit` MRCR rows with `needles` needles and a size in range.
|
|
120
|
+
|
|
121
|
+
`min_chars` and `max_chars` bound the `n_chars` size of a row. The JSON Lines file
|
|
122
|
+
`cache` keeps each row that `fetch` downloaded, and only the size of a row out of
|
|
123
|
+
range. So a second call with the same or a smaller selection downloads nothing. An
|
|
124
|
+
unknown needle count raises ValueError.
|
|
125
|
+
"""
|
|
126
|
+
if needles not in NEEDLES:
|
|
127
|
+
raise ValueError(f"needles must be one of {sorted(NEEDLES)}: {needles!r}")
|
|
128
|
+
path = Path(cache)
|
|
129
|
+
known: dict[int, dict[str, Any]] = {}
|
|
130
|
+
if path.is_file():
|
|
131
|
+
for line in path.read_text(encoding="utf-8").splitlines():
|
|
132
|
+
if line.strip():
|
|
133
|
+
row = json.loads(line)
|
|
134
|
+
known[int(row["row_idx"])] = row
|
|
135
|
+
chosen: list[dict[str, Any]] = []
|
|
136
|
+
first = NEEDLES[needles]
|
|
137
|
+
for index in range(first, first + ROWS_PER_NEEDLES):
|
|
138
|
+
if len(chosen) >= limit:
|
|
139
|
+
break
|
|
140
|
+
row = known.get(index)
|
|
141
|
+
if row is None:
|
|
142
|
+
row = dict(fetch(index))
|
|
143
|
+
row["row_idx"] = index
|
|
144
|
+
if not min_chars <= int(row["n_chars"]) <= max_chars:
|
|
145
|
+
row = {key: row[key] for key in ("row_idx", "n_needles", "n_chars")}
|
|
146
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
147
|
+
with path.open("a", encoding="utf-8") as target:
|
|
148
|
+
target.write(json.dumps(row) + "\n")
|
|
149
|
+
known[index] = row
|
|
150
|
+
if "prompt" in row and min_chars <= int(row["n_chars"]) <= max_chars:
|
|
151
|
+
chosen.append(row)
|
|
152
|
+
return chosen
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def mrcr_task_name(row: Mapping[str, Any]) -> str:
|
|
156
|
+
"""Return the Harbor task name of `row`, for example `mrcr-2n-17`."""
|
|
157
|
+
return f"mrcr-{int(row['n_needles'])}n-{int(row['row_idx'])}"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _tokens(text: str) -> int:
|
|
161
|
+
return len(text) // CHARS_PER_TOKEN
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _message_text(message: Mapping[str, Any]) -> str:
|
|
165
|
+
role = "USER" if message.get("role") == "user" else "ASSISTANT"
|
|
166
|
+
return f"{role}: {str(message.get('content', '')).strip()}"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def task_turns(row: Mapping[str, Any], *, turn_tokens: int = TURN_TOKENS) -> list[str]:
|
|
170
|
+
"""Return the user messages of the task for `row`.
|
|
171
|
+
|
|
172
|
+
The earlier messages of the MRCR conversation come in pasted turns of about
|
|
173
|
+
`turn_tokens` tokens. A message never splits. The last turn gives the last request
|
|
174
|
+
of the conversation.
|
|
175
|
+
"""
|
|
176
|
+
messages = json.loads(row["prompt"])
|
|
177
|
+
if not messages or messages[-1].get("role") != "user":
|
|
178
|
+
raise ValueError(f"MRCR row {row.get('row_idx')} does not end with a request")
|
|
179
|
+
turns: list[list[str]] = [[]]
|
|
180
|
+
size = 0
|
|
181
|
+
for message in messages[:-1]:
|
|
182
|
+
text = _message_text(message)
|
|
183
|
+
if turns[-1] and size + _tokens(text) > turn_tokens:
|
|
184
|
+
turns.append([])
|
|
185
|
+
size = 0
|
|
186
|
+
turns[-1].append(text)
|
|
187
|
+
size += _tokens(text)
|
|
188
|
+
pasted = [
|
|
189
|
+
f"{_FIRST if number == 0 else _MORE}\n\n" + "\n\n".join(chunk)
|
|
190
|
+
for number, chunk in enumerate(turns)
|
|
191
|
+
if chunk
|
|
192
|
+
]
|
|
193
|
+
return [*pasted, _LAST.format(request=str(messages[-1]["content"]).strip())]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def write_mrcr_tasks(
|
|
197
|
+
dataset: Path | str,
|
|
198
|
+
rows: Iterable[Mapping[str, Any]],
|
|
199
|
+
*,
|
|
200
|
+
turn_tokens: int = TURN_TOKENS,
|
|
201
|
+
min_ratio: float = MIN_RATIO,
|
|
202
|
+
) -> tuple[HarborTask, ...]:
|
|
203
|
+
"""Write one Harbor task for each MRCR row in the folder `dataset`.
|
|
204
|
+
|
|
205
|
+
A task passes when the answer starts with the random string of the row and matches
|
|
206
|
+
at least `min_ratio` of the expected answer.
|
|
207
|
+
"""
|
|
208
|
+
written: list[HarborTask] = []
|
|
209
|
+
for row in rows:
|
|
210
|
+
turns = task_turns(row, turn_tokens=turn_tokens)
|
|
211
|
+
name = mrcr_task_name(row)
|
|
212
|
+
written.append(
|
|
213
|
+
write_task(
|
|
214
|
+
dataset,
|
|
215
|
+
name,
|
|
216
|
+
turns,
|
|
217
|
+
{"want_answer_program": PROGRAM_FILE},
|
|
218
|
+
agent_timeout_sec=TURN_TIMEOUT_SEC * len(turns),
|
|
219
|
+
metadata={
|
|
220
|
+
"benchmark": "mrcr",
|
|
221
|
+
"row": int(row["row_idx"]),
|
|
222
|
+
"needles": int(row["n_needles"]),
|
|
223
|
+
"chars": int(row["n_chars"]),
|
|
224
|
+
"turns": len(turns),
|
|
225
|
+
"history_tokens": sum(_tokens(turn) for turn in turns),
|
|
226
|
+
},
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
tests = Path(dataset) / name / "tests"
|
|
230
|
+
(tests / PROGRAM_FILE).write_text(_PROGRAM, encoding="utf-8")
|
|
231
|
+
expected = {
|
|
232
|
+
"answer": str(row["answer"]),
|
|
233
|
+
"prefix": str(row["random_string_to_prepend"]),
|
|
234
|
+
"min_ratio": min_ratio,
|
|
235
|
+
}
|
|
236
|
+
(tests / EXPECTED_FILE).write_text(
|
|
237
|
+
json.dumps(expected, indent=2) + "\n", encoding="utf-8"
|
|
238
|
+
)
|
|
239
|
+
return tuple(written)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
243
|
+
parser = argparse.ArgumentParser(
|
|
244
|
+
prog="python -m blockether.vis.evals.mrcr",
|
|
245
|
+
description="Write OpenAI MRCR conversations as long multi-turn Harbor tasks.",
|
|
246
|
+
)
|
|
247
|
+
parser.add_argument("dataset", type=Path, help="folder for the new tasks")
|
|
248
|
+
parser.add_argument(
|
|
249
|
+
"--cache", type=Path, required=True, help="JSON Lines file of downloaded rows"
|
|
250
|
+
)
|
|
251
|
+
parser.add_argument("--needles", type=int, choices=sorted(NEEDLES), default=2)
|
|
252
|
+
parser.add_argument("--min-chars", type=int, default=MIN_CHARS)
|
|
253
|
+
parser.add_argument("--max-chars", type=int, default=MAX_CHARS)
|
|
254
|
+
parser.add_argument("--limit", type=int, default=5, help="rows to write")
|
|
255
|
+
parser.add_argument("--turn-tokens", type=int, default=TURN_TOKENS)
|
|
256
|
+
parser.add_argument("--min-ratio", type=float, default=MIN_RATIO)
|
|
257
|
+
args = parser.parse_args(argv)
|
|
258
|
+
rows = load_rows(
|
|
259
|
+
args.cache,
|
|
260
|
+
needles=args.needles,
|
|
261
|
+
min_chars=args.min_chars,
|
|
262
|
+
max_chars=args.max_chars,
|
|
263
|
+
limit=args.limit,
|
|
264
|
+
)
|
|
265
|
+
written = write_mrcr_tasks(
|
|
266
|
+
args.dataset, rows, turn_tokens=args.turn_tokens, min_ratio=args.min_ratio
|
|
267
|
+
)
|
|
268
|
+
print(f"wrote {len(written)} tasks to {args.dataset}")
|
|
269
|
+
return 0
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
if __name__ == "__main__":
|
|
273
|
+
raise SystemExit(main())
|