synth-ai 0.2.4.dev6__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 +13 -13
- 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 +5 -5
- synth_ai/environments/examples/wordle/engine.py +32 -25
- synth_ai/environments/examples/wordle/environment.py +21 -16
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +6 -6
- synth_ai/environments/examples/wordle/taskset.py +20 -12
- synth_ai/environments/reproducibility/core.py +1 -1
- synth_ai/environments/reproducibility/tree.py +21 -21
- synth_ai/environments/service/app.py +3 -2
- synth_ai/environments/service/core_routes.py +104 -110
- 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 +15 -10
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +26 -14
- synth_ai/learning/prompts/mipro.py +61 -52
- synth_ai/learning/prompts/random_search.py +42 -43
- synth_ai/learning/prompts/run_mipro_banking77.py +32 -20
- synth_ai/learning/prompts/run_random_search_banking77.py +71 -52
- 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 +7 -8
- synth_ai/lm/overrides.py +21 -19
- synth_ai/lm/provider_support/__init__.py +1 -1
- synth_ai/lm/provider_support/anthropic.py +15 -15
- synth_ai/lm/provider_support/openai.py +23 -21
- 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 +36 -27
- 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 +113 -87
- 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.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/METADATA +1 -11
- synth_ai-0.2.4.dev7.dist-info/RECORD +299 -0
- synth_ai-0.2.4.dev6.dist-info/RECORD +0 -299
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/top_level.txt +0 -0
@@ -1,32 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
from typing import Dict, Any, List, Optional
|
4
|
-
from types import SimpleNamespace
|
5
|
-
from pydantic import BaseModel
|
1
|
+
import base64
|
2
|
+
import logging
|
6
3
|
import os
|
7
|
-
import json
|
8
4
|
import pickle
|
9
|
-
import base64
|
10
|
-
from io import BytesIO
|
11
|
-
import numpy as np
|
12
5
|
import tempfile
|
13
|
-
from dataclasses import dataclass
|
14
6
|
import time
|
15
|
-
import
|
7
|
+
from dataclasses import dataclass
|
8
|
+
from io import BytesIO
|
9
|
+
from types import SimpleNamespace
|
10
|
+
from typing import Any
|
11
|
+
from uuid import uuid4
|
16
12
|
|
13
|
+
import numpy as np
|
14
|
+
from fastapi import APIRouter, Body, HTTPException
|
15
|
+
from pydantic import BaseModel
|
16
|
+
from synth_ai.environments.environment.tools import EnvToolCall
|
17
17
|
from synth_ai.environments.service.registry import get_environment_cls, list_supported_env_types
|
18
18
|
from synth_ai.environments.stateful.core import StatefulEnvironment
|
19
|
-
from synth_ai.environments.environment.tools import EnvToolCall
|
20
19
|
|
21
20
|
# Set up logging
|
22
21
|
logger = logging.getLogger(__name__)
|
23
22
|
|
24
23
|
# Import tracing abstractions from v3
|
25
|
-
from synth_ai.tracing_v3.abstractions import (
|
26
|
-
RuntimeEvent,
|
27
|
-
SessionEventMarkovBlanketMessage,
|
28
|
-
TimeRecord,
|
29
|
-
)
|
30
24
|
|
31
25
|
# Try to import Redis for persistent storage
|
32
26
|
try:
|
@@ -53,7 +47,7 @@ if os.getenv("SYNTH_USE_INMEM", "1") == "1":
|
|
53
47
|
api_router = APIRouter()
|
54
48
|
|
55
49
|
# Fallback in-memory store if Redis is not available
|
56
|
-
instances:
|
50
|
+
instances: dict[str, StatefulEnvironment] = {}
|
57
51
|
|
58
52
|
|
59
53
|
# Environment-specific task instance creation
|
@@ -68,9 +62,9 @@ class MinimalTaskInstanceMetadata:
|
|
68
62
|
class MinimalIntent:
|
69
63
|
"""Minimal intent for environments that need it."""
|
70
64
|
|
71
|
-
rubric:
|
72
|
-
gold_trajectories:
|
73
|
-
gold_state_diff:
|
65
|
+
rubric: dict[str, Any]
|
66
|
+
gold_trajectories: Any | None = None
|
67
|
+
gold_state_diff: dict = None
|
74
68
|
deterministic_eval_functions: list = None
|
75
69
|
|
76
70
|
def __post_init__(self):
|
@@ -89,8 +83,8 @@ class MinimalImpetus:
|
|
89
83
|
|
90
84
|
def create_task_instance_for_environment(
|
91
85
|
env_name: str,
|
92
|
-
initial_state:
|
93
|
-
config:
|
86
|
+
initial_state: dict[str, Any] | None = None,
|
87
|
+
config: dict[str, Any] | None = None,
|
94
88
|
) -> Any:
|
95
89
|
"""Create appropriate task instance for different environments."""
|
96
90
|
|
@@ -173,7 +167,7 @@ def create_task_instance_for_environment(
|
|
173
167
|
|
174
168
|
|
175
169
|
async def reconstruct_task_instance_from_serialized(
|
176
|
-
env_name: str, serialized_data:
|
170
|
+
env_name: str, serialized_data: dict[str, Any]
|
177
171
|
) -> Any:
|
178
172
|
"""Reconstruct a task instance from serialized data for specific environment types."""
|
179
173
|
|
@@ -219,9 +213,9 @@ async def reconstruct_task_instance_from_serialized(
|
|
219
213
|
|
220
214
|
elif env_name == "Verilog":
|
221
215
|
# Verilog needs special handling with snapshot_dir
|
216
|
+
import tempfile
|
222
217
|
from types import SimpleNamespace
|
223
218
|
from uuid import UUID
|
224
|
-
import tempfile
|
225
219
|
|
226
220
|
task = SimpleNamespace()
|
227
221
|
task.id = UUID(serialized_data.get("id", str(uuid4())))
|
@@ -249,10 +243,11 @@ async def reconstruct_task_instance_from_serialized(
|
|
249
243
|
|
250
244
|
elif env_name == "NetHack":
|
251
245
|
# NetHack needs proper TaskInstance structure with NetHackTaskInstanceMetadata
|
252
|
-
from synth_ai.environments.examples.nethack.taskset import NetHackTaskInstanceMetadata
|
253
246
|
from types import SimpleNamespace
|
254
247
|
from uuid import UUID
|
255
248
|
|
249
|
+
from synth_ai.environments.examples.nethack.taskset import NetHackTaskInstanceMetadata
|
250
|
+
|
256
251
|
# Extract metadata from serialized data
|
257
252
|
metadata_data = serialized_data.get("metadata", {})
|
258
253
|
metadata = NetHackTaskInstanceMetadata(
|
@@ -382,7 +377,7 @@ class InstanceStorage:
|
|
382
377
|
else:
|
383
378
|
print(f"✅ Stored environment {env_id} in-memory (Redis not available)")
|
384
379
|
|
385
|
-
async def get(self, env_id: str) ->
|
380
|
+
async def get(self, env_id: str) -> StatefulEnvironment | None:
|
386
381
|
"""Retrieve an environment instance"""
|
387
382
|
# Try in-memory first (most reliable)
|
388
383
|
if env_id in instances:
|
@@ -406,7 +401,7 @@ class InstanceStorage:
|
|
406
401
|
print(f"❌ Environment {env_id} not found in either store")
|
407
402
|
return None
|
408
403
|
|
409
|
-
async def remove(self, env_id: str) ->
|
404
|
+
async def remove(self, env_id: str) -> StatefulEnvironment | None:
|
410
405
|
"""Remove and return an environment instance"""
|
411
406
|
# Get the environment first
|
412
407
|
env = await self.get(env_id)
|
@@ -433,7 +428,6 @@ storage = InstanceStorage()
|
|
433
428
|
|
434
429
|
def convert_numpy_types(obj):
|
435
430
|
"""Convert numpy types to native Python types for JSON serialization"""
|
436
|
-
import numpy as np
|
437
431
|
from dataclasses import is_dataclass
|
438
432
|
|
439
433
|
if isinstance(obj, dict):
|
@@ -485,15 +479,15 @@ def convert_numpy_types(obj):
|
|
485
479
|
|
486
480
|
# Request/Response models for better API documentation
|
487
481
|
class InitializeRequest(BaseModel):
|
488
|
-
initial_state:
|
489
|
-
config:
|
490
|
-
task_instance:
|
482
|
+
initial_state: dict[str, Any] | None = None
|
483
|
+
config: dict[str, Any] | None = None
|
484
|
+
task_instance: dict[str, Any] | None = None # Add task_instance field
|
491
485
|
|
492
486
|
|
493
487
|
class StepRequest(BaseModel):
|
494
488
|
env_id: str
|
495
|
-
request_id:
|
496
|
-
action:
|
489
|
+
request_id: str | None = None
|
490
|
+
action: dict[str, Any]
|
497
491
|
|
498
492
|
|
499
493
|
class TerminateRequest(BaseModel):
|
@@ -506,7 +500,7 @@ async def get_health():
|
|
506
500
|
|
507
501
|
|
508
502
|
@api_router.post("/env/{env_name}/initialize")
|
509
|
-
async def initialize_env(env_name: str, request: InitializeRequest = Body(...)) ->
|
503
|
+
async def initialize_env(env_name: str, request: InitializeRequest = Body(...)) -> dict[str, Any]:
|
510
504
|
"""Initialize a new environment instance."""
|
511
505
|
import traceback
|
512
506
|
|
@@ -518,11 +512,11 @@ async def initialize_env(env_name: str, request: InitializeRequest = Body(...))
|
|
518
512
|
|
519
513
|
# Handle task_instance parameter - use it if provided, otherwise create a new one
|
520
514
|
if request.task_instance:
|
521
|
-
print(
|
515
|
+
print("🔍 Using provided task_instance...")
|
522
516
|
task = await reconstruct_task_instance_from_serialized(env_name, request.task_instance)
|
523
517
|
print(f"✅ Reconstructed task instance: {type(task)}")
|
524
518
|
else:
|
525
|
-
print(
|
519
|
+
print("🔍 Creating new task instance...")
|
526
520
|
# Create environment-specific task instance
|
527
521
|
task = create_task_instance_for_environment(
|
528
522
|
env_name, request.initial_state, request.config
|
@@ -530,28 +524,28 @@ async def initialize_env(env_name: str, request: InitializeRequest = Body(...))
|
|
530
524
|
print(f"✅ Created task instance: {type(task)}")
|
531
525
|
|
532
526
|
# This is where recursion might happen for Sokoban
|
533
|
-
print(
|
527
|
+
print("🔍 Creating environment instance...")
|
534
528
|
env = cls(task)
|
535
|
-
print(
|
529
|
+
print("✅ Created environment instance")
|
536
530
|
|
537
531
|
# Generate unique environment ID
|
538
532
|
env_id = str(uuid4())
|
539
533
|
print(f"✅ Generated env_id: {env_id}")
|
540
534
|
|
541
535
|
# Initialize and get first observation - this might also cause recursion
|
542
|
-
print(
|
536
|
+
print("🔍 Calling env.initialize()...")
|
543
537
|
obs = await env.initialize()
|
544
538
|
print(f"✅ Environment initialized, observation type: {type(obs)}")
|
545
539
|
|
546
540
|
# Store the fully initialized environment (fixes Redis initialization bug)
|
547
|
-
print(
|
541
|
+
print("🔍 Storing environment...")
|
548
542
|
await storage.store(env_id, env)
|
549
|
-
print(
|
543
|
+
print("✅ Environment stored")
|
550
544
|
|
551
545
|
# Convert numpy types to Python types for JSON serialization
|
552
|
-
print(
|
546
|
+
print("🔍 Converting numpy types...")
|
553
547
|
obs_serializable = convert_numpy_types(obs)
|
554
|
-
print(
|
548
|
+
print("✅ Numpy types converted")
|
555
549
|
|
556
550
|
return {"env_id": env_id, "observation": obs_serializable, "done": False, "info": {}}
|
557
551
|
|
@@ -562,7 +556,7 @@ async def initialize_env(env_name: str, request: InitializeRequest = Body(...))
|
|
562
556
|
print(stack_trace)
|
563
557
|
raise HTTPException(
|
564
558
|
status_code=400, detail=f"Recursion error during {env_name} initialization: {str(e)}"
|
565
|
-
)
|
559
|
+
) from e
|
566
560
|
|
567
561
|
except Exception as e:
|
568
562
|
# Capture all other errors
|
@@ -571,14 +565,14 @@ async def initialize_env(env_name: str, request: InitializeRequest = Body(...))
|
|
571
565
|
print(stack_trace)
|
572
566
|
raise HTTPException(
|
573
567
|
status_code=400, detail=f"Error during {env_name} initialization: {str(e)}"
|
574
|
-
)
|
568
|
+
) from e
|
575
569
|
|
576
570
|
|
577
571
|
@api_router.post("/env/{env_name}/step")
|
578
|
-
async def step_env(env_name: str, request: StepRequest = Body(...)) ->
|
572
|
+
async def step_env(env_name: str, request: StepRequest = Body(...)) -> dict[str, Any]:
|
579
573
|
"""Execute a step in the environment."""
|
580
|
-
import uuid as uuid_module
|
581
574
|
import sys
|
575
|
+
import uuid as uuid_module
|
582
576
|
|
583
577
|
# Use provided request_id or generate one
|
584
578
|
request_id = request.request_id or str(uuid_module.uuid4())[:8]
|
@@ -701,11 +695,11 @@ async def step_env(env_name: str, request: StepRequest = Body(...)) -> Dict[str,
|
|
701
695
|
logger.error(
|
702
696
|
f"🌐 [{request_id}] STEP FAILED - env: {env_name}, time: {elapsed_time:.3f}s, error: {type(e).__name__} - {e}"
|
703
697
|
)
|
704
|
-
raise HTTPException(status_code=400, detail=str(e))
|
698
|
+
raise HTTPException(status_code=400, detail=str(e)) from e
|
705
699
|
|
706
700
|
|
707
701
|
@api_router.post("/env/{env_name}/terminate")
|
708
|
-
async def terminate_env(env_name: str, request: TerminateRequest = Body(...)) ->
|
702
|
+
async def terminate_env(env_name: str, request: TerminateRequest = Body(...)) -> dict[str, Any]:
|
709
703
|
"""Terminate an environment instance."""
|
710
704
|
logger.info(f"🚪 Terminating environment: {env_name}, env_id: {request.env_id}")
|
711
705
|
env = await storage.remove(request.env_id)
|
@@ -725,11 +719,11 @@ async def terminate_env(env_name: str, request: TerminateRequest = Body(...)) ->
|
|
725
719
|
"private": {"instance_id": request.env_id},
|
726
720
|
}
|
727
721
|
except Exception as e:
|
728
|
-
raise HTTPException(status_code=400, detail=str(e))
|
722
|
+
raise HTTPException(status_code=400, detail=str(e)) from e
|
729
723
|
|
730
724
|
|
731
725
|
@api_router.get("/env/{env_name}/frame")
|
732
|
-
async def get_env_frame(env_name: str, env_id: str) ->
|
726
|
+
async def get_env_frame(env_name: str, env_id: str) -> dict[str, Any]:
|
733
727
|
"""Return the current rendered frame of the environment as base64 PNG.
|
734
728
|
|
735
729
|
This provides a lightweight way for clients to capture before/after snapshots
|
@@ -741,7 +735,11 @@ async def get_env_frame(env_name: str, env_id: str) -> Dict[str, Any]:
|
|
741
735
|
|
742
736
|
try:
|
743
737
|
# For CrafterClassic, underlying engine exposes env.render() -> RGB ndarray
|
744
|
-
if
|
738
|
+
if (
|
739
|
+
hasattr(env, "engine")
|
740
|
+
and hasattr(env.engine, "env")
|
741
|
+
and hasattr(env.engine.env, "render")
|
742
|
+
):
|
745
743
|
rgb = env.engine.env.render()
|
746
744
|
else:
|
747
745
|
raise RuntimeError("Environment does not support render()")
|
@@ -752,21 +750,22 @@ async def get_env_frame(env_name: str, env_id: str) -> Dict[str, Any]:
|
|
752
750
|
# Encode to PNG base64
|
753
751
|
try:
|
754
752
|
from PIL import Image # type: ignore
|
753
|
+
|
755
754
|
img = Image.fromarray(rgb.astype("uint8"), "RGB")
|
756
755
|
buf = BytesIO()
|
757
756
|
img.save(buf, format="PNG")
|
758
757
|
b64 = base64.b64encode(buf.getvalue()).decode("ascii")
|
759
758
|
except Exception as e:
|
760
|
-
raise RuntimeError(f"failed to encode frame: {e}")
|
759
|
+
raise RuntimeError(f"failed to encode frame: {e}") from e
|
761
760
|
|
762
761
|
return {"env_id": env_id, "image_base64": b64}
|
763
762
|
except Exception as e:
|
764
763
|
logger.error(f"Error rendering frame for {env_id}: {e}")
|
765
|
-
raise HTTPException(status_code=500, detail=str(e))
|
764
|
+
raise HTTPException(status_code=500, detail=str(e)) from e
|
766
765
|
|
767
766
|
|
768
767
|
@api_router.get("/env/{env_name}/metadata")
|
769
|
-
async def get_env_metadata(env_name: str, env_id: str) ->
|
768
|
+
async def get_env_metadata(env_name: str, env_id: str) -> dict[str, Any]:
|
770
769
|
"""Get metadata about an environment instance."""
|
771
770
|
env = await storage.get(env_id)
|
772
771
|
if not env:
|
@@ -807,16 +806,16 @@ async def get_env_metadata(env_name: str, env_id: str) -> Dict[str, Any]:
|
|
807
806
|
return metadata
|
808
807
|
except Exception as e:
|
809
808
|
logger.error(f"Error getting metadata for environment {env_id}: {e}")
|
810
|
-
raise HTTPException(status_code=500, detail=str(e))
|
809
|
+
raise HTTPException(status_code=500, detail=str(e)) from e
|
811
810
|
|
812
811
|
|
813
812
|
# Keep backward compatibility endpoints but mark as deprecated
|
814
813
|
@api_router.post("/{env_type}/create", deprecated=True)
|
815
814
|
async def create_env_legacy(
|
816
815
|
env_type: str,
|
817
|
-
config:
|
818
|
-
initial_state:
|
819
|
-
) ->
|
816
|
+
config: dict[str, Any] | None = None,
|
817
|
+
initial_state: dict[str, Any] | None = None,
|
818
|
+
) -> dict[str, str]:
|
820
819
|
"""[DEPRECATED] Use /env/{env_name}/initialize instead."""
|
821
820
|
cls = get_environment_cls(env_type)
|
822
821
|
task = create_task_instance_for_environment(env_type, initial_state, config)
|
@@ -831,8 +830,8 @@ async def create_env_legacy(
|
|
831
830
|
|
832
831
|
@api_router.post("/{env_type}/{instance_id}/reset", deprecated=True)
|
833
832
|
async def reset_env_legacy(
|
834
|
-
env_type: str, instance_id: str, seed:
|
835
|
-
) ->
|
833
|
+
env_type: str, instance_id: str, seed: int | None = None
|
834
|
+
) -> dict[str, Any]:
|
836
835
|
"""[DEPRECATED] Use /env/{env_name}/initialize instead."""
|
837
836
|
env = await storage.get(instance_id)
|
838
837
|
if not env:
|
@@ -843,7 +842,7 @@ async def reset_env_legacy(
|
|
843
842
|
|
844
843
|
|
845
844
|
@api_router.post("/{env_type}/{instance_id}/step", deprecated=True)
|
846
|
-
async def step_env_legacy(env_type: str, instance_id: str, calls:
|
845
|
+
async def step_env_legacy(env_type: str, instance_id: str, calls: list[Any]) -> dict[str, Any]:
|
847
846
|
"""[DEPRECATED] Use /env/{env_name}/step instead."""
|
848
847
|
env = await storage.get(instance_id)
|
849
848
|
if not env:
|
@@ -865,7 +864,7 @@ async def terminate_env_legacy(env_type: str, instance_id: str) -> Any:
|
|
865
864
|
|
866
865
|
|
867
866
|
@api_router.get("/{env_type}/{instance_id}/checkpoint")
|
868
|
-
async def checkpoint_env(env_type: str, instance_id: str) ->
|
867
|
+
async def checkpoint_env(env_type: str, instance_id: str) -> dict[str, Any]:
|
869
868
|
"""Get a checkpoint of the environment state."""
|
870
869
|
env = await storage.get(instance_id)
|
871
870
|
if not env:
|
@@ -877,11 +876,12 @@ async def checkpoint_env(env_type: str, instance_id: str) -> Dict[str, Any]:
|
|
877
876
|
|
878
877
|
# ===== Dynamic Environment Registration API =====
|
879
878
|
|
879
|
+
|
880
880
|
class RegisterEnvironmentRequest(BaseModel):
|
881
881
|
name: str
|
882
882
|
module_path: str
|
883
883
|
class_name: str
|
884
|
-
description:
|
884
|
+
description: str | None = None
|
885
885
|
|
886
886
|
|
887
887
|
class UnregisterEnvironmentRequest(BaseModel):
|
@@ -889,13 +889,13 @@ class UnregisterEnvironmentRequest(BaseModel):
|
|
889
889
|
|
890
890
|
|
891
891
|
@api_router.post("/registry/environments")
|
892
|
-
async def register_environment_api(request: RegisterEnvironmentRequest) ->
|
892
|
+
async def register_environment_api(request: RegisterEnvironmentRequest) -> dict[str, Any]:
|
893
893
|
"""
|
894
894
|
Dynamically register a new environment at runtime.
|
895
|
-
|
895
|
+
|
896
896
|
This endpoint allows third-party packages to register environments without
|
897
897
|
restarting the service. The environment class will be imported and validated.
|
898
|
-
|
898
|
+
|
899
899
|
Example:
|
900
900
|
POST /registry/environments
|
901
901
|
{
|
@@ -908,119 +908,113 @@ async def register_environment_api(request: RegisterEnvironmentRequest) -> Dict[
|
|
908
908
|
try:
|
909
909
|
# Import the module
|
910
910
|
import importlib
|
911
|
+
|
911
912
|
module = importlib.import_module(request.module_path)
|
912
|
-
|
913
|
+
|
913
914
|
# Get the class from the module
|
914
915
|
if not hasattr(module, request.class_name):
|
915
916
|
raise HTTPException(
|
916
917
|
status_code=400,
|
917
|
-
detail=f"Class '{request.class_name}' not found in module '{request.module_path}'"
|
918
|
+
detail=f"Class '{request.class_name}' not found in module '{request.module_path}'",
|
918
919
|
)
|
919
|
-
|
920
|
+
|
920
921
|
env_cls = getattr(module, request.class_name)
|
921
|
-
|
922
|
+
|
922
923
|
# Validate that it's a StatefulEnvironment subclass
|
923
924
|
from synth_ai.environments.stateful.core import StatefulEnvironment
|
925
|
+
|
924
926
|
if not issubclass(env_cls, StatefulEnvironment):
|
925
927
|
raise HTTPException(
|
926
928
|
status_code=400,
|
927
|
-
detail=f"Class '{request.class_name}' is not a subclass of StatefulEnvironment"
|
929
|
+
detail=f"Class '{request.class_name}' is not a subclass of StatefulEnvironment",
|
928
930
|
)
|
929
|
-
|
931
|
+
|
930
932
|
# Register the environment
|
931
933
|
from synth_ai.environments.environment.registry import register_environment
|
934
|
+
|
932
935
|
register_environment(request.name, env_cls)
|
933
|
-
|
936
|
+
|
934
937
|
logger.info(f"Dynamically registered environment: {request.name}")
|
935
|
-
|
938
|
+
|
936
939
|
return {
|
937
940
|
"success": True,
|
938
941
|
"message": f"Environment '{request.name}' registered successfully",
|
939
942
|
"name": request.name,
|
940
943
|
"module_path": request.module_path,
|
941
944
|
"class_name": request.class_name,
|
942
|
-
"description": request.description
|
945
|
+
"description": request.description,
|
943
946
|
}
|
944
|
-
|
947
|
+
|
945
948
|
except ImportError as e:
|
946
949
|
raise HTTPException(
|
947
|
-
status_code=400,
|
948
|
-
|
949
|
-
)
|
950
|
+
status_code=400, detail=f"Failed to import module '{request.module_path}': {str(e)}"
|
951
|
+
) from e
|
950
952
|
except Exception as e:
|
951
953
|
logger.error(f"Failed to register environment {request.name}: {e}")
|
952
|
-
raise HTTPException(
|
953
|
-
status_code=500,
|
954
|
-
detail=f"Failed to register environment: {str(e)}"
|
955
|
-
)
|
954
|
+
raise HTTPException(status_code=500, detail=f"Failed to register environment: {str(e)}") from e
|
956
955
|
|
957
956
|
|
958
957
|
@api_router.delete("/registry/environments/{env_name}")
|
959
|
-
async def unregister_environment_api(env_name: str) ->
|
958
|
+
async def unregister_environment_api(env_name: str) -> dict[str, Any]:
|
960
959
|
"""
|
961
960
|
Unregister an environment from the registry.
|
962
|
-
|
961
|
+
|
963
962
|
This removes the environment from the in-memory registry, making it
|
964
963
|
unavailable for new instances. Existing instances are not affected.
|
965
964
|
"""
|
966
965
|
try:
|
967
966
|
from synth_ai.environments.environment.registry import ENV_REGISTRY
|
968
|
-
|
967
|
+
|
969
968
|
if env_name not in ENV_REGISTRY:
|
970
969
|
raise HTTPException(
|
971
|
-
status_code=404,
|
972
|
-
detail=f"Environment '{env_name}' not found in registry"
|
970
|
+
status_code=404, detail=f"Environment '{env_name}' not found in registry"
|
973
971
|
)
|
974
|
-
|
972
|
+
|
975
973
|
# Remove from registry
|
976
974
|
removed_cls = ENV_REGISTRY.pop(env_name)
|
977
|
-
|
975
|
+
|
978
976
|
logger.info(f"Unregistered environment: {env_name}")
|
979
|
-
|
977
|
+
|
980
978
|
return {
|
981
979
|
"success": True,
|
982
980
|
"message": f"Environment '{env_name}' unregistered successfully",
|
983
981
|
"name": env_name,
|
984
|
-
"class_name": removed_cls.__name__
|
982
|
+
"class_name": removed_cls.__name__,
|
985
983
|
}
|
986
|
-
|
984
|
+
|
987
985
|
except Exception as e:
|
988
986
|
logger.error(f"Failed to unregister environment {env_name}: {e}")
|
989
|
-
raise HTTPException(
|
990
|
-
status_code=500,
|
991
|
-
detail=f"Failed to unregister environment: {str(e)}"
|
992
|
-
)
|
987
|
+
raise HTTPException(status_code=500, detail=f"Failed to unregister environment: {str(e)}") from e
|
993
988
|
|
994
989
|
|
995
990
|
@api_router.get("/registry/environments")
|
996
|
-
async def list_registered_environments() ->
|
991
|
+
async def list_registered_environments() -> dict[str, Any]:
|
997
992
|
"""
|
998
993
|
List all registered environments with their details.
|
999
|
-
|
994
|
+
|
1000
995
|
Returns information about all available environments in the registry,
|
1001
996
|
including both built-in and dynamically registered environments.
|
1002
997
|
"""
|
1003
998
|
try:
|
1004
999
|
from synth_ai.environments.environment.registry import ENV_REGISTRY
|
1005
|
-
|
1000
|
+
|
1006
1001
|
environments = []
|
1007
1002
|
for name, env_cls in ENV_REGISTRY.items():
|
1008
1003
|
env_info = {
|
1009
1004
|
"name": name,
|
1010
1005
|
"class_name": env_cls.__name__,
|
1011
1006
|
"module": env_cls.__module__,
|
1012
|
-
"description": getattr(env_cls, "__doc__", "").split("\n")[0]
|
1007
|
+
"description": getattr(env_cls, "__doc__", "").split("\n")[0]
|
1008
|
+
if env_cls.__doc__
|
1009
|
+
else None,
|
1013
1010
|
}
|
1014
1011
|
environments.append(env_info)
|
1015
|
-
|
1012
|
+
|
1016
1013
|
return {
|
1017
1014
|
"environments": sorted(environments, key=lambda x: x["name"]),
|
1018
|
-
"total_count": len(environments)
|
1015
|
+
"total_count": len(environments),
|
1019
1016
|
}
|
1020
|
-
|
1017
|
+
|
1021
1018
|
except Exception as e:
|
1022
1019
|
logger.error(f"Failed to list environments: {e}")
|
1023
|
-
raise HTTPException(
|
1024
|
-
status_code=500,
|
1025
|
-
detail=f"Failed to list environments: {str(e)}"
|
1026
|
-
)
|
1020
|
+
raise HTTPException(status_code=500, detail=f"Failed to list environments: {str(e)}") from e
|
@@ -6,7 +6,6 @@ This module provides functionality to register environments from external packag
|
|
6
6
|
|
7
7
|
import importlib
|
8
8
|
import logging
|
9
|
-
from typing import List, Dict
|
10
9
|
|
11
10
|
logger = logging.getLogger(__name__)
|
12
11
|
|
@@ -14,7 +13,7 @@ logger = logging.getLogger(__name__)
|
|
14
13
|
class ExternalRegistryConfig:
|
15
14
|
"""Configuration for external environment registries."""
|
16
15
|
|
17
|
-
def __init__(self, external_environments:
|
16
|
+
def __init__(self, external_environments: list[dict[str, str]] = None):
|
18
17
|
self.external_environments = external_environments or []
|
19
18
|
|
20
19
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# This file re-exports the actual registry functions from synth_ai.environments.environment.registry
|
2
2
|
# to be used by the service layer, maintaining a clean separation if needed.
|
3
3
|
from synth_ai.environments.environment.registry import (
|
4
|
-
register_environment,
|
5
4
|
get_environment_cls,
|
6
5
|
list_supported_env_types,
|
6
|
+
register_environment,
|
7
7
|
)
|
8
8
|
|
9
9
|
__all__ = ["register_environment", "get_environment_cls", "list_supported_env_types"]
|
@@ -1,5 +1,4 @@
|
|
1
1
|
from abc import abstractmethod
|
2
|
-
from typing import List
|
3
2
|
|
4
3
|
from synth_ai.environments.environment.shared_engine import Engine, InternalObservation
|
5
4
|
from synth_ai.environments.environment.tools import EnvToolCall
|
@@ -108,7 +107,7 @@ class StatefulEnvironment(Engine):
|
|
108
107
|
pass
|
109
108
|
|
110
109
|
@abstractmethod
|
111
|
-
async def step(self, tool_calls:
|
110
|
+
async def step(self, tool_calls: list[EnvToolCall]) -> InternalObservation:
|
112
111
|
"""
|
113
112
|
Execute tool calls and return the resulting observation.
|
114
113
|
|
@@ -1,8 +1,10 @@
|
|
1
|
-
from typing import Optional, Dict, List, Callable, Set, Any
|
2
|
-
from synth_ai.environments.v0_observability.history import SynthGlobalTrajectory
|
3
|
-
from uuid import UUID
|
4
1
|
from abc import abstractmethod
|
2
|
+
from collections.abc import Callable
|
5
3
|
from dataclasses import dataclass, field
|
4
|
+
from typing import Any, Optional
|
5
|
+
from uuid import UUID
|
6
|
+
|
7
|
+
from synth_ai.environments.v0_observability.history import SynthGlobalTrajectory
|
6
8
|
|
7
9
|
|
8
10
|
@dataclass
|
@@ -11,7 +13,7 @@ class Task:
|
|
11
13
|
global_constraints: str
|
12
14
|
global_objectives: str
|
13
15
|
|
14
|
-
shared_env_params:
|
16
|
+
shared_env_params: dict | None
|
15
17
|
|
16
18
|
|
17
19
|
@dataclass
|
@@ -21,10 +23,10 @@ class TaskInstanceMetadata:
|
|
21
23
|
|
22
24
|
@dataclass
|
23
25
|
class Intent:
|
24
|
-
rubric:
|
25
|
-
gold_trajectories:
|
26
|
-
gold_state_diff:
|
27
|
-
deterministic_eval_functions:
|
26
|
+
rubric: dict[str, Any]
|
27
|
+
gold_trajectories: SynthGlobalTrajectory | None
|
28
|
+
gold_state_diff: dict
|
29
|
+
deterministic_eval_functions: list[Callable] = field(default_factory=list)
|
28
30
|
|
29
31
|
|
30
32
|
@dataclass
|
@@ -44,7 +46,7 @@ class TaskInstance:
|
|
44
46
|
initial_engine_snapshot: Optional["StatefulEngineSnapshot"]
|
45
47
|
|
46
48
|
@abstractmethod
|
47
|
-
async def serialize(self) ->
|
49
|
+
async def serialize(self) -> dict:
|
48
50
|
pass
|
49
51
|
|
50
52
|
@abstractmethod
|
@@ -65,8 +67,8 @@ class TaskInstanceMetadataFilter:
|
|
65
67
|
|
66
68
|
@dataclass
|
67
69
|
class SplitInfo:
|
68
|
-
val_instance_ids:
|
69
|
-
test_instance_ids:
|
70
|
+
val_instance_ids: set[str]
|
71
|
+
test_instance_ids: set[str]
|
70
72
|
_is_split_defined: bool
|
71
73
|
|
72
74
|
|
@@ -74,5 +76,5 @@ class SplitInfo:
|
|
74
76
|
class TaskInstanceSet:
|
75
77
|
name: str
|
76
78
|
description: str
|
77
|
-
instances:
|
79
|
+
instances: list[TaskInstance]
|
78
80
|
split_info: SplitInfo
|