plato-sdk-v2 2.4.2__py3-none-any.whl → 2.5.1__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/v1/cli/chronos.py +2 -2
- plato/v1/cli/world.py +10 -43
- plato/worlds/__init__.py +12 -1
- plato/worlds/base.py +12 -2
- plato/worlds/config.py +55 -10
- {plato_sdk_v2-2.4.2.dist-info → plato_sdk_v2-2.5.1.dist-info}/METADATA +1 -1
- {plato_sdk_v2-2.4.2.dist-info → plato_sdk_v2-2.5.1.dist-info}/RECORD +9 -9
- {plato_sdk_v2-2.4.2.dist-info → plato_sdk_v2-2.5.1.dist-info}/WHEEL +0 -0
- {plato_sdk_v2-2.4.2.dist-info → plato_sdk_v2-2.5.1.dist-info}/entry_points.txt +0 -0
plato/v1/cli/chronos.py
CHANGED
|
@@ -536,8 +536,8 @@ async def _run_dev_impl(
|
|
|
536
536
|
plato_session_id = session.session_id
|
|
537
537
|
console.print(f"[green]✅ Created Plato session: {plato_session_id}[/green]")
|
|
538
538
|
|
|
539
|
-
# Add session to config
|
|
540
|
-
config_data["plato_session"] = session.dump()
|
|
539
|
+
# Add session to config (convert Pydantic model to dict for JSON serialization)
|
|
540
|
+
config_data["plato_session"] = session.dump().model_dump()
|
|
541
541
|
|
|
542
542
|
# Create Chronos session
|
|
543
543
|
console.print("[blue]Creating Chronos session...[/blue]")
|
plato/v1/cli/world.py
CHANGED
|
@@ -62,41 +62,6 @@ def _extract_schema_from_wheel(wheel_path: Path, module_name: str) -> dict | Non
|
|
|
62
62
|
return None
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def _register_world_schema(
|
|
66
|
-
api_url: str,
|
|
67
|
-
api_key: str,
|
|
68
|
-
package_name: str,
|
|
69
|
-
version: str,
|
|
70
|
-
schema_data: dict,
|
|
71
|
-
) -> bool:
|
|
72
|
-
"""Register world schema with plato.so registry."""
|
|
73
|
-
import httpx
|
|
74
|
-
|
|
75
|
-
try:
|
|
76
|
-
with httpx.Client(base_url=api_url, timeout=30.0) as client:
|
|
77
|
-
response = client.post(
|
|
78
|
-
f"/v2/pypi/worlds/packages/{package_name}/schema",
|
|
79
|
-
json={
|
|
80
|
-
"version": version,
|
|
81
|
-
"config_schema": schema_data,
|
|
82
|
-
},
|
|
83
|
-
headers={"X-API-Key": api_key},
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
if response.status_code in (200, 201):
|
|
87
|
-
return True
|
|
88
|
-
elif response.status_code == 404:
|
|
89
|
-
# Endpoint doesn't exist yet - that's ok
|
|
90
|
-
console.print("[dim]Schema registration endpoint not available[/dim]")
|
|
91
|
-
return False
|
|
92
|
-
else:
|
|
93
|
-
console.print(f"[yellow]Warning: Schema registration returned {response.status_code}[/yellow]")
|
|
94
|
-
return False
|
|
95
|
-
except Exception as e:
|
|
96
|
-
console.print(f"[yellow]Warning: Could not register schema: {e}[/yellow]")
|
|
97
|
-
return False
|
|
98
|
-
|
|
99
|
-
|
|
100
65
|
@world_app.command(name="publish")
|
|
101
66
|
def world_publish(
|
|
102
67
|
path: str = typer.Argument(".", help="Path to the world package directory (default: current directory)"),
|
|
@@ -218,11 +183,19 @@ def world_publish(
|
|
|
218
183
|
schema_data = _extract_schema_from_wheel(wheel_file, module_name)
|
|
219
184
|
if schema_data:
|
|
220
185
|
props = schema_data.get("properties", {})
|
|
221
|
-
|
|
186
|
+
agents = schema_data.get("agents", [])
|
|
187
|
+
secrets = schema_data.get("secrets", [])
|
|
188
|
+
console.print(
|
|
189
|
+
f"[green]Schema found:[/green] {len(props)} properties, {len(agents)} agents, {len(secrets)} secrets"
|
|
190
|
+
)
|
|
222
191
|
console.print(f"[dim] Properties: {', '.join(props.keys()) if props else 'none'}[/dim]")
|
|
192
|
+
if agents:
|
|
193
|
+
console.print(f"[dim] Agents: {', '.join(a.get('name', '?') for a in agents)}[/dim]")
|
|
223
194
|
else:
|
|
224
|
-
console.print("[
|
|
195
|
+
console.print("[red]Error: No schema.json found in wheel[/red]")
|
|
225
196
|
console.print("[dim] Add a hatch build hook to generate schema.json from get_schema()[/dim]")
|
|
197
|
+
console.print("[dim] See: https://docs.plato.so/worlds/publishing#schema-generation[/dim]")
|
|
198
|
+
raise SystemExit(1)
|
|
226
199
|
|
|
227
200
|
if dry_run:
|
|
228
201
|
console.print("\n[yellow]Dry run - skipping upload[/yellow]")
|
|
@@ -272,11 +245,5 @@ def world_publish(
|
|
|
272
245
|
console.print(f"[red]Upload error: {e}[/red]")
|
|
273
246
|
raise typer.Exit(1) from e
|
|
274
247
|
|
|
275
|
-
# Register schema if we have one
|
|
276
|
-
if schema_data:
|
|
277
|
-
console.print("\n[cyan]Registering schema...[/cyan]")
|
|
278
|
-
if _register_world_schema(api_url, api_key, package_name, version, schema_data):
|
|
279
|
-
console.print("[green]Schema registered![/green]")
|
|
280
|
-
|
|
281
248
|
console.print("\n[bold]Install with:[/bold]")
|
|
282
249
|
console.print(f" uv add {package_name} --index-url {api_url}/v2/pypi/worlds/simple/")
|
plato/worlds/__init__.py
CHANGED
|
@@ -52,7 +52,17 @@ from plato.worlds.base import (
|
|
|
52
52
|
get_world,
|
|
53
53
|
register_world,
|
|
54
54
|
)
|
|
55
|
-
from plato.worlds.config import
|
|
55
|
+
from plato.worlds.config import (
|
|
56
|
+
Agent,
|
|
57
|
+
AgentConfig,
|
|
58
|
+
CheckpointConfig,
|
|
59
|
+
Env,
|
|
60
|
+
EnvConfig,
|
|
61
|
+
EnvList,
|
|
62
|
+
RunConfig,
|
|
63
|
+
Secret,
|
|
64
|
+
StateConfig,
|
|
65
|
+
)
|
|
56
66
|
from plato.worlds.runner import run_world
|
|
57
67
|
|
|
58
68
|
__all__ = [
|
|
@@ -72,6 +82,7 @@ __all__ = [
|
|
|
72
82
|
"Agent",
|
|
73
83
|
"Secret",
|
|
74
84
|
"Env",
|
|
85
|
+
"EnvList",
|
|
75
86
|
"EnvConfig",
|
|
76
87
|
# Env types (re-exported from generated models)
|
|
77
88
|
"EnvFromArtifact",
|
plato/worlds/base.py
CHANGED
|
@@ -155,11 +155,11 @@ class BaseWorld(ABC, Generic[ConfigT]):
|
|
|
155
155
|
|
|
156
156
|
@classmethod
|
|
157
157
|
def get_schema(cls) -> dict:
|
|
158
|
-
"""Get full schema including world config, agents, and
|
|
158
|
+
"""Get full schema including world config, agents, secrets, and envs."""
|
|
159
159
|
config_class = cls.get_config_class()
|
|
160
160
|
schema = config_class.get_json_schema()
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
result = {
|
|
163
163
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
164
164
|
"type": "object",
|
|
165
165
|
"properties": schema.get("properties", {}),
|
|
@@ -169,6 +169,16 @@ class BaseWorld(ABC, Generic[ConfigT]):
|
|
|
169
169
|
"envs": schema.get("envs", []),
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
# Include env_list if present (for worlds with arbitrary environment lists)
|
|
173
|
+
if "env_list" in schema:
|
|
174
|
+
result["env_list"] = schema["env_list"]
|
|
175
|
+
|
|
176
|
+
# Include $defs if present (for nested type references)
|
|
177
|
+
if "$defs" in schema:
|
|
178
|
+
result["$defs"] = schema["$defs"]
|
|
179
|
+
|
|
180
|
+
return result
|
|
181
|
+
|
|
172
182
|
@abstractmethod
|
|
173
183
|
async def reset(self) -> Observation:
|
|
174
184
|
"""Setup the world and return initial observation.
|
plato/worlds/config.py
CHANGED
|
@@ -55,7 +55,7 @@ class Secret:
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
class Env:
|
|
58
|
-
"""Annotation marker for environment fields.
|
|
58
|
+
"""Annotation marker for single environment fields.
|
|
59
59
|
|
|
60
60
|
Environments are VMs that run alongside the world's runtime.
|
|
61
61
|
They can be specified by artifact ID, simulator name, or resource config.
|
|
@@ -72,6 +72,24 @@ class Env:
|
|
|
72
72
|
self.required = required
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
class EnvList:
|
|
76
|
+
"""Annotation marker for a list of arbitrary environments.
|
|
77
|
+
|
|
78
|
+
Use this when the world accepts a dynamic list of environments
|
|
79
|
+
rather than named, fixed environment fields.
|
|
80
|
+
|
|
81
|
+
Usage:
|
|
82
|
+
envs: Annotated[
|
|
83
|
+
list[EnvFromSimulator | EnvFromArtifact | EnvFromResource],
|
|
84
|
+
EnvList(description="Environments to create for this task"),
|
|
85
|
+
Field(discriminator="type"),
|
|
86
|
+
] = []
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, description: str = ""):
|
|
90
|
+
self.description = description
|
|
91
|
+
|
|
92
|
+
|
|
75
93
|
class StateConfig(BaseModel):
|
|
76
94
|
"""Configuration for world state persistence.
|
|
77
95
|
|
|
@@ -92,12 +110,12 @@ class CheckpointConfig(BaseModel):
|
|
|
92
110
|
"""Configuration for automatic checkpointing during world execution.
|
|
93
111
|
|
|
94
112
|
Attributes:
|
|
95
|
-
enabled: Whether to enable automatic checkpoints after steps.
|
|
113
|
+
enabled: Whether to enable automatic checkpoints after steps (default: False).
|
|
96
114
|
interval: Create checkpoint every N steps (default: 1 = every step).
|
|
97
115
|
exclude_envs: Environment aliases to exclude from checkpoints (default: ["runtime"]).
|
|
98
116
|
"""
|
|
99
117
|
|
|
100
|
-
enabled: bool =
|
|
118
|
+
enabled: bool = False
|
|
101
119
|
interval: int = 1
|
|
102
120
|
exclude_envs: list[str] = Field(default_factory=lambda: ["runtime"])
|
|
103
121
|
|
|
@@ -150,16 +168,16 @@ class RunConfig(BaseModel):
|
|
|
150
168
|
model_config = {"extra": "allow"}
|
|
151
169
|
|
|
152
170
|
@classmethod
|
|
153
|
-
def get_field_annotations(cls) -> dict[str, Agent | Secret | Env | None]:
|
|
154
|
-
"""Get Agent/Secret/Env annotations for each field."""
|
|
155
|
-
result: dict[str, Agent | Secret | Env | None] = {}
|
|
171
|
+
def get_field_annotations(cls) -> dict[str, Agent | Secret | Env | EnvList | None]:
|
|
172
|
+
"""Get Agent/Secret/Env/EnvList annotations for each field."""
|
|
173
|
+
result: dict[str, Agent | Secret | Env | EnvList | None] = {}
|
|
156
174
|
|
|
157
175
|
for field_name, field_info in cls.model_fields.items():
|
|
158
176
|
marker = None
|
|
159
177
|
|
|
160
178
|
# Pydantic stores Annotated metadata in field_info.metadata
|
|
161
179
|
for meta in field_info.metadata:
|
|
162
|
-
if isinstance(meta, (Agent, Secret, Env)):
|
|
180
|
+
if isinstance(meta, (Agent, Secret, Env, EnvList)):
|
|
163
181
|
marker = meta
|
|
164
182
|
break
|
|
165
183
|
|
|
@@ -182,6 +200,7 @@ class RunConfig(BaseModel):
|
|
|
182
200
|
agents = []
|
|
183
201
|
secrets = []
|
|
184
202
|
envs = []
|
|
203
|
+
env_list_field: dict | None = None # For EnvList marker (arbitrary envs)
|
|
185
204
|
|
|
186
205
|
# Skip runtime fields
|
|
187
206
|
runtime_fields = {"session_id", "otel_url", "upload_url", "all_secrets", "plato_session", "checkpoint", "state"}
|
|
@@ -228,6 +247,12 @@ class RunConfig(BaseModel):
|
|
|
228
247
|
"default": default_value,
|
|
229
248
|
}
|
|
230
249
|
)
|
|
250
|
+
elif isinstance(marker, EnvList):
|
|
251
|
+
# Field marked as a list of arbitrary environments
|
|
252
|
+
env_list_field = {
|
|
253
|
+
"name": field_name,
|
|
254
|
+
"description": marker.description,
|
|
255
|
+
}
|
|
231
256
|
else:
|
|
232
257
|
world_properties[field_name] = prop_schema
|
|
233
258
|
|
|
@@ -236,7 +261,7 @@ class RunConfig(BaseModel):
|
|
|
236
261
|
r for r in full_schema.get("required", []) if r not in runtime_fields and annotations.get(r) is None
|
|
237
262
|
]
|
|
238
263
|
|
|
239
|
-
|
|
264
|
+
result = {
|
|
240
265
|
"properties": world_properties,
|
|
241
266
|
"required": required,
|
|
242
267
|
"agents": agents,
|
|
@@ -244,6 +269,16 @@ class RunConfig(BaseModel):
|
|
|
244
269
|
"envs": envs,
|
|
245
270
|
}
|
|
246
271
|
|
|
272
|
+
# Include $defs if present (for nested type references)
|
|
273
|
+
if "$defs" in full_schema:
|
|
274
|
+
result["$defs"] = full_schema["$defs"]
|
|
275
|
+
|
|
276
|
+
# Add env_list if present (for worlds with arbitrary environment lists)
|
|
277
|
+
if env_list_field:
|
|
278
|
+
result["env_list"] = env_list_field
|
|
279
|
+
|
|
280
|
+
return result
|
|
281
|
+
|
|
247
282
|
def get_envs(self) -> list[EnvConfig]:
|
|
248
283
|
"""Get all environment configurations from this config.
|
|
249
284
|
|
|
@@ -287,8 +322,13 @@ class RunConfig(BaseModel):
|
|
|
287
322
|
# Handle secrets dict -> individual secret fields
|
|
288
323
|
secrets_dict = data.pop("secrets", {})
|
|
289
324
|
|
|
290
|
-
#
|
|
291
|
-
|
|
325
|
+
# Check if there's an EnvList field - if so, don't pop envs as a dict
|
|
326
|
+
has_env_list = any(isinstance(m, EnvList) for m in annotations.values())
|
|
327
|
+
|
|
328
|
+
# Handle envs dict -> individual env fields (only for named Env pattern)
|
|
329
|
+
envs_dict: dict = {}
|
|
330
|
+
if not has_env_list:
|
|
331
|
+
envs_dict = data.pop("envs", {})
|
|
292
332
|
|
|
293
333
|
# Merge world_config into top-level
|
|
294
334
|
parsed.update(world_config)
|
|
@@ -310,6 +350,11 @@ class RunConfig(BaseModel):
|
|
|
310
350
|
parsed[field_name] = _parse_env_config(env_data)
|
|
311
351
|
else:
|
|
312
352
|
parsed[field_name] = env_data
|
|
353
|
+
elif isinstance(marker, EnvList) and field_name in parsed:
|
|
354
|
+
# Parse list of env configs
|
|
355
|
+
env_list = parsed[field_name]
|
|
356
|
+
if isinstance(env_list, list):
|
|
357
|
+
parsed[field_name] = [_parse_env_config(e) if isinstance(e, dict) else e for e in env_list]
|
|
313
358
|
|
|
314
359
|
# Store all secrets for agent use
|
|
315
360
|
parsed["all_secrets"] = secrets_dict
|
|
@@ -387,14 +387,14 @@ plato/v1/sync_flow_executor.py,sha256=kgvNYOtA9FHeNfP7qb8ZPUIlTsfIss_Z98W8uX5vec
|
|
|
387
387
|
plato/v1/sync_sdk.py,sha256=2sedg1QJiSxr1I3kCyfaLAnlAgHlbblc3QQP_47O30k,25697
|
|
388
388
|
plato/v1/cli/__init__.py,sha256=om4b7PxgsoI7rEwuQelmQkqPdhMVn53_5qEN8kvksYw,105
|
|
389
389
|
plato/v1/cli/agent.py,sha256=EbmEKWCMC5DJjmVDrYuwGenhIDgPjie8hdwrDOTSXaY,43766
|
|
390
|
-
plato/v1/cli/chronos.py,sha256=
|
|
390
|
+
plato/v1/cli/chronos.py,sha256=uSgvj9uvBElBWP5me30OhH5eFVvFtH8TxvcIPEie3gA,25601
|
|
391
391
|
plato/v1/cli/main.py,sha256=iKUz6Mu-4-dgr29qOUmDqBaumOCzNQKZsHAalVtaH0Q,6932
|
|
392
392
|
plato/v1/cli/pm.py,sha256=TIvXBIWFDjr4s1girMMCuvHWQJkjpmsS-igAamddIWE,49746
|
|
393
393
|
plato/v1/cli/sandbox.py,sha256=jhTney-Pr8bGmWIXOjVIMtZJ7v7uIoRnuh3wfG7weRg,98718
|
|
394
394
|
plato/v1/cli/ssh.py,sha256=enrf7Y01ZeRIyHDEX0Yt7up5zEe7MCvE9u8SP4Oqiz4,6926
|
|
395
395
|
plato/v1/cli/utils.py,sha256=ba7Crv4OjDmgCv4SeB8UeZDin-iOdQw_3N6fd-g5XVk,4572
|
|
396
396
|
plato/v1/cli/verify.py,sha256=7QmQwfOOkr8a51f8xfVIr2zif7wGl2E8HOZTbOaIoV0,20671
|
|
397
|
-
plato/v1/cli/world.py,sha256=
|
|
397
|
+
plato/v1/cli/world.py,sha256=05onBuwVQOJ0PV9lEIZQkl80SZWeTOtHWEDd49P3Xkk,8709
|
|
398
398
|
plato/v1/cli/templates/world-runner.Dockerfile,sha256=p59nPCAOUgphSiOpWA3eteRXUWTmZV6n57zn3dWUoYM,932
|
|
399
399
|
plato/v1/examples/doordash_tasks.py,sha256=8Sz9qx-vTmiOAiCAbrDRvZGsA1qQQBr1KHbxXdjr7OI,23233
|
|
400
400
|
plato/v1/examples/loadtest.py,sha256=ZsQYNN_fZjE7CbrbVJb4KDc0OLaH7b66iPrEHDhuw0U,5609
|
|
@@ -459,12 +459,12 @@ plato/v2/utils/db_cleanup.py,sha256=lnI5lsMHNHpG85Y99MaE4Rzc3618piuzhvH-uXO1zIc,
|
|
|
459
459
|
plato/v2/utils/models.py,sha256=PwehSSnIRG-tM3tWL1PzZEH77ZHhIAZ9R0UPs6YknbM,1441
|
|
460
460
|
plato/v2/utils/proxy_tunnel.py,sha256=8ZTd0jCGSfIHMvSv1fgEyacuISWnGPHLPbDglWroTzY,10463
|
|
461
461
|
plato/worlds/README.md,sha256=XFOkEA3cNNcrWkk-Cxnsl-zn-y0kvUENKQRSqFKpdqw,5479
|
|
462
|
-
plato/worlds/__init__.py,sha256=
|
|
463
|
-
plato/worlds/base.py,sha256=
|
|
462
|
+
plato/worlds/__init__.py,sha256=nwuEerEkP2TSfadPiOMcUE3p6u1vhaS7ZxfTh2zNcF8,2217
|
|
463
|
+
plato/worlds/base.py,sha256=8skzeNlufimvXdWBKVAjmgkZ3xGGt0ijRnu6darPxsk,31017
|
|
464
464
|
plato/worlds/build_hook.py,sha256=KSoW0kqa5b7NyZ7MYOw2qsZ_2FkWuz0M3Ru7AKOP7Qw,3486
|
|
465
|
-
plato/worlds/config.py,sha256=
|
|
465
|
+
plato/worlds/config.py,sha256=OJtBygnVACQl_kGF8iLofTIk8zMu8tTCNYav6lHdwNI,12874
|
|
466
466
|
plato/worlds/runner.py,sha256=r9B2BxBae8_dM7y5cJf9xhThp_I1Qvf_tlPq2rs8qC8,4013
|
|
467
|
-
plato_sdk_v2-2.
|
|
468
|
-
plato_sdk_v2-2.
|
|
469
|
-
plato_sdk_v2-2.
|
|
470
|
-
plato_sdk_v2-2.
|
|
467
|
+
plato_sdk_v2-2.5.1.dist-info/METADATA,sha256=0yaSuouj-8F9kczKswZHAKzku3xRaSzCZol_Vf-Nh9I,8652
|
|
468
|
+
plato_sdk_v2-2.5.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
469
|
+
plato_sdk_v2-2.5.1.dist-info/entry_points.txt,sha256=upGMbJCx6YWUTKrPoYvYUYfFCqYr75nHDwhA-45m6p8,136
|
|
470
|
+
plato_sdk_v2-2.5.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|