fleet-python 0.2.17__tar.gz → 0.2.18__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.

Potentially problematic release.


This version of fleet-python might be problematic. Click here for more details.

Files changed (75) hide show
  1. {fleet_python-0.2.17 → fleet_python-0.2.18}/PKG-INFO +1 -1
  2. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/__init__.py +0 -5
  3. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/client.py +4 -10
  4. fleet_python-0.2.18/fleet/verifiers/code.py +2 -0
  5. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet_python.egg-info/PKG-INFO +1 -1
  6. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet_python.egg-info/SOURCES.txt +0 -2
  7. {fleet_python-0.2.17 → fleet_python-0.2.18}/pyproject.toml +1 -1
  8. fleet_python-0.2.17/fleet/_async/playwright.py +0 -291
  9. fleet_python-0.2.17/fleet/playwright.py +0 -290
  10. fleet_python-0.2.17/fleet/verifiers/code.py +0 -1
  11. {fleet_python-0.2.17 → fleet_python-0.2.18}/LICENSE +0 -0
  12. {fleet_python-0.2.17 → fleet_python-0.2.18}/README.md +0 -0
  13. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/diff_example.py +0 -0
  14. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/dsl_example.py +0 -0
  15. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example.py +0 -0
  16. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example_action_log.py +0 -0
  17. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example_client.py +0 -0
  18. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example_mcp_anthropic.py +0 -0
  19. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example_mcp_openai.py +0 -0
  20. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example_sync.py +0 -0
  21. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example_task.py +0 -0
  22. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/example_verifier.py +0 -0
  23. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/gemini_example.py +0 -0
  24. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/json_tasks_example.py +0 -0
  25. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/nova_act_example.py +0 -0
  26. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/openai_example.py +0 -0
  27. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/openai_simple_example.py +0 -0
  28. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/query_builder_example.py +0 -0
  29. {fleet_python-0.2.17 → fleet_python-0.2.18}/examples/quickstart.py +0 -0
  30. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/__init__.py +0 -0
  31. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/base.py +0 -0
  32. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/client.py +0 -0
  33. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/env/__init__.py +0 -0
  34. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/env/client.py +0 -0
  35. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/exceptions.py +0 -0
  36. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/instance/__init__.py +0 -0
  37. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/instance/base.py +0 -0
  38. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/instance/client.py +0 -0
  39. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/resources/__init__.py +0 -0
  40. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/resources/base.py +0 -0
  41. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/resources/browser.py +0 -0
  42. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/resources/sqlite.py +0 -0
  43. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/tasks.py +0 -0
  44. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/verifiers/__init__.py +0 -0
  45. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/verifiers/bundler.py +0 -0
  46. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/_async/verifiers/verifier.py +0 -0
  47. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/base.py +0 -0
  48. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/config.py +0 -0
  49. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/env/__init__.py +0 -0
  50. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/env/client.py +0 -0
  51. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/exceptions.py +0 -0
  52. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/instance/__init__.py +0 -0
  53. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/instance/base.py +0 -0
  54. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/instance/client.py +0 -0
  55. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/instance/models.py +0 -0
  56. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/models.py +0 -0
  57. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/resources/__init__.py +0 -0
  58. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/resources/base.py +0 -0
  59. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/resources/browser.py +0 -0
  60. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/resources/mcp.py +0 -0
  61. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/resources/sqlite.py +0 -0
  62. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/tasks.py +0 -0
  63. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/types.py +0 -0
  64. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/verifiers/__init__.py +0 -0
  65. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/verifiers/bundler.py +0 -0
  66. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/verifiers/db.py +0 -0
  67. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/verifiers/decorator.py +0 -0
  68. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/verifiers/sql_differ.py +0 -0
  69. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet/verifiers/verifier.py +0 -0
  70. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet_python.egg-info/dependency_links.txt +0 -0
  71. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet_python.egg-info/requires.txt +0 -0
  72. {fleet_python-0.2.17 → fleet_python-0.2.18}/fleet_python.egg-info/top_level.txt +0 -0
  73. {fleet_python-0.2.17 → fleet_python-0.2.18}/scripts/fix_sync_imports.py +0 -0
  74. {fleet_python-0.2.17 → fleet_python-0.2.18}/scripts/unasync.py +0 -0
  75. {fleet_python-0.2.17 → fleet_python-0.2.18}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.17
3
+ Version: 0.2.18
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -37,9 +37,6 @@ from .verifiers import (
37
37
  TASK_SUCCESSFUL_SCORE,
38
38
  )
39
39
 
40
- # Import Playwright wrapper
41
- from .playwright import FleetPlaywrightWrapper
42
-
43
40
  # Import async verifiers (default verifier is async for modern usage)
44
41
  from ._async.verifiers import (
45
42
  verifier,
@@ -75,8 +72,6 @@ __all__ = [
75
72
  "FleetAPIError",
76
73
  "FleetTimeoutError",
77
74
  "FleetConfigurationError",
78
- # Playwright wrapper
79
- "FleetPlaywrightWrapper",
80
75
  # Verifiers (async is default)
81
76
  "verifier",
82
77
  "verifier_sync",
@@ -537,12 +537,6 @@ class Fleet:
537
537
  browser = new_env.browser()
538
538
  browser.start(width=snapshot.viewport_size[0], height=snapshot.viewport_size[1])
539
539
 
540
- from fleet.playwright import FleetPlaywrightWrapper
541
-
542
- playwright_wrapper = FleetPlaywrightWrapper(
543
- cdp_url=browser.cdp_url(), instance_client=new_env.instance
544
- )
545
-
546
540
  # Replay tool logs in order
547
541
  validation_errors = []
548
542
  last_timestamp = None
@@ -559,7 +553,7 @@ class Fleet:
559
553
 
560
554
  # Replay the tool action
561
555
  _replay_tool_action(
562
- playwright_wrapper,
556
+ None,
563
557
  tool_log,
564
558
  new_env.instance._client,
565
559
  replay_session_id,
@@ -588,7 +582,7 @@ class Fleet:
588
582
 
589
583
  if validate:
590
584
  validation = _validate_resumed_state(
591
- new_env, snapshot, playwright_wrapper, validation_errors
585
+ new_env, snapshot, None, validation_errors
592
586
  )
593
587
 
594
588
  return new_env, validation
@@ -664,7 +658,7 @@ def _execute_verifier_remote(
664
658
 
665
659
 
666
660
  def _replay_tool_action(
667
- playwright_wrapper: "FleetPlaywrightWrapper",
661
+ playwright_wrapper,
668
662
  tool_log: ToolLogEntry,
669
663
  client: "SyncWrapper",
670
664
  session_id: str,
@@ -760,7 +754,7 @@ def _replay_tool_action(
760
754
  def _validate_resumed_state(
761
755
  new_env: Environment,
762
756
  snapshot: EnvironmentSnapshot,
763
- playwright_wrapper: "FleetPlaywrightWrapper",
757
+ playwright_wrapper,
764
758
  existing_errors: List[str],
765
759
  ) -> SnapshotValidation:
766
760
  """Validate that the resumed state matches the snapshot."""
@@ -0,0 +1,2 @@
1
+ TASK_SUCCESSFUL_SCORE = 1
2
+ TASK_FAILED_SCORE = 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.17
3
+ Version: 0.2.18
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -24,14 +24,12 @@ fleet/client.py
24
24
  fleet/config.py
25
25
  fleet/exceptions.py
26
26
  fleet/models.py
27
- fleet/playwright.py
28
27
  fleet/tasks.py
29
28
  fleet/types.py
30
29
  fleet/_async/__init__.py
31
30
  fleet/_async/base.py
32
31
  fleet/_async/client.py
33
32
  fleet/_async/exceptions.py
34
- fleet/_async/playwright.py
35
33
  fleet/_async/tasks.py
36
34
  fleet/_async/env/__init__.py
37
35
  fleet/_async/env/client.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fleet-python"
7
- version = "0.2.17"
7
+ version = "0.2.18"
8
8
  description = "Python SDK for Fleet environments"
9
9
  authors = [
10
10
  {name = "Fleet AI", email = "nic@fleet.so"},
@@ -1,291 +0,0 @@
1
- import base64
2
- from typing import List, Dict, Any
3
- from playwright.async_api import async_playwright, Browser, Page
4
- from .client import AsyncEnv
5
-
6
-
7
- # Key mapping for computer use actions
8
- CUA_KEY_TO_PLAYWRIGHT_KEY = {
9
- "/": "Divide",
10
- "\\": "Backslash",
11
- "alt": "Alt",
12
- "arrowdown": "ArrowDown",
13
- "arrowleft": "ArrowLeft",
14
- "arrowright": "ArrowRight",
15
- "arrowup": "ArrowUp",
16
- "backspace": "Backspace",
17
- "capslock": "CapsLock",
18
- "cmd": "Meta",
19
- "ctrl": "Control",
20
- "delete": "Delete",
21
- "end": "End",
22
- "enter": "Enter",
23
- "esc": "Escape",
24
- "home": "Home",
25
- "insert": "Insert",
26
- "option": "Alt",
27
- "pagedown": "PageDown",
28
- "pageup": "PageUp",
29
- "shift": "Shift",
30
- "space": " ",
31
- "super": "Meta",
32
- "tab": "Tab",
33
- "win": "Meta",
34
- }
35
-
36
-
37
- class AsyncFleetPlaywrightWrapper:
38
- """
39
- A wrapper that adds Playwright browser automation to Fleet environment instances.
40
-
41
- This class handles:
42
- - Browser connection via CDP
43
- - Computer actions (click, scroll, type, etc.)
44
- - Screenshot capture
45
- - Integration with OpenAI computer use API
46
-
47
- Usage:
48
- instance = await fleet.env.make(env_key="hubspot", version="v1.2.7")
49
- browser = AsyncFleetPlaywrightWrapper(instance)
50
- await browser.start()
51
-
52
- # Use browser methods
53
- screenshot = await browser.screenshot()
54
- tools = [browser.openai_cua_tool]
55
-
56
- # Clean up when done
57
- await browser.close()
58
- """
59
-
60
- def get_environment(self):
61
- return "browser"
62
-
63
- def get_dimensions(self):
64
- return (1920, 1080)
65
-
66
- def __init__(
67
- self,
68
- env: AsyncEnv,
69
- display_width: int = 1920,
70
- display_height: int = 1080,
71
- ):
72
- """
73
- Initialize the Fleet Playwright wrapper.
74
-
75
- Args:
76
- env: Fleet environment instance
77
- display_width: Browser viewport width
78
- display_height: Browser viewport height
79
- """
80
- self.env = env
81
- self.display_width = display_width
82
- self.display_height = display_height
83
-
84
- self._playwright = None
85
- self._browser: Browser | None = None
86
- self._page: Page | None = None
87
- self._started = False
88
-
89
- async def start(self):
90
- """Start the browser and establish connection."""
91
- if self._started:
92
- return
93
-
94
- # Start Playwright
95
- self._playwright = await async_playwright().start()
96
-
97
- # Start browser on the Fleet instance
98
- print("Starting browser...")
99
- await self.env.browser().start()
100
- cdp = await self.env.browser().describe()
101
-
102
- # Connect to browser
103
- self._browser = await self._playwright.chromium.connect_over_cdp(
104
- cdp.cdp_browser_url
105
- )
106
- self._page = self._browser.contexts[0].pages[0]
107
- await self._page.set_viewport_size(
108
- {"width": self.display_width, "height": self.display_height}
109
- )
110
-
111
- self._started = True
112
- print(f"Track agent: {cdp.cdp_devtools_url}")
113
-
114
- async def close(self):
115
- """Close the browser connection."""
116
- if self._playwright:
117
- await self._playwright.stop()
118
- self._playwright = None
119
- self._browser = None
120
- self._page = None
121
- self._started = False
122
-
123
- def _ensure_started(self):
124
- """Ensure browser is started before operations."""
125
- if not self._started:
126
- raise RuntimeError("Browser not started. Call await browser.start() first.")
127
-
128
- @property
129
- def openai_cua_tool(self) -> Dict[str, Any]:
130
- """
131
- Tool definition for OpenAI computer use API.
132
-
133
- Returns:
134
- Tool definition dict for use with OpenAI responses API
135
- """
136
- return {
137
- "type": "computer_use_preview",
138
- "display_width": self.display_width,
139
- "display_height": self.display_height,
140
- "environment": "browser",
141
- }
142
-
143
- async def screenshot(self) -> str:
144
- """
145
- Take a screenshot and return base64 encoded string.
146
-
147
- Returns:
148
- Base64 encoded PNG screenshot
149
- """
150
- self._ensure_started()
151
-
152
- png_bytes = await self._page.screenshot(full_page=False)
153
- return base64.b64encode(png_bytes).decode("utf-8")
154
-
155
- def get_current_url(self) -> str:
156
- """Get the current page URL."""
157
- self._ensure_started()
158
- return self._page.url
159
-
160
- async def execute_computer_action(self, action: Dict[str, Any]) -> Dict[str, Any]:
161
- """
162
- Execute a computer action and return the result for OpenAI API.
163
-
164
- Args:
165
- action: Computer action dict from OpenAI response
166
-
167
- Returns:
168
- Result dict for computer_call_output
169
- """
170
- self._ensure_started()
171
-
172
- action_type = action["type"]
173
- action_args = {k: v for k, v in action.items() if k != "type"}
174
-
175
- print(f"Executing: {action_type}({action_args})")
176
-
177
- # Execute the action
178
- if hasattr(self, f"_{action_type}"):
179
- method = getattr(self, f"_{action_type}")
180
- await method(**action_args)
181
- else:
182
- raise ValueError(f"Unsupported action type: {action_type}")
183
-
184
- # Take screenshot after action
185
- screenshot_base64 = await self.screenshot()
186
-
187
- return {
188
- "type": "input_image",
189
- "image_url": f"data:image/png;base64,{screenshot_base64}",
190
- "current_url": self.get_current_url(),
191
- }
192
-
193
- # Computer action implementations
194
- async def _click(self, x: int, y: int, button: str = "left") -> None:
195
- """Click at coordinates."""
196
- self._ensure_started()
197
- await self._page.mouse.click(x, y, button=button)
198
-
199
- async def _double_click(self, x: int, y: int) -> None:
200
- """Double-click at coordinates."""
201
- self._ensure_started()
202
- await self._page.mouse.dblclick(x, y)
203
-
204
- async def _scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None:
205
- """Scroll from coordinates."""
206
- self._ensure_started()
207
- await self._page.mouse.move(x, y)
208
- await self._page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})")
209
-
210
- async def _type(self, text: str) -> None:
211
- """Type text."""
212
- self._ensure_started()
213
- await self._page.keyboard.type(text)
214
-
215
- async def _keypress(self, keys: List[str]) -> None:
216
- """Press key combination."""
217
- self._ensure_started()
218
- mapped_keys = [CUA_KEY_TO_PLAYWRIGHT_KEY.get(key.lower(), key) for key in keys]
219
- for key in mapped_keys:
220
- await self._page.keyboard.down(key)
221
- for key in reversed(mapped_keys):
222
- await self._page.keyboard.up(key)
223
-
224
- async def _move(self, x: int, y: int) -> None:
225
- """Move mouse to coordinates."""
226
- self._ensure_started()
227
- await self._page.mouse.move(x, y)
228
-
229
- async def _drag(self, path: List[Dict[str, int]]) -> None:
230
- """Drag mouse along path."""
231
- self._ensure_started()
232
- if not path:
233
- return
234
- await self._page.mouse.move(path[0]["x"], path[0]["y"])
235
- await self._page.mouse.down()
236
- for point in path[1:]:
237
- await self._page.mouse.move(point["x"], point["y"])
238
- await self._page.mouse.up()
239
-
240
- async def _wait(self, ms: int = 1000) -> None:
241
- """Wait for specified milliseconds."""
242
- import asyncio
243
-
244
- await asyncio.sleep(ms / 1000)
245
-
246
- # Browser-specific actions
247
- async def _goto(self, url: str) -> None:
248
- """Navigate to URL."""
249
- self._ensure_started()
250
- try:
251
- await self._page.goto(url)
252
- except Exception as e:
253
- print(f"Error navigating to {url}: {e}")
254
-
255
- async def _back(self) -> None:
256
- """Go back in browser history."""
257
- self._ensure_started()
258
- await self._page.go_back()
259
-
260
- async def _forward(self) -> None:
261
- """Go forward in browser history."""
262
- self._ensure_started()
263
- await self._page.go_forward()
264
-
265
- async def _refresh(self) -> None:
266
- """Refresh the page."""
267
- self._ensure_started()
268
- await self._page.reload()
269
-
270
- # ------------------------------------------------------------------
271
- # Public aliases (no leading underscore) expected by the Agent &
272
- # OpenAI computer-use API. They forward directly to the underscored
273
- # implementations above so the external interface matches the older
274
- # BasePlaywrightComputer class.
275
- # ------------------------------------------------------------------
276
-
277
- # Mouse / keyboard actions
278
- click = _click
279
- double_click = _double_click
280
- scroll = _scroll
281
- type = _type # noqa: A003 – shadowing built-in for API compatibility
282
- keypress = _keypress
283
- move = _move
284
- drag = _drag
285
- wait = _wait
286
-
287
- # Browser navigation actions
288
- goto = _goto
289
- back = _back
290
- forward = _forward
291
- refresh = _refresh
@@ -1,290 +0,0 @@
1
- import base64
2
- from typing import List, Dict, Any
3
- from playwright.sync_api import sync_playwright, Browser, Page
4
- from .client import Environment
5
-
6
-
7
- # Key mapping for computer use actions
8
- CUA_KEY_TO_PLAYWRIGHT_KEY = {
9
- "/": "Divide",
10
- "\\": "Backslash",
11
- "alt": "Alt",
12
- "arrowdown": "ArrowDown",
13
- "arrowleft": "ArrowLeft",
14
- "arrowright": "ArrowRight",
15
- "arrowup": "ArrowUp",
16
- "backspace": "Backspace",
17
- "capslock": "CapsLock",
18
- "cmd": "Meta",
19
- "ctrl": "Control",
20
- "delete": "Delete",
21
- "end": "End",
22
- "enter": "Enter",
23
- "esc": "Escape",
24
- "home": "Home",
25
- "insert": "Insert",
26
- "option": "Alt",
27
- "pagedown": "PageDown",
28
- "pageup": "PageUp",
29
- "shift": "Shift",
30
- "space": " ",
31
- "super": "Meta",
32
- "tab": "Tab",
33
- "win": "Meta",
34
- }
35
-
36
-
37
- class FleetPlaywrightWrapper:
38
- """
39
- A wrapper that adds Playwright browser automation to Fleet environment instances.
40
-
41
- This class handles:
42
- - Browser connection via CDP
43
- - Computer actions (click, scroll, type, etc.)
44
- - Screenshot capture
45
- - Integration with OpenAI computer use API
46
-
47
- Usage:
48
- instance = fleet.env.make(env_key="hubspot", version="v1.2.7")
49
- browser = FleetPlaywrightWrapper(instance)
50
- browser.start()
51
-
52
- # Use browser methods
53
- screenshot = browser.screenshot()
54
- tools = [browser.openai_cua_tool]
55
-
56
- # Clean up when done
57
- browser.close()
58
- """
59
-
60
- def get_environment(self):
61
- return "browser"
62
-
63
- def get_dimensions(self):
64
- return (1920, 1080)
65
-
66
- def __init__(
67
- self,
68
- env: Environment,
69
- display_width: int = 1920,
70
- display_height: int = 1080,
71
- ):
72
- """
73
- Initialize the Fleet Playwright wrapper.
74
-
75
- Args:
76
- env: Fleet environment instance
77
- display_width: Browser viewport width
78
- display_height: Browser viewport height
79
- """
80
- self.env = env
81
- self.display_width = display_width
82
- self.display_height = display_height
83
-
84
- self._playwright = None
85
- self._browser: Browser | None = None
86
- self._page: Page | None = None
87
- self._started = False
88
-
89
- def start(self):
90
- """Start the browser and establish connection."""
91
- if self._started:
92
- return
93
-
94
- # Start Playwright
95
- self._playwright = sync_playwright().start()
96
-
97
- # Start browser on the Fleet instance
98
- print("Starting browser...")
99
- self.env.browser().start()
100
- cdp = self.env.browser().describe()
101
-
102
- # Connect to browser
103
- self._browser = self._playwright.chromium.connect_over_cdp(
104
- cdp.cdp_browser_url
105
- )
106
- self._page = self._browser.contexts[0].pages[0]
107
- self._page.set_viewport_size(
108
- {"width": self.display_width, "height": self.display_height}
109
- )
110
-
111
- self._started = True
112
- print(f"Track agent: {cdp.cdp_devtools_url}")
113
-
114
- def close(self):
115
- """Close the browser connection."""
116
- if self._playwright:
117
- self._playwright.stop()
118
- self._playwright = None
119
- self._browser = None
120
- self._page = None
121
- self._started = False
122
-
123
- def _ensure_started(self):
124
- """Ensure browser is started before operations."""
125
- if not self._started:
126
- raise RuntimeError("Browser not started. Call browser.start() first.")
127
-
128
- @property
129
- def openai_cua_tool(self) -> Dict[str, Any]:
130
- """
131
- Tool definition for OpenAI computer use API.
132
-
133
- Returns:
134
- Tool definition dict for use with OpenAI responses API
135
- """
136
- return {
137
- "type": "computer_use_preview",
138
- "display_width": self.display_width,
139
- "display_height": self.display_height,
140
- "environment": "browser",
141
- }
142
-
143
- def screenshot(self) -> str:
144
- """
145
- Take a screenshot and return base64 encoded string.
146
-
147
- Returns:
148
- Base64 encoded PNG screenshot
149
- """
150
- self._ensure_started()
151
-
152
- png_bytes = self._page.screenshot(full_page=False)
153
- return base64.b64encode(png_bytes).decode("utf-8")
154
-
155
- def get_current_url(self) -> str:
156
- """Get the current page URL."""
157
- self._ensure_started()
158
- return self._page.url
159
-
160
- def execute_computer_action(self, action: Dict[str, Any]) -> Dict[str, Any]:
161
- """
162
- Execute a computer action and return the result for OpenAI API.
163
-
164
- Args:
165
- action: Computer action dict from OpenAI response
166
-
167
- Returns:
168
- Result dict for computer_call_output
169
- """
170
- self._ensure_started()
171
-
172
- action_type = action["type"]
173
- action_args = {k: v for k, v in action.items() if k != "type"}
174
-
175
- print(f"Executing: {action_type}({action_args})")
176
-
177
- # Execute the action
178
- if hasattr(self, f"_{action_type}"):
179
- method = getattr(self, f"_{action_type}")
180
- method(**action_args)
181
- else:
182
- raise ValueError(f"Unsupported action type: {action_type}")
183
-
184
- # Take screenshot after action
185
- screenshot_base64 = self.screenshot()
186
-
187
- return {
188
- "type": "input_image",
189
- "image_url": f"data:image/png;base64,{screenshot_base64}",
190
- "current_url": self.get_current_url(),
191
- }
192
-
193
- # Computer action implementations
194
- def _click(self, x: int, y: int, button: str = "left") -> None:
195
- """Click at coordinates."""
196
- self._ensure_started()
197
- self._page.mouse.click(x, y, button=button)
198
-
199
- def _double_click(self, x: int, y: int) -> None:
200
- """Double-click at coordinates."""
201
- self._ensure_started()
202
- self._page.mouse.dblclick(x, y)
203
-
204
- def _scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None:
205
- """Scroll from coordinates."""
206
- self._ensure_started()
207
- self._page.mouse.move(x, y)
208
- self._page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})")
209
-
210
- def _type(self, text: str) -> None:
211
- """Type text."""
212
- self._ensure_started()
213
- self._page.keyboard.type(text)
214
-
215
- def _keypress(self, keys: List[str]) -> None:
216
- """Press key combination."""
217
- self._ensure_started()
218
- mapped_keys = [CUA_KEY_TO_PLAYWRIGHT_KEY.get(key.lower(), key) for key in keys]
219
- for key in mapped_keys:
220
- self._page.keyboard.down(key)
221
- for key in reversed(mapped_keys):
222
- self._page.keyboard.up(key)
223
-
224
- def _move(self, x: int, y: int) -> None:
225
- """Move mouse to coordinates."""
226
- self._ensure_started()
227
- self._page.mouse.move(x, y)
228
-
229
- def _drag(self, path: List[Dict[str, int]]) -> None:
230
- """Drag mouse along path."""
231
- self._ensure_started()
232
- if not path:
233
- return
234
- self._page.mouse.move(path[0]["x"], path[0]["y"])
235
- self._page.mouse.down()
236
- for point in path[1:]:
237
- self._page.mouse.move(point["x"], point["y"])
238
- self._page.mouse.up()
239
-
240
- def _wait(self, ms: int = 1000) -> None:
241
- """Wait for specified milliseconds."""
242
-
243
- time.sleep(ms / 1000)
244
-
245
- # Browser-specific actions
246
- def _goto(self, url: str) -> None:
247
- """Navigate to URL."""
248
- self._ensure_started()
249
- try:
250
- self._page.goto(url)
251
- except Exception as e:
252
- print(f"Error navigating to {url}: {e}")
253
-
254
- def _back(self) -> None:
255
- """Go back in browser history."""
256
- self._ensure_started()
257
- self._page.go_back()
258
-
259
- def _forward(self) -> None:
260
- """Go forward in browser history."""
261
- self._ensure_started()
262
- self._page.go_forward()
263
-
264
- def _refresh(self) -> None:
265
- """Refresh the page."""
266
- self._ensure_started()
267
- self._page.reload()
268
-
269
- # ------------------------------------------------------------------
270
- # Public aliases (no leading underscore) expected by the Agent &
271
- # OpenAI computer-use API. They forward directly to the underscored
272
- # implementations above so the external interface matches the older
273
- # BasePlaywrightComputer class.
274
- # ------------------------------------------------------------------
275
-
276
- # Mouse / keyboard actions
277
- click = _click
278
- double_click = _double_click
279
- scroll = _scroll
280
- type = _type # noqa: A003 – shadowing built-in for API compatibility
281
- keypress = _keypress
282
- move = _move
283
- drag = _drag
284
- wait = _wait
285
-
286
- # Browser navigation actions
287
- goto = _goto
288
- back = _back
289
- forward = _forward
290
- refresh = _refresh
@@ -1 +0,0 @@
1
- TASK_SUCCESSFUL_SCORE = 1
File without changes
File without changes
File without changes