fleet-python 0.2.12__py3-none-any.whl → 0.2.15__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.

Potentially problematic release.


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

Files changed (51) hide show
  1. examples/diff_example.py +161 -0
  2. examples/dsl_example.py +50 -1
  3. examples/example.py +1 -1
  4. examples/example_action_log.py +28 -0
  5. examples/example_mcp_anthropic.py +77 -0
  6. examples/example_mcp_openai.py +27 -0
  7. examples/example_sync.py +1 -1
  8. examples/example_task.py +199 -0
  9. examples/example_verifier.py +71 -0
  10. examples/query_builder_example.py +117 -0
  11. fleet/__init__.py +51 -40
  12. fleet/_async/base.py +15 -2
  13. fleet/_async/client.py +141 -23
  14. fleet/_async/env/client.py +5 -5
  15. fleet/_async/instance/__init__.py +2 -3
  16. fleet/_async/instance/base.py +5 -2
  17. fleet/_async/instance/client.py +5 -4
  18. fleet/_async/playwright.py +2 -2
  19. fleet/_async/resources/base.py +1 -1
  20. fleet/_async/resources/browser.py +1 -1
  21. fleet/_async/resources/sqlite.py +656 -2
  22. fleet/_async/tasks.py +44 -0
  23. fleet/_async/verifiers/__init__.py +17 -0
  24. fleet/_async/verifiers/bundler.py +699 -0
  25. fleet/_async/verifiers/verifier.py +301 -0
  26. fleet/base.py +14 -1
  27. fleet/client.py +650 -17
  28. fleet/config.py +2 -1
  29. fleet/instance/__init__.py +1 -2
  30. fleet/instance/base.py +5 -2
  31. fleet/instance/client.py +16 -6
  32. fleet/models.py +171 -4
  33. fleet/resources/browser.py +7 -8
  34. fleet/resources/mcp.py +60 -0
  35. fleet/resources/sqlite.py +654 -0
  36. fleet/tasks.py +44 -0
  37. fleet/types.py +18 -0
  38. fleet/verifiers/__init__.py +11 -5
  39. fleet/verifiers/bundler.py +699 -0
  40. fleet/verifiers/decorator.py +103 -0
  41. fleet/verifiers/verifier.py +301 -0
  42. {fleet_python-0.2.12.dist-info → fleet_python-0.2.15.dist-info}/METADATA +3 -42
  43. fleet_python-0.2.15.dist-info/RECORD +69 -0
  44. scripts/fix_sync_imports.py +30 -12
  45. fleet/_async/config.py +0 -8
  46. fleet/_async/instance/models.py +0 -141
  47. fleet/_async/models.py +0 -109
  48. fleet_python-0.2.12.dist-info/RECORD +0 -55
  49. {fleet_python-0.2.12.dist-info → fleet_python-0.2.15.dist-info}/WHEEL +0 -0
  50. {fleet_python-0.2.12.dist-info → fleet_python-0.2.15.dist-info}/licenses/LICENSE +0 -0
  51. {fleet_python-0.2.12.dist-info → fleet_python-0.2.15.dist-info}/top_level.txt +0 -0
fleet/config.py CHANGED
@@ -1,8 +1,9 @@
1
1
  DEFAULT_MAX_RETRIES = 5
2
+ DEFAULT_TIMEOUT = 300.0
2
3
 
3
4
  GLOBAL_BASE_URL = "https://orchestrator.fleetai.com"
4
5
  REGION_BASE_URL = {
5
6
  "us-west-1": "https://us-west-1.fleetai.com",
6
7
  "us-east-1": "https://us-east-1.fleetai.com",
7
8
  "eu-west-2": "https://eu-west-2.fleetai.com",
8
- }
9
+ }
@@ -1,6 +1,6 @@
1
1
  """Fleet SDK Environment Module."""
2
2
 
3
- from .client import InstanceClient, ValidatorType
3
+ from .client import InstanceClient
4
4
  from .models import (
5
5
  ResetRequest,
6
6
  ResetResponse,
@@ -12,7 +12,6 @@ from .models import (
12
12
  )
13
13
 
14
14
  __all__ = [
15
- "ValidatorType",
16
15
  "InstanceClient",
17
16
  "ResetRequest",
18
17
  "ResetResponse",
fleet/instance/base.py CHANGED
@@ -3,7 +3,10 @@ import httpx_retries
3
3
  from typing import Dict, Any, Optional
4
4
 
5
5
 
6
- def default_httpx_client(max_retries: int) -> httpx.Client:
6
+ def default_httpx_client(max_retries: int, timeout: float) -> httpx.Client:
7
+ if max_retries <= 0:
8
+ return httpx.Client(timeout=timeout)
9
+
7
10
  policy = httpx_retries.Retry(
8
11
  total=max_retries,
9
12
  status_forcelist=[
@@ -21,7 +24,7 @@ def default_httpx_client(max_retries: int) -> httpx.Client:
21
24
  transport=httpx.HTTPTransport(retries=2), retry=policy
22
25
  )
23
26
  return httpx.Client(
24
- timeout=300.0,
27
+ timeout=timeout,
25
28
  transport=retry,
26
29
  )
27
30
 
fleet/instance/client.py CHANGED
@@ -10,11 +10,12 @@ from urllib.parse import urlparse
10
10
  from ..resources.sqlite import SQLiteResource
11
11
  from ..resources.browser import BrowserResource
12
12
  from ..resources.base import Resource
13
+ from ..resources.mcp import MCPResource
13
14
 
14
15
  from ..verifiers import DatabaseSnapshot
15
16
 
16
17
  from ..exceptions import FleetEnvironmentError
17
- from ..config import DEFAULT_MAX_RETRIES
18
+ from ..config import DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT
18
19
 
19
20
  from .base import SyncWrapper, default_httpx_client
20
21
  from .models import (
@@ -46,12 +47,15 @@ class InstanceClient:
46
47
  def __init__(
47
48
  self,
48
49
  url: str,
50
+ env_key: str,
49
51
  httpx_client: Optional[httpx.Client] = None,
50
52
  ):
51
53
  self.base_url = url
54
+ self._env_key = env_key
52
55
  self.client = SyncWrapper(
53
56
  url=self.base_url,
54
- httpx_client=httpx_client or default_httpx_client(DEFAULT_MAX_RETRIES),
57
+ httpx_client=httpx_client
58
+ or default_httpx_client(DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT),
55
59
  )
56
60
  self._resources: Optional[List[ResourceModel]] = None
57
61
  self._resources_state: Dict[str, Dict[str, Resource]] = {
@@ -61,9 +65,7 @@ class InstanceClient:
61
65
  def load(self) -> None:
62
66
  self._load_resources()
63
67
 
64
- def reset(
65
- self, reset_request: Optional[ResetRequest] = None
66
- ) -> ResetResponse:
68
+ def reset(self, reset_request: Optional[ResetRequest] = None) -> ResetResponse:
67
69
  response = self.client.request(
68
70
  "POST", "/reset", json=reset_request.model_dump() if reset_request else None
69
71
  )
@@ -87,6 +89,14 @@ class InstanceClient:
87
89
  self._resources_state[ResourceType.db.value][name], self.client
88
90
  )
89
91
 
92
+ def mcp(self) -> MCPResource:
93
+ import time
94
+ time.sleep(5)
95
+ mcp_url = f"{self.base_url}/mcp"
96
+ if mcp_url.endswith("/api/v1/env/mcp"):
97
+ mcp_url = mcp_url.replace("/api/v1/env/mcp", "/mcp")
98
+ return MCPResource(mcp_url, self._env_key)
99
+
90
100
  def browser(self, name: str) -> BrowserResource:
91
101
  return BrowserResource(
92
102
  self._resources_state[ResourceType.cdp.value][name], self.client
@@ -136,7 +146,7 @@ class InstanceClient:
136
146
 
137
147
  self._resources = [ResourceModel(**resource) for resource in resources_list]
138
148
  for resource in self._resources:
139
- if resource.type not in self._resources_state:
149
+ if resource.type.value not in self._resources_state:
140
150
  self._resources_state[resource.type.value] = {}
141
151
  self._resources_state[resource.type.value][resource.name] = (
142
152
  RESOURCE_TYPES[resource.type](resource, self.client)
fleet/models.py CHANGED
@@ -1,14 +1,83 @@
1
1
  # generated by datamodel-codegen:
2
- # filename: openapi.json
3
- # timestamp: 2025-07-08T18:21:47+00:00
2
+ # filename: http://0.0.0.0:8000/openapi.json
3
+ # timestamp: 2025-07-20T02:00:30+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from enum import Enum
8
- from typing import Dict, List, Optional, Union
8
+ from typing import Any, Dict, List, Optional, Union, Tuple
9
9
 
10
- from pydantic import BaseModel, Field
10
+ from pydantic import BaseModel, Field, conint
11
11
 
12
+ class ToolLogEntry(BaseModel):
13
+ id: int
14
+ timestamp: str
15
+ tool_name: str
16
+ action: str
17
+ parameters: Dict[str, Any]
18
+ result: Optional[Dict[str, Any]] = None
19
+ success: bool = True
20
+ error: Optional[str] = None
21
+ duration_ms: Optional[int] = None
22
+ session_id: Optional[str] = None
23
+ user_agent: Optional[str] = None
24
+
25
+
26
+ class ActionLogEntry(BaseModel):
27
+ id: int
28
+ timestamp: str
29
+ action_type: str
30
+ payload: str
31
+ sql: Optional[str] = None
32
+ args: Optional[str] = None
33
+ path: Optional[str] = None
34
+
35
+
36
+ class EnvironmentSnapshot(BaseModel):
37
+ env_key: str
38
+ instance_id: str
39
+ timestamp: str
40
+ session_id: str
41
+ tool_logs: List[ToolLogEntry]
42
+ action_logs: List[ActionLogEntry]
43
+ page_url: str
44
+ viewport_size: Tuple[int, int]
45
+ metadata: Dict[str, Any] = {}
46
+
47
+
48
+ class SnapshotValidation(BaseModel):
49
+ success: bool
50
+ page_match: bool
51
+ action_log_match: bool
52
+ discrepancies: List[str] = []
53
+ message: str
54
+
55
+
56
+ class ToolLogResponse(BaseModel):
57
+ success: bool
58
+ log_id: Optional[int] = None
59
+ message: str
60
+
61
+
62
+ class ToolSessionStartRequest(BaseModel):
63
+ session_id: str
64
+ metadata: Optional[Dict[str, Any]] = None
65
+
66
+
67
+ class ToolSessionStartResponse(BaseModel):
68
+ success: bool
69
+ session_id: str
70
+ message: str
71
+
72
+
73
+ class ToolLogQueryRequest(BaseModel):
74
+ tool_name: Optional[str] = None
75
+ action: Optional[str] = None
76
+ session_id: Optional[str] = None
77
+ start_time: Optional[str] = None
78
+ end_time: Optional[str] = None
79
+ limit: Optional[int] = 10000
80
+ offset: int = 0
12
81
 
13
82
  class Environment(BaseModel):
14
83
  env_key: str = Field(..., title="Env Key")
@@ -68,6 +137,104 @@ class ValidationError(BaseModel):
68
137
  type: str = Field(..., title="Error Type")
69
138
 
70
139
 
140
+ class VerifiersCheckResponse(BaseModel):
141
+ key: Optional[str] = Field(
142
+ None, description="Verifier artifact key", title="Key"
143
+ )
144
+ version: Optional[int] = Field(
145
+ None, description="Version of the verifier artifact", title="Version"
146
+ )
147
+ display_src: Optional[str] = Field(
148
+ None, description="Display source code of the verifier", title="Display Src"
149
+ )
150
+ created_at: Optional[str] = Field(
151
+ None, description="Creation timestamp", title="Created At"
152
+ )
153
+ created_by: Optional[str] = Field(
154
+ None, description="Creator of the verifier", title="Created By"
155
+ )
156
+ comment: Optional[str] = Field(
157
+ None, description="Comment about the verifier", title="Comment"
158
+ )
159
+ success: bool = Field(
160
+ ..., description="Whether the verification was successful", title="Success"
161
+ )
162
+
163
+
164
+ class VerifiersExecuteRequest(BaseModel):
165
+ sha256: str = Field(..., description="SHA256 hash of the function", title="SHA256")
166
+ key: Optional[str] = Field(None, description="Verifier key", title="Key")
167
+ bundle: Optional[str] = Field(None, description="Base64 encoded bundle data", title="Bundle")
168
+ args: Optional[str] = Field(None, description="Base64 encoded arguments", title="Args")
169
+ function_name: Optional[str] = Field(
170
+ "verify", description="Name of the function to execute", title="Function Name"
171
+ )
172
+ timeout: Optional[conint(ge=1, le=300)] = Field(
173
+ 60, description="Execution timeout in seconds", title="Timeout"
174
+ )
175
+ region: Optional[str] = Field(
176
+ None, description="AWS region for execution", title="Region"
177
+ )
178
+
179
+
180
+ class VerifiersExecuteResponse(BaseModel):
181
+ key: Optional[str] = Field(
182
+ None, description="Key of the verifier artifact", title="Key"
183
+ )
184
+ version: Optional[int] = Field(
185
+ None, description="Version of the verifier artifact", title="Version"
186
+ )
187
+ display_src: Optional[str] = Field(
188
+ None, description="Display source code of the verifier", title="Display Src"
189
+ )
190
+ created_at: Optional[str] = Field(
191
+ None, description="Creation timestamp", title="Created At"
192
+ )
193
+ comment: Optional[str] = Field(
194
+ None, description="Comment about the verifier", title="Comment"
195
+ )
196
+ success: bool = Field(
197
+ ..., description="Whether the verification was successful", title="Success"
198
+ )
199
+ result: Any = Field(
200
+ ..., description="The return value of the function", title="Result"
201
+ )
202
+ error: Optional[Dict[str, Any]] = Field(
203
+ None, description="Error details if verification failed", title="Error"
204
+ )
205
+ execution_time_ms: int = Field(
206
+ ..., description="Execution time in milliseconds", title="Execution Time Ms"
207
+ )
208
+ bundle_cache_hit: bool = Field(
209
+ False,
210
+ description="Whether the bundle was already cached",
211
+ title="Bundle Cache Hit",
212
+ )
213
+
214
+
215
+ class VerificationResponse(BaseModel):
216
+ success: bool = Field(
217
+ ..., description="Whether the verification was successful", title="Success"
218
+ )
219
+ result: Optional[Any] = Field(
220
+ None, description="The return value of the function", title="Result"
221
+ )
222
+ error: Optional[Dict[str, Any]] = Field(
223
+ None, description="Error details if verification failed", title="Error"
224
+ )
225
+ execution_time_ms: int = Field(
226
+ ..., description="Execution time in milliseconds", title="Execution Time Ms"
227
+ )
228
+ bundle_cache_hit: Optional[bool] = Field(
229
+ False,
230
+ description="Whether the bundle was already cached",
231
+ title="Bundle Cache Hit",
232
+ )
233
+ meta: Optional[Dict[str, Any]] = Field(
234
+ None, description="Metadata about the execution", title="Meta"
235
+ )
236
+
237
+
71
238
  class HTTPValidationError(BaseModel):
72
239
  detail: Optional[List[ValidationError]] = Field(None, title="Detail")
73
240
 
@@ -17,16 +17,15 @@ class BrowserResource(Resource):
17
17
  def __init__(self, resource: ResourceModel, client: "SyncWrapper"):
18
18
  super().__init__(resource)
19
19
  self.client = client
20
-
20
+
21
21
  def start(self, width: int = 1920, height: int = 1080) -> CDPDescribeResponse:
22
- response = self.client.request(
23
- "POST",
24
- "/resources/cdp/start",
25
- json=ChromeStartRequest(resolution=f"{width},{height}").model_dump(),
26
- )
27
- ChromeStartResponse(**response.json())
22
+ """Start browser and return CDP information."""
23
+ request = ChromeStartRequest(resolution=f"{width}x{height}")
24
+ response = self.client.request("POST", "/resources/cdp/start", json=request.model_dump())
25
+
26
+ # After starting, get the CDP information via describe
28
27
  return self.describe()
29
-
28
+
30
29
  def describe(self) -> CDPDescribeResponse:
31
30
  response = self.client.request("GET", "/resources/cdp/describe")
32
31
  if response.status_code != 200:
fleet/resources/mcp.py ADDED
@@ -0,0 +1,60 @@
1
+ from typing import Dict
2
+ import aiohttp
3
+ import json
4
+
5
+
6
+ class MCPResource:
7
+ def __init__(self, url: str, env_key: str):
8
+ self.url = url
9
+ self._env_key = env_key
10
+
11
+ def openai(self) -> Dict[str, str]:
12
+ return {
13
+ "type": "mcp",
14
+ "server_label": self._env_key,
15
+ "server_url": self.url,
16
+ "require_approval": "never",
17
+ }
18
+
19
+ def anthropic(self) -> Dict[str, str]:
20
+ return {
21
+ "type": "url",
22
+ "url": self.url,
23
+ "name": self._env_key,
24
+ }
25
+
26
+ async def list_tools(self):
27
+ """
28
+ Make an async request to list available tools from the MCP endpoint.
29
+
30
+ Returns:
31
+ List of available tools with name, description, and input_schema
32
+ """
33
+ async with aiohttp.ClientSession() as session:
34
+ payload = {
35
+ "jsonrpc": "2.0",
36
+ "method": "tools/list",
37
+ "params": {},
38
+ "id": 2
39
+ }
40
+
41
+ async with session.post(self.url, json=payload) as response:
42
+ data = await response.json()
43
+
44
+ # Extract tools from the response
45
+ if "result" in data and "tools" in data["result"]:
46
+ tools = data["result"]["tools"]
47
+
48
+ available_tools = [
49
+ {
50
+ "name": tool.get("name"),
51
+ "description": tool.get("description"),
52
+ "input_schema": tool.get("inputSchema"),
53
+ }
54
+ for tool in tools
55
+ ]
56
+
57
+ return available_tools
58
+ else:
59
+ # Handle error or empty response
60
+ return []