plato-sdk-v2 2.3.8__py3-none-any.whl → 2.3.10__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/agents/runner.py CHANGED
@@ -152,21 +152,34 @@ async def run_agent(
152
152
  docker_cmd.extend(["--instruction", instruction])
153
153
 
154
154
  # Run container - agents emit their own OTel spans
155
+ # Use large limit to handle agents that output long lines (e.g., JSON with file contents)
155
156
  process = await asyncio.create_subprocess_exec(
156
157
  *docker_cmd,
157
158
  stdout=asyncio.subprocess.PIPE,
158
159
  stderr=asyncio.subprocess.STDOUT,
160
+ limit=100 * 1024 * 1024, # 100MB buffer limit
159
161
  )
160
162
 
161
- # Capture output for error reporting
163
+ # Capture output for error reporting using chunked reads to handle large lines
162
164
  output_lines: list[str] = []
163
165
  assert process.stdout is not None
166
+ buffer = ""
164
167
  while True:
165
- line = await process.stdout.readline()
166
- if not line:
168
+ try:
169
+ chunk = await process.stdout.read(65536)
170
+ except Exception:
171
+ break
172
+ if not chunk:
167
173
  break
168
- decoded_line = line.decode().rstrip()
169
- output_lines.append(decoded_line)
174
+ buffer += chunk.decode(errors="replace")
175
+
176
+ while "\n" in buffer:
177
+ line, buffer = buffer.split("\n", 1)
178
+ output_lines.append(line)
179
+
180
+ # Handle any remaining content in buffer
181
+ if buffer.strip():
182
+ output_lines.append(buffer)
170
183
 
171
184
  await process.wait()
172
185
 
@@ -10,10 +10,18 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field
10
10
 
11
11
 
12
12
  class AgentConfig(BaseModel):
13
+ """Agent config - supports multiple formats.
14
+
15
+ New format: agent + version (version optional, defaults to latest)
16
+ Legacy format: agent_id (public_id)
17
+ """
18
+
13
19
  model_config = ConfigDict(
14
20
  extra="allow",
15
21
  )
16
- agent_id: Annotated[str, Field(title="Agent Id")]
22
+ agent: Annotated[str | None, Field(title="Agent")] = None
23
+ version: Annotated[str | None, Field(title="Version")] = None
24
+ agent_id: Annotated[str | None, Field(title="Agent Id")] = None # backwards compat
17
25
  config: Annotated[dict[str, Any] | None, Field(title="Config")] = {}
18
26
 
19
27
 
@@ -0,0 +1,219 @@
1
+ """Plato Chronos CLI - Launch and manage Chronos jobs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import typer
9
+
10
+ from plato.v1.cli.utils import console
11
+
12
+ chronos_app = typer.Typer(help="Chronos job management commands.")
13
+
14
+
15
+ @chronos_app.command()
16
+ def launch(
17
+ config: Path = typer.Argument(
18
+ ...,
19
+ help="Path to job config JSON file",
20
+ exists=True,
21
+ readable=True,
22
+ ),
23
+ chronos_url: str = typer.Option(
24
+ None,
25
+ "--url",
26
+ "-u",
27
+ envvar="CHRONOS_URL",
28
+ help="Chronos API URL (default: https://chronos.plato.so)",
29
+ ),
30
+ api_key: str = typer.Option(
31
+ None,
32
+ "--api-key",
33
+ "-k",
34
+ envvar="PLATO_API_KEY",
35
+ help="Plato API key for authentication",
36
+ ),
37
+ wait: bool = typer.Option(
38
+ False,
39
+ "--wait",
40
+ "-w",
41
+ help="Wait for job completion and stream logs",
42
+ ),
43
+ ):
44
+ """
45
+ Launch a Chronos job from a config file.
46
+
47
+ The config file should be a JSON file with the following structure:
48
+
49
+ \b
50
+ {
51
+ "world_package": "plato-world-structured-execution",
52
+ "world_version": "0.1.13", // optional, uses latest if not specified
53
+ "world_config": { ... },
54
+ "agent_configs": {
55
+ "skill_runner": {
56
+ "agent_id": "claude-code:2.1.5",
57
+ "config": { ... }
58
+ }
59
+ },
60
+ "secret_ids": [1, 2, 3] // IDs of secrets from Chronos
61
+ }
62
+
63
+ Examples:
64
+ plato chronos launch config.json
65
+ plato chronos launch config.json --wait
66
+ plato chronos launch config.json --url https://chronos.example.com
67
+ """
68
+ import httpx
69
+
70
+ from plato.chronos.api.jobs import launch_job
71
+ from plato.chronos.models import AgentConfig, LaunchJobRequest
72
+
73
+ # Set defaults
74
+ if not chronos_url:
75
+ chronos_url = "https://chronos.plato.so"
76
+
77
+ if not api_key:
78
+ console.print("[red]❌ No API key provided[/red]")
79
+ console.print("Set PLATO_API_KEY environment variable or use --api-key")
80
+ raise typer.Exit(1)
81
+
82
+ # Load config
83
+ try:
84
+ with open(config) as f:
85
+ job_config = json.load(f)
86
+ except json.JSONDecodeError as e:
87
+ console.print(f"[red]❌ Invalid JSON in config file: {e}[/red]")
88
+ raise typer.Exit(1)
89
+
90
+ # Validate required fields
91
+ if "world_package" not in job_config:
92
+ console.print("[red]❌ Missing required field: world_package[/red]")
93
+ raise typer.Exit(1)
94
+
95
+ if "agent_configs" not in job_config:
96
+ console.print("[red]❌ Missing required field: agent_configs[/red]")
97
+ raise typer.Exit(1)
98
+
99
+ # Build agent configs - supports both formats:
100
+ # - {"agent": "claude-code", "version": "2.1.5"} (new)
101
+ # - {"agent_id": "ag_xxxxx"} (backwards compat)
102
+ agent_configs = {}
103
+ for slot_name, agent_cfg in job_config.get("agent_configs", {}).items():
104
+ if "agent" not in agent_cfg and "agent_id" not in agent_cfg:
105
+ console.print(f"[red]❌ Missing 'agent' or 'agent_id' for slot: {slot_name}[/red]")
106
+ raise typer.Exit(1)
107
+
108
+ config_kwargs = {"config": agent_cfg.get("config", {})}
109
+ if "agent" in agent_cfg:
110
+ config_kwargs["agent"] = agent_cfg["agent"]
111
+ config_kwargs["version"] = agent_cfg.get("version")
112
+ else:
113
+ config_kwargs["agent_id"] = agent_cfg["agent_id"]
114
+
115
+ agent_configs[slot_name] = AgentConfig(**config_kwargs)
116
+
117
+ # Build request
118
+ request = LaunchJobRequest(
119
+ world_package=job_config["world_package"],
120
+ world_version=job_config.get("world_version"),
121
+ world_config=job_config.get("world_config"),
122
+ agent_configs=agent_configs,
123
+ secret_ids=job_config.get("secret_ids"),
124
+ runtime_artifact_id=job_config.get("runtime_artifact_id"),
125
+ )
126
+
127
+ console.print("[blue]🚀 Launching job...[/blue]")
128
+ console.print(f" World: {request.world_package}")
129
+ if request.world_version:
130
+ console.print(f" Version: {request.world_version}")
131
+ console.print(f" Agents: {list(agent_configs.keys())}")
132
+
133
+ try:
134
+ with httpx.Client(base_url=chronos_url, timeout=60) as client:
135
+ response = launch_job.sync(client, request, x_api_key=api_key)
136
+
137
+ console.print("\n[green]✅ Job launched successfully![/green]")
138
+ console.print(f" Session ID: {response.session_id}")
139
+ console.print(f" Plato Session: {response.plato_session_id}")
140
+ console.print(f" Status: {response.status}")
141
+ console.print(f"\n[dim]View at: {chronos_url}/sessions/{response.session_id}[/dim]")
142
+
143
+ if wait:
144
+ console.print("\n[yellow]--wait not yet implemented[/yellow]")
145
+
146
+ except Exception as e:
147
+ console.print(f"[red]❌ Failed to launch job: {e}[/red]")
148
+ raise typer.Exit(1)
149
+
150
+
151
+ @chronos_app.command()
152
+ def example(
153
+ world: str = typer.Argument(
154
+ "structured-execution",
155
+ help="World to generate example config for",
156
+ ),
157
+ output: Path = typer.Option(
158
+ None,
159
+ "--output",
160
+ "-o",
161
+ help="Output file path (prints to stdout if not specified)",
162
+ ),
163
+ ):
164
+ """
165
+ Generate an example job config file.
166
+
167
+ Examples:
168
+ plato chronos example
169
+ plato chronos example structured-execution -o config.json
170
+ """
171
+ examples = {
172
+ "structured-execution": {
173
+ "world_package": "plato-world-structured-execution",
174
+ "world_version": "0.1.13",
175
+ "world_config": {
176
+ "sim_name": "my-sim",
177
+ "github_url": "https://github.com/example/repo",
178
+ "max_attempts": 3,
179
+ "use_backtrack": True,
180
+ "skill_runner": {
181
+ "image": "claude-code:2.1.5",
182
+ "config": {"model_name": "anthropic/claude-sonnet-4-20250514", "max_turns": 100},
183
+ },
184
+ },
185
+ "agent_configs": {
186
+ "skill_runner": {
187
+ "agent": "claude-code",
188
+ "version": "2.1.5",
189
+ "config": {"model_name": "anthropic/claude-sonnet-4-20250514", "max_turns": 100},
190
+ }
191
+ },
192
+ "secret_ids": [],
193
+ "_comment": "Add secret IDs from Chronos. Secrets should include: plato_api_key, anthropic_api_key (or claude_oauth_credentials for the agent)",
194
+ },
195
+ "code-world": {
196
+ "world_package": "plato-world-code",
197
+ "world_config": {"task": "Fix the bug in src/main.py", "repo_url": "https://github.com/example/repo"},
198
+ "agent_configs": {
199
+ "coder": {"agent": "claude-code", "config": {"model_name": "anthropic/claude-sonnet-4-20250514"}}
200
+ },
201
+ "secret_ids": [],
202
+ "_comment": "version is optional - uses latest if not specified",
203
+ },
204
+ }
205
+
206
+ if world not in examples:
207
+ console.print(f"[red]❌ Unknown world: {world}[/red]")
208
+ console.print(f"Available examples: {list(examples.keys())}")
209
+ raise typer.Exit(1)
210
+
211
+ example_config = examples[world]
212
+ json_output = json.dumps(example_config, indent=2)
213
+
214
+ if output:
215
+ with open(output, "w") as f:
216
+ f.write(json_output)
217
+ console.print(f"[green]✅ Example config written to {output}[/green]")
218
+ else:
219
+ console.print(json_output)
plato/v1/cli/main.py CHANGED
@@ -9,6 +9,7 @@ import typer
9
9
  from dotenv import load_dotenv
10
10
 
11
11
  from plato.v1.cli.agent import agent_app
12
+ from plato.v1.cli.chronos import chronos_app
12
13
  from plato.v1.cli.pm import pm_app
13
14
  from plato.v1.cli.sandbox import sandbox_app
14
15
  from plato.v1.cli.utils import console
@@ -71,6 +72,7 @@ app.add_typer(sandbox_app, name="sandbox")
71
72
  app.add_typer(pm_app, name="pm")
72
73
  app.add_typer(agent_app, name="agent")
73
74
  app.add_typer(world_app, name="world")
75
+ app.add_typer(chronos_app, name="chronos")
74
76
 
75
77
 
76
78
  # =============================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plato-sdk-v2
3
- Version: 2.3.8
3
+ Version: 2.3.10
4
4
  Summary: Python SDK for the Plato API
5
5
  Author-email: Plato <support@plato.so>
6
6
  License-Expression: MIT
@@ -303,7 +303,7 @@ plato/agents/base.py,sha256=vUbPQuNSo6Ka2lIB_ZOXgi4EoAjtAD7GIj9LnNotam0,4577
303
303
  plato/agents/build.py,sha256=CNMbVQFs2_pYit1dA29Davve28Yi4c7TNK9wBB7odrE,1621
304
304
  plato/agents/config.py,sha256=CmRS6vOAg7JeqX4Hgp_KpA1YWBX_LuMicHm7SBjQEbs,5077
305
305
  plato/agents/otel.py,sha256=UOEBeMyyfbffsC3tRXlHAydoj9bXwJupA_UeLZ5h97w,8585
306
- plato/agents/runner.py,sha256=Ei20Ib-Fn5XOaS6V1Rtw0UEw34XflEWaXMpazPjmnrE,6061
306
+ plato/agents/runner.py,sha256=ZzEK-O0tCZMBb-y-d2Y8f0BkfPgC-vKkZVOnHfZd7e8,6560
307
307
  plato/agents/trajectory.py,sha256=WdiBmua0KvCrNaM3qgPI7-7B4xmSkfbP4oZ_9_8qHzU,10529
308
308
  plato/chronos/__init__.py,sha256=RHMvSrQS_-vkKOyTRuAkp2gKDP1HEuBLDnw8jcZs1Jg,739
309
309
  plato/chronos/client.py,sha256=YcOGtHWERyOD9z8LKt8bRMVL0cEwL2hiAP4qQgdZlUI,5495
@@ -367,7 +367,7 @@ plato/chronos/api/worlds/create_world.py,sha256=H6yl5QIazNXgryOR5rvscSIMf8Y9kjc6
367
367
  plato/chronos/api/worlds/delete_world.py,sha256=UETu3Zk0e2VkDdAyMilv1ev-0g_j-oujH1Dc8DBqQOc,1239
368
368
  plato/chronos/api/worlds/get_world.py,sha256=eHTM1U5JiNTaZwYLh7x4QVBoRQeI5kaJ9o6xSi4-nos,1356
369
369
  plato/chronos/api/worlds/list_worlds.py,sha256=hBAuGb69tlasyn-kV_LNr9x6Rr7SHhST5hXJn1uqMf8,1253
370
- plato/chronos/models/__init__.py,sha256=5Hil8v_jFX1YU6LpOfqyJM4WV867Ckv6CX052Q4SCso,20996
370
+ plato/chronos/models/__init__.py,sha256=t9Kn9qwMBm2S9qs9weF0-CBCg4o1-u_W_3kDFQb-aDU,21328
371
371
  plato/sims/README.md,sha256=FIbJhNVNAV-SO6dq_cXX3Rg0C7HdQCfEY9YxGlkCmsM,6902
372
372
  plato/sims/__init__.py,sha256=tnoCGKZwNx6h22tEWLujdpLv6K4PpFU2RnDOhL1o-Uc,1494
373
373
  plato/sims/agent_helpers.py,sha256=kITvQywoTCS8mGhro3jZWuPJHDlje-UZujhjoahqhd0,10291
@@ -387,7 +387,8 @@ 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=G6TV3blG_BqMDBWS-CG7GwzqoqcJTMsIKQ88jvLXb4k,43745
390
- plato/v1/cli/main.py,sha256=ktPtBvMwykR7AjXmTQ6bmZkHdzpAjhX5Fq66cDbGSzA,6844
390
+ plato/v1/cli/chronos.py,sha256=g0Re69Ma3bBYZzoQcSABdl61UEWCD7_hqrpzzc4nb1U,7335
391
+ plato/v1/cli/main.py,sha256=iKUz6Mu-4-dgr29qOUmDqBaumOCzNQKZsHAalVtaH0Q,6932
391
392
  plato/v1/cli/pm.py,sha256=uLM6WszKqxq9Czg1FraDyWb9_INUuHZq63imvRYfRLw,49734
392
393
  plato/v1/cli/sandbox.py,sha256=N7DIpXsxExtZB47tWKxp-3cV0_gLnI7C-mTKW3tTi8Y,95360
393
394
  plato/v1/cli/ssh.py,sha256=enrf7Y01ZeRIyHDEX0Yt7up5zEe7MCvE9u8SP4Oqiz4,6926
@@ -462,7 +463,7 @@ plato/worlds/base.py,sha256=kIX02gMQR43ZsZK25vZvCoNkYtICVRMeofNk-im5IRM,28215
462
463
  plato/worlds/build_hook.py,sha256=KSoW0kqa5b7NyZ7MYOw2qsZ_2FkWuz0M3Ru7AKOP7Qw,3486
463
464
  plato/worlds/config.py,sha256=a5frj3mt06rSlT25kE-L8Q2b2MTWkR-8cUoBKpC8tG4,11036
464
465
  plato/worlds/runner.py,sha256=2H5EV77bTYrMyI7qez0kwxOp9EApQxG19Ob9a_GTdbw,19383
465
- plato_sdk_v2-2.3.8.dist-info/METADATA,sha256=GnoZAAlPmFfWxSwHFjkab7s6MmWfk8rXOvOynTb2r28,8652
466
- plato_sdk_v2-2.3.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
467
- plato_sdk_v2-2.3.8.dist-info/entry_points.txt,sha256=upGMbJCx6YWUTKrPoYvYUYfFCqYr75nHDwhA-45m6p8,136
468
- plato_sdk_v2-2.3.8.dist-info/RECORD,,
466
+ plato_sdk_v2-2.3.10.dist-info/METADATA,sha256=dx-ovSiFY62KWqAM0mR_wfXlILoZLq9qGSBaluEcFJQ,8653
467
+ plato_sdk_v2-2.3.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
468
+ plato_sdk_v2-2.3.10.dist-info/entry_points.txt,sha256=upGMbJCx6YWUTKrPoYvYUYfFCqYr75nHDwhA-45m6p8,136
469
+ plato_sdk_v2-2.3.10.dist-info/RECORD,,