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 +18 -5
- plato/chronos/models/__init__.py +9 -1
- plato/v1/cli/chronos.py +219 -0
- plato/v1/cli/main.py +2 -0
- {plato_sdk_v2-2.3.8.dist-info → plato_sdk_v2-2.3.10.dist-info}/METADATA +1 -1
- {plato_sdk_v2-2.3.8.dist-info → plato_sdk_v2-2.3.10.dist-info}/RECORD +8 -7
- {plato_sdk_v2-2.3.8.dist-info → plato_sdk_v2-2.3.10.dist-info}/WHEEL +0 -0
- {plato_sdk_v2-2.3.8.dist-info → plato_sdk_v2-2.3.10.dist-info}/entry_points.txt +0 -0
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
|
-
|
|
166
|
-
|
|
168
|
+
try:
|
|
169
|
+
chunk = await process.stdout.read(65536)
|
|
170
|
+
except Exception:
|
|
171
|
+
break
|
|
172
|
+
if not chunk:
|
|
167
173
|
break
|
|
168
|
-
|
|
169
|
-
|
|
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
|
|
plato/chronos/models/__init__.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
plato/v1/cli/chronos.py
ADDED
|
@@ -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
|
# =============================================================================
|
|
@@ -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=
|
|
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=
|
|
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/
|
|
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.
|
|
466
|
-
plato_sdk_v2-2.3.
|
|
467
|
-
plato_sdk_v2-2.3.
|
|
468
|
-
plato_sdk_v2-2.3.
|
|
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,,
|
|
File without changes
|
|
File without changes
|