plato-sdk-v2 2.1.11__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/_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 +15 -6
- plato/agents/logging.py +401 -0
- plato/agents/runner.py +98 -302
- plato/agents/trajectory.py +4 -4
- plato/chronos/models/__init__.py +1 -1
- plato/sims/cli.py +299 -123
- plato/sims/registry.py +77 -4
- plato/v1/cli/agent.py +10 -0
- plato/v1/cli/main.py +2 -0
- plato/v1/cli/pm.py +84 -44
- plato/v1/cli/sandbox.py +47 -9
- 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/async_/session.py +4 -4
- plato/v2/sync/session.py +4 -4
- plato/worlds/__init__.py +21 -2
- plato/worlds/base.py +222 -2
- plato/worlds/config.py +97 -7
- plato/worlds/runner.py +339 -1
- {plato_sdk_v2-2.1.11.dist-info → plato_sdk_v2-2.2.4.dist-info}/METADATA +1 -1
- {plato_sdk_v2-2.1.11.dist-info → plato_sdk_v2-2.2.4.dist-info}/RECORD +34 -29
- plato/agents/callback.py +0 -246
- {plato_sdk_v2-2.1.11.dist-info → plato_sdk_v2-2.2.4.dist-info}/WHEEL +0 -0
- {plato_sdk_v2-2.1.11.dist-info → plato_sdk_v2-2.2.4.dist-info}/entry_points.txt +0 -0
plato/agents/runner.py
CHANGED
|
@@ -1,252 +1,99 @@
|
|
|
1
|
-
"""Agent runner -
|
|
1
|
+
"""Agent runner - run agents in Docker containers."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import json
|
|
6
7
|
import logging
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import tempfile
|
|
7
11
|
from pathlib import Path
|
|
8
|
-
from typing import Annotated
|
|
9
12
|
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
from plato.agents.callback import ChronosCallback
|
|
13
|
-
|
|
14
|
-
app = typer.Typer(
|
|
15
|
-
name="plato-agent-runner",
|
|
16
|
-
help="Run Plato agents",
|
|
17
|
-
no_args_is_help=True,
|
|
18
|
-
)
|
|
13
|
+
from plato.agents.logging import log_event, span, upload_artifacts
|
|
19
14
|
|
|
20
15
|
logger = logging.getLogger(__name__)
|
|
21
16
|
|
|
22
17
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"""
|
|
32
|
-
import importlib.metadata
|
|
33
|
-
|
|
34
|
-
try:
|
|
35
|
-
eps = importlib.metadata.entry_points(group="plato.agents")
|
|
36
|
-
except TypeError:
|
|
37
|
-
# Python < 3.10 compatibility
|
|
38
|
-
eps = importlib.metadata.entry_points().get("plato.agents", [])
|
|
39
|
-
|
|
40
|
-
for ep in eps:
|
|
41
|
-
try:
|
|
42
|
-
ep.load()
|
|
43
|
-
logger.debug(f"Loaded agent: {ep.name}")
|
|
44
|
-
except Exception as e:
|
|
45
|
-
logger.warning(f"Failed to load agent '{ep.name}': {e}")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@app.command()
|
|
49
|
-
def run(
|
|
50
|
-
agent: Annotated[str, typer.Option("--agent", "-a", help="Agent name to run")],
|
|
51
|
-
instruction: Annotated[str, typer.Option("--instruction", "-i", help="Task instruction")],
|
|
52
|
-
config: Annotated[Path, typer.Option("--config", "-c", help="Path to config JSON file")],
|
|
53
|
-
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose logging")] = False,
|
|
54
|
-
) -> None:
|
|
55
|
-
"""Run an agent with the given instruction."""
|
|
56
|
-
# Setup logging
|
|
57
|
-
log_level = logging.DEBUG if verbose else logging.INFO
|
|
58
|
-
logging.basicConfig(
|
|
59
|
-
level=log_level,
|
|
60
|
-
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
if not config.exists():
|
|
64
|
-
typer.echo(f"Error: Config file not found: {config}", err=True)
|
|
65
|
-
raise typer.Exit(1)
|
|
66
|
-
|
|
67
|
-
# Discover agents
|
|
68
|
-
discover_agents()
|
|
69
|
-
|
|
70
|
-
from plato.agents.base import get_agent, get_registered_agents
|
|
71
|
-
|
|
72
|
-
agent_cls = get_agent(agent)
|
|
73
|
-
if agent_cls is None:
|
|
74
|
-
available = list(get_registered_agents().keys())
|
|
75
|
-
typer.echo(f"Error: Agent '{agent}' not found. Available: {available}", err=True)
|
|
76
|
-
raise typer.Exit(1)
|
|
77
|
-
|
|
78
|
-
# Load config using the agent's typed config class
|
|
79
|
-
config_class = agent_cls.get_config_class()
|
|
80
|
-
agent_config = config_class.from_file(config)
|
|
81
|
-
|
|
82
|
-
try:
|
|
83
|
-
agent_instance = agent_cls()
|
|
84
|
-
agent_instance.config = agent_config
|
|
85
|
-
asyncio.run(agent_instance.run(instruction))
|
|
86
|
-
except Exception as e:
|
|
87
|
-
logger.exception(f"Agent execution failed: {e}")
|
|
88
|
-
raise typer.Exit(1)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@app.command("list")
|
|
92
|
-
def list_agents(
|
|
93
|
-
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose logging")] = False,
|
|
18
|
+
async def run_agent(
|
|
19
|
+
image: str,
|
|
20
|
+
config: dict,
|
|
21
|
+
secrets: dict[str, str],
|
|
22
|
+
instruction: str,
|
|
23
|
+
workspace: str,
|
|
24
|
+
logs_dir: str | None = None,
|
|
25
|
+
pull: bool = True,
|
|
94
26
|
) -> None:
|
|
95
|
-
"""
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
typer.echo("No agents found.")
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
typer.echo("Available agents:")
|
|
109
|
-
for name, cls in agents.items():
|
|
110
|
-
desc = getattr(cls, "description", "") or ""
|
|
111
|
-
version = cls.get_version()
|
|
112
|
-
typer.echo(f" {name} (v{version}): {desc}")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def main() -> None:
|
|
116
|
-
"""CLI entry point."""
|
|
117
|
-
app()
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# =============================================================================
|
|
121
|
-
# AgentRunner - Docker-based execution (existing functionality)
|
|
122
|
-
# =============================================================================
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class AgentRunResult:
|
|
126
|
-
"""Result of running an agent in Docker.
|
|
127
|
-
|
|
128
|
-
This class is an async iterator that yields output lines from the agent.
|
|
129
|
-
It also provides access to the logs directory where agent logs are stored.
|
|
27
|
+
"""Run an agent in a Docker container.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
image: Docker image URI
|
|
31
|
+
config: Agent configuration dict
|
|
32
|
+
secrets: Secret values (API keys, etc.)
|
|
33
|
+
instruction: Task instruction for the agent
|
|
34
|
+
workspace: Host directory to mount as /workspace
|
|
35
|
+
logs_dir: Host directory for logs (temp dir if None)
|
|
36
|
+
pull: Whether to pull the image first
|
|
130
37
|
"""
|
|
38
|
+
logs_dir = logs_dir or tempfile.mkdtemp(prefix="agent_logs_")
|
|
39
|
+
agent_name = image.split("/")[-1].split(":")[0]
|
|
131
40
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
image: str,
|
|
135
|
-
config: dict,
|
|
136
|
-
secrets: dict[str, str],
|
|
137
|
-
instruction: str,
|
|
138
|
-
workspace: str,
|
|
139
|
-
logs_dir: str | None,
|
|
140
|
-
pull: bool,
|
|
141
|
-
callback_url: str,
|
|
142
|
-
session_id: str,
|
|
143
|
-
):
|
|
144
|
-
self._image = image
|
|
145
|
-
self._config = config
|
|
146
|
-
self._secrets = secrets
|
|
147
|
-
self._instruction = instruction
|
|
148
|
-
self._workspace = workspace
|
|
149
|
-
self._pull = pull
|
|
150
|
-
|
|
151
|
-
self._callback = ChronosCallback(
|
|
152
|
-
callback_url=callback_url,
|
|
153
|
-
session_id=session_id,
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
if logs_dir is None:
|
|
157
|
-
import tempfile
|
|
158
|
-
|
|
159
|
-
self._logs_dir = tempfile.mkdtemp(prefix="agent_logs_")
|
|
160
|
-
else:
|
|
161
|
-
self._logs_dir = logs_dir
|
|
162
|
-
|
|
163
|
-
@property
|
|
164
|
-
def logs_dir(self) -> str:
|
|
165
|
-
"""Host path where agent logs are stored."""
|
|
166
|
-
return self._logs_dir
|
|
41
|
+
async with span(agent_name, span_type="agent", source="agent") as agent_span:
|
|
42
|
+
agent_span.log(f"Starting agent: {agent_name} ({image})")
|
|
167
43
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return self._callback
|
|
172
|
-
|
|
173
|
-
def __aiter__(self):
|
|
174
|
-
return self._stream()
|
|
175
|
-
|
|
176
|
-
async def _check_iptables_support(self) -> bool:
|
|
177
|
-
"""Check if iptables is available."""
|
|
178
|
-
try:
|
|
179
|
-
proc = await asyncio.create_subprocess_exec(
|
|
180
|
-
"iptables",
|
|
181
|
-
"-L",
|
|
182
|
-
"-n",
|
|
183
|
-
stdout=asyncio.subprocess.DEVNULL,
|
|
184
|
-
stderr=asyncio.subprocess.DEVNULL,
|
|
185
|
-
)
|
|
186
|
-
await proc.wait()
|
|
187
|
-
return proc.returncode == 0
|
|
188
|
-
except (FileNotFoundError, PermissionError):
|
|
189
|
-
return False
|
|
190
|
-
|
|
191
|
-
async def _stream(self):
|
|
192
|
-
"""Stream output from the agent."""
|
|
193
|
-
import json
|
|
194
|
-
import os
|
|
195
|
-
import tempfile
|
|
196
|
-
|
|
197
|
-
log_buffer: list[dict] = []
|
|
198
|
-
|
|
199
|
-
async def flush_logs():
|
|
200
|
-
if not log_buffer or not self._callback.enabled:
|
|
201
|
-
return
|
|
202
|
-
await self._callback.push_logs(log_buffer.copy())
|
|
203
|
-
log_buffer.clear()
|
|
204
|
-
|
|
205
|
-
agent_name = self._image.split("/")[-1].split(":")[0]
|
|
206
|
-
await self._callback.push_log(f"Starting agent: {agent_name} ({self._image})")
|
|
207
|
-
|
|
208
|
-
if self._pull:
|
|
44
|
+
# Pull image if requested
|
|
45
|
+
if pull:
|
|
46
|
+
agent_span.log(f"Pulling image: {image}")
|
|
209
47
|
pull_proc = await asyncio.create_subprocess_exec(
|
|
210
48
|
"docker",
|
|
211
49
|
"pull",
|
|
212
|
-
|
|
50
|
+
image,
|
|
213
51
|
stdout=asyncio.subprocess.PIPE,
|
|
214
52
|
stderr=asyncio.subprocess.STDOUT,
|
|
215
53
|
)
|
|
216
|
-
assert pull_proc.stdout is not None
|
|
217
|
-
while True:
|
|
218
|
-
line = await pull_proc.stdout.readline()
|
|
219
|
-
if not line:
|
|
220
|
-
break
|
|
221
|
-
yield f"[pull] {line.decode().rstrip()}"
|
|
222
54
|
await pull_proc.wait()
|
|
223
55
|
|
|
224
|
-
|
|
225
|
-
os.makedirs(
|
|
226
|
-
|
|
56
|
+
# Setup
|
|
57
|
+
os.makedirs(os.path.join(logs_dir, "agent"), exist_ok=True)
|
|
227
58
|
config_file = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
|
|
228
|
-
json.dump(
|
|
59
|
+
json.dump(config, config_file)
|
|
229
60
|
config_file.close()
|
|
230
61
|
|
|
231
|
-
agent_failed = False
|
|
232
|
-
error_message = ""
|
|
233
|
-
|
|
234
62
|
try:
|
|
235
|
-
|
|
236
|
-
if use_host_network:
|
|
237
|
-
yield "[info] iptables not available, using host network mode"
|
|
238
|
-
|
|
63
|
+
# Build docker command
|
|
239
64
|
docker_cmd = ["docker", "run", "--rm"]
|
|
240
65
|
|
|
66
|
+
# Determine if we need host networking:
|
|
67
|
+
# - Required on Linux without iptables for connectivity
|
|
68
|
+
# - Skip on macOS where --network=host doesn't work properly
|
|
69
|
+
use_host_network = False
|
|
70
|
+
is_macos = platform.system() == "Darwin"
|
|
71
|
+
|
|
72
|
+
if not is_macos:
|
|
73
|
+
try:
|
|
74
|
+
proc = await asyncio.create_subprocess_exec(
|
|
75
|
+
"iptables",
|
|
76
|
+
"-L",
|
|
77
|
+
"-n",
|
|
78
|
+
stdout=asyncio.subprocess.DEVNULL,
|
|
79
|
+
stderr=asyncio.subprocess.DEVNULL,
|
|
80
|
+
)
|
|
81
|
+
await proc.wait()
|
|
82
|
+
has_iptables = proc.returncode == 0
|
|
83
|
+
except (FileNotFoundError, PermissionError):
|
|
84
|
+
has_iptables = False
|
|
85
|
+
|
|
86
|
+
use_host_network = not has_iptables
|
|
87
|
+
|
|
241
88
|
if use_host_network:
|
|
242
89
|
docker_cmd.extend(["--network=host", "--add-host=localhost:127.0.0.1"])
|
|
243
90
|
|
|
244
91
|
docker_cmd.extend(
|
|
245
92
|
[
|
|
246
93
|
"-v",
|
|
247
|
-
f"{
|
|
94
|
+
f"{workspace}:/workspace",
|
|
248
95
|
"-v",
|
|
249
|
-
f"{
|
|
96
|
+
f"{logs_dir}:/logs",
|
|
250
97
|
"-v",
|
|
251
98
|
f"{config_file.name}:/config.json:ro",
|
|
252
99
|
"-w",
|
|
@@ -254,112 +101,61 @@ class AgentRunResult:
|
|
|
254
101
|
]
|
|
255
102
|
)
|
|
256
103
|
|
|
257
|
-
for key, value in
|
|
104
|
+
for key, value in secrets.items():
|
|
258
105
|
docker_cmd.extend(["-e", f"{key.upper()}={value}"])
|
|
259
106
|
|
|
260
|
-
docker_cmd.append(
|
|
261
|
-
docker_cmd.extend(["--instruction", self._instruction])
|
|
107
|
+
docker_cmd.append(image)
|
|
262
108
|
|
|
109
|
+
# Pass instruction via CLI arg (agents expect --instruction flag)
|
|
110
|
+
docker_cmd.extend(["--instruction", instruction])
|
|
111
|
+
|
|
112
|
+
# Run container and stream output
|
|
263
113
|
process = await asyncio.create_subprocess_exec(
|
|
264
114
|
*docker_cmd,
|
|
265
115
|
stdout=asyncio.subprocess.PIPE,
|
|
266
116
|
stderr=asyncio.subprocess.STDOUT,
|
|
267
117
|
)
|
|
268
|
-
assert process.stdout is not None
|
|
269
118
|
|
|
119
|
+
# Stream output line by line
|
|
120
|
+
assert process.stdout is not None
|
|
270
121
|
while True:
|
|
271
122
|
line = await process.stdout.readline()
|
|
272
123
|
if not line:
|
|
273
124
|
break
|
|
274
|
-
|
|
275
|
-
yield decoded
|
|
276
|
-
|
|
277
|
-
if self._callback.enabled:
|
|
278
|
-
log_buffer.append({"level": "info", "message": decoded})
|
|
279
|
-
if len(log_buffer) >= 10:
|
|
280
|
-
await flush_logs()
|
|
125
|
+
logger.info(f"[agent] {line.decode().rstrip()}")
|
|
281
126
|
|
|
282
127
|
await process.wait()
|
|
283
128
|
|
|
284
129
|
if process.returncode != 0:
|
|
285
|
-
|
|
286
|
-
error_message = f"Agent failed with exit code {process.returncode}"
|
|
287
|
-
except Exception as e:
|
|
288
|
-
agent_failed = True
|
|
289
|
-
error_message = str(e)
|
|
290
|
-
raise
|
|
291
|
-
finally:
|
|
292
|
-
os.unlink(config_file.name)
|
|
293
|
-
await flush_logs()
|
|
294
|
-
|
|
295
|
-
if self._callback.enabled:
|
|
296
|
-
if agent_failed:
|
|
297
|
-
await self._callback.push_log(error_message, level="error")
|
|
298
|
-
else:
|
|
299
|
-
await self._callback.push_log("Agent completed successfully")
|
|
300
|
-
await self._callback.upload_artifacts(logs_dir=self._logs_dir)
|
|
301
|
-
|
|
302
|
-
if agent_failed:
|
|
303
|
-
raise RuntimeError(error_message)
|
|
304
|
-
|
|
130
|
+
raise RuntimeError(f"Agent failed with exit code {process.returncode}")
|
|
305
131
|
|
|
306
|
-
|
|
307
|
-
"""Run agents in Docker containers.
|
|
308
|
-
|
|
309
|
-
This provides isolated execution of agents. For direct execution,
|
|
310
|
-
use the plato-agent-runner CLI or instantiate the agent directly.
|
|
311
|
-
|
|
312
|
-
Example:
|
|
313
|
-
async for line in AgentRunner.run(
|
|
314
|
-
image="us-docker.pkg.dev/plato-prod/agents/openhands:latest",
|
|
315
|
-
config={"model_name": "anthropic/claude-sonnet-4"},
|
|
316
|
-
secrets={"anthropic_api_key": "sk-..."},
|
|
317
|
-
instruction="Fix the bug",
|
|
318
|
-
workspace="/path/to/repo",
|
|
319
|
-
):
|
|
320
|
-
print(line)
|
|
321
|
-
"""
|
|
322
|
-
|
|
323
|
-
@staticmethod
|
|
324
|
-
def run(
|
|
325
|
-
image: str,
|
|
326
|
-
config: dict,
|
|
327
|
-
secrets: dict[str, str],
|
|
328
|
-
instruction: str,
|
|
329
|
-
workspace: str,
|
|
330
|
-
logs_dir: str | None = None,
|
|
331
|
-
pull: bool = True,
|
|
332
|
-
callback_url: str = "",
|
|
333
|
-
session_id: str = "",
|
|
334
|
-
) -> AgentRunResult:
|
|
335
|
-
"""Run an agent in a Docker container.
|
|
336
|
-
|
|
337
|
-
Args:
|
|
338
|
-
image: Docker image URI
|
|
339
|
-
config: Agent configuration dict
|
|
340
|
-
secrets: Secret values (API keys, etc.)
|
|
341
|
-
instruction: Task instruction for the agent
|
|
342
|
-
workspace: Host directory to mount as /workspace
|
|
343
|
-
logs_dir: Host directory for logs (temp dir if None)
|
|
344
|
-
pull: Whether to pull the image first
|
|
345
|
-
callback_url: Chronos callback URL
|
|
346
|
-
session_id: Chronos session ID
|
|
347
|
-
|
|
348
|
-
Returns:
|
|
349
|
-
AgentRunResult that can be async-iterated for output.
|
|
350
|
-
"""
|
|
351
|
-
return AgentRunResult(
|
|
352
|
-
image=image,
|
|
353
|
-
config=config,
|
|
354
|
-
secrets=secrets,
|
|
355
|
-
instruction=instruction,
|
|
356
|
-
workspace=workspace,
|
|
357
|
-
logs_dir=logs_dir,
|
|
358
|
-
pull=pull,
|
|
359
|
-
callback_url=callback_url,
|
|
360
|
-
session_id=session_id,
|
|
361
|
-
)
|
|
132
|
+
agent_span.log("Agent completed successfully")
|
|
362
133
|
|
|
134
|
+
finally:
|
|
135
|
+
os.unlink(config_file.name)
|
|
363
136
|
|
|
364
|
-
|
|
365
|
-
|
|
137
|
+
# Load trajectory and add to span
|
|
138
|
+
trajectory_path = Path(logs_dir) / "agent" / "trajectory.json"
|
|
139
|
+
if trajectory_path.exists():
|
|
140
|
+
try:
|
|
141
|
+
with open(trajectory_path) as f:
|
|
142
|
+
trajectory = json.load(f)
|
|
143
|
+
if isinstance(trajectory, dict) and "schema_version" in trajectory:
|
|
144
|
+
# Add agent image to trajectory
|
|
145
|
+
agent_data = trajectory.get("agent", {})
|
|
146
|
+
extra = agent_data.get("extra") or {}
|
|
147
|
+
extra["image"] = image
|
|
148
|
+
agent_data["extra"] = extra
|
|
149
|
+
trajectory["agent"] = agent_data
|
|
150
|
+
|
|
151
|
+
# Log trajectory as separate event
|
|
152
|
+
await log_event(
|
|
153
|
+
span_type="trajectory",
|
|
154
|
+
log_type="atif",
|
|
155
|
+
extra=trajectory,
|
|
156
|
+
source="agent",
|
|
157
|
+
)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.warning(f"Failed to load trajectory: {e}")
|
|
160
|
+
|
|
161
|
+
await upload_artifacts(logs_dir)
|
plato/agents/trajectory.py
CHANGED
|
@@ -9,7 +9,7 @@ Spec: https://harborframework.com/docs/trajectory-format
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from datetime import datetime
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
13
|
from typing import Any, Literal
|
|
14
14
|
|
|
15
15
|
from pydantic import BaseModel, Field
|
|
@@ -115,7 +115,7 @@ class Step(BaseModel):
|
|
|
115
115
|
"""Create a user step."""
|
|
116
116
|
return cls(
|
|
117
117
|
step_id=step_id,
|
|
118
|
-
timestamp=datetime.
|
|
118
|
+
timestamp=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
119
119
|
source="user",
|
|
120
120
|
message=message,
|
|
121
121
|
**kwargs,
|
|
@@ -136,7 +136,7 @@ class Step(BaseModel):
|
|
|
136
136
|
"""Create an agent step."""
|
|
137
137
|
return cls(
|
|
138
138
|
step_id=step_id,
|
|
139
|
-
timestamp=datetime.
|
|
139
|
+
timestamp=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
140
140
|
source="agent",
|
|
141
141
|
message=message,
|
|
142
142
|
model_name=model_name,
|
|
@@ -152,7 +152,7 @@ class Step(BaseModel):
|
|
|
152
152
|
"""Create a system step."""
|
|
153
153
|
return cls(
|
|
154
154
|
step_id=step_id,
|
|
155
|
-
timestamp=datetime.
|
|
155
|
+
timestamp=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
156
156
|
source="system",
|
|
157
157
|
message=message,
|
|
158
158
|
**kwargs,
|