synth-ai 0.2.4.dev5__py3-none-any.whl → 0.2.4.dev7__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 +18 -9
- synth_ai/cli/__init__.py +10 -5
- synth_ai/cli/balance.py +22 -17
- synth_ai/cli/calc.py +2 -3
- synth_ai/cli/demo.py +3 -5
- synth_ai/cli/legacy_root_backup.py +58 -32
- synth_ai/cli/man.py +22 -19
- synth_ai/cli/recent.py +9 -8
- synth_ai/cli/root.py +58 -13
- synth_ai/cli/status.py +13 -6
- synth_ai/cli/traces.py +45 -21
- synth_ai/cli/watch.py +40 -37
- synth_ai/config/base_url.py +1 -3
- synth_ai/core/experiment.py +1 -2
- synth_ai/environments/__init__.py +2 -6
- synth_ai/environments/environment/artifacts/base.py +3 -1
- synth_ai/environments/environment/db/sqlite.py +1 -1
- synth_ai/environments/environment/registry.py +19 -20
- synth_ai/environments/environment/resources/sqlite.py +2 -3
- synth_ai/environments/environment/rewards/core.py +3 -2
- synth_ai/environments/environment/tools/__init__.py +6 -4
- synth_ai/environments/examples/crafter_classic/__init__.py +1 -1
- synth_ai/environments/examples/crafter_classic/engine.py +21 -17
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +1 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +3 -2
- synth_ai/environments/examples/crafter_classic/environment.py +16 -15
- synth_ai/environments/examples/crafter_classic/taskset.py +2 -2
- synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +2 -3
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +2 -1
- synth_ai/environments/examples/crafter_custom/crafter/__init__.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/config.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/env.py +1 -5
- synth_ai/environments/examples/crafter_custom/crafter/objects.py +1 -2
- synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +1 -2
- synth_ai/environments/examples/crafter_custom/dataset_builder.py +5 -5
- synth_ai/environments/examples/crafter_custom/environment.py +13 -13
- synth_ai/environments/examples/crafter_custom/run_dataset.py +5 -5
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +2 -2
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +5 -4
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +2 -1
- synth_ai/environments/examples/enron/engine.py +18 -14
- synth_ai/environments/examples/enron/environment.py +12 -11
- synth_ai/environments/examples/enron/taskset.py +7 -7
- synth_ai/environments/examples/minigrid/__init__.py +6 -6
- synth_ai/environments/examples/minigrid/engine.py +6 -6
- synth_ai/environments/examples/minigrid/environment.py +6 -6
- synth_ai/environments/examples/minigrid/puzzle_loader.py +3 -2
- synth_ai/environments/examples/minigrid/taskset.py +13 -13
- synth_ai/environments/examples/nethack/achievements.py +1 -1
- synth_ai/environments/examples/nethack/engine.py +8 -7
- synth_ai/environments/examples/nethack/environment.py +10 -9
- synth_ai/environments/examples/nethack/helpers/__init__.py +8 -9
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +1 -1
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +2 -1
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +1 -1
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +3 -4
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +6 -5
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +5 -5
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +7 -6
- synth_ai/environments/examples/nethack/taskset.py +5 -5
- synth_ai/environments/examples/red/engine.py +9 -8
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +7 -7
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +3 -2
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +2 -1
- synth_ai/environments/examples/red/environment.py +18 -15
- synth_ai/environments/examples/red/taskset.py +5 -3
- synth_ai/environments/examples/sokoban/engine.py +16 -13
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +7 -5
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +1 -1
- synth_ai/environments/examples/sokoban/environment.py +15 -14
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +5 -3
- synth_ai/environments/examples/sokoban/puzzle_loader.py +3 -2
- synth_ai/environments/examples/sokoban/taskset.py +13 -10
- synth_ai/environments/examples/tictactoe/engine.py +6 -6
- synth_ai/environments/examples/tictactoe/environment.py +8 -7
- synth_ai/environments/examples/tictactoe/taskset.py +6 -5
- synth_ai/environments/examples/verilog/engine.py +4 -3
- synth_ai/environments/examples/verilog/environment.py +11 -10
- synth_ai/environments/examples/verilog/taskset.py +14 -12
- synth_ai/environments/examples/wordle/__init__.py +29 -0
- synth_ai/environments/examples/wordle/engine.py +398 -0
- synth_ai/environments/examples/wordle/environment.py +159 -0
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +75 -0
- synth_ai/environments/examples/wordle/taskset.py +230 -0
- synth_ai/environments/reproducibility/core.py +1 -1
- synth_ai/environments/reproducibility/tree.py +21 -21
- synth_ai/environments/service/app.py +11 -2
- synth_ai/environments/service/core_routes.py +137 -105
- synth_ai/environments/service/external_registry.py +1 -2
- synth_ai/environments/service/registry.py +1 -1
- synth_ai/environments/stateful/core.py +1 -2
- synth_ai/environments/stateful/engine.py +1 -1
- synth_ai/environments/tasks/api.py +4 -4
- synth_ai/environments/tasks/core.py +14 -12
- synth_ai/environments/tasks/filters.py +6 -4
- synth_ai/environments/tasks/utils.py +13 -11
- synth_ai/evals/base.py +2 -3
- synth_ai/experimental/synth_oss.py +4 -4
- synth_ai/learning/gateway.py +1 -3
- synth_ai/learning/prompts/banking77_injection_eval.py +168 -0
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +213 -0
- synth_ai/learning/prompts/mipro.py +282 -1
- synth_ai/learning/prompts/random_search.py +246 -0
- synth_ai/learning/prompts/run_mipro_banking77.py +172 -0
- synth_ai/learning/prompts/run_random_search_banking77.py +324 -0
- synth_ai/lm/__init__.py +5 -5
- synth_ai/lm/caching/ephemeral.py +9 -9
- synth_ai/lm/caching/handler.py +20 -20
- synth_ai/lm/caching/persistent.py +10 -10
- synth_ai/lm/config.py +3 -3
- synth_ai/lm/constants.py +7 -7
- synth_ai/lm/core/all.py +17 -3
- synth_ai/lm/core/exceptions.py +0 -2
- synth_ai/lm/core/main.py +26 -41
- synth_ai/lm/core/main_v3.py +20 -10
- synth_ai/lm/core/vendor_clients.py +18 -17
- synth_ai/lm/injection.py +80 -0
- synth_ai/lm/overrides.py +206 -0
- synth_ai/lm/provider_support/__init__.py +1 -1
- synth_ai/lm/provider_support/anthropic.py +51 -24
- synth_ai/lm/provider_support/openai.py +51 -22
- synth_ai/lm/structured_outputs/handler.py +34 -32
- synth_ai/lm/structured_outputs/inject.py +24 -27
- synth_ai/lm/structured_outputs/rehabilitate.py +19 -15
- synth_ai/lm/tools/base.py +17 -16
- synth_ai/lm/unified_interface.py +17 -18
- synth_ai/lm/vendors/base.py +20 -18
- synth_ai/lm/vendors/core/anthropic_api.py +50 -25
- synth_ai/lm/vendors/core/gemini_api.py +31 -36
- synth_ai/lm/vendors/core/mistral_api.py +19 -19
- synth_ai/lm/vendors/core/openai_api.py +11 -10
- synth_ai/lm/vendors/openai_standard.py +144 -88
- synth_ai/lm/vendors/openai_standard_responses.py +74 -61
- synth_ai/lm/vendors/retries.py +9 -1
- synth_ai/lm/vendors/supported/custom_endpoint.py +26 -26
- synth_ai/lm/vendors/supported/deepseek.py +10 -10
- synth_ai/lm/vendors/supported/grok.py +8 -8
- synth_ai/lm/vendors/supported/ollama.py +2 -1
- synth_ai/lm/vendors/supported/openrouter.py +11 -9
- synth_ai/lm/vendors/synth_client.py +69 -63
- synth_ai/lm/warmup.py +8 -7
- synth_ai/tracing/__init__.py +22 -10
- synth_ai/tracing_v1/__init__.py +22 -20
- synth_ai/tracing_v3/__init__.py +7 -7
- synth_ai/tracing_v3/abstractions.py +56 -52
- synth_ai/tracing_v3/config.py +4 -2
- synth_ai/tracing_v3/db_config.py +6 -8
- synth_ai/tracing_v3/decorators.py +29 -30
- synth_ai/tracing_v3/examples/basic_usage.py +12 -12
- synth_ai/tracing_v3/hooks.py +21 -21
- synth_ai/tracing_v3/llm_call_record_helpers.py +85 -98
- synth_ai/tracing_v3/lm_call_record_abstractions.py +2 -4
- synth_ai/tracing_v3/migration_helper.py +3 -5
- synth_ai/tracing_v3/replica_sync.py +30 -32
- synth_ai/tracing_v3/session_tracer.py +35 -29
- synth_ai/tracing_v3/storage/__init__.py +1 -1
- synth_ai/tracing_v3/storage/base.py +8 -7
- synth_ai/tracing_v3/storage/config.py +4 -4
- synth_ai/tracing_v3/storage/factory.py +4 -4
- synth_ai/tracing_v3/storage/utils.py +9 -9
- synth_ai/tracing_v3/turso/__init__.py +3 -3
- synth_ai/tracing_v3/turso/daemon.py +9 -9
- synth_ai/tracing_v3/turso/manager.py +60 -48
- synth_ai/tracing_v3/turso/models.py +24 -19
- synth_ai/tracing_v3/utils.py +5 -5
- synth_ai/tui/__main__.py +1 -1
- synth_ai/tui/cli/query_experiments.py +2 -3
- synth_ai/tui/cli/query_experiments_v3.py +2 -3
- synth_ai/tui/dashboard.py +97 -86
- synth_ai/v0/tracing/abstractions.py +28 -28
- synth_ai/v0/tracing/base_client.py +9 -9
- synth_ai/v0/tracing/client_manager.py +7 -7
- synth_ai/v0/tracing/config.py +7 -7
- synth_ai/v0/tracing/context.py +6 -6
- synth_ai/v0/tracing/decorators.py +6 -5
- synth_ai/v0/tracing/events/manage.py +1 -1
- synth_ai/v0/tracing/events/store.py +5 -4
- synth_ai/v0/tracing/immediate_client.py +4 -5
- synth_ai/v0/tracing/local.py +3 -3
- synth_ai/v0/tracing/log_client_base.py +4 -5
- synth_ai/v0/tracing/retry_queue.py +5 -6
- synth_ai/v0/tracing/trackers.py +25 -25
- synth_ai/v0/tracing/upload.py +6 -0
- synth_ai/v0/tracing_v1/__init__.py +1 -1
- synth_ai/v0/tracing_v1/abstractions.py +28 -28
- synth_ai/v0/tracing_v1/base_client.py +9 -9
- synth_ai/v0/tracing_v1/client_manager.py +7 -7
- synth_ai/v0/tracing_v1/config.py +7 -7
- synth_ai/v0/tracing_v1/context.py +6 -6
- synth_ai/v0/tracing_v1/decorators.py +7 -6
- synth_ai/v0/tracing_v1/events/manage.py +1 -1
- synth_ai/v0/tracing_v1/events/store.py +5 -4
- synth_ai/v0/tracing_v1/immediate_client.py +4 -5
- synth_ai/v0/tracing_v1/local.py +3 -3
- synth_ai/v0/tracing_v1/log_client_base.py +4 -5
- synth_ai/v0/tracing_v1/retry_queue.py +5 -6
- synth_ai/v0/tracing_v1/trackers.py +25 -25
- synth_ai/v0/tracing_v1/upload.py +25 -24
- synth_ai/zyk/__init__.py +1 -0
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/METADATA +2 -11
- synth_ai-0.2.4.dev7.dist-info/RECORD +299 -0
- synth_ai-0.2.4.dev5.dist-info/RECORD +0 -287
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,230 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import json
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from pathlib import Path
|
6
|
+
from uuid import UUID
|
7
|
+
|
8
|
+
from synth_ai.environments.tasks.core import (
|
9
|
+
Impetus,
|
10
|
+
Intent,
|
11
|
+
SplitInfo,
|
12
|
+
TaskInstance,
|
13
|
+
TaskInstanceMetadata,
|
14
|
+
TaskInstanceSet,
|
15
|
+
)
|
16
|
+
|
17
|
+
from .engine import DEFAULT_SOLUTIONS
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class WordleTaskInstanceMetadata(TaskInstanceMetadata):
|
22
|
+
word_length: int
|
23
|
+
max_guesses: int
|
24
|
+
target_word: str
|
25
|
+
enforce_wordlist: bool
|
26
|
+
seed: int | None = None
|
27
|
+
consume_invalid_attempts: bool = True
|
28
|
+
|
29
|
+
|
30
|
+
@dataclass
|
31
|
+
class WordleTaskInstance(TaskInstance):
|
32
|
+
async def serialize(self) -> dict:
|
33
|
+
return {
|
34
|
+
"id": str(self.id),
|
35
|
+
"impetus": {"instructions": self.impetus.instructions},
|
36
|
+
"intent": {
|
37
|
+
"rubric": self.intent.rubric,
|
38
|
+
"gold_trajectories": self.intent.gold_trajectories,
|
39
|
+
"gold_state_diff": self.intent.gold_state_diff,
|
40
|
+
},
|
41
|
+
"metadata": {
|
42
|
+
"word_length": self.metadata.word_length,
|
43
|
+
"max_guesses": self.metadata.max_guesses,
|
44
|
+
"target_word": self.metadata.target_word,
|
45
|
+
"enforce_wordlist": self.metadata.enforce_wordlist,
|
46
|
+
"seed": self.metadata.seed,
|
47
|
+
"consume_invalid_attempts": self.metadata.consume_invalid_attempts,
|
48
|
+
},
|
49
|
+
"is_reproducible": self.is_reproducible,
|
50
|
+
"initial_engine_snapshot": self.initial_engine_snapshot,
|
51
|
+
}
|
52
|
+
|
53
|
+
@classmethod
|
54
|
+
async def deserialize(cls, data: dict) -> WordleTaskInstance:
|
55
|
+
from uuid import UUID
|
56
|
+
|
57
|
+
metadata = WordleTaskInstanceMetadata(
|
58
|
+
word_length=data["metadata"]["word_length"],
|
59
|
+
max_guesses=data["metadata"]["max_guesses"],
|
60
|
+
target_word=data["metadata"]["target_word"],
|
61
|
+
enforce_wordlist=data["metadata"]["enforce_wordlist"],
|
62
|
+
seed=data["metadata"].get("seed"),
|
63
|
+
consume_invalid_attempts=data["metadata"].get("consume_invalid_attempts", True),
|
64
|
+
)
|
65
|
+
|
66
|
+
return cls(
|
67
|
+
id=UUID(data["id"]),
|
68
|
+
impetus=Impetus(instructions=data["impetus"]["instructions"]),
|
69
|
+
intent=Intent(
|
70
|
+
rubric=data["intent"]["rubric"],
|
71
|
+
gold_trajectories=data["intent"]["gold_trajectories"],
|
72
|
+
gold_state_diff=data["intent"]["gold_state_diff"],
|
73
|
+
),
|
74
|
+
metadata=metadata,
|
75
|
+
is_reproducible=data["is_reproducible"],
|
76
|
+
initial_engine_snapshot=data["initial_engine_snapshot"],
|
77
|
+
)
|
78
|
+
|
79
|
+
|
80
|
+
def _stable_uuid_for_instance(idx: int, target: str) -> UUID:
|
81
|
+
import uuid
|
82
|
+
|
83
|
+
return uuid.uuid5(uuid.NAMESPACE_URL, f"wordle-fixed-v1:{idx}:{target}")
|
84
|
+
|
85
|
+
|
86
|
+
def _load_fixed_instances_json() -> tuple[list[dict], dict]:
|
87
|
+
"""Load fixed instances definition from instances.json (if present).
|
88
|
+
|
89
|
+
Returns a tuple (instances, defaults) where instances is a list of dicts with at least
|
90
|
+
target_word fields, and defaults contains default params.
|
91
|
+
"""
|
92
|
+
import os
|
93
|
+
|
94
|
+
# Allow override via env var
|
95
|
+
override = os.getenv("WORDLE_INSTANCES_JSON")
|
96
|
+
p = Path(override) if override else Path(__file__).with_name("instances.json")
|
97
|
+
if not p.exists():
|
98
|
+
return [], {}
|
99
|
+
try:
|
100
|
+
data = json.loads(p.read_text())
|
101
|
+
defaults = data.get("defaults", {}) or {}
|
102
|
+
insts = data.get("instances", []) or []
|
103
|
+
return insts, defaults
|
104
|
+
except Exception:
|
105
|
+
return [], {}
|
106
|
+
|
107
|
+
|
108
|
+
# Note: generation helpers removed from runtime. Use the provided script in tools/
|
109
|
+
_ = None
|
110
|
+
|
111
|
+
|
112
|
+
async def create_wordle_taskset(
|
113
|
+
*,
|
114
|
+
word_length: int = 5,
|
115
|
+
max_guesses: int = 6,
|
116
|
+
enforce_wordlist: bool = False,
|
117
|
+
sample_size: int = 30,
|
118
|
+
consume_invalid_attempts: bool = True,
|
119
|
+
) -> TaskInstanceSet:
|
120
|
+
"""Create a Wordle taskset.
|
121
|
+
|
122
|
+
Priority:
|
123
|
+
1) If instances.json exists, use it to produce a fixed, stable taskset with deterministic IDs.
|
124
|
+
2) Otherwise, fall back to a procedural slice of DEFAULT_SOLUTIONS (stable ordering).
|
125
|
+
"""
|
126
|
+
|
127
|
+
json_insts, json_defaults = _load_fixed_instances_json()
|
128
|
+
|
129
|
+
instances: list[WordleTaskInstance] = []
|
130
|
+
# Assemble fixed targets from JSON only (no runtime generation)
|
131
|
+
fixed_targets: list[str] = []
|
132
|
+
if json_insts:
|
133
|
+
fixed_targets.extend(
|
134
|
+
[
|
135
|
+
str(r.get("target_word", "")).strip().lower()
|
136
|
+
for r in json_insts
|
137
|
+
if r.get("target_word")
|
138
|
+
]
|
139
|
+
)
|
140
|
+
|
141
|
+
if fixed_targets:
|
142
|
+
# Use fixed_targets, honoring defaults and slicing by sample_size
|
143
|
+
chosen = fixed_targets[:sample_size]
|
144
|
+
for i, tgt in enumerate(chosen):
|
145
|
+
md = WordleTaskInstanceMetadata(
|
146
|
+
word_length=int(word_length),
|
147
|
+
max_guesses=int(max_guesses),
|
148
|
+
target_word=tgt,
|
149
|
+
enforce_wordlist=bool(enforce_wordlist),
|
150
|
+
seed=i,
|
151
|
+
consume_invalid_attempts=bool(consume_invalid_attempts),
|
152
|
+
)
|
153
|
+
impetus = Impetus(
|
154
|
+
instructions=(
|
155
|
+
"Play Wordle. Submit one word per turn consisting only of letters. "
|
156
|
+
f"You have up to {md.max_guesses} guesses to find the {md.word_length}-letter target word. "
|
157
|
+
"Feedback per letter: G=correct position, Y=present elsewhere, B=absent."
|
158
|
+
)
|
159
|
+
)
|
160
|
+
intent = Intent(
|
161
|
+
rubric={"goal": "Guess the target word in as few moves as possible"},
|
162
|
+
gold_trajectories=None,
|
163
|
+
gold_state_diff={"target_known": False},
|
164
|
+
)
|
165
|
+
inst = WordleTaskInstance(
|
166
|
+
id=_stable_uuid_for_instance(i, md.target_word),
|
167
|
+
impetus=impetus,
|
168
|
+
intent=intent,
|
169
|
+
metadata=md,
|
170
|
+
is_reproducible=True,
|
171
|
+
initial_engine_snapshot=None,
|
172
|
+
)
|
173
|
+
instances.append(inst)
|
174
|
+
else:
|
175
|
+
# Procedural fallback: stable ordering from DEFAULT_SOLUTIONS
|
176
|
+
pool = [w for w in DEFAULT_SOLUTIONS if len(w) == word_length] or [
|
177
|
+
w for w in DEFAULT_SOLUTIONS if len(w) == 5
|
178
|
+
]
|
179
|
+
sample = pool[:sample_size]
|
180
|
+
for i, target in enumerate(sample):
|
181
|
+
seed = i
|
182
|
+
md = WordleTaskInstanceMetadata(
|
183
|
+
word_length=word_length,
|
184
|
+
max_guesses=max_guesses,
|
185
|
+
target_word=target,
|
186
|
+
enforce_wordlist=enforce_wordlist,
|
187
|
+
seed=seed,
|
188
|
+
consume_invalid_attempts=consume_invalid_attempts,
|
189
|
+
)
|
190
|
+
impetus = Impetus(
|
191
|
+
instructions=(
|
192
|
+
"Play Wordle. Submit one word per turn consisting only of letters. "
|
193
|
+
f"You have up to {max_guesses} guesses to find the {word_length}-letter target word. "
|
194
|
+
"Feedback per letter: G=correct position, Y=present elsewhere, B=absent."
|
195
|
+
)
|
196
|
+
)
|
197
|
+
intent = Intent(
|
198
|
+
rubric={"goal": "Guess the target word in as few moves as possible"},
|
199
|
+
gold_trajectories=None,
|
200
|
+
gold_state_diff={"target_known": False},
|
201
|
+
)
|
202
|
+
inst = WordleTaskInstance(
|
203
|
+
id=_stable_uuid_for_instance(i, target),
|
204
|
+
impetus=impetus,
|
205
|
+
intent=intent,
|
206
|
+
metadata=md,
|
207
|
+
is_reproducible=True,
|
208
|
+
initial_engine_snapshot=None,
|
209
|
+
)
|
210
|
+
instances.append(inst)
|
211
|
+
|
212
|
+
# Deterministic split based on index positions
|
213
|
+
val_ids = {instances[i].id for i in range(0, len(instances), 5)}
|
214
|
+
test_ids = {instances[i].id for i in range(0, len(instances), 7)}
|
215
|
+
split = SplitInfo(val_instance_ids=val_ids, test_instance_ids=test_ids, _is_split_defined=True)
|
216
|
+
|
217
|
+
return TaskInstanceSet(
|
218
|
+
name="Wordle Fixed TaskSet" if json_insts else "Wordle Example TaskSet",
|
219
|
+
description=(
|
220
|
+
"Fixed set from instances.json (stable ordering)."
|
221
|
+
if json_insts
|
222
|
+
else "Lightweight Wordle tasks with fixed targets and seeds."
|
223
|
+
),
|
224
|
+
instances=instances,
|
225
|
+
split_info=split,
|
226
|
+
)
|
227
|
+
|
228
|
+
|
229
|
+
# Alias
|
230
|
+
taskset = create_wordle_taskset
|
@@ -13,13 +13,14 @@ big “backend.production” code-base.
|
|
13
13
|
|
14
14
|
from __future__ import annotations
|
15
15
|
|
16
|
-
import json
|
17
|
-
import sqlite3
|
18
16
|
import gzip
|
19
|
-
import
|
17
|
+
import json
|
20
18
|
import logging
|
19
|
+
import pickle
|
20
|
+
import sqlite3
|
21
|
+
from collections.abc import Iterable
|
21
22
|
from pathlib import Path
|
22
|
-
from typing import Any
|
23
|
+
from typing import Any
|
23
24
|
|
24
25
|
import networkx as nx
|
25
26
|
|
@@ -31,10 +32,9 @@ log = logging.getLogger(__name__)
|
|
31
32
|
# --------------------------------------------------------------------------- #
|
32
33
|
# lightweight metadata record #
|
33
34
|
# --------------------------------------------------------------------------- #
|
34
|
-
import os
|
35
35
|
import hashlib
|
36
36
|
import logging
|
37
|
-
|
37
|
+
import os
|
38
38
|
|
39
39
|
log = logging.getLogger(__name__)
|
40
40
|
|
@@ -51,7 +51,7 @@ class FilesystemSnapshotStore:
|
|
51
51
|
is the SHA-256 hash of its compressed content.
|
52
52
|
"""
|
53
53
|
|
54
|
-
def __init__(self, base_dir:
|
54
|
+
def __init__(self, base_dir: str | Path = DEFAULT_SNAPSHOT_DIR):
|
55
55
|
self.base_dir = Path(base_dir)
|
56
56
|
try:
|
57
57
|
self.base_dir.mkdir(parents=True, exist_ok=True)
|
@@ -69,7 +69,7 @@ class FilesystemSnapshotStore:
|
|
69
69
|
filename = f"{key}.snapshot.gz"
|
70
70
|
return self.base_dir / filename
|
71
71
|
|
72
|
-
def write(self, blob:
|
72
|
+
def write(self, blob: bytes | dict[str, Any]) -> str:
|
73
73
|
"""
|
74
74
|
Stores a snapshot blob (bytes or dict) and returns its SHA-256 key.
|
75
75
|
|
@@ -95,7 +95,7 @@ class FilesystemSnapshotStore:
|
|
95
95
|
log.error(f"Failed to write snapshot: {e}", exc_info=True)
|
96
96
|
raise
|
97
97
|
|
98
|
-
def read(self, key: str) ->
|
98
|
+
def read(self, key: str) -> bytes | None:
|
99
99
|
"""
|
100
100
|
Retrieves the raw *compressed* snapshot bytes for a given key.
|
101
101
|
|
@@ -143,12 +143,12 @@ class TrajectorySnapshot:
|
|
143
143
|
def __init__(
|
144
144
|
self,
|
145
145
|
snap_id: str,
|
146
|
-
parent_id:
|
146
|
+
parent_id: str | None,
|
147
147
|
depth: int,
|
148
|
-
action:
|
148
|
+
action: Any | None,
|
149
149
|
reward: float = 0.0,
|
150
150
|
terminated: bool = False,
|
151
|
-
info:
|
151
|
+
info: dict[str, Any] | None = None,
|
152
152
|
):
|
153
153
|
self.snap_id = snap_id
|
154
154
|
self.parent_id = parent_id
|
@@ -186,9 +186,9 @@ class TrajectoryTreeStore:
|
|
186
186
|
|
187
187
|
def __init__(
|
188
188
|
self,
|
189
|
-
snapshot_store:
|
189
|
+
snapshot_store: FilesystemSnapshotStore | None = None,
|
190
190
|
*,
|
191
|
-
db_path:
|
191
|
+
db_path: Path | str | None = None,
|
192
192
|
):
|
193
193
|
self.snap_store = snapshot_store or FilesystemSnapshotStore()
|
194
194
|
self.graph: nx.DiGraph = nx.DiGraph()
|
@@ -202,7 +202,7 @@ class TrajectoryTreeStore:
|
|
202
202
|
|
203
203
|
# insertion -------------------------------------------------------------
|
204
204
|
|
205
|
-
def add_root(self, snapshot_blob: bytes, *, info:
|
205
|
+
def add_root(self, snapshot_blob: bytes, *, info: dict[str, Any] | None = None) -> str:
|
206
206
|
"""Insert the very first node and return its content-hash key."""
|
207
207
|
snap_id = self.snap_store.write(snapshot_blob)
|
208
208
|
self._add_node(TrajectorySnapshot(snap_id, None, 0, None, 0.0, False, info))
|
@@ -216,7 +216,7 @@ class TrajectoryTreeStore:
|
|
216
216
|
action: Any,
|
217
217
|
reward: float,
|
218
218
|
terminated: bool = False,
|
219
|
-
info:
|
219
|
+
info: dict[str, Any] | None = None,
|
220
220
|
) -> str:
|
221
221
|
"""Attach `snapshot_blob` as a child reached by `action` from *parent_id*."""
|
222
222
|
if parent_id not in self.graph:
|
@@ -230,10 +230,10 @@ class TrajectoryTreeStore:
|
|
230
230
|
|
231
231
|
# read-side helpers -----------------------------------------------------
|
232
232
|
|
233
|
-
def get_children(self, snap_id: str) ->
|
233
|
+
def get_children(self, snap_id: str) -> tuple[str, ...]:
|
234
234
|
return tuple(self.graph.successors(snap_id))
|
235
235
|
|
236
|
-
def get_parent(self, snap_id: str) ->
|
236
|
+
def get_parent(self, snap_id: str) -> str | None:
|
237
237
|
preds = tuple(self.graph.predecessors(snap_id))
|
238
238
|
return preds[0] if preds else None
|
239
239
|
|
@@ -246,17 +246,17 @@ class TrajectoryTreeStore:
|
|
246
246
|
"""Yield snapshot-ids that currently have no children."""
|
247
247
|
return (n for n in self.graph.nodes if self.is_leaf(n))
|
248
248
|
|
249
|
-
def path_to_root(self, snap_id: str) ->
|
249
|
+
def path_to_root(self, snap_id: str) -> tuple[str, ...]:
|
250
250
|
"""Return (snap_id, …, root_id)"""
|
251
251
|
path = [snap_id]
|
252
252
|
while (p := self.get_parent(path[-1])) is not None:
|
253
253
|
path.append(p)
|
254
254
|
return tuple(path)
|
255
255
|
|
256
|
-
def reconstruct_actions(self, snap_id: str) ->
|
256
|
+
def reconstruct_actions(self, snap_id: str) -> tuple[Any, ...]:
|
257
257
|
"""Return the sequence of *actions* from the root → `snap_id`."""
|
258
258
|
actions = []
|
259
|
-
for child, parent in zip(self.path_to_root(snap_id)[:-1], self.path_to_root(snap_id)[1:]):
|
259
|
+
for child, parent in zip(self.path_to_root(snap_id)[:-1], self.path_to_root(snap_id)[1:], strict=False):
|
260
260
|
actions.append(self.graph.edges[parent, child]["action"])
|
261
261
|
return tuple(reversed(actions))
|
262
262
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import sys
|
2
1
|
import os # Added to ensure os is available before use
|
2
|
+
import sys
|
3
3
|
|
4
4
|
# Ensure local 'src' directory is on PYTHONPATH for dev installs
|
5
5
|
# Current file: <repo>/src/synth_env/service/app.py
|
@@ -12,12 +12,12 @@ print(f"SYS.PATH IN APP.PY: {sys.path}")
|
|
12
12
|
import logging
|
13
13
|
|
14
14
|
from fastapi import FastAPI
|
15
|
-
from synth_ai.environments.service.registry import list_supported_env_types, register_environment
|
16
15
|
from synth_ai.environments.service.core_routes import api_router
|
17
16
|
from synth_ai.environments.service.external_registry import (
|
18
17
|
ExternalRegistryConfig,
|
19
18
|
load_external_environments,
|
20
19
|
)
|
20
|
+
from synth_ai.environments.service.registry import list_supported_env_types, register_environment
|
21
21
|
|
22
22
|
# Configure logging with more detail
|
23
23
|
logging.basicConfig(
|
@@ -38,6 +38,15 @@ import synth_ai.environments.examples.crafter_custom.environment as ccustom
|
|
38
38
|
|
39
39
|
register_environment("CrafterCustom", ccustom.CrafterCustomEnvironment)
|
40
40
|
|
41
|
+
# Register Wordle example environment
|
42
|
+
try:
|
43
|
+
import synth_ai.environments.examples.wordle.environment as wordle_mod
|
44
|
+
|
45
|
+
register_environment("Wordle", wordle_mod.WordleEnvironment)
|
46
|
+
except Exception as _e:
|
47
|
+
# Keep service robust even if example env import fails
|
48
|
+
logging.getLogger(__name__).warning(f"Wordle env not registered: {_e}")
|
49
|
+
|
41
50
|
app = FastAPI(title="Environment Service")
|
42
51
|
|
43
52
|
|