plato-sdk-v2 2.0.50__py3-none-any.whl → 2.2.4__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.
- plato/__init__.py +7 -6
- plato/_generated/__init__.py +1 -1
- plato/_generated/api/v1/env/evaluate_session.py +3 -3
- plato/_generated/api/v1/env/log_state_mutation.py +4 -4
- plato/_generated/api/v1/sandbox/checkpoint_vm.py +3 -3
- plato/_generated/api/v1/sandbox/save_vm_snapshot.py +3 -3
- plato/_generated/api/v1/sandbox/setup_sandbox.py +8 -8
- plato/_generated/api/v1/session/__init__.py +2 -0
- plato/_generated/api/v1/session/get_sessions_for_archival.py +100 -0
- plato/_generated/api/v1/testcases/__init__.py +6 -2
- plato/_generated/api/v1/testcases/get_mutation_groups_for_testcase.py +98 -0
- plato/_generated/api/v1/testcases/{get_next_output_testcase_for_scoring.py → get_next_testcase_for_scoring.py} +23 -10
- plato/_generated/api/v1/testcases/get_testcase_metadata_for_scoring.py +74 -0
- plato/_generated/api/v2/__init__.py +2 -1
- plato/_generated/api/v2/jobs/__init__.py +4 -0
- plato/_generated/api/v2/jobs/checkpoint.py +3 -3
- plato/_generated/api/v2/jobs/disk_snapshot.py +3 -3
- plato/_generated/api/v2/jobs/log_for_job.py +4 -39
- plato/_generated/api/v2/jobs/make.py +4 -4
- plato/_generated/api/v2/jobs/setup_sandbox.py +97 -0
- plato/_generated/api/v2/jobs/snapshot.py +3 -3
- plato/_generated/api/v2/jobs/snapshot_store.py +91 -0
- plato/_generated/api/v2/sessions/__init__.py +4 -0
- plato/_generated/api/v2/sessions/checkpoint.py +3 -3
- plato/_generated/api/v2/sessions/disk_snapshot.py +3 -3
- plato/_generated/api/v2/sessions/evaluate.py +3 -3
- plato/_generated/api/v2/sessions/log_job_mutation.py +4 -39
- plato/_generated/api/v2/sessions/make.py +4 -4
- plato/_generated/api/v2/sessions/setup_sandbox.py +98 -0
- plato/_generated/api/v2/sessions/snapshot.py +3 -3
- plato/_generated/api/v2/sessions/snapshot_store.py +94 -0
- plato/_generated/api/v2/user/__init__.py +7 -0
- plato/_generated/api/v2/user/get_current_user.py +76 -0
- plato/_generated/models/__init__.py +174 -23
- plato/_sims_generator/__init__.py +19 -4
- plato/_sims_generator/instruction.py +203 -0
- plato/_sims_generator/templates/instruction/helpers.py.jinja +161 -0
- plato/_sims_generator/templates/instruction/init.py.jinja +43 -0
- plato/agents/__init__.py +107 -517
- plato/agents/base.py +145 -0
- plato/agents/build.py +61 -0
- plato/agents/config.py +160 -0
- plato/agents/logging.py +401 -0
- plato/agents/runner.py +161 -0
- plato/agents/trajectory.py +266 -0
- plato/chronos/__init__.py +37 -0
- plato/chronos/api/__init__.py +3 -0
- plato/chronos/api/agents/__init__.py +13 -0
- plato/chronos/api/agents/create_agent.py +63 -0
- plato/chronos/api/agents/delete_agent.py +61 -0
- plato/chronos/api/agents/get_agent.py +62 -0
- plato/chronos/api/agents/get_agent_schema.py +72 -0
- plato/chronos/api/agents/get_agent_versions.py +62 -0
- plato/chronos/api/agents/list_agents.py +57 -0
- plato/chronos/api/agents/lookup_agent.py +74 -0
- plato/chronos/api/auth/__init__.py +9 -0
- plato/chronos/api/auth/debug_auth_api_auth_debug_get.py +43 -0
- plato/chronos/api/auth/get_auth_status_api_auth_status_get.py +61 -0
- plato/chronos/api/auth/get_current_user_route_api_auth_me_get.py +60 -0
- plato/chronos/api/callback/__init__.py +11 -0
- plato/chronos/api/callback/push_agent_logs.py +61 -0
- plato/chronos/api/callback/update_agent_status.py +57 -0
- plato/chronos/api/callback/upload_artifacts.py +59 -0
- plato/chronos/api/callback/upload_logs_zip.py +57 -0
- plato/chronos/api/callback/upload_trajectory.py +57 -0
- plato/chronos/api/default/__init__.py +7 -0
- plato/chronos/api/default/health.py +43 -0
- plato/chronos/api/jobs/__init__.py +7 -0
- plato/chronos/api/jobs/launch_job.py +63 -0
- plato/chronos/api/registry/__init__.py +19 -0
- plato/chronos/api/registry/get_agent_schema_api_registry_agents__agent_name__schema_get.py +62 -0
- plato/chronos/api/registry/get_agent_versions_api_registry_agents__agent_name__versions_get.py +52 -0
- plato/chronos/api/registry/get_world_schema_api_registry_worlds__package_name__schema_get.py +68 -0
- plato/chronos/api/registry/get_world_versions_api_registry_worlds__package_name__versions_get.py +52 -0
- plato/chronos/api/registry/list_registry_agents_api_registry_agents_get.py +44 -0
- plato/chronos/api/registry/list_registry_worlds_api_registry_worlds_get.py +44 -0
- plato/chronos/api/runtimes/__init__.py +11 -0
- plato/chronos/api/runtimes/create_runtime.py +63 -0
- plato/chronos/api/runtimes/delete_runtime.py +61 -0
- plato/chronos/api/runtimes/get_runtime.py +62 -0
- plato/chronos/api/runtimes/list_runtimes.py +57 -0
- plato/chronos/api/runtimes/test_runtime.py +67 -0
- plato/chronos/api/secrets/__init__.py +11 -0
- plato/chronos/api/secrets/create_secret.py +63 -0
- plato/chronos/api/secrets/delete_secret.py +61 -0
- plato/chronos/api/secrets/get_secret.py +62 -0
- plato/chronos/api/secrets/list_secrets.py +57 -0
- plato/chronos/api/secrets/update_secret.py +68 -0
- plato/chronos/api/sessions/__init__.py +10 -0
- plato/chronos/api/sessions/get_session.py +62 -0
- plato/chronos/api/sessions/get_session_logs.py +72 -0
- plato/chronos/api/sessions/get_session_logs_download.py +62 -0
- plato/chronos/api/sessions/list_sessions.py +57 -0
- plato/chronos/api/status/__init__.py +8 -0
- plato/chronos/api/status/get_status_api_status_get.py +44 -0
- plato/chronos/api/status/get_version_info_api_version_get.py +44 -0
- plato/chronos/api/templates/__init__.py +11 -0
- plato/chronos/api/templates/create_template.py +63 -0
- plato/chronos/api/templates/delete_template.py +61 -0
- plato/chronos/api/templates/get_template.py +62 -0
- plato/chronos/api/templates/list_templates.py +57 -0
- plato/chronos/api/templates/update_template.py +68 -0
- plato/chronos/api/trajectories/__init__.py +8 -0
- plato/chronos/api/trajectories/get_trajectory.py +62 -0
- plato/chronos/api/trajectories/list_trajectories.py +62 -0
- plato/chronos/api/worlds/__init__.py +10 -0
- plato/chronos/api/worlds/create_world.py +63 -0
- plato/chronos/api/worlds/delete_world.py +61 -0
- plato/chronos/api/worlds/get_world.py +62 -0
- plato/chronos/api/worlds/list_worlds.py +57 -0
- plato/chronos/client.py +171 -0
- plato/chronos/errors.py +141 -0
- plato/chronos/models/__init__.py +647 -0
- plato/chronos/py.typed +0 -0
- plato/sims/cli.py +299 -123
- plato/sims/registry.py +77 -4
- plato/v1/cli/agent.py +88 -84
- plato/v1/cli/main.py +2 -0
- plato/v1/cli/pm.py +441 -119
- plato/v1/cli/sandbox.py +747 -191
- plato/v1/cli/sim.py +11 -0
- plato/v1/cli/verify.py +1269 -0
- plato/v1/cli/world.py +3 -0
- plato/v1/flow_executor.py +21 -17
- plato/v1/models/env.py +11 -11
- plato/v1/sdk.py +2 -2
- plato/v1/sync_env.py +11 -11
- plato/v1/sync_flow_executor.py +21 -17
- plato/v1/sync_sdk.py +4 -2
- plato/v2/__init__.py +2 -0
- plato/v2/async_/environment.py +20 -1
- plato/v2/async_/session.py +54 -3
- plato/v2/sync/environment.py +2 -1
- plato/v2/sync/session.py +52 -2
- plato/worlds/README.md +218 -0
- plato/worlds/__init__.py +54 -18
- plato/worlds/base.py +304 -93
- plato/worlds/config.py +239 -73
- plato/worlds/runner.py +391 -80
- {plato_sdk_v2-2.0.50.dist-info → plato_sdk_v2-2.2.4.dist-info}/METADATA +1 -3
- {plato_sdk_v2-2.0.50.dist-info → plato_sdk_v2-2.2.4.dist-info}/RECORD +143 -68
- {plato_sdk_v2-2.0.50.dist-info → plato_sdk_v2-2.2.4.dist-info}/entry_points.txt +1 -0
- plato/_generated/api/v2/interfaces/__init__.py +0 -27
- plato/_generated/api/v2/interfaces/v2_interface_browser_create.py +0 -68
- plato/_generated/api/v2/interfaces/v2_interface_cdp_url.py +0 -65
- plato/_generated/api/v2/interfaces/v2_interface_click.py +0 -64
- plato/_generated/api/v2/interfaces/v2_interface_close.py +0 -59
- plato/_generated/api/v2/interfaces/v2_interface_computer_create.py +0 -68
- plato/_generated/api/v2/interfaces/v2_interface_cursor.py +0 -64
- plato/_generated/api/v2/interfaces/v2_interface_key.py +0 -68
- plato/_generated/api/v2/interfaces/v2_interface_screenshot.py +0 -65
- plato/_generated/api/v2/interfaces/v2_interface_scroll.py +0 -70
- plato/_generated/api/v2/interfaces/v2_interface_type.py +0 -64
- plato/world/__init__.py +0 -44
- plato/world/base.py +0 -267
- plato/world/config.py +0 -139
- plato/world/types.py +0 -47
- {plato_sdk_v2-2.0.50.dist-info → plato_sdk_v2-2.2.4.dist-info}/WHEEL +0 -0
plato/worlds/config.py
CHANGED
|
@@ -3,120 +3,286 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel, Field
|
|
9
9
|
|
|
10
|
+
from plato._generated.models import (
|
|
11
|
+
EnvFromArtifact,
|
|
12
|
+
EnvFromResource,
|
|
13
|
+
EnvFromSimulator,
|
|
14
|
+
)
|
|
15
|
+
from plato.v2.async_.session import SerializedSession
|
|
16
|
+
|
|
17
|
+
# Union type for environment configurations
|
|
18
|
+
EnvConfig = EnvFromArtifact | EnvFromSimulator | EnvFromResource
|
|
19
|
+
|
|
10
20
|
|
|
11
21
|
class AgentConfig(BaseModel):
|
|
12
|
-
"""Configuration for
|
|
22
|
+
"""Configuration for an agent.
|
|
13
23
|
|
|
14
24
|
Attributes:
|
|
15
25
|
image: Docker image URI for the agent
|
|
16
|
-
config: Agent-specific configuration
|
|
26
|
+
config: Agent-specific configuration passed to the agent
|
|
17
27
|
"""
|
|
18
28
|
|
|
19
29
|
image: str
|
|
20
30
|
config: dict[str, Any] = Field(default_factory=dict)
|
|
21
31
|
|
|
22
32
|
|
|
23
|
-
class
|
|
24
|
-
"""
|
|
33
|
+
class Agent:
|
|
34
|
+
"""Annotation marker for agent fields.
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
coder: Annotated[AgentConfig, Agent(description="Coding agent")]
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, description: str = "", required: bool = True):
|
|
41
|
+
self.description = description
|
|
42
|
+
self.required = required
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Secret:
|
|
46
|
+
"""Annotation marker for secret fields.
|
|
25
47
|
|
|
26
|
-
|
|
48
|
+
Usage:
|
|
49
|
+
api_key: Annotated[str | None, Secret(description="API key")] = None
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, description: str = "", required: bool = False):
|
|
53
|
+
self.description = description
|
|
54
|
+
self.required = required
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Env:
|
|
58
|
+
"""Annotation marker for environment fields.
|
|
27
59
|
|
|
28
|
-
|
|
60
|
+
Environments are VMs that run alongside the world's runtime.
|
|
61
|
+
They can be specified by artifact ID, simulator name, or resource config.
|
|
62
|
+
|
|
63
|
+
Usage:
|
|
64
|
+
gitea: Annotated[EnvConfig, Env(description="Git server")] = EnvFromArtifact(
|
|
65
|
+
artifact_id="abc123",
|
|
66
|
+
alias="gitea",
|
|
67
|
+
)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, description: str = "", required: bool = True):
|
|
71
|
+
self.description = description
|
|
72
|
+
self.required = required
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class RunConfig(BaseModel):
|
|
76
|
+
"""Base configuration for running a world.
|
|
77
|
+
|
|
78
|
+
Subclass this with your world-specific fields, agents, secrets, and envs:
|
|
79
|
+
|
|
80
|
+
class CodeWorldConfig(RunConfig):
|
|
81
|
+
# World-specific fields
|
|
29
82
|
repository_url: str
|
|
30
|
-
checkout: str = "main"
|
|
31
83
|
prompt: str
|
|
32
84
|
|
|
33
|
-
|
|
85
|
+
# Agents (typed)
|
|
86
|
+
coder: Annotated[AgentConfig, Agent(description="Coding agent")]
|
|
87
|
+
|
|
88
|
+
# Secrets (typed)
|
|
89
|
+
git_token: Annotated[str | None, Secret(description="GitHub token")] = None
|
|
90
|
+
|
|
91
|
+
# Environments (typed)
|
|
92
|
+
gitea: Annotated[EnvConfig, Env(description="Git server")] = EnvFromArtifact(
|
|
93
|
+
artifact_id="abc123",
|
|
94
|
+
alias="gitea",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
Attributes:
|
|
98
|
+
session_id: Unique Chronos session identifier
|
|
99
|
+
callback_url: Callback URL for status updates
|
|
100
|
+
plato_session: Serialized Plato session for connecting to existing VM session
|
|
34
101
|
"""
|
|
35
102
|
|
|
103
|
+
session_id: str = ""
|
|
104
|
+
callback_url: str = ""
|
|
105
|
+
all_secrets: dict[str, str] = Field(default_factory=dict) # All secrets (world + agent)
|
|
106
|
+
|
|
107
|
+
# Serialized Plato session for connecting to VM and sending heartbeats
|
|
108
|
+
# This is the output of Session.dump() - used to restore session with Session.load()
|
|
109
|
+
plato_session: SerializedSession | None = None
|
|
110
|
+
|
|
36
111
|
model_config = {"extra": "allow"}
|
|
37
112
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
113
|
+
@classmethod
|
|
114
|
+
def get_field_annotations(cls) -> dict[str, Agent | Secret | Env | None]:
|
|
115
|
+
"""Get Agent/Secret/Env annotations for each field."""
|
|
116
|
+
result: dict[str, Agent | Secret | Env | None] = {}
|
|
117
|
+
|
|
118
|
+
for field_name, field_info in cls.model_fields.items():
|
|
119
|
+
marker = None
|
|
120
|
+
|
|
121
|
+
# Pydantic stores Annotated metadata in field_info.metadata
|
|
122
|
+
for meta in field_info.metadata:
|
|
123
|
+
if isinstance(meta, (Agent, Secret, Env)):
|
|
124
|
+
marker = meta
|
|
125
|
+
break
|
|
126
|
+
|
|
127
|
+
result[field_name] = marker
|
|
128
|
+
|
|
129
|
+
return result
|
|
41
130
|
|
|
42
131
|
@classmethod
|
|
43
132
|
def get_json_schema(cls) -> dict:
|
|
44
|
-
"""Get JSON schema
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return schema
|
|
133
|
+
"""Get JSON schema with agents, secrets, and envs separated."""
|
|
134
|
+
# Get full Pydantic schema
|
|
135
|
+
full_schema = cls.model_json_schema()
|
|
136
|
+
full_schema.pop("title", None)
|
|
49
137
|
|
|
138
|
+
# Separate fields by annotation type
|
|
139
|
+
annotations = cls.get_field_annotations()
|
|
140
|
+
properties = full_schema.get("properties", {})
|
|
50
141
|
|
|
51
|
-
|
|
52
|
-
|
|
142
|
+
world_properties = {}
|
|
143
|
+
agents = []
|
|
144
|
+
secrets = []
|
|
145
|
+
envs = []
|
|
53
146
|
|
|
147
|
+
# Skip runtime fields
|
|
148
|
+
runtime_fields = {"session_id", "callback_url", "all_secrets", "plato_session"}
|
|
54
149
|
|
|
55
|
-
|
|
56
|
-
|
|
150
|
+
for field_name, prop_schema in properties.items():
|
|
151
|
+
if field_name in runtime_fields:
|
|
152
|
+
continue
|
|
57
153
|
|
|
58
|
-
|
|
59
|
-
needed to execute a world. Use with a type parameter for typed config access:
|
|
154
|
+
marker = annotations.get(field_name)
|
|
60
155
|
|
|
61
|
-
|
|
62
|
-
|
|
156
|
+
if isinstance(marker, Agent):
|
|
157
|
+
agents.append(
|
|
158
|
+
{
|
|
159
|
+
"name": field_name,
|
|
160
|
+
"description": marker.description,
|
|
161
|
+
"required": marker.required,
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
elif isinstance(marker, Secret):
|
|
165
|
+
secrets.append(
|
|
166
|
+
{
|
|
167
|
+
"name": field_name,
|
|
168
|
+
"description": marker.description,
|
|
169
|
+
"required": marker.required,
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
elif isinstance(marker, Env):
|
|
173
|
+
# Get default value for this env field
|
|
174
|
+
field_info = cls.model_fields.get(field_name)
|
|
175
|
+
default_value = None
|
|
176
|
+
if field_info and field_info.default is not None:
|
|
177
|
+
# Serialize the default EnvConfig to dict
|
|
178
|
+
default_env = field_info.default
|
|
179
|
+
if hasattr(default_env, "model_dump"):
|
|
180
|
+
default_value = default_env.model_dump()
|
|
181
|
+
elif isinstance(default_env, dict):
|
|
182
|
+
default_value = default_env
|
|
63
183
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
184
|
+
envs.append(
|
|
185
|
+
{
|
|
186
|
+
"name": field_name,
|
|
187
|
+
"description": marker.description,
|
|
188
|
+
"required": marker.required,
|
|
189
|
+
"default": default_value,
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
else:
|
|
193
|
+
world_properties[field_name] = prop_schema
|
|
70
194
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
callback_url: str = "" # URL for agent to push logs back to Chronos
|
|
195
|
+
# Compute required fields (excluding runtime and annotated fields)
|
|
196
|
+
required = [
|
|
197
|
+
r for r in full_schema.get("required", []) if r not in runtime_fields and annotations.get(r) is None
|
|
198
|
+
]
|
|
76
199
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
200
|
+
return {
|
|
201
|
+
"properties": world_properties,
|
|
202
|
+
"required": required,
|
|
203
|
+
"agents": agents,
|
|
204
|
+
"secrets": secrets,
|
|
205
|
+
"envs": envs,
|
|
206
|
+
}
|
|
80
207
|
|
|
81
|
-
def
|
|
82
|
-
"""Get
|
|
83
|
-
return self.secrets.get(key, default)
|
|
208
|
+
def get_envs(self) -> list[EnvConfig]:
|
|
209
|
+
"""Get all environment configurations from this config.
|
|
84
210
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
cls,
|
|
88
|
-
path: str | Path,
|
|
89
|
-
config_class: type[WorldConfig] | None = None,
|
|
90
|
-
) -> RunConfig:
|
|
91
|
-
"""Load run config from a JSON file.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
path: Path to JSON config file
|
|
95
|
-
config_class: Optional WorldConfig subclass to parse world_config into.
|
|
96
|
-
Defaults to WorldConfig if not provided.
|
|
211
|
+
Returns:
|
|
212
|
+
List of EnvConfig objects (EnvFromArtifact, EnvFromSimulator, or EnvFromResource)
|
|
97
213
|
"""
|
|
214
|
+
annotations = self.get_field_annotations()
|
|
215
|
+
envs: list[EnvConfig] = []
|
|
216
|
+
|
|
217
|
+
for field_name, marker in annotations.items():
|
|
218
|
+
if isinstance(marker, Env):
|
|
219
|
+
value = getattr(self, field_name, None)
|
|
220
|
+
if value is not None:
|
|
221
|
+
envs.append(value)
|
|
222
|
+
|
|
223
|
+
return envs
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def from_file(cls, path: str | Path) -> RunConfig:
|
|
227
|
+
"""Load config from a JSON file."""
|
|
98
228
|
import json
|
|
99
229
|
|
|
100
230
|
path = Path(path)
|
|
101
231
|
with open(path) as f:
|
|
102
232
|
data = json.load(f)
|
|
103
233
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
234
|
+
return cls._from_dict(data)
|
|
235
|
+
|
|
236
|
+
@classmethod
|
|
237
|
+
def _from_dict(cls, data: dict) -> RunConfig:
|
|
238
|
+
"""Parse config from a dictionary, handling nested structures."""
|
|
239
|
+
annotations = cls.get_field_annotations()
|
|
240
|
+
parsed: dict[str, Any] = {}
|
|
241
|
+
|
|
242
|
+
# Handle world_config if present (for backwards compatibility)
|
|
243
|
+
world_config = data.pop("world_config", {})
|
|
244
|
+
|
|
245
|
+
# Handle agents dict -> individual agent fields
|
|
246
|
+
agents_dict = data.pop("agents", {})
|
|
247
|
+
|
|
248
|
+
# Handle secrets dict -> individual secret fields
|
|
249
|
+
secrets_dict = data.pop("secrets", {})
|
|
250
|
+
|
|
251
|
+
# Handle envs dict -> individual env fields
|
|
252
|
+
envs_dict = data.pop("envs", {})
|
|
253
|
+
|
|
254
|
+
# Merge world_config into top-level
|
|
255
|
+
parsed.update(world_config)
|
|
256
|
+
parsed.update(data)
|
|
257
|
+
|
|
258
|
+
# Map agents dict to typed fields
|
|
259
|
+
for field_name, marker in annotations.items():
|
|
260
|
+
if isinstance(marker, Agent) and field_name in agents_dict:
|
|
261
|
+
agent_data = agents_dict[field_name]
|
|
262
|
+
if isinstance(agent_data, dict):
|
|
263
|
+
parsed[field_name] = AgentConfig(**agent_data)
|
|
264
|
+
else:
|
|
265
|
+
parsed[field_name] = AgentConfig(image=str(agent_data))
|
|
266
|
+
elif isinstance(marker, Secret) and field_name in secrets_dict:
|
|
267
|
+
parsed[field_name] = secrets_dict[field_name]
|
|
268
|
+
elif isinstance(marker, Env) and field_name in envs_dict:
|
|
269
|
+
env_data = envs_dict[field_name]
|
|
270
|
+
if isinstance(env_data, dict):
|
|
271
|
+
parsed[field_name] = _parse_env_config(env_data)
|
|
272
|
+
else:
|
|
273
|
+
parsed[field_name] = env_data
|
|
274
|
+
|
|
275
|
+
# Store all secrets for agent use
|
|
276
|
+
parsed["all_secrets"] = secrets_dict
|
|
277
|
+
|
|
278
|
+
return cls(**parsed)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _parse_env_config(data: dict) -> EnvConfig:
|
|
282
|
+
"""Parse an env config dict into the appropriate type."""
|
|
283
|
+
if "artifact_id" in data:
|
|
284
|
+
return EnvFromArtifact(**data)
|
|
285
|
+
elif "sim_config" in data:
|
|
286
|
+
return EnvFromResource(**data)
|
|
287
|
+
else:
|
|
288
|
+
return EnvFromSimulator(**data)
|