plato-sdk-v2 2.0.64__py3-none-any.whl → 2.3.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.
Files changed (46) hide show
  1. plato/__init__.py +0 -9
  2. plato/_sims_generator/__init__.py +19 -4
  3. plato/_sims_generator/instruction.py +203 -0
  4. plato/_sims_generator/templates/instruction/helpers.py.jinja +161 -0
  5. plato/_sims_generator/templates/instruction/init.py.jinja +43 -0
  6. plato/agents/__init__.py +99 -430
  7. plato/agents/base.py +145 -0
  8. plato/agents/build.py +61 -0
  9. plato/agents/config.py +160 -0
  10. plato/agents/logging.py +515 -0
  11. plato/agents/runner.py +191 -0
  12. plato/agents/trajectory.py +266 -0
  13. plato/chronos/models/__init__.py +1 -1
  14. plato/sims/cli.py +299 -123
  15. plato/sims/registry.py +77 -4
  16. plato/v1/cli/agent.py +88 -84
  17. plato/v1/cli/pm.py +84 -44
  18. plato/v1/cli/sandbox.py +241 -61
  19. plato/v1/cli/ssh.py +16 -4
  20. plato/v1/cli/verify.py +685 -0
  21. plato/v1/cli/world.py +3 -0
  22. plato/v1/flow_executor.py +21 -17
  23. plato/v1/models/env.py +11 -11
  24. plato/v1/sdk.py +2 -2
  25. plato/v1/sync_env.py +11 -11
  26. plato/v1/sync_flow_executor.py +21 -17
  27. plato/v1/sync_sdk.py +4 -2
  28. plato/v2/__init__.py +2 -0
  29. plato/v2/async_/environment.py +31 -0
  30. plato/v2/async_/session.py +72 -4
  31. plato/v2/sync/environment.py +31 -0
  32. plato/v2/sync/session.py +72 -4
  33. plato/worlds/README.md +71 -56
  34. plato/worlds/__init__.py +56 -18
  35. plato/worlds/base.py +578 -93
  36. plato/worlds/config.py +276 -74
  37. plato/worlds/runner.py +475 -80
  38. {plato_sdk_v2-2.0.64.dist-info → plato_sdk_v2-2.3.4.dist-info}/METADATA +3 -3
  39. {plato_sdk_v2-2.0.64.dist-info → plato_sdk_v2-2.3.4.dist-info}/RECORD +41 -36
  40. {plato_sdk_v2-2.0.64.dist-info → plato_sdk_v2-2.3.4.dist-info}/entry_points.txt +1 -0
  41. plato/agents/callback.py +0 -246
  42. plato/world/__init__.py +0 -44
  43. plato/world/base.py +0 -267
  44. plato/world/config.py +0 -139
  45. plato/world/types.py +0 -47
  46. {plato_sdk_v2-2.0.64.dist-info → plato_sdk_v2-2.3.4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,266 @@
1
+ """ATIF (Agent Trajectory Interchange Format) models.
2
+
3
+ This module provides Pydantic models for the ATIF specification,
4
+ a standardized format for logging agent interaction history.
5
+
6
+ Matches Harbor's trajectory format exactly for compatibility.
7
+ Spec: https://harborframework.com/docs/trajectory-format
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from datetime import datetime, timezone
13
+ from typing import Any, Literal
14
+
15
+ from pydantic import BaseModel, Field
16
+
17
+ SCHEMA_VERSION: Literal["ATIF-v1.0", "ATIF-v1.1", "ATIF-v1.2", "ATIF-v1.3", "ATIF-v1.4", "ATIF-v1.5"] = "ATIF-v1.5"
18
+
19
+
20
+ class Metrics(BaseModel):
21
+ """LLM operational and confidence data."""
22
+
23
+ model_config = {"extra": "forbid"}
24
+
25
+ prompt_tokens: int | None = Field(default=None, description="Number of tokens in the prompt")
26
+ completion_tokens: int | None = Field(default=None, description="Number of tokens in the completion")
27
+ cached_tokens: int | None = Field(default=None, description="Number of cached tokens used")
28
+ cost_usd: float | None = Field(default=None, description="Cost in USD for this step")
29
+ prompt_token_ids: list[int] | None = Field(default=None, description="Token IDs for the prompt")
30
+ completion_token_ids: list[int] | None = Field(default=None, description="Token IDs for the completion")
31
+ logprobs: list[float] | None = Field(default=None, description="Log probabilities for completion tokens")
32
+ extra: dict[str, Any] | None = Field(default=None, description="Custom metrics")
33
+
34
+
35
+ class FinalMetrics(BaseModel):
36
+ """Trajectory-level aggregate statistics."""
37
+
38
+ model_config = {"extra": "forbid"}
39
+
40
+ total_prompt_tokens: int | None = Field(
41
+ default=None,
42
+ description="Sum of all prompt tokens across all steps, including cached tokens",
43
+ )
44
+ total_completion_tokens: int | None = Field(
45
+ default=None,
46
+ description="Sum of all completion tokens across all steps",
47
+ )
48
+ total_cached_tokens: int | None = Field(
49
+ default=None,
50
+ description="Sum of all cached tokens across all steps",
51
+ )
52
+ total_cost_usd: float | None = Field(
53
+ default=None,
54
+ description="Total real monetary cost for the entire trajectory",
55
+ )
56
+ total_steps: int | None = Field(
57
+ default=None,
58
+ ge=0,
59
+ description="Number of steps in the trajectory",
60
+ )
61
+ extra: dict[str, Any] | None = Field(default=None, description="Custom aggregate metrics")
62
+
63
+
64
+ class ToolCall(BaseModel):
65
+ """A function/tool invocation."""
66
+
67
+ model_config = {"extra": "forbid"}
68
+
69
+ tool_call_id: str = Field(..., description="Unique identifier for this specific tool call")
70
+ function_name: str = Field(..., description="The name of the function or tool being invoked")
71
+ arguments: dict[str, Any] = Field(..., description="Arguments passed to the function (can be empty dict)")
72
+
73
+
74
+ class ObservationResult(BaseModel):
75
+ """Result from a single tool call."""
76
+
77
+ model_config = {"extra": "forbid"}
78
+
79
+ source_call_id: str = Field(..., description="ID of the tool call that produced this result")
80
+ content: str = Field(..., description="The result content from the tool execution")
81
+
82
+
83
+ class Observation(BaseModel):
84
+ """Environment feedback from tool execution."""
85
+
86
+ model_config = {"extra": "forbid"}
87
+
88
+ results: list[ObservationResult] = Field(..., description="Results from tool executions")
89
+
90
+
91
+ class Step(BaseModel):
92
+ """A single interaction step in the trajectory."""
93
+
94
+ model_config = {"extra": "forbid"}
95
+
96
+ step_id: int = Field(..., ge=1, description="Ordinal index of the turn (starting from 1)")
97
+ timestamp: str | None = Field(default=None, description="ISO 8601 timestamp")
98
+ source: Literal["user", "agent", "system"] = Field(..., description="Step originator")
99
+ message: str = Field(..., description="Dialogue content (allows empty string)")
100
+
101
+ # Agent-only fields
102
+ model_name: str | None = Field(default=None, description="LLM model used for this step")
103
+ reasoning_effort: str | float | None = Field(default=None, description="Effort measurement")
104
+ reasoning_content: str | None = Field(default=None, description="Agent's internal reasoning")
105
+ tool_calls: list[ToolCall] | None = Field(default=None, description="Structured action objects")
106
+ observation: Observation | None = Field(default=None, description="Environment feedback")
107
+ metrics: Metrics | None = Field(default=None, description="LLM operational and confidence data")
108
+ is_copied_context: bool | None = Field(default=None, description="Context reuse indicator (ATIF-v1.5+)")
109
+
110
+ # Custom metadata
111
+ extra: dict[str, Any] | None = Field(default=None, description="Custom metadata")
112
+
113
+ @classmethod
114
+ def user(cls, step_id: int, message: str, **kwargs: Any) -> Step:
115
+ """Create a user step."""
116
+ return cls(
117
+ step_id=step_id,
118
+ timestamp=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
119
+ source="user",
120
+ message=message,
121
+ **kwargs,
122
+ )
123
+
124
+ @classmethod
125
+ def agent(
126
+ cls,
127
+ step_id: int,
128
+ message: str,
129
+ model_name: str,
130
+ tool_calls: list[ToolCall] | None = None,
131
+ observation: Observation | None = None,
132
+ metrics: Metrics | None = None,
133
+ reasoning_content: str | None = None,
134
+ **kwargs: Any,
135
+ ) -> Step:
136
+ """Create an agent step."""
137
+ return cls(
138
+ step_id=step_id,
139
+ timestamp=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
140
+ source="agent",
141
+ message=message,
142
+ model_name=model_name,
143
+ tool_calls=tool_calls,
144
+ observation=observation,
145
+ metrics=metrics,
146
+ reasoning_content=reasoning_content,
147
+ **kwargs,
148
+ )
149
+
150
+ @classmethod
151
+ def system(cls, step_id: int, message: str, **kwargs: Any) -> Step:
152
+ """Create a system step."""
153
+ return cls(
154
+ step_id=step_id,
155
+ timestamp=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
156
+ source="system",
157
+ message=message,
158
+ **kwargs,
159
+ )
160
+
161
+
162
+ class Agent(BaseModel):
163
+ """Agent metadata."""
164
+
165
+ model_config = {"extra": "forbid"}
166
+
167
+ name: str = Field(..., description="The name of the agent system")
168
+ version: str = Field(..., description="The version identifier of the agent system")
169
+ model_name: str | None = Field(default=None, description="Default LLM model used for this trajectory")
170
+ tool_definitions: list[dict[str, Any]] | None = Field(
171
+ default=None,
172
+ description="Array of tool/function definitions available to the agent",
173
+ )
174
+ extra: dict[str, Any] | None = Field(default=None, description="Custom agent configuration details")
175
+
176
+
177
+ class Trajectory(BaseModel):
178
+ """ATIF trajectory - the complete interaction history.
179
+
180
+ Example:
181
+ trajectory = Trajectory(
182
+ session_id="abc-123",
183
+ agent=Agent(name="openhands", version="0.37.0", model_name="claude-sonnet-4"),
184
+ steps=[
185
+ Step.user(1, "Fix the bug in main.py"),
186
+ Step.agent(2, "I'll analyze the code...", model_name="claude-sonnet-4"),
187
+ ],
188
+ )
189
+ trajectory.to_file("/logs/agent/trajectory.json")
190
+ """
191
+
192
+ model_config = {"extra": "forbid"}
193
+
194
+ schema_version: Literal["ATIF-v1.0", "ATIF-v1.1", "ATIF-v1.2", "ATIF-v1.3", "ATIF-v1.4", "ATIF-v1.5"] = Field(
195
+ default="ATIF-v1.5",
196
+ description="String defining ATIF compatibility",
197
+ )
198
+ session_id: str = Field(..., description="Unique identifier for the entire agent run")
199
+ agent: Agent = Field(..., description="Object specifying the agent configuration")
200
+ steps: list[Step] = Field(
201
+ ..., min_length=1, description="Array of step objects representing the complete interaction history"
202
+ )
203
+ notes: str | None = Field(default=None, description="Custom information, design notes, or explanations")
204
+ final_metrics: FinalMetrics | None = Field(default=None, description="Summary metrics for the entire trajectory")
205
+ continued_trajectory_ref: str | None = Field(
206
+ default=None,
207
+ description="Reference to continuation trajectory file if this trajectory is continued elsewhere",
208
+ )
209
+ extra: dict[str, Any] | None = Field(default=None, description="Custom root-level metadata")
210
+
211
+ def add_step(self, step: Step) -> None:
212
+ """Add a step to the trajectory."""
213
+ self.steps.append(step)
214
+
215
+ def compute_final_metrics(self) -> FinalMetrics:
216
+ """Compute aggregate metrics from all steps."""
217
+ total_prompt = 0
218
+ total_completion = 0
219
+ total_cached = 0
220
+ total_cost = 0.0
221
+
222
+ for step in self.steps:
223
+ if step.metrics:
224
+ if step.metrics.prompt_tokens:
225
+ total_prompt += step.metrics.prompt_tokens
226
+ if step.metrics.completion_tokens:
227
+ total_completion += step.metrics.completion_tokens
228
+ if step.metrics.cached_tokens:
229
+ total_cached += step.metrics.cached_tokens
230
+ if step.metrics.cost_usd:
231
+ total_cost += step.metrics.cost_usd
232
+
233
+ self.final_metrics = FinalMetrics(
234
+ total_prompt_tokens=total_prompt if total_prompt > 0 else None,
235
+ total_completion_tokens=total_completion if total_completion > 0 else None,
236
+ total_steps=len(self.steps),
237
+ total_cached_tokens=total_cached if total_cached > 0 else None,
238
+ total_cost_usd=total_cost if total_cost > 0 else None,
239
+ )
240
+ return self.final_metrics
241
+
242
+ def to_dict(self, exclude_none: bool = True) -> dict[str, Any]:
243
+ """Convert to dictionary, optionally excluding None values."""
244
+ return self.model_dump(exclude_none=exclude_none)
245
+
246
+ def to_json(self, indent: int = 2) -> str:
247
+ """Convert to JSON string."""
248
+ return self.model_dump_json(indent=indent, exclude_none=True)
249
+
250
+ def to_file(self, path: str) -> None:
251
+ """Write trajectory to a JSON file."""
252
+ import json
253
+ from pathlib import Path as PathLib
254
+
255
+ PathLib(path).parent.mkdir(parents=True, exist_ok=True)
256
+ with open(path, "w") as f:
257
+ json.dump(self.to_dict(), f, indent=2)
258
+
259
+ @classmethod
260
+ def from_file(cls, path: str) -> Trajectory:
261
+ """Load trajectory from a JSON file."""
262
+ import json
263
+
264
+ with open(path) as f:
265
+ data = json.load(f)
266
+ return cls(**data)
@@ -4,7 +4,7 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from typing import Annotated, Any, Dict, List
7
+ from typing import Annotated, Any
8
8
 
9
9
  from pydantic import AwareDatetime, BaseModel, ConfigDict, Field
10
10