synth-ai 0.1.9__py3-none-any.whl → 0.2.1.dev0__py3-none-any.whl
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.
- synth_ai/__init__.py +28 -2
- synth_ai/core/system.py +4 -0
- synth_ai/environments/__init__.py +35 -0
- synth_ai/environments/environment/__init__.py +1 -0
- synth_ai/environments/environment/artifacts/__init__.py +1 -0
- synth_ai/environments/environment/artifacts/base.py +50 -0
- synth_ai/environments/environment/core.py +22 -0
- synth_ai/environments/environment/db/__init__.py +1 -0
- synth_ai/environments/environment/db/sqlite.py +45 -0
- synth_ai/environments/environment/registry.py +24 -0
- synth_ai/environments/environment/resources/sqlite.py +46 -0
- synth_ai/environments/environment/results.py +1 -0
- synth_ai/environments/environment/rewards/__init__.py +1 -0
- synth_ai/environments/environment/rewards/core.py +28 -0
- synth_ai/environments/environment/shared_engine.py +26 -0
- synth_ai/environments/environment/tools/__init__.py +34 -0
- synth_ai/environments/examples/__init__.py +1 -0
- synth_ai/environments/examples/crafter_classic/__init__.py +8 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +58 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +152 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +1194 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +51 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +872 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +1412 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/test_crafter_react_agent.py +1110 -0
- synth_ai/environments/examples/crafter_classic/config_logging.py +111 -0
- synth_ai/environments/examples/crafter_classic/engine.py +502 -0
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +63 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +5 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +74 -0
- synth_ai/environments/examples/crafter_classic/environment.py +255 -0
- synth_ai/environments/examples/crafter_classic/taskset.py +228 -0
- synth_ai/environments/examples/enron/agent_demos/test_synth_react.py +535 -0
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +156 -0
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +280 -0
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +24 -0
- synth_ai/environments/examples/enron/engine.py +291 -0
- synth_ai/environments/examples/enron/environment.py +165 -0
- synth_ai/environments/examples/enron/taskset.py +112 -0
- synth_ai/environments/examples/enron/units/keyword_stats.py +111 -0
- synth_ai/environments/examples/enron/units/test_email_index.py +8 -0
- synth_ai/environments/examples/minigrid/__init__.py +48 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +1188 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +47 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +562 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +220 -0
- synth_ai/environments/examples/minigrid/agent_demos/test_minigrid_react_agent.py +393 -0
- synth_ai/environments/examples/minigrid/engine.py +589 -0
- synth_ai/environments/examples/minigrid/environment.py +274 -0
- synth_ai/environments/examples/minigrid/environment_mapping.py +242 -0
- synth_ai/environments/examples/minigrid/puzzle_loader.py +416 -0
- synth_ai/environments/examples/minigrid/taskset.py +583 -0
- synth_ai/environments/examples/minigrid/units/test_action_behavior.py +226 -0
- synth_ai/environments/examples/minigrid/units/test_debug_messages.py +83 -0
- synth_ai/environments/examples/minigrid/units/test_exploration.py +120 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_engine.py +214 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_environment.py +238 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_environment_mapping.py +301 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_taskset.py +210 -0
- synth_ai/environments/examples/nethack/__init__.py +7 -0
- synth_ai/environments/examples/nethack/achievements.py +337 -0
- synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +981 -0
- synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +74 -0
- synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +832 -0
- synth_ai/environments/examples/nethack/agent_demos/test_nethack_react_agent.py +1112 -0
- synth_ai/environments/examples/nethack/engine.py +738 -0
- synth_ai/environments/examples/nethack/environment.py +255 -0
- synth_ai/environments/examples/nethack/helpers/__init__.py +42 -0
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +301 -0
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +401 -0
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +433 -0
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +201 -0
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +268 -0
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +308 -0
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +430 -0
- synth_ai/environments/examples/nethack/taskset.py +323 -0
- synth_ai/environments/examples/nethack/units/test_nethack_engine.py +277 -0
- synth_ai/environments/examples/nethack/units/test_nethack_environment.py +281 -0
- synth_ai/environments/examples/nethack/units/test_nethack_taskset.py +213 -0
- synth_ai/environments/examples/nethack/units/test_recording.py +307 -0
- synth_ai/environments/examples/red/__init__.py +7 -0
- synth_ai/environments/examples/red/agent_demos/__init__.py +1 -0
- synth_ai/environments/examples/red/agent_demos/test_synth_react.py +1471 -0
- synth_ai/environments/examples/red/config_logging.py +110 -0
- synth_ai/environments/examples/red/engine.py +693 -0
- synth_ai/environments/examples/red/engine_helpers/__init__.py +1 -0
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +28 -0
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +275 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +142 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +56 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +283 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +149 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +137 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +56 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +330 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +120 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +558 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +312 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +147 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +246 -0
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +367 -0
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +139 -0
- synth_ai/environments/examples/red/environment.py +235 -0
- synth_ai/environments/examples/red/taskset.py +77 -0
- synth_ai/environments/examples/red/test_fixes.py +125 -0
- synth_ai/environments/examples/red/test_fixes_mock.py +148 -0
- synth_ai/environments/examples/red/units/__init__.py +1 -0
- synth_ai/environments/examples/red/units/test_basic_functionality.py +97 -0
- synth_ai/environments/examples/red/units/test_button_press_requirements.py +217 -0
- synth_ai/environments/examples/red/units/test_engine.py +192 -0
- synth_ai/environments/examples/red/units/test_environment.py +455 -0
- synth_ai/environments/examples/red/units/test_exploration_strategy.py +227 -0
- synth_ai/environments/examples/red/units/test_integration.py +217 -0
- synth_ai/environments/examples/red/units/test_memory_extraction.py +111 -0
- synth_ai/environments/examples/red/units/test_menu_bug_reproduction.py +1100 -0
- synth_ai/environments/examples/red/units/test_movement_debug.py +255 -0
- synth_ai/environments/examples/red/units/test_pokemon_mcts_debug.py +163 -0
- synth_ai/environments/examples/red/units/test_pokemon_mcts_verbose.py +117 -0
- synth_ai/environments/examples/red/units/test_red_basic.py +145 -0
- synth_ai/environments/examples/red/units/test_red_comprehensive.py +323 -0
- synth_ai/environments/examples/red/units/test_retry_movement.py +195 -0
- synth_ai/environments/examples/red/units/test_reward_components.py +186 -0
- synth_ai/environments/examples/red/units/test_rom_integration.py +260 -0
- synth_ai/environments/examples/red/units/test_taskset.py +116 -0
- synth_ai/environments/examples/red/units/test_tree.py +448 -0
- synth_ai/environments/examples/sokoban/__init__.py +1 -0
- synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +900 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_dspy_react.py +1 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_sokoban_react_agent.py +498 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_synth_lats.py +1 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_synth_react_locally.py +748 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_synth_react_service.py +296 -0
- synth_ai/environments/examples/sokoban/engine.py +675 -0
- synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +1 -0
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +656 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +17 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +3 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +129 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +370 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +331 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +305 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +66 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +114 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +122 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +394 -0
- synth_ai/environments/examples/sokoban/environment.py +228 -0
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +438 -0
- synth_ai/environments/examples/sokoban/puzzle_loader.py +311 -0
- synth_ai/environments/examples/sokoban/taskset.py +425 -0
- synth_ai/environments/examples/sokoban/units/astar_common.py +94 -0
- synth_ai/environments/examples/sokoban/units/test_building_task_set.py +49 -0
- synth_ai/environments/examples/sokoban/units/test_false_positive.py +120 -0
- synth_ai/environments/examples/sokoban/units/test_simple_run_through_environment.py +119 -0
- synth_ai/environments/examples/sokoban/units/test_sokoban_environment.py +98 -0
- synth_ai/environments/examples/sokoban/units/test_tree.py +364 -0
- synth_ai/environments/examples/tictactoe/__init__.py +1 -0
- synth_ai/environments/examples/tictactoe/agent_demos/test_synth_react.py +266 -0
- synth_ai/environments/examples/tictactoe/agent_demos/test_tictactoe_react_agent.py +470 -0
- synth_ai/environments/examples/tictactoe/engine.py +368 -0
- synth_ai/environments/examples/tictactoe/environment.py +239 -0
- synth_ai/environments/examples/tictactoe/taskset.py +214 -0
- synth_ai/environments/examples/tictactoe/units/test_tictactoe_engine.py +393 -0
- synth_ai/environments/examples/tictactoe/units/test_tictactoe_environment.py +493 -0
- synth_ai/environments/examples/tictactoe/units/test_tictactoe_taskset.py +191 -0
- synth_ai/environments/examples/verilog/__init__.py +10 -0
- synth_ai/environments/examples/verilog/agent_demos/test_synth_react.py +520 -0
- synth_ai/environments/examples/verilog/engine.py +328 -0
- synth_ai/environments/examples/verilog/environment.py +349 -0
- synth_ai/environments/examples/verilog/taskset.py +418 -0
- synth_ai/environments/examples/verilog/units/test_verilog_engine.py +466 -0
- synth_ai/environments/examples/verilog/units/test_verilog_environment.py +585 -0
- synth_ai/environments/examples/verilog/units/test_verilog_integration.py +383 -0
- synth_ai/environments/examples/verilog/units/test_verilog_taskset.py +457 -0
- synth_ai/environments/reproducibility/core.py +42 -0
- synth_ai/environments/reproducibility/tree.py +364 -0
- synth_ai/environments/service/app.py +78 -0
- synth_ai/environments/service/core_routes.py +775 -0
- synth_ai/environments/service/external_registry.py +57 -0
- synth_ai/environments/service/registry.py +9 -0
- synth_ai/environments/stateful/__init__.py +1 -0
- synth_ai/environments/stateful/core.py +28 -0
- synth_ai/environments/stateful/engine.py +21 -0
- synth_ai/environments/stateful/state.py +7 -0
- synth_ai/environments/tasks/api.py +19 -0
- synth_ai/environments/tasks/core.py +78 -0
- synth_ai/environments/tasks/filters.py +39 -0
- synth_ai/environments/tasks/utils.py +89 -0
- synth_ai/environments/v0_observability/history.py +3 -0
- synth_ai/environments/v0_observability/log.py +2 -0
- synth_ai/lm/caching/constants.py +1 -0
- synth_ai/{zyk/lms → lm}/caching/ephemeral.py +4 -8
- synth_ai/{zyk/lms → lm}/caching/handler.py +15 -15
- synth_ai/{zyk/lms → lm}/caching/initialize.py +2 -4
- synth_ai/{zyk/lms → lm}/caching/persistent.py +4 -10
- synth_ai/{zyk/lms → lm}/config.py +2 -1
- synth_ai/{zyk/lms → lm}/constants.py +2 -2
- synth_ai/{zyk/lms → lm}/core/all.py +10 -10
- synth_ai/{zyk/lms → lm}/core/main.py +57 -33
- synth_ai/{zyk/lms → lm}/core/vendor_clients.py +12 -10
- synth_ai/lm/cost/monitor.py +1 -0
- synth_ai/lm/cost/statefulness.py +1 -0
- synth_ai/lm/provider_support/__init__.py +8 -0
- synth_ai/lm/provider_support/anthropic.py +945 -0
- synth_ai/lm/provider_support/openai.py +1115 -0
- synth_ai/lm/provider_support/suppress_logging.py +31 -0
- synth_ai/{zyk/lms → lm}/structured_outputs/handler.py +58 -80
- synth_ai/{zyk/lms → lm}/structured_outputs/inject.py +6 -20
- synth_ai/{zyk/lms → lm}/structured_outputs/rehabilitate.py +6 -12
- synth_ai/{zyk/lms → lm}/vendors/core/anthropic_api.py +21 -30
- synth_ai/{zyk/lms → lm}/vendors/core/gemini_api.py +37 -32
- synth_ai/{zyk/lms → lm}/vendors/core/mistral_api.py +19 -28
- synth_ai/{zyk/lms → lm}/vendors/core/openai_api.py +26 -36
- synth_ai/{zyk/lms → lm}/vendors/openai_standard.py +29 -33
- synth_ai/{zyk/lms → lm}/vendors/retries.py +1 -1
- synth_ai/lm/vendors/supported/__init__.py +0 -0
- synth_ai/{zyk/lms → lm}/vendors/supported/custom_endpoint.py +131 -118
- synth_ai/{zyk/lms → lm}/vendors/supported/deepseek.py +4 -8
- synth_ai/{zyk/lms → lm}/vendors/supported/grok.py +6 -8
- synth_ai/{zyk/lms → lm}/vendors/supported/groq.py +1 -1
- synth_ai/{zyk/lms → lm}/vendors/supported/ollama.py +2 -2
- synth_ai/{zyk/lms → lm}/vendors/supported/openrouter.py +18 -16
- synth_ai/{zyk/lms → lm}/vendors/supported/together.py +1 -1
- synth_ai/tracing/__init__.py +0 -0
- synth_ai/tracing/abstractions.py +224 -0
- synth_ai/tracing/base_client.py +91 -0
- synth_ai/tracing/client_manager.py +131 -0
- synth_ai/tracing/config.py +140 -0
- synth_ai/tracing/context.py +146 -0
- synth_ai/tracing/decorators.py +679 -0
- synth_ai/tracing/events/__init__.py +0 -0
- synth_ai/tracing/events/manage.py +147 -0
- synth_ai/tracing/events/scope.py +86 -0
- synth_ai/tracing/events/store.py +227 -0
- synth_ai/tracing/immediate_client.py +152 -0
- synth_ai/tracing/local.py +18 -0
- synth_ai/tracing/log_client_base.py +74 -0
- synth_ai/tracing/retry_queue.py +187 -0
- synth_ai/tracing/trackers.py +515 -0
- synth_ai/tracing/upload.py +504 -0
- synth_ai/tracing/utils.py +9 -0
- synth_ai/zyk/__init__.py +28 -2
- synth_ai-0.2.1.dev0.dist-info/METADATA +349 -0
- synth_ai-0.2.1.dev0.dist-info/RECORD +261 -0
- synth_ai/zyk/lms/caching/constants.py +0 -1
- synth_ai/zyk/lms/cost/monitor.py +0 -1
- synth_ai/zyk/lms/cost/statefulness.py +0 -1
- synth_ai-0.1.9.dist-info/METADATA +0 -37
- synth_ai-0.1.9.dist-info/RECORD +0 -50
- /synth_ai/{zyk/lms/__init__.py → environments/reproducibility/helpers.py} +0 -0
- /synth_ai/{zyk/lms/caching → lm}/__init__.py +0 -0
- /synth_ai/{zyk/lms/core → lm/caching}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/caching/dbs.py +0 -0
- /synth_ai/{zyk/lms/cost → lm/core}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/core/exceptions.py +0 -0
- /synth_ai/{zyk/lms/structured_outputs → lm/cost}/__init__.py +0 -0
- /synth_ai/{zyk/lms/vendors → lm/structured_outputs}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/tools/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/tools/base.py +0 -0
- /synth_ai/{zyk/lms/vendors/core → lm/vendors}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/vendors/base.py +0 -0
- /synth_ai/{zyk/lms/vendors/local → lm/vendors/core}/__init__.py +0 -0
- /synth_ai/{zyk/lms/vendors/supported → lm/vendors/local}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/vendors/local/ollama.py +0 -0
- {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/WHEEL +0 -0
- {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
import importlib
|
2
|
+
import numpy as np
|
3
|
+
from typing import Any, Dict
|
4
|
+
|
5
|
+
# Minimal attributes to serialize per object type
|
6
|
+
BASIC_ATTRS: Dict[str, list] = {
|
7
|
+
"Player": [
|
8
|
+
"pos",
|
9
|
+
"facing",
|
10
|
+
"health",
|
11
|
+
"inventory",
|
12
|
+
"achievements",
|
13
|
+
"action",
|
14
|
+
"sleeping",
|
15
|
+
"_last_health",
|
16
|
+
"_hunger",
|
17
|
+
"_thirst",
|
18
|
+
"_fatigue",
|
19
|
+
"_recover",
|
20
|
+
],
|
21
|
+
"Cow": ["pos", "health"],
|
22
|
+
"Zombie": ["pos", "health", "cooldown"],
|
23
|
+
"Skeleton": ["pos", "health", "reload"],
|
24
|
+
"Arrow": ["pos", "facing"],
|
25
|
+
"Plant": ["pos", "health", "grown"],
|
26
|
+
"Stone": ["pos"],
|
27
|
+
"Table": ["pos"],
|
28
|
+
"Furnace": ["pos"],
|
29
|
+
# Add other types as needed
|
30
|
+
}
|
31
|
+
|
32
|
+
|
33
|
+
def serialize_world_object(obj: Any) -> Dict[str, Any]:
|
34
|
+
"""Convert a crafter object into a JSON-friendly dict."""
|
35
|
+
cls_name = obj.__class__.__name__
|
36
|
+
fields = BASIC_ATTRS.get(cls_name, ["pos"])
|
37
|
+
payload: Dict[str, Any] = {}
|
38
|
+
for field in fields:
|
39
|
+
val = getattr(obj, field)
|
40
|
+
if isinstance(val, np.ndarray):
|
41
|
+
payload[field] = val.tolist()
|
42
|
+
else:
|
43
|
+
payload[field] = val
|
44
|
+
return {
|
45
|
+
"type": f"{obj.__class__.__module__}.{cls_name}",
|
46
|
+
"state": payload,
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
def deserialize_world_object(blob: Dict[str, Any], world: Any) -> Any:
|
51
|
+
"""Reconstruct a crafter object from its serialized dict."""
|
52
|
+
type_str = blob.get("type", "")
|
53
|
+
module_name, cls_name = type_str.rsplit(".", 1)
|
54
|
+
module = importlib.import_module(module_name)
|
55
|
+
cls = getattr(module, cls_name)
|
56
|
+
# Bypass __init__; create empty instance
|
57
|
+
state = blob.get("state", {})
|
58
|
+
obj = cls.__new__(cls)
|
59
|
+
# Initialize required base attributes
|
60
|
+
obj.world = world
|
61
|
+
obj.random = world.random
|
62
|
+
obj.removed = False
|
63
|
+
# Ensure inventory exists for health setter
|
64
|
+
obj.inventory = {"health": 0}
|
65
|
+
# Set attributes from state
|
66
|
+
for field, value in state.items():
|
67
|
+
if field == "pos":
|
68
|
+
# restore position as numpy array
|
69
|
+
obj.pos = np.array(value)
|
70
|
+
else:
|
71
|
+
# restore other attributes (including property setters)
|
72
|
+
# convert lists back to arrays only for known ndarray fields
|
73
|
+
setattr(obj, field, value)
|
74
|
+
return obj
|
@@ -0,0 +1,255 @@
|
|
1
|
+
"""CrafterClassicEnvironment — thin wrapper exposing CrafterEngine via StatefulEnvironment API."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import List, Optional, Any, Dict, Union
|
6
|
+
import dataclasses
|
7
|
+
import logging
|
8
|
+
|
9
|
+
# Import logging configuration to suppress JAX debug messages
|
10
|
+
from .config_logging import safe_compare
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
from synth_ai.environments.examples.crafter_classic.engine import (
|
15
|
+
CrafterEngine,
|
16
|
+
CrafterPrivateState,
|
17
|
+
CrafterPublicState,
|
18
|
+
CrafterEngineSnapshot,
|
19
|
+
)
|
20
|
+
from synth_ai.environments.examples.crafter_classic.taskset import CrafterTaskInstance
|
21
|
+
from synth_ai.environments.environment.shared_engine import (
|
22
|
+
GetObservationCallable,
|
23
|
+
InternalObservation,
|
24
|
+
)
|
25
|
+
from synth_ai.environments.reproducibility.core import ReproducibleEnvironment
|
26
|
+
from synth_ai.environments.stateful.core import StatefulEnvironment
|
27
|
+
from synth_ai.environments.environment.tools import (
|
28
|
+
AbstractTool,
|
29
|
+
EnvToolCall,
|
30
|
+
ToolResult,
|
31
|
+
TOOL_REGISTRY,
|
32
|
+
register_tool,
|
33
|
+
)
|
34
|
+
from pydantic import BaseModel, Field
|
35
|
+
|
36
|
+
|
37
|
+
# --- Tool Definition ---
|
38
|
+
class CrafterActionInput(BaseModel):
|
39
|
+
action: int = Field(..., description="Integer action for the Crafter environment.")
|
40
|
+
|
41
|
+
|
42
|
+
class CrafterInteractTool(AbstractTool):
|
43
|
+
name = "interact"
|
44
|
+
description = "Performs an action in the Crafter environment."
|
45
|
+
call_schema = CrafterActionInput
|
46
|
+
result_schema = ToolResult
|
47
|
+
|
48
|
+
def __init__(self, engine: CrafterEngine):
|
49
|
+
self.engine = engine
|
50
|
+
|
51
|
+
async def __call__(self, call: EnvToolCall) -> ToolResult:
|
52
|
+
try:
|
53
|
+
validated_args = self.call_schema(**call.args)
|
54
|
+
action_to_pass = self.engine._validate_action_engine(validated_args.action)
|
55
|
+
priv_state, pub_state = await self.engine._step_engine(action_to_pass)
|
56
|
+
return ToolResult(
|
57
|
+
ok=True,
|
58
|
+
payload={
|
59
|
+
"public_state": pub_state,
|
60
|
+
"private_state": priv_state,
|
61
|
+
},
|
62
|
+
)
|
63
|
+
except Exception as e:
|
64
|
+
pub_state_on_error = self.engine._get_public_state_from_env() # Use engine helper
|
65
|
+
# Get a safe private state for error cases
|
66
|
+
health_dead = safe_compare(0, self.engine.env._player.health, ">=")
|
67
|
+
step_exceeded = safe_compare(self.engine.env._length, self.engine.env._step, "<=")
|
68
|
+
priv_state_on_error = self.engine._get_private_state_from_env(
|
69
|
+
0, health_dead, step_exceeded
|
70
|
+
)
|
71
|
+
return ToolResult(
|
72
|
+
ok=False,
|
73
|
+
error=str(e),
|
74
|
+
payload={
|
75
|
+
"public_state": pub_state_on_error,
|
76
|
+
"private_state": priv_state_on_error,
|
77
|
+
},
|
78
|
+
)
|
79
|
+
|
80
|
+
|
81
|
+
# Default observation callable (can be customized via __init__)
|
82
|
+
class SynthCrafterObservationCallable(GetObservationCallable):
|
83
|
+
async def get_observation(
|
84
|
+
self, pub: CrafterPublicState, priv: CrafterPrivateState
|
85
|
+
) -> InternalObservation:
|
86
|
+
# Example: return a dictionary combining public and selected private info
|
87
|
+
# Actual observation structure depends on agent's needs.
|
88
|
+
obs_dict: Dict[str, Any] = dataclasses.asdict(pub) # type: ignore
|
89
|
+
obs_dict["reward_last_step"] = priv.reward_last_step
|
90
|
+
obs_dict["total_reward_episode"] = priv.total_reward_episode
|
91
|
+
obs_dict["terminated"] = priv.terminated
|
92
|
+
obs_dict["truncated"] = priv.truncated
|
93
|
+
if pub.error_info:
|
94
|
+
obs_dict["tool_error"] = pub.error_info
|
95
|
+
return obs_dict
|
96
|
+
|
97
|
+
|
98
|
+
class CrafterClassicEnvironment(StatefulEnvironment, ReproducibleEnvironment[CrafterEngine]):
|
99
|
+
"""Environment wrapper bridging agent tool‑calls to `crafter.Env` dynamics."""
|
100
|
+
|
101
|
+
def __init__(
|
102
|
+
self,
|
103
|
+
task_instance: "CrafterTaskInstance",
|
104
|
+
custom_step_obs: Optional[GetObservationCallable] = None,
|
105
|
+
custom_ckpt_obs: Optional[GetObservationCallable] = None,
|
106
|
+
) -> None:
|
107
|
+
self.name = "CrafterClassic"
|
108
|
+
self.task_instance = task_instance
|
109
|
+
self.custom_step_observation_callable = custom_step_obs or SynthCrafterObservationCallable()
|
110
|
+
self.custom_checkpoint_observation_callable = (
|
111
|
+
custom_ckpt_obs or SynthCrafterObservationCallable()
|
112
|
+
)
|
113
|
+
self.engine = CrafterEngine(task_instance)
|
114
|
+
|
115
|
+
self._interact_tool = CrafterInteractTool(self.engine)
|
116
|
+
if self._interact_tool.name not in TOOL_REGISTRY:
|
117
|
+
register_tool(self._interact_tool)
|
118
|
+
|
119
|
+
# ────────────────────────────────────────────────────────────────────
|
120
|
+
# Lifecycle helpers
|
121
|
+
# ────────────────────────────────────────────────────────────────────
|
122
|
+
|
123
|
+
async def initialize(self) -> InternalObservation: # type: ignore[override]
|
124
|
+
priv, pub = await self.engine._reset_engine()
|
125
|
+
return await self._to_observation(priv, pub, self.custom_step_observation_callable)
|
126
|
+
|
127
|
+
async def terminate(self) -> InternalObservation: # type: ignore[override]
|
128
|
+
pub = self.engine._get_public_state_from_env()
|
129
|
+
priv = self.engine._get_private_state_from_env(0, True, False) # Terminated state
|
130
|
+
priv.terminated = True
|
131
|
+
obs_dict = {"status": "Environment terminated."}
|
132
|
+
return await self._to_observation(
|
133
|
+
priv, pub, self.custom_step_observation_callable, extra_obs=obs_dict
|
134
|
+
)
|
135
|
+
|
136
|
+
# ────────────────────────────────────────────────────────────────────
|
137
|
+
# Step + checkpoint
|
138
|
+
# ────────────────────────────────────────────────────────────────────
|
139
|
+
|
140
|
+
def validate_tool_calls(
|
141
|
+
self, tool_calls: Union[EnvToolCall, List[EnvToolCall], List[List[EnvToolCall]]]
|
142
|
+
) -> EnvToolCall:
|
143
|
+
# Normalize and validate to a single EnvToolCall (same as Sokoban)
|
144
|
+
if isinstance(tool_calls, list):
|
145
|
+
if not tool_calls:
|
146
|
+
raise ValueError("Received empty list of tool calls.")
|
147
|
+
if isinstance(tool_calls[0], list):
|
148
|
+
if not tool_calls[0]:
|
149
|
+
raise ValueError("Received empty inner list of tool calls.")
|
150
|
+
agent_call = tool_calls[0][0]
|
151
|
+
else:
|
152
|
+
agent_call = tool_calls[0]
|
153
|
+
elif isinstance(tool_calls, EnvToolCall):
|
154
|
+
agent_call = tool_calls
|
155
|
+
else:
|
156
|
+
raise TypeError(f"Unexpected type for tool_calls: {type(tool_calls)}")
|
157
|
+
|
158
|
+
if not isinstance(agent_call, EnvToolCall):
|
159
|
+
raise TypeError(f"Processed call is not EnvToolCall: {type(agent_call)}")
|
160
|
+
if agent_call.tool != "interact":
|
161
|
+
raise ValueError(f"Unknown tool: {agent_call.tool}. Expected 'interact'.")
|
162
|
+
return agent_call
|
163
|
+
|
164
|
+
async def step(
|
165
|
+
self, tool_calls: Union[EnvToolCall, List[EnvToolCall], List[List[EnvToolCall]]]
|
166
|
+
) -> InternalObservation: # type: ignore[override]
|
167
|
+
agent_call = self.validate_tool_calls(tool_calls)
|
168
|
+
tool_result: ToolResult = await self._interact_tool(agent_call)
|
169
|
+
|
170
|
+
payload_dict = tool_result.payload
|
171
|
+
pub_state: CrafterPublicState
|
172
|
+
priv_state: CrafterPrivateState
|
173
|
+
|
174
|
+
if tool_result.ok:
|
175
|
+
# payload contains the actual state objects from the interact tool
|
176
|
+
priv_state = payload_dict.get("private_state")
|
177
|
+
pub_state = payload_dict.get("public_state")
|
178
|
+
|
179
|
+
# Validate we got the expected state objects
|
180
|
+
if not isinstance(priv_state, CrafterPrivateState) or not isinstance(
|
181
|
+
pub_state, CrafterPublicState
|
182
|
+
):
|
183
|
+
logger.error(
|
184
|
+
f"Invalid state types in payload: priv={type(priv_state)}, pub={type(pub_state)}"
|
185
|
+
)
|
186
|
+
# Fall back to getting current state
|
187
|
+
pub_state = self.engine._get_public_state_from_env()
|
188
|
+
health_dead = safe_compare(0, self.engine.env._player.health, ">=")
|
189
|
+
step_exceeded = safe_compare(self.engine.env._length, self.engine.env._step, "<=")
|
190
|
+
priv_state = self.engine._get_private_state_from_env(0, health_dead, step_exceeded)
|
191
|
+
pub_state.error_info = "Invalid state types in tool result"
|
192
|
+
else:
|
193
|
+
# Tool call failed, use states from payload if available, otherwise get current state
|
194
|
+
priv_state = payload_dict.get("private_state")
|
195
|
+
pub_state = payload_dict.get("public_state")
|
196
|
+
|
197
|
+
if not isinstance(priv_state, CrafterPrivateState) or not isinstance(
|
198
|
+
pub_state, CrafterPublicState
|
199
|
+
):
|
200
|
+
# Fall back to getting current state
|
201
|
+
pub_state = self.engine._get_public_state_from_env()
|
202
|
+
health_dead = safe_compare(0, self.engine.env._player.health, ">=")
|
203
|
+
step_exceeded = safe_compare(self.engine.env._length, self.engine.env._step, "<=")
|
204
|
+
priv_state = self.engine._get_private_state_from_env(0, health_dead, step_exceeded)
|
205
|
+
|
206
|
+
if tool_result.error:
|
207
|
+
pub_state.error_info = tool_result.error
|
208
|
+
|
209
|
+
return await self._to_observation(
|
210
|
+
priv_state, pub_state, self.custom_step_observation_callable
|
211
|
+
)
|
212
|
+
|
213
|
+
async def checkpoint(self) -> InternalObservation: # type: ignore[override]
|
214
|
+
engine_snapshot: CrafterEngineSnapshot = await self.engine._serialize_engine()
|
215
|
+
priv = self.engine._get_private_state_from_env(0, False, False) # Get current state for obs
|
216
|
+
pub = self.engine._get_public_state_from_env()
|
217
|
+
obs_data = await self._to_observation(
|
218
|
+
priv, pub, self.custom_checkpoint_observation_callable
|
219
|
+
)
|
220
|
+
if isinstance(obs_data, dict):
|
221
|
+
obs_data["engine_snapshot_data"] = engine_snapshot.model_dump()
|
222
|
+
return obs_data
|
223
|
+
|
224
|
+
# ────────────────────────────────────────────────────────────────────
|
225
|
+
# Helpers
|
226
|
+
# ────────────────────────────────────────────────────────────────────
|
227
|
+
|
228
|
+
async def _to_observation(
|
229
|
+
self,
|
230
|
+
priv: CrafterPrivateState,
|
231
|
+
pub: CrafterPublicState,
|
232
|
+
obs_cb: Optional[GetObservationCallable],
|
233
|
+
extra_obs: Optional[Dict[str, Any]] = None,
|
234
|
+
) -> InternalObservation:
|
235
|
+
active_obs_cb = obs_cb or SynthCrafterObservationCallable()
|
236
|
+
observation = await active_obs_cb.get_observation(pub, priv)
|
237
|
+
if extra_obs and isinstance(observation, dict):
|
238
|
+
observation.update(extra_obs)
|
239
|
+
return observation
|
240
|
+
|
241
|
+
# ────────────────────────────────────────────────────────────────────
|
242
|
+
# ReproducibleEnvironment plumbing
|
243
|
+
# ────────────────────────────────────────────────────────────────────
|
244
|
+
|
245
|
+
async def _serialize_engine(self) -> CrafterEngineSnapshot:
|
246
|
+
return await self.engine._serialize_engine()
|
247
|
+
|
248
|
+
@classmethod
|
249
|
+
async def _deserialize_engine(
|
250
|
+
cls, snapshot: CrafterEngineSnapshot, task_instance: "CrafterTaskInstance"
|
251
|
+
) -> "CrafterClassicEnvironment":
|
252
|
+
eng = await CrafterEngine._deserialize_engine(snapshot, task_instance)
|
253
|
+
env = cls(task_instance)
|
254
|
+
env.engine = eng
|
255
|
+
return env
|
@@ -0,0 +1,228 @@
|
|
1
|
+
"""Procedural Crafter taskset generation with seed filtering by world traits.
|
2
|
+
Run this to build a TaskInstanceSet with reproducible initial snapshots.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from __future__ import annotations
|
6
|
+
|
7
|
+
import asyncio
|
8
|
+
import random
|
9
|
+
from dataclasses import dataclass, asdict, fields
|
10
|
+
from typing import Dict, List
|
11
|
+
from uuid import UUID, uuid4
|
12
|
+
|
13
|
+
import numpy as np
|
14
|
+
import crafter
|
15
|
+
from crafter import objects
|
16
|
+
|
17
|
+
from synth_ai.environments.tasks.core import (
|
18
|
+
Impetus,
|
19
|
+
Intent,
|
20
|
+
SplitInfo,
|
21
|
+
Task,
|
22
|
+
TaskInstance,
|
23
|
+
TaskInstanceMetadata,
|
24
|
+
TaskInstanceSet,
|
25
|
+
)
|
26
|
+
|
27
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
28
|
+
# Config
|
29
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
30
|
+
TASK = Task(
|
31
|
+
global_premises="Procedural Crafter seed generation",
|
32
|
+
global_constraints="",
|
33
|
+
global_objectives="Survive and unlock achievements.",
|
34
|
+
shared_env_params={},
|
35
|
+
)
|
36
|
+
|
37
|
+
AREA = (64, 64)
|
38
|
+
LEN = 10000
|
39
|
+
RADIUS = 10 # Manhattan distance for local trait count
|
40
|
+
SEED_START = 0
|
41
|
+
NUM_INSTANCES = 50
|
42
|
+
|
43
|
+
# Desired trait ranges per difficulty tier
|
44
|
+
TRAIT_BOUNDS = {
|
45
|
+
"easy": {
|
46
|
+
"min_trees": 4,
|
47
|
+
"max_hostiles": 0,
|
48
|
+
},
|
49
|
+
"medium": {
|
50
|
+
"min_trees": 2,
|
51
|
+
"max_hostiles": 2,
|
52
|
+
},
|
53
|
+
"hard": {
|
54
|
+
"min_trees": 0,
|
55
|
+
"max_hostiles": 5,
|
56
|
+
},
|
57
|
+
}
|
58
|
+
|
59
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
60
|
+
# Metadata + instance helpers
|
61
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
62
|
+
|
63
|
+
|
64
|
+
@dataclass
|
65
|
+
class CrafterTaskInstanceMetadata(TaskInstanceMetadata):
|
66
|
+
difficulty: str
|
67
|
+
seed: int
|
68
|
+
num_trees_radius: int
|
69
|
+
num_cows_radius: int
|
70
|
+
num_hostiles_radius: int
|
71
|
+
|
72
|
+
|
73
|
+
@dataclass
|
74
|
+
class CrafterTaskInstance(TaskInstance):
|
75
|
+
async def serialize(self) -> dict: # identical to Sokoban pattern
|
76
|
+
data = asdict(self)
|
77
|
+
if isinstance(data.get("id"), UUID):
|
78
|
+
data["id"] = str(data["id"])
|
79
|
+
if "intent" in data and data["intent"] is not None:
|
80
|
+
data["intent"]["deterministic_eval_functions"] = []
|
81
|
+
return data
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
async def deserialize(cls, data: dict) -> "CrafterTaskInstance":
|
85
|
+
if "id" in data:
|
86
|
+
try:
|
87
|
+
data["id"] = UUID(str(data["id"]))
|
88
|
+
except Exception:
|
89
|
+
pass
|
90
|
+
if "impetus" in data and isinstance(data["impetus"], dict):
|
91
|
+
impetus_data = data["impetus"]
|
92
|
+
# Ensure instructions field exists with default if missing
|
93
|
+
if "instructions" not in impetus_data:
|
94
|
+
impetus_data["instructions"] = "Survive and unlock achievements"
|
95
|
+
data["impetus"] = Impetus(**impetus_data)
|
96
|
+
if "intent" in data and isinstance(data["intent"], dict):
|
97
|
+
intent_data = data["intent"]
|
98
|
+
# Ensure required fields exist with defaults if missing
|
99
|
+
if "rubric" not in intent_data:
|
100
|
+
intent_data["rubric"] = {"goal": "Unlock achievements"}
|
101
|
+
if "gold_trajectories" not in intent_data:
|
102
|
+
intent_data["gold_trajectories"] = None
|
103
|
+
if "gold_state_diff" not in intent_data:
|
104
|
+
intent_data["gold_state_diff"] = {}
|
105
|
+
intent_data["deterministic_eval_functions"] = []
|
106
|
+
data["intent"] = Intent(**intent_data)
|
107
|
+
if "metadata" in data and isinstance(data["metadata"], dict):
|
108
|
+
metadata_data = data["metadata"]
|
109
|
+
# Ensure required fields exist with defaults if missing
|
110
|
+
if "difficulty" not in metadata_data:
|
111
|
+
metadata_data["difficulty"] = "medium"
|
112
|
+
if "seed" not in metadata_data:
|
113
|
+
metadata_data["seed"] = 0
|
114
|
+
if "num_trees_radius" not in metadata_data:
|
115
|
+
metadata_data["num_trees_radius"] = 0
|
116
|
+
if "num_cows_radius" not in metadata_data:
|
117
|
+
metadata_data["num_cows_radius"] = 0
|
118
|
+
if "num_hostiles_radius" not in metadata_data:
|
119
|
+
metadata_data["num_hostiles_radius"] = 0
|
120
|
+
data["metadata"] = CrafterTaskInstanceMetadata(**metadata_data)
|
121
|
+
keep = {f.name for f in fields(cls)}
|
122
|
+
return cls(**{k: v for k, v in data.items() if k in keep})
|
123
|
+
|
124
|
+
|
125
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
126
|
+
# Trait extraction util
|
127
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
128
|
+
|
129
|
+
|
130
|
+
def world_traits(env: crafter.Env, radius: int = RADIUS) -> Dict[str, int]:
|
131
|
+
player = env._player # type: ignore[attr-defined]
|
132
|
+
pos = np.array(player.pos)
|
133
|
+
counts = {"trees": 0, "cows": 0, "hostiles": 0}
|
134
|
+
for obj in env._world._objects: # type: ignore[attr-defined]
|
135
|
+
if obj is None or obj is player:
|
136
|
+
continue
|
137
|
+
if np.abs(obj.pos - pos).sum() > radius:
|
138
|
+
continue
|
139
|
+
if isinstance(obj, objects.Plant) and getattr(obj, "kind", "") == "tree":
|
140
|
+
counts["trees"] += 1
|
141
|
+
elif isinstance(obj, objects.Cow):
|
142
|
+
counts["cows"] += 1
|
143
|
+
elif isinstance(obj, (objects.Zombie, objects.Skeleton)):
|
144
|
+
counts["hostiles"] += 1
|
145
|
+
return counts
|
146
|
+
|
147
|
+
|
148
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
149
|
+
# Main generator
|
150
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
151
|
+
|
152
|
+
|
153
|
+
async def create_crafter_taskset(num_instances: int = NUM_INSTANCES) -> TaskInstanceSet:
|
154
|
+
instances: List[CrafterTaskInstance] = []
|
155
|
+
seed = SEED_START
|
156
|
+
while len(instances) < num_instances:
|
157
|
+
env = crafter.Env(area=AREA, length=LEN, seed=seed)
|
158
|
+
_ = env.reset()
|
159
|
+
traits = world_traits(env)
|
160
|
+
# assign difficulty tier first match
|
161
|
+
difficulty: str | None = None
|
162
|
+
for diff, bounds in TRAIT_BOUNDS.items():
|
163
|
+
if (
|
164
|
+
traits["trees"] >= bounds["min_trees"]
|
165
|
+
and traits["hostiles"] <= bounds["max_hostiles"]
|
166
|
+
):
|
167
|
+
difficulty = diff
|
168
|
+
break
|
169
|
+
if difficulty is None:
|
170
|
+
seed += 1
|
171
|
+
continue
|
172
|
+
# build instance
|
173
|
+
impetus = Impetus(instructions=f"Survive and unlock achievements. Difficulty={difficulty}.")
|
174
|
+
intent = Intent(
|
175
|
+
rubric={"goal": "Unlock as many achievements as possible."},
|
176
|
+
gold_trajectories=None,
|
177
|
+
gold_state_diff={},
|
178
|
+
)
|
179
|
+
metadata = CrafterTaskInstanceMetadata(
|
180
|
+
difficulty=difficulty,
|
181
|
+
seed=seed,
|
182
|
+
num_trees_radius=traits["trees"],
|
183
|
+
num_cows_radius=traits["cows"],
|
184
|
+
num_hostiles_radius=traits["hostiles"],
|
185
|
+
)
|
186
|
+
instance = CrafterTaskInstance(
|
187
|
+
id=uuid4(),
|
188
|
+
impetus=impetus,
|
189
|
+
intent=intent,
|
190
|
+
metadata=metadata,
|
191
|
+
is_reproducible=True,
|
192
|
+
initial_engine_snapshot=None, # will be filled lazily when env starts
|
193
|
+
)
|
194
|
+
instances.append(instance)
|
195
|
+
seed += 1
|
196
|
+
|
197
|
+
# simple random split 80/10/10
|
198
|
+
random.shuffle(instances)
|
199
|
+
n = len(instances)
|
200
|
+
val_ids = {inst.id for inst in instances[int(0.8 * n) : int(0.9 * n)]}
|
201
|
+
test_ids = {inst.id for inst in instances[int(0.9 * n) :]}
|
202
|
+
split = SplitInfo(val_instance_ids=val_ids, test_instance_ids=test_ids, _is_split_defined=True)
|
203
|
+
|
204
|
+
return TaskInstanceSet(
|
205
|
+
name="Crafter Procedural TaskSet",
|
206
|
+
description="Crafter seeds filtered by local world traits around spawn.",
|
207
|
+
instances=instances,
|
208
|
+
split_info=split,
|
209
|
+
)
|
210
|
+
|
211
|
+
|
212
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
213
|
+
# CLI example
|
214
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
215
|
+
|
216
|
+
if __name__ == "__main__":
|
217
|
+
import json
|
218
|
+
import pathlib
|
219
|
+
|
220
|
+
async def _main():
|
221
|
+
ts = await create_crafter_taskset(30)
|
222
|
+
serial = await asyncio.gather(*(inst.serialize() for inst in ts.instances))
|
223
|
+
out = pathlib.Path("dataset/crafter_instances.json")
|
224
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
225
|
+
out.write_text(json.dumps(serial, indent=2))
|
226
|
+
print(f"Saved {len(serial)} instances → {out}")
|
227
|
+
|
228
|
+
asyncio.run(_main())
|