plato-sdk 1.0.0__tar.gz

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.
@@ -0,0 +1,10 @@
1
+ # python generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # venv
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.12.8
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: plato-sdk
3
+ Version: 1.0.0
4
+ Summary: Plato SDK
5
+ Author-email: Pranav Putta <pranav@plato.so>, Rob Farlow <rob@plato.so>
6
+ Requires-Python: >=3.8
7
+ Requires-Dist: aiohttp>=3.11.13
8
+ Requires-Dist: build>=1.2.2.post1
9
+ Requires-Dist: httpx>=0.28.1
10
+ Requires-Dist: pydantic-settings>=2.8.1
11
+ Requires-Dist: pydantic>=2.10.6
12
+ Requires-Dist: twine>=6.1.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Plato SDK
16
+
17
+
18
+ ## Usage
19
+ ```
20
+ class Task(BaseModel):
21
+ ...
22
+
23
+ async with env := plato.make(ENV_ID):
24
+ cdp_url = env.cdp_url
25
+
26
+ # connect to browser and do modifications
27
+
28
+ # get state
29
+ state_mutations: dict = env.get_state()
30
+
31
+ # evaluate
32
+
33
+ ...
34
+
35
+ ```
@@ -0,0 +1,21 @@
1
+ # Plato SDK
2
+
3
+
4
+ ## Usage
5
+ ```
6
+ class Task(BaseModel):
7
+ ...
8
+
9
+ async with env := plato.make(ENV_ID):
10
+ cdp_url = env.cdp_url
11
+
12
+ # connect to browser and do modifications
13
+
14
+ # get state
15
+ state_mutations: dict = env.get_state()
16
+
17
+ # evaluate
18
+
19
+ ...
20
+
21
+ ```
@@ -0,0 +1,32 @@
1
+ [project]
2
+ name = "plato-sdk"
3
+ version = "1.0.0"
4
+ description = "Plato SDK"
5
+ authors = [
6
+ { name = "Pranav Putta", email = "pranav@plato.so" },
7
+ {name = "Rob Farlow", email="rob@plato.so"}
8
+ ]
9
+ dependencies = [
10
+ "httpx>=0.28.1",
11
+ "pydantic>=2.10.6",
12
+ "pydantic-settings>=2.8.1",
13
+ "aiohttp>=3.11.13",
14
+ "build>=1.2.2.post1",
15
+ "twine>=6.1.0",
16
+ ]
17
+ readme = "README.md"
18
+ requires-python = ">= 3.8"
19
+
20
+ [build-system]
21
+ requires = ["hatchling"]
22
+ build-backend = "hatchling.build"
23
+
24
+ [tool.rye]
25
+ managed = true
26
+ dev-dependencies = []
27
+
28
+ [tool.hatch.metadata]
29
+ allow-direct-references = true
30
+
31
+ [tool.hatch.build.targets.wheel]
32
+ packages = ["src/plato"]
@@ -0,0 +1,115 @@
1
+ # generated by rye
2
+ # use `rye lock` or `rye sync` to update this lockfile
3
+ #
4
+ # last locked with the following flags:
5
+ # pre: false
6
+ # features: []
7
+ # all-features: false
8
+ # with-sources: false
9
+ # generate-hashes: false
10
+ # universal: false
11
+
12
+ -e file:.
13
+ aiohappyeyeballs==2.6.1
14
+ # via aiohttp
15
+ aiohttp==3.11.13
16
+ # via plato
17
+ aiosignal==1.3.2
18
+ # via aiohttp
19
+ annotated-types==0.7.0
20
+ # via pydantic
21
+ anyio==4.8.0
22
+ # via httpx
23
+ attrs==25.2.0
24
+ # via aiohttp
25
+ build==1.2.2.post1
26
+ # via plato
27
+ certifi==2025.1.31
28
+ # via httpcore
29
+ # via httpx
30
+ # via requests
31
+ charset-normalizer==3.4.1
32
+ # via requests
33
+ docutils==0.21.2
34
+ # via readme-renderer
35
+ frozenlist==1.5.0
36
+ # via aiohttp
37
+ # via aiosignal
38
+ h11==0.14.0
39
+ # via httpcore
40
+ httpcore==1.0.7
41
+ # via httpx
42
+ httpx==0.28.1
43
+ # via plato
44
+ id==1.5.0
45
+ # via twine
46
+ idna==3.10
47
+ # via anyio
48
+ # via httpx
49
+ # via requests
50
+ # via yarl
51
+ jaraco-classes==3.4.0
52
+ # via keyring
53
+ jaraco-context==6.0.1
54
+ # via keyring
55
+ jaraco-functools==4.1.0
56
+ # via keyring
57
+ keyring==25.6.0
58
+ # via twine
59
+ markdown-it-py==3.0.0
60
+ # via rich
61
+ mdurl==0.1.2
62
+ # via markdown-it-py
63
+ more-itertools==10.6.0
64
+ # via jaraco-classes
65
+ # via jaraco-functools
66
+ multidict==6.1.0
67
+ # via aiohttp
68
+ # via yarl
69
+ nh3==0.2.21
70
+ # via readme-renderer
71
+ packaging==24.2
72
+ # via build
73
+ # via twine
74
+ propcache==0.3.0
75
+ # via aiohttp
76
+ # via yarl
77
+ pydantic==2.10.6
78
+ # via plato
79
+ # via pydantic-settings
80
+ pydantic-core==2.27.2
81
+ # via pydantic
82
+ pydantic-settings==2.8.1
83
+ # via plato
84
+ pygments==2.19.1
85
+ # via readme-renderer
86
+ # via rich
87
+ pyproject-hooks==1.2.0
88
+ # via build
89
+ python-dotenv==1.0.1
90
+ # via pydantic-settings
91
+ readme-renderer==44.0
92
+ # via twine
93
+ requests==2.32.3
94
+ # via id
95
+ # via requests-toolbelt
96
+ # via twine
97
+ requests-toolbelt==1.0.0
98
+ # via twine
99
+ rfc3986==2.0.0
100
+ # via twine
101
+ rich==13.9.4
102
+ # via twine
103
+ sniffio==1.3.1
104
+ # via anyio
105
+ twine==6.1.0
106
+ # via plato
107
+ typing-extensions==4.12.2
108
+ # via anyio
109
+ # via pydantic
110
+ # via pydantic-core
111
+ urllib3==2.3.0
112
+ # via requests
113
+ # via twine
114
+ yarl==1.18.3
115
+ # via aiohttp
@@ -0,0 +1,115 @@
1
+ # generated by rye
2
+ # use `rye lock` or `rye sync` to update this lockfile
3
+ #
4
+ # last locked with the following flags:
5
+ # pre: false
6
+ # features: []
7
+ # all-features: false
8
+ # with-sources: false
9
+ # generate-hashes: false
10
+ # universal: false
11
+
12
+ -e file:.
13
+ aiohappyeyeballs==2.6.1
14
+ # via aiohttp
15
+ aiohttp==3.11.13
16
+ # via plato
17
+ aiosignal==1.3.2
18
+ # via aiohttp
19
+ annotated-types==0.7.0
20
+ # via pydantic
21
+ anyio==4.8.0
22
+ # via httpx
23
+ attrs==25.2.0
24
+ # via aiohttp
25
+ build==1.2.2.post1
26
+ # via plato
27
+ certifi==2025.1.31
28
+ # via httpcore
29
+ # via httpx
30
+ # via requests
31
+ charset-normalizer==3.4.1
32
+ # via requests
33
+ docutils==0.21.2
34
+ # via readme-renderer
35
+ frozenlist==1.5.0
36
+ # via aiohttp
37
+ # via aiosignal
38
+ h11==0.14.0
39
+ # via httpcore
40
+ httpcore==1.0.7
41
+ # via httpx
42
+ httpx==0.28.1
43
+ # via plato
44
+ id==1.5.0
45
+ # via twine
46
+ idna==3.10
47
+ # via anyio
48
+ # via httpx
49
+ # via requests
50
+ # via yarl
51
+ jaraco-classes==3.4.0
52
+ # via keyring
53
+ jaraco-context==6.0.1
54
+ # via keyring
55
+ jaraco-functools==4.1.0
56
+ # via keyring
57
+ keyring==25.6.0
58
+ # via twine
59
+ markdown-it-py==3.0.0
60
+ # via rich
61
+ mdurl==0.1.2
62
+ # via markdown-it-py
63
+ more-itertools==10.6.0
64
+ # via jaraco-classes
65
+ # via jaraco-functools
66
+ multidict==6.1.0
67
+ # via aiohttp
68
+ # via yarl
69
+ nh3==0.2.21
70
+ # via readme-renderer
71
+ packaging==24.2
72
+ # via build
73
+ # via twine
74
+ propcache==0.3.0
75
+ # via aiohttp
76
+ # via yarl
77
+ pydantic==2.10.6
78
+ # via plato
79
+ # via pydantic-settings
80
+ pydantic-core==2.27.2
81
+ # via pydantic
82
+ pydantic-settings==2.8.1
83
+ # via plato
84
+ pygments==2.19.1
85
+ # via readme-renderer
86
+ # via rich
87
+ pyproject-hooks==1.2.0
88
+ # via build
89
+ python-dotenv==1.0.1
90
+ # via pydantic-settings
91
+ readme-renderer==44.0
92
+ # via twine
93
+ requests==2.32.3
94
+ # via id
95
+ # via requests-toolbelt
96
+ # via twine
97
+ requests-toolbelt==1.0.0
98
+ # via twine
99
+ rfc3986==2.0.0
100
+ # via twine
101
+ rich==13.9.4
102
+ # via twine
103
+ sniffio==1.3.1
104
+ # via anyio
105
+ twine==6.1.0
106
+ # via plato
107
+ typing-extensions==4.12.2
108
+ # via anyio
109
+ # via pydantic
110
+ # via pydantic-core
111
+ urllib3==2.3.0
112
+ # via requests
113
+ # via twine
114
+ yarl==1.18.3
115
+ # via aiohttp
@@ -0,0 +1,203 @@
1
+ from typing import Optional, Dict, Any
2
+ import aiohttp
3
+ from plato.config import get_config
4
+ from plato.models import PlatoTask, PlatoEnvironment
5
+
6
+ config = get_config()
7
+
8
+ class PlatoClient:
9
+ """Client for interacting with the Plato API.
10
+
11
+ This class provides methods to create and manage Plato environments, handle API authentication,
12
+ and manage HTTP sessions.
13
+
14
+ Attributes:
15
+ api_key (str): The API key used for authentication with Plato API.
16
+ base_url (str): The base URL of the Plato API.
17
+ http_session (Optional[aiohttp.ClientSession]): The aiohttp session for making HTTP requests.
18
+ """
19
+
20
+ def __init__(self, api_key: Optional[str] = None):
21
+ """Initialize a new PlatoClient.
22
+
23
+ Args:
24
+ api_key (Optional[str]): The API key for authentication. If not provided,
25
+ falls back to the key from config.
26
+ """
27
+ self.api_key = api_key or config.api_key
28
+ self.base_url = config.base_url
29
+ self._http_session: Optional[aiohttp.ClientSession] = None
30
+
31
+ @property
32
+ def http_session(self) -> aiohttp.ClientSession:
33
+ """Get or create an aiohttp client session.
34
+
35
+ Returns:
36
+ aiohttp.ClientSession: The active HTTP client session.
37
+ """
38
+ if self._http_session is None or self._http_session.closed:
39
+ self._http_session = aiohttp.ClientSession()
40
+ return self._http_session
41
+
42
+ async def close(self):
43
+ """Close the aiohttp client session if it exists."""
44
+ if self._http_session is not None and not self._http_session.closed:
45
+ await self._http_session.close()
46
+ self._http_session = None
47
+
48
+ async def make_environment(self, env_id: str) -> PlatoEnvironment:
49
+ """Create a new Plato environment for the given task.
50
+
51
+ Args:
52
+ task (PlatoTask): The task to create an environment for.
53
+
54
+ Returns:
55
+ PlatoEnvironment: The created environment instance.
56
+
57
+ Raises:
58
+ aiohttp.ClientError: If the API request fails.
59
+ """
60
+ headers = {"X-API-Key": self.api_key}
61
+ async with self.http_session.post(
62
+ f"{self.base_url}/env/make",
63
+ json={"config": {
64
+ "type": "browser_sim",
65
+ "env_id": env_id,
66
+ "sim_connection_type": "fastapi"
67
+ }},
68
+ headers=headers
69
+ ) as response:
70
+ response.raise_for_status()
71
+ data = await response.json()
72
+ return PlatoEnvironment(
73
+ client=self,
74
+ id=data["job_id"]
75
+ )
76
+
77
+ async def get_job_status(self, job_id: str) -> Dict[str, Any]:
78
+ """Get the status of a job.
79
+
80
+ Args:
81
+ job_id (str): The ID of the job to check.
82
+
83
+ Returns:
84
+ Dict[str, Any]: The job status information.
85
+
86
+ Raises:
87
+ aiohttp.ClientError: If the API request fails.
88
+ """
89
+ headers = {"X-API-Key": self.api_key}
90
+ async with self.http_session.get(
91
+ f"{self.base_url}/env/{job_id}/status",
92
+ headers=headers
93
+ ) as response:
94
+ response.raise_for_status()
95
+ return await response.json()
96
+
97
+ async def get_cdp_url(self, job_id: str) -> str:
98
+ """Get the Chrome DevTools Protocol URL for a job.
99
+
100
+ Args:
101
+ job_id (str): The ID of the job to get the CDP URL for.
102
+
103
+ Returns:
104
+ str: The CDP URL for the job.
105
+
106
+ Raises:
107
+ aiohttp.ClientError: If the API request fails.
108
+ """
109
+ headers = {"X-API-Key": self.api_key}
110
+ async with self.http_session.get(
111
+ f"{self.base_url}/env/{job_id}/cdp_url",
112
+ headers=headers
113
+ ) as response:
114
+ response.raise_for_status()
115
+ data = await response.json()
116
+ return data["cdp_url"]
117
+
118
+ async def close_environment(self, job_id: str) -> Dict[str, Any]:
119
+ """Close an environment.
120
+
121
+ Args:
122
+ job_id (str): The ID of the job to close.
123
+
124
+ Returns:
125
+ Dict[str, Any]: The response from the server.
126
+
127
+ Raises:
128
+ aiohttp.ClientError: If the API request fails.
129
+ """
130
+ headers = {"X-API-Key": self.api_key}
131
+ async with self.http_session.post(
132
+ f"{self.base_url}/env/{job_id}/close",
133
+ headers=headers
134
+ ) as response:
135
+ response.raise_for_status()
136
+ return await response.json()
137
+
138
+ async def reset_environment(self, job_id: str, task: Optional[PlatoTask] = None) -> Dict[str, Any]:
139
+ """Reset an environment with an optional new task.
140
+
141
+ Args:
142
+ job_id (str): The ID of the job to reset.
143
+ task (Optional[PlatoTask]): Optional new task for the environment.
144
+
145
+ Returns:
146
+ Dict[str, Any]: The response from the server.
147
+
148
+ Raises:
149
+ aiohttp.ClientError: If the API request fails.
150
+ """
151
+ headers = {"X-API-Key": self.api_key}
152
+ params = {"task": task.dict() if task else None}
153
+ async with self.http_session.post(
154
+ f"{self.base_url}/env/{job_id}/reset",
155
+ headers=headers,
156
+ ) as response:
157
+ response.raise_for_status()
158
+ return await response.json()
159
+
160
+ async def get_environment_state(self, job_id: str) -> Dict[str, Any]:
161
+ """Get the current state of an environment.
162
+
163
+ Args:
164
+ job_id (str): The ID of the job to get state for.
165
+
166
+ Returns:
167
+ Dict[str, Any]: The current state of the environment.
168
+
169
+ Raises:
170
+ aiohttp.ClientError: If the API request fails.
171
+ """
172
+ headers = {"X-API-Key": self.api_key}
173
+ async with self.http_session.get(
174
+ f"{self.base_url}/env/{job_id}/state",
175
+ headers=headers
176
+ ) as response:
177
+ response.raise_for_status()
178
+ return await response.json()
179
+
180
+ async def get_worker_ready(self, job_id: str) -> Dict[str, Any]:
181
+ """Check if the worker for this job is ready and healthy.
182
+
183
+ Args:
184
+ job_id (str): The ID of the job to check.
185
+
186
+ Returns:
187
+ Dict[str, Any]: The worker ready status information including:
188
+ - ready (bool): Whether the worker is ready
189
+ - worker_ip (Optional[str]): The worker's IP if ready
190
+ - worker_port (Optional[int]): The worker's port if ready
191
+ - health_status (Optional[Dict]): The worker's health status if ready
192
+ - error (Optional[str]): Error message if not ready
193
+
194
+ Raises:
195
+ aiohttp.ClientError: If the API request fails.
196
+ """
197
+ headers = {"X-API-Key": self.api_key}
198
+ async with self.http_session.get(
199
+ f"{self.base_url}/env/{job_id}/worker_ready",
200
+ headers=headers
201
+ ) as response:
202
+ response.raise_for_status()
203
+ return await response.json()
@@ -0,0 +1,22 @@
1
+ """Configuration settings for the Plato client."""
2
+
3
+ from pydantic import Field
4
+ from pydantic_settings import BaseSettings, SettingsConfigDict
5
+ from functools import lru_cache
6
+
7
+ class Config(BaseSettings):
8
+ model_config = SettingsConfigDict(env_file=".env", env_prefix="PLATO_")
9
+ base_url: str = Field(
10
+ default="http://localhost:25565/api",
11
+ description="Base URL for the Plato API",
12
+ validation_alias="PLATO_BASE_URL ",
13
+ )
14
+ api_key: str = Field(
15
+ default="",
16
+ description="API key for the Plato API",
17
+ validation_alias="PLATO_API_KEY",
18
+ )
19
+
20
+ @lru_cache
21
+ def get_config() -> Config:
22
+ return Config()
@@ -0,0 +1,4 @@
1
+ from .task import PlatoTask
2
+ from .env import PlatoEnvironment
3
+
4
+ __all__ = ["PlatoTask", "PlatoEnvironment"]
@@ -0,0 +1,158 @@
1
+ from pydantic import BaseModel, Field
2
+ from plato.models import PlatoTask
3
+ from typing import List, Optional, Type, Dict, Any
4
+ import time
5
+ import asyncio
6
+ from aiohttp import ClientError
7
+
8
+
9
+ class PlatoEnvironment:
10
+ """A base environment class for Plato that handles task execution and state management.
11
+
12
+ This class provides the core interface for creating, managing, and interacting with
13
+ task environments. It implements an async context manager pattern and defines the
14
+ basic contract that all Plato environments must fulfill.
15
+
16
+ Attributes:
17
+ _client (PlatoClient): The client instance for interacting with the environment
18
+ _current_task (Optional[PlatoTask]): The task currently being executed
19
+ id (str): Unique identifier for this environment instance (job ID)
20
+ """
21
+
22
+ _current_task: Optional[PlatoTask] = Field(
23
+ description="The current task for the environment", default=None
24
+ )
25
+ _client: "PlatoClient" = Field(description="The client for the environment")
26
+ id: str = Field(description="The ID for the environment (job ID)")
27
+
28
+ def __init__(self, client: "PlatoClient", id: str):
29
+ self._client = client
30
+ self.id = id
31
+
32
+ async def wait_for_ready(self, timeout: Optional[float] = None) -> None:
33
+ """Wait for the environment to be ready.
34
+
35
+ This method checks both the job status and worker health until everything is ready.
36
+
37
+ Args:
38
+ timeout (Optional[float]): Maximum time to wait in seconds before raising an error.
39
+ If None, will wait indefinitely.
40
+
41
+ Raises:
42
+ RuntimeError: If the environment fails to start within the timeout period.
43
+ """
44
+ start_time = time.time()
45
+
46
+ # wait for the job to be running
47
+ while True:
48
+ status = await self._client.get_job_status(self.id)
49
+ if status["status"].lower() == "running":
50
+ break
51
+ await asyncio.sleep(0.1)
52
+ if timeout and time.time() - start_time > timeout:
53
+ raise RuntimeError(
54
+ "Environment failed to start - job never entered running state"
55
+ )
56
+
57
+ # wait for the worker to be ready and healthy
58
+ while True:
59
+ worker_status = await self._client.get_worker_ready(self.id)
60
+ if worker_status["ready"]:
61
+ break
62
+ await asyncio.sleep(0.1)
63
+ if timeout and time.time() - start_time > timeout:
64
+ error_msg = worker_status.get("error", "Unknown error")
65
+ raise RuntimeError(
66
+ f"Environment failed to start - worker not ready: {error_msg}"
67
+ )
68
+
69
+ # wait for the cdp url to be ready
70
+ while True:
71
+ try:
72
+ cdp_url = await self._client.get_cdp_url(self.id)
73
+ if cdp_url:
74
+ break
75
+ except ClientError as e:
76
+ await asyncio.sleep(0.1)
77
+ if timeout and time.time() - start_time > timeout:
78
+ raise RuntimeError("Environment failed to start - cdp url not ready")
79
+
80
+ async def __aenter__(self):
81
+ """Enter the async context manager.
82
+
83
+ Calls make() to initialize the environment and returns self.
84
+
85
+ Returns:
86
+ PlatoEnvironment: The initialized environment instance.
87
+ """
88
+ await self.wait_for_ready()
89
+ return self
90
+
91
+ async def __aexit__(
92
+ self,
93
+ exc_type: Optional[Type[BaseException]],
94
+ exc_val: Optional[BaseException],
95
+ exc_tb: Optional[Type[BaseException]],
96
+ ) -> None:
97
+ """Exit the async context manager.
98
+
99
+ Performs cleanup by calling close() when the context manager exits.
100
+
101
+ Args:
102
+ exc_type: The type of the exception that was raised, if any
103
+ exc_val: The instance of the exception that was raised, if any
104
+ exc_tb: The traceback of the exception that was raised, if any
105
+ """
106
+ await self.close()
107
+
108
+ async def get_cdp_url(self) -> str:
109
+ """Get the Chrome DevTools Protocol URL for this environment's session.
110
+
111
+ Returns:
112
+ str: The CDP URL for connecting to this environment's browser session.
113
+ """
114
+ return await self._client.get_cdp_url(self.id)
115
+
116
+ async def reset(self, task: Optional[PlatoTask] = None) -> None:
117
+ """Reset the environment with an optional new task.
118
+
119
+ Args:
120
+ task (Optional[PlatoTask]): The new task to set up the environment for.
121
+ """
122
+ await self._client.reset_environment(self.id, task)
123
+ if task:
124
+ self._current_task = task
125
+
126
+ async def get_state(self) -> Dict[str, Any]:
127
+ """Get the current state of the environment.
128
+
129
+ Returns:
130
+ Dict[str, Any]: A dictionary representing the current state of the environment.
131
+ """
132
+ return await self._client.get_environment_state(self.id)
133
+
134
+ async def get_state_mutations(self) -> List[Dict[str, Any]]:
135
+ """Get a list of state mutations that have occurred in the environment.
136
+
137
+ Returns:
138
+ List[Dict[str, Any]]: A list of dictionaries representing state changes.
139
+ """
140
+ state = await self.get_state()
141
+ return state.get("mutations", [])
142
+
143
+ async def evaluate(self) -> bool:
144
+ """Evaluate whether the current task has been completed successfully.
145
+
146
+ Returns:
147
+ bool: True if the task is completed successfully, False otherwise.
148
+ """
149
+ state = await self.get_state()
150
+ return state.get("completed", False)
151
+
152
+ async def close(self) -> None:
153
+ """Clean up and close the environment.
154
+
155
+ This method handles cleanup of resources by closing the environment
156
+ through the API client.
157
+ """
158
+ await self._client.close_environment(self.id)
@@ -0,0 +1,53 @@
1
+ from pydantic import BaseModel
2
+ from typing import Optional, Literal, List
3
+
4
+
5
+ class BasePlatoEvalConfig(BaseModel):
6
+ """Base configuration class for Plato evaluation settings.
7
+
8
+ This class serves as the base configuration for different types of evaluation
9
+ methods in the Plato system.
10
+
11
+ Attributes:
12
+ type (Literal["base", "state_mutation_match"]): The type of evaluation configuration.
13
+ Can be either "base" or "state_mutation_match".
14
+ """
15
+ type: Literal["base", "state_mutation_match"]
16
+
17
+
18
+ class StateMutationMatchEvalConfig(BasePlatoEvalConfig):
19
+ """Configuration for state mutation matching evaluation.
20
+
21
+ This class defines the configuration for evaluating tasks based on matching
22
+ state mutations. It inherits from BasePlatoEvalConfig and specifies
23
+ state mutations that should be matched during evaluation.
24
+
25
+ Attributes:
26
+ type (Literal["state_mutation_match"]): The type of evaluation, fixed as
27
+ "state_mutation_match" for this configuration.
28
+ state_mutations (List[dict]): A list of state mutation specifications that
29
+ define the expected changes in state during task execution.
30
+ """
31
+ type: Literal["state_mutation_match"] = "state_mutation_match"
32
+ state_mutations: List[dict]
33
+
34
+
35
+ class PlatoTask(BaseModel):
36
+ """Represents a task in the Plato system.
37
+
38
+ This class defines the structure of a task, including its name, metadata,
39
+ initial state, and evaluation configuration.
40
+
41
+ Attributes:
42
+ name (Optional[str]): The name of the task. Defaults to None.
43
+ metadata (Optional[dict]): Additional metadata associated with the task.
44
+ Defaults to None.
45
+ initial_state (Optional[dict]): The initial state of the task before
46
+ execution. Defaults to None.
47
+ eval_config (Optional[BasePlatoEvalConfig]): The evaluation configuration
48
+ for the task. Defaults to None.
49
+ """
50
+ name: Optional[str] = None
51
+ metadata: Optional[dict] = None
52
+ initial_state: Optional[dict] = None
53
+ eval_config: Optional[BasePlatoEvalConfig] = None
@@ -0,0 +1,42 @@
1
+ import asyncio
2
+ from plato.client import PlatoClient
3
+ from plato.models import PlatoEnvironment, PlatoTask
4
+
5
+ async def test_environment_lifecycle():
6
+ """Test the lifecycle of a Plato environment including creation, reset, and closure."""
7
+ # Initialize the client
8
+ client = PlatoClient()
9
+
10
+ # Create a sample task
11
+ task = PlatoTask(
12
+ name="example_task",
13
+ metadata={"type": "test"},
14
+ initial_state={"url": "https://example.com"}
15
+ )
16
+
17
+ # Create and initialize the environment
18
+ env = await client.make_environment("doordash")
19
+
20
+ try:
21
+ # Wait for the environment to be ready
22
+ await env.wait_for_ready(timeout=30.0)
23
+
24
+ # Get the CDP URL for browser connection
25
+ cdp_url = await env.get_cdp_url()
26
+ print(f"Environment ready with CDP URL: {cdp_url}")
27
+
28
+ # Reset the environment with a new task
29
+ await env.reset()
30
+ print("Environment reset with new task")
31
+ breakpoint()
32
+
33
+ finally:
34
+ # Always ensure we close the environment
35
+ await env.close()
36
+ print("Environment closed")
37
+ # Close the client to cleanup aiohttp session
38
+ await client.close()
39
+ print("Client closed")
40
+
41
+ if __name__ == "__main__":
42
+ asyncio.run(test_environment_lifecycle())
File without changes