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.
- examples/diff_example.py +161 -0
- examples/dsl_example.py +50 -1
- examples/example.py +1 -1
- examples/example_action_log.py +28 -0
- examples/example_mcp_anthropic.py +77 -0
- examples/example_mcp_openai.py +27 -0
- examples/example_sync.py +1 -1
- examples/example_task.py +199 -0
- examples/example_verifier.py +71 -0
- examples/query_builder_example.py +117 -0
- fleet/__init__.py +51 -40
- fleet/_async/base.py +15 -2
- fleet/_async/client.py +141 -23
- fleet/_async/env/client.py +5 -5
- fleet/_async/instance/__init__.py +2 -3
- fleet/_async/instance/base.py +5 -2
- fleet/_async/instance/client.py +5 -4
- fleet/_async/playwright.py +2 -2
- fleet/_async/resources/base.py +1 -1
- fleet/_async/resources/browser.py +1 -1
- fleet/_async/resources/sqlite.py +656 -2
- fleet/_async/tasks.py +44 -0
- fleet/_async/verifiers/__init__.py +17 -0
- fleet/_async/verifiers/bundler.py +699 -0
- fleet/_async/verifiers/verifier.py +301 -0
- fleet/base.py +14 -1
- fleet/client.py +650 -17
- fleet/config.py +2 -1
- fleet/instance/__init__.py +1 -2
- fleet/instance/base.py +5 -2
- fleet/instance/client.py +16 -6
- fleet/models.py +171 -4
- fleet/resources/browser.py +7 -8
- fleet/resources/mcp.py +60 -0
- fleet/resources/sqlite.py +654 -0
- fleet/tasks.py +44 -0
- fleet/types.py +18 -0
- fleet/verifiers/__init__.py +11 -5
- fleet/verifiers/bundler.py +699 -0
- fleet/verifiers/decorator.py +103 -0
- fleet/verifiers/verifier.py +301 -0
- {fleet_python-0.2.12.dist-info → fleet_python-0.2.15.dist-info}/METADATA +3 -42
- fleet_python-0.2.15.dist-info/RECORD +69 -0
- scripts/fix_sync_imports.py +30 -12
- fleet/_async/config.py +0 -8
- fleet/_async/instance/models.py +0 -141
- fleet/_async/models.py +0 -109
- fleet_python-0.2.12.dist-info/RECORD +0 -55
- {fleet_python-0.2.12.dist-info → fleet_python-0.2.15.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.12.dist-info → fleet_python-0.2.15.dist-info}/licenses/LICENSE +0 -0
- {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
|
+
}
|
fleet/instance/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Fleet SDK Environment Module."""
|
|
2
2
|
|
|
3
|
-
from .client import InstanceClient
|
|
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=
|
|
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
|
|
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-
|
|
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
|
|
fleet/resources/browser.py
CHANGED
|
@@ -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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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 []
|