fleet-python 0.2.13__py3-none-any.whl → 0.2.16__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_action_log.py +28 -0
- examples/example_mcp_anthropic.py +77 -0
- examples/example_mcp_openai.py +27 -0
- 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 +14 -1
- fleet/_async/client.py +155 -19
- fleet/_async/env/client.py +4 -4
- fleet/_async/instance/__init__.py +1 -2
- fleet/_async/instance/client.py +3 -2
- fleet/_async/playwright.py +2 -2
- fleet/_async/resources/sqlite.py +654 -0
- 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 +664 -12
- fleet/config.py +1 -1
- fleet/instance/__init__.py +1 -2
- fleet/instance/client.py +15 -5
- 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.13.dist-info → fleet_python-0.2.16.dist-info}/METADATA +3 -42
- fleet_python-0.2.16.dist-info/RECORD +69 -0
- fleet_python-0.2.13.dist-info/RECORD +0 -52
- {fleet_python-0.2.13.dist-info → fleet_python-0.2.16.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.13.dist-info → fleet_python-0.2.16.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.13.dist-info → fleet_python-0.2.16.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Fleet SDK Verifier Decorator - Async Version.
|
|
2
|
+
|
|
3
|
+
Provides a @verifier decorator that can wrap any sync function to support
|
|
4
|
+
both local execution and remote execution via .remote() method.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import inspect
|
|
8
|
+
import functools
|
|
9
|
+
import traceback
|
|
10
|
+
from typing import Any, Callable, Dict, Optional, TypeVar, Union
|
|
11
|
+
import uuid
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
F = TypeVar('F', bound=Callable[..., Any])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SyncVerifierFunction:
|
|
23
|
+
"""Wrapper for a verified function that supports local execution with env-first pattern."""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
func: F,
|
|
28
|
+
key: str,
|
|
29
|
+
verifier_id: str
|
|
30
|
+
):
|
|
31
|
+
self.func = func
|
|
32
|
+
self.key = key
|
|
33
|
+
self.name = key # Keep name for backward compatibility
|
|
34
|
+
self.verifier_id = verifier_id
|
|
35
|
+
|
|
36
|
+
# Copy function metadata
|
|
37
|
+
functools.update_wrapper(self, func)
|
|
38
|
+
|
|
39
|
+
def __call__(self, env, *args, **kwargs) -> float:
|
|
40
|
+
"""Local execution of the verifier function with env as first parameter."""
|
|
41
|
+
try:
|
|
42
|
+
result = self.func(env, *args, **kwargs)
|
|
43
|
+
|
|
44
|
+
# Handle different return types
|
|
45
|
+
if isinstance(result, (int, float)):
|
|
46
|
+
# Direct score return
|
|
47
|
+
return float(result)
|
|
48
|
+
elif isinstance(result, dict) and "score" in result:
|
|
49
|
+
return float(result["score"])
|
|
50
|
+
else:
|
|
51
|
+
# Try to extract score from object attributes
|
|
52
|
+
if hasattr(result, 'score'):
|
|
53
|
+
return float(result.score)
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError(f"Verifier function must return a score (number). Got {type(result)}")
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.error(f"Error in verifier {self.key}: {e}")
|
|
59
|
+
# Return error score 0
|
|
60
|
+
return 0.0
|
|
61
|
+
|
|
62
|
+
def verifier(
|
|
63
|
+
key: Optional[str] = None,
|
|
64
|
+
verifier_id: Optional[str] = None
|
|
65
|
+
) -> Callable[[F], SyncVerifierFunction]:
|
|
66
|
+
"""
|
|
67
|
+
Decorator to create a verifier function with env-first pattern.
|
|
68
|
+
|
|
69
|
+
The decorated function must take 'env' as its first parameter, making it explicit
|
|
70
|
+
that verifiers operate within an environment context. This makes verifiers reusable
|
|
71
|
+
across different environments.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
key: Optional key for the verifier. Defaults to function name.
|
|
75
|
+
verifier_id: Optional unique ID for the verifier. Defaults to generated UUID.
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
@verifier(key="test_database_state")
|
|
79
|
+
def check_user_count(env, expected_count: int) -> float:
|
|
80
|
+
db = env.db()
|
|
81
|
+
result = db.query("SELECT COUNT(*) FROM users")
|
|
82
|
+
actual_count = result.rows[0][0]
|
|
83
|
+
return 1.0 if actual_count >= expected_count else 0.0
|
|
84
|
+
|
|
85
|
+
# Usage with different environments
|
|
86
|
+
env1 = flt.env.make("fira")
|
|
87
|
+
env2 = flt.env.make("another_env")
|
|
88
|
+
|
|
89
|
+
# Local execution
|
|
90
|
+
result = await check_user_count(env1, 5)
|
|
91
|
+
result = await check_user_count(env2, 5) # Same verifier, different env
|
|
92
|
+
"""
|
|
93
|
+
def decorator(func: F) -> SyncVerifierFunction:
|
|
94
|
+
verifier_key = key or func.__name__
|
|
95
|
+
verifier_uuid = verifier_id or str(uuid.uuid4())
|
|
96
|
+
|
|
97
|
+
return SyncVerifierFunction(
|
|
98
|
+
func,
|
|
99
|
+
verifier_key,
|
|
100
|
+
verifier_uuid
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return decorator
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""Fleet SDK Verifier - Async Version.
|
|
2
|
+
|
|
3
|
+
Provides a @verifier decorator that can wrap any sync function to support
|
|
4
|
+
both local execution and remote execution via .remote() method.
|
|
5
|
+
|
|
6
|
+
The decorated function must take 'env' as its first parameter, making it explicit
|
|
7
|
+
that verifiers operate within an environment context.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import functools
|
|
11
|
+
import uuid
|
|
12
|
+
import asyncio
|
|
13
|
+
import logging
|
|
14
|
+
import hashlib
|
|
15
|
+
import inspect
|
|
16
|
+
from typing import Any, Callable, Dict, Optional, List, TypeVar, Set, Union
|
|
17
|
+
|
|
18
|
+
from .bundler import FunctionBundler
|
|
19
|
+
from ..client import Environment
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
F = TypeVar('F', bound=Callable[..., Any])
|
|
24
|
+
|
|
25
|
+
# Global cache to track which bundle SHAs have been uploaded to S3
|
|
26
|
+
_uploaded_bundle_shas: Set[str] = set()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@functools.lru_cache(maxsize=128)
|
|
30
|
+
def _get_bundle_sha(bundle_data: bytes) -> str:
|
|
31
|
+
"""Calculate SHA256 hash of bundle data with LRU caching."""
|
|
32
|
+
return hashlib.sha256(bundle_data).hexdigest()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SyncVerifierFunction:
|
|
36
|
+
"""Wrapper for a verified function that supports local execution with env-first pattern."""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
func: F,
|
|
41
|
+
key: str,
|
|
42
|
+
extra_requirements: Optional[List[str]] = None,
|
|
43
|
+
verifier_id: Optional[str] = None
|
|
44
|
+
):
|
|
45
|
+
self.func = func
|
|
46
|
+
self.key = key
|
|
47
|
+
self.name = key # Keep name for backward compatibility
|
|
48
|
+
self.verifier_id = verifier_id or str(uuid.uuid4())
|
|
49
|
+
self.extra_requirements = extra_requirements or []
|
|
50
|
+
self._bundler = FunctionBundler()
|
|
51
|
+
self._bundle_sha: Optional[str] = None # Cached bundle SHA
|
|
52
|
+
self._bundle_data: Optional[bytes] = None # Cached bundle data
|
|
53
|
+
self._is_async = asyncio.iscoroutinefunction(func)
|
|
54
|
+
|
|
55
|
+
# Copy function metadata
|
|
56
|
+
functools.update_wrapper(self, func)
|
|
57
|
+
|
|
58
|
+
def _get_or_create_bundle(self) -> tuple[bytes, str]:
|
|
59
|
+
"""Get or create bundle data and return (bundle_data, sha)."""
|
|
60
|
+
if self._bundle_data is None or self._bundle_sha is None:
|
|
61
|
+
# Create bundle and cache it
|
|
62
|
+
self._bundle_data = self._bundler.create_bundle(
|
|
63
|
+
self.func,
|
|
64
|
+
self.extra_requirements,
|
|
65
|
+
self.verifier_id
|
|
66
|
+
)
|
|
67
|
+
self._bundle_sha = _get_bundle_sha(self._bundle_data)
|
|
68
|
+
logger.debug(f"Created bundle for {self.name} with SHA: {self._bundle_sha}")
|
|
69
|
+
|
|
70
|
+
return self._bundle_data, self._bundle_sha
|
|
71
|
+
|
|
72
|
+
def _check_bundle_status(self, env: Environment) -> tuple[str, bool]:
|
|
73
|
+
"""Check if bundle needs to be uploaded and return (sha, needs_upload)."""
|
|
74
|
+
bundle_data, bundle_sha = self._get_or_create_bundle()
|
|
75
|
+
|
|
76
|
+
# 1. Check local process cache first
|
|
77
|
+
if bundle_sha in _uploaded_bundle_shas:
|
|
78
|
+
logger.debug(f"Bundle {bundle_sha[:8]}... found in local cache")
|
|
79
|
+
return bundle_sha, False # Already uploaded, no upload needed
|
|
80
|
+
|
|
81
|
+
# 2. Check if bundle exists on server (pseudocode)
|
|
82
|
+
# TODO: Add endpoint to check if bundle SHA exists in S3
|
|
83
|
+
try:
|
|
84
|
+
exists = env.check_bundle_exists(bundle_sha)
|
|
85
|
+
if exists.success:
|
|
86
|
+
logger.info(f"Bundle {bundle_sha[:8]}... found on server, updating cache")
|
|
87
|
+
_uploaded_bundle_shas.add(bundle_sha)
|
|
88
|
+
return bundle_sha, False # Found on server, no upload needed
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.warning(f"Failed to check bundle existence: {e}")
|
|
91
|
+
|
|
92
|
+
# 3. Bundle not found locally or on server - upload needed
|
|
93
|
+
logger.info(f"Bundle {bundle_sha[:8]}... needs to be uploaded")
|
|
94
|
+
return bundle_sha, True # Upload needed
|
|
95
|
+
|
|
96
|
+
def __call__(self, env: Environment, *args, **kwargs) -> float:
|
|
97
|
+
"""Local execution of the verifier function with env as first parameter."""
|
|
98
|
+
try:
|
|
99
|
+
if self._is_async:
|
|
100
|
+
# For async functions, await the result
|
|
101
|
+
result = self.func(env, *args, **kwargs)
|
|
102
|
+
else:
|
|
103
|
+
# For sync functions, call directly
|
|
104
|
+
result = self.func(env, *args, **kwargs)
|
|
105
|
+
|
|
106
|
+
# Handle different return types
|
|
107
|
+
if isinstance(result, (int, float)):
|
|
108
|
+
# Direct score return
|
|
109
|
+
return float(result)
|
|
110
|
+
elif isinstance(result, dict) and "score" in result:
|
|
111
|
+
# For local execution, return the full dict if that's what the function returns
|
|
112
|
+
return result
|
|
113
|
+
else:
|
|
114
|
+
# Try to extract score from object attributes
|
|
115
|
+
if hasattr(result, 'score'):
|
|
116
|
+
return float(result.score)
|
|
117
|
+
else:
|
|
118
|
+
raise ValueError(f"Verifier function must return a score (number). Got {type(result)}")
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.error(f"Error in verifier {self.name}: {e}")
|
|
122
|
+
# Return error score 0
|
|
123
|
+
return 0.0
|
|
124
|
+
|
|
125
|
+
def remote(self, env: Environment, *args, **kwargs) -> float:
|
|
126
|
+
"""Remote execution of the verifier function with SHA-based bundle caching."""
|
|
127
|
+
if self._is_async:
|
|
128
|
+
raise NotImplementedError(
|
|
129
|
+
f"Async verifier '{self.name}' cannot be executed remotely. "
|
|
130
|
+
"The remote execution environment only supports synchronous functions. "
|
|
131
|
+
"Please provide a synchronous version of your verifier."
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
# Check if bundle needs to be uploaded
|
|
136
|
+
bundle_sha, needs_upload = self._check_bundle_status(env)
|
|
137
|
+
|
|
138
|
+
if needs_upload:
|
|
139
|
+
# Need to upload bundle to S3
|
|
140
|
+
logger.info(f"Uploading bundle {bundle_sha[:8]}... for {self.key}")
|
|
141
|
+
bundle_data, _ = self._get_or_create_bundle()
|
|
142
|
+
|
|
143
|
+
response = env.execute_verifier_remote(
|
|
144
|
+
bundle_data=bundle_data,
|
|
145
|
+
bundle_sha=bundle_sha,
|
|
146
|
+
key=self.key,
|
|
147
|
+
function_name=self.func.__name__,
|
|
148
|
+
args=args,
|
|
149
|
+
kwargs=kwargs,
|
|
150
|
+
needs_upload=True
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Mark as uploaded after successful execution
|
|
154
|
+
_uploaded_bundle_shas.add(bundle_sha)
|
|
155
|
+
logger.debug(f"Registered bundle {bundle_sha[:8]}... as uploaded")
|
|
156
|
+
|
|
157
|
+
else:
|
|
158
|
+
# Bundle already available - execute without upload
|
|
159
|
+
logger.info(f"Executing cached bundle {bundle_sha[:8]}... for {self.key}")
|
|
160
|
+
bundle_data, _ = self._get_or_create_bundle()
|
|
161
|
+
|
|
162
|
+
response = env.execute_verifier_remote(
|
|
163
|
+
bundle_data=bundle_data, # Still need bundle_data for local caching
|
|
164
|
+
bundle_sha=bundle_sha,
|
|
165
|
+
key=self.key,
|
|
166
|
+
function_name=self.func.__name__,
|
|
167
|
+
args=args,
|
|
168
|
+
kwargs=kwargs,
|
|
169
|
+
needs_upload=False # Don't upload, just execute
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Handle response
|
|
173
|
+
if response.success:
|
|
174
|
+
return self._process_result(response.result)
|
|
175
|
+
else:
|
|
176
|
+
self._raise_remote_error(response.error)
|
|
177
|
+
|
|
178
|
+
except Exception as e:
|
|
179
|
+
logger.error(f"Remote execution failed for {self.key}: {e}")
|
|
180
|
+
# If it's an HTTP error, try to get more details
|
|
181
|
+
if hasattr(e, 'response') and hasattr(e.response, 'text'):
|
|
182
|
+
logger.error(f"Server response: {e.response.text}")
|
|
183
|
+
raise
|
|
184
|
+
|
|
185
|
+
def _process_result(self, result: Any) -> float:
|
|
186
|
+
"""Process remote execution result, handling different return types."""
|
|
187
|
+
# Handle different return types like local execution
|
|
188
|
+
if isinstance(result, (int, float)):
|
|
189
|
+
return float(result)
|
|
190
|
+
elif isinstance(result, dict) and "score" in result:
|
|
191
|
+
return float(result["score"])
|
|
192
|
+
else:
|
|
193
|
+
# Try to extract score from object attributes
|
|
194
|
+
if hasattr(result, 'score'):
|
|
195
|
+
return float(result.score)
|
|
196
|
+
else:
|
|
197
|
+
# Best effort conversion
|
|
198
|
+
try:
|
|
199
|
+
return float(result)
|
|
200
|
+
except (ValueError, TypeError):
|
|
201
|
+
logger.warning(f"Could not convert result to float: {result}")
|
|
202
|
+
return 0.0
|
|
203
|
+
|
|
204
|
+
def _raise_remote_error(self, error_info: Dict[str, Any]):
|
|
205
|
+
"""Reconstruct remote error as local exception."""
|
|
206
|
+
error_type = error_info.get("type", "RuntimeError")
|
|
207
|
+
message = error_info.get("message", "Remote execution failed")
|
|
208
|
+
traceback_str = error_info.get("traceback", "")
|
|
209
|
+
|
|
210
|
+
# Create a rich error message
|
|
211
|
+
full_message = f"""
|
|
212
|
+
Remote verifier execution failed:
|
|
213
|
+
{message}
|
|
214
|
+
|
|
215
|
+
Remote traceback:
|
|
216
|
+
{traceback_str}
|
|
217
|
+
""".strip()
|
|
218
|
+
|
|
219
|
+
# Try to raise the original exception type
|
|
220
|
+
try:
|
|
221
|
+
exception_class = getattr(__builtins__, error_type, RuntimeError)
|
|
222
|
+
raise exception_class(full_message)
|
|
223
|
+
except:
|
|
224
|
+
raise RuntimeError(full_message)
|
|
225
|
+
|
|
226
|
+
def _get_env_id(self, env: Environment) -> str:
|
|
227
|
+
"""Generate a unique identifier for the environment."""
|
|
228
|
+
# Use instance base URL or similar unique identifier
|
|
229
|
+
if hasattr(env, 'instance') and hasattr(env.instance, 'base_url'):
|
|
230
|
+
return f"{env.instance.base_url}"
|
|
231
|
+
else:
|
|
232
|
+
# Fallback to object id (less ideal but works)
|
|
233
|
+
return str(id(env))
|
|
234
|
+
|
|
235
|
+
def _is_bundle_not_found_error(self, error: Exception) -> bool:
|
|
236
|
+
"""Check if the error indicates the bundle was not found on the server."""
|
|
237
|
+
# Check for common "bundle not found" error patterns
|
|
238
|
+
error_msg = str(error).lower()
|
|
239
|
+
return (
|
|
240
|
+
"bundle not found" in error_msg or
|
|
241
|
+
"verifier not found" in error_msg or
|
|
242
|
+
"404" in error_msg or
|
|
243
|
+
"not found" in error_msg
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def verifier(
|
|
248
|
+
key: Optional[str] = None,
|
|
249
|
+
extra_requirements: Optional[List[str]] = None
|
|
250
|
+
) -> Callable[[F], SyncVerifierFunction]:
|
|
251
|
+
"""
|
|
252
|
+
Decorator to create a verifier function with env-first pattern.
|
|
253
|
+
|
|
254
|
+
The decorated function must take 'env' as its first parameter, making it explicit
|
|
255
|
+
that verifiers operate within an environment context. This makes verifiers reusable
|
|
256
|
+
across different environments.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
key: Optional key for the verifier. Defaults to function name.
|
|
260
|
+
extra_requirements: Additional PyPI packages needed by the verifier.
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
# Synchronous verifier (works locally and remotely)
|
|
264
|
+
@verifier(key="check_user_count")
|
|
265
|
+
def check_user_count(env, expected_count: int) -> float:
|
|
266
|
+
db = env.db()
|
|
267
|
+
result = db.query("SELECT COUNT(*) FROM users")
|
|
268
|
+
actual_count = result.rows[0][0]
|
|
269
|
+
return 1.0 if actual_count >= expected_count else 0.0
|
|
270
|
+
|
|
271
|
+
# Async verifier (only works locally)
|
|
272
|
+
@verifier(key="check_user_async")
|
|
273
|
+
async def check_user_async(env, expected_count: int) -> float:
|
|
274
|
+
db = env.db()
|
|
275
|
+
result = await db.query("SELECT COUNT(*) FROM users")
|
|
276
|
+
actual_count = result.rows[0][0]
|
|
277
|
+
return 1.0 if actual_count >= expected_count else 0.0
|
|
278
|
+
|
|
279
|
+
# Usage
|
|
280
|
+
env = await flt.env.make_async("fira")
|
|
281
|
+
|
|
282
|
+
# Local execution
|
|
283
|
+
result = await check_user_count(env, 5) # sync verifier
|
|
284
|
+
result = await check_user_async(env, 5) # async verifier
|
|
285
|
+
|
|
286
|
+
# Remote execution
|
|
287
|
+
result = await check_user_count.remote(env, 5) # sync verifier works
|
|
288
|
+
# await check_user_async.remote(env, 5) # raises NotImplementedError
|
|
289
|
+
"""
|
|
290
|
+
def decorator(func: F) -> SyncVerifierFunction:
|
|
291
|
+
verifier_key = key or func.__name__
|
|
292
|
+
verifier_uuid = str(uuid.uuid4())
|
|
293
|
+
|
|
294
|
+
return SyncVerifierFunction(
|
|
295
|
+
func,
|
|
296
|
+
verifier_key,
|
|
297
|
+
extra_requirements,
|
|
298
|
+
verifier_uuid
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fleet-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.16
|
|
4
4
|
Summary: Python SDK for Fleet environments
|
|
5
5
|
Author-email: Fleet AI <nic@fleet.so>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -25,6 +25,8 @@ Requires-Dist: pydantic>=2.0.0
|
|
|
25
25
|
Requires-Dist: httpx>=0.27.0
|
|
26
26
|
Requires-Dist: httpx-retries>=0.4.0
|
|
27
27
|
Requires-Dist: typing-extensions>=4.0.0
|
|
28
|
+
Requires-Dist: modulegraph2>=0.2.0
|
|
29
|
+
Requires-Dist: cloudpickle==3.1.1
|
|
28
30
|
Provides-Extra: dev
|
|
29
31
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
30
32
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -114,44 +116,3 @@ running_instances = flt.env.list_instances(status_filter="running")
|
|
|
114
116
|
# List available environment types
|
|
115
117
|
available_envs = flt.env.list_envs()
|
|
116
118
|
```
|
|
117
|
-
|
|
118
|
-
## Development
|
|
119
|
-
|
|
120
|
-
### Code Structure
|
|
121
|
-
|
|
122
|
-
This SDK uses `unasync` to maintain both async and sync versions of the code from a single source:
|
|
123
|
-
|
|
124
|
-
- **`fleet/_async/`** - The source code (async version) - **EDIT THIS**
|
|
125
|
-
- **`fleet/`** - The generated sync version - **DO NOT EDIT** (auto-generated)
|
|
126
|
-
|
|
127
|
-
### Making Changes
|
|
128
|
-
|
|
129
|
-
⚠️ **Important**: All code modifications should be made in the `fleet/_async/` directory. The synchronous versions in `fleet/` are automatically generated.
|
|
130
|
-
|
|
131
|
-
1. Make your changes in `fleet/_async/`
|
|
132
|
-
2. Run `make unasync` to generate the sync versions
|
|
133
|
-
3. Test both async and sync versions
|
|
134
|
-
4. Commit all changes (both async source and generated sync files)
|
|
135
|
-
|
|
136
|
-
Example workflow:
|
|
137
|
-
```bash
|
|
138
|
-
# Edit the async source files
|
|
139
|
-
vim fleet/_async/client.py
|
|
140
|
-
|
|
141
|
-
# Generate sync versions
|
|
142
|
-
make unasync
|
|
143
|
-
|
|
144
|
-
# Run tests
|
|
145
|
-
python examples/examle.py
|
|
146
|
-
|
|
147
|
-
# Commit both source and generated files
|
|
148
|
-
git add fleet/_async/ fleet/
|
|
149
|
-
git commit -m "Add new feature"
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Why This Structure?
|
|
153
|
-
|
|
154
|
-
- Single source of truth for business logic
|
|
155
|
-
- Automatic sync/async API generation
|
|
156
|
-
- Consistent behavior between sync and async versions
|
|
157
|
-
- Easier maintenance and testing
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
examples/diff_example.py,sha256=zXEPFC-_Ap_uEihf0Shs5R7W7Pq1I5i_Y_KC-TYxyQw,5643
|
|
2
|
+
examples/dsl_example.py,sha256=tYHfSyuyhJUSL-nKDhMA4hgZgDVNQkF5RRJLof6H_WY,7163
|
|
3
|
+
examples/example.py,sha256=SAz5iPxca8n9MvnHVoWHI7GCaTgJwkegX78rMutF9gE,1082
|
|
4
|
+
examples/example_action_log.py,sha256=XrLqFNS8hD-K6nKVSohYi6oJkYotLCZJYJsGSbHuqEc,627
|
|
5
|
+
examples/example_client.py,sha256=70HKEhz_Gb79YcvKQauCPdS08AAwjo9unt2dh1jN_Oo,1030
|
|
6
|
+
examples/example_mcp_anthropic.py,sha256=iyO0OCem5SWEtJQGan6pBuGfN3LZXeeIN9DdMHw9f9M,2542
|
|
7
|
+
examples/example_mcp_openai.py,sha256=5BQzMWrNdtBrBCNcUYgU6qB9Aabc10UTMKvaFb4NerI,492
|
|
8
|
+
examples/example_sync.py,sha256=l8l-QOGTMnijiSK0mt5aofzXqbv-kKU3WCow4QS-7yg,976
|
|
9
|
+
examples/example_task.py,sha256=vXIUQHXRPM3lVEJOT7o0oVFZU8no3u_2BoSK1VRnHKA,6811
|
|
10
|
+
examples/example_verifier.py,sha256=PfGphI6gnfhSM_SRb4KyhHYddJmapk2hhRrSIaxAtr4,2025
|
|
11
|
+
examples/gemini_example.py,sha256=8mDXGGCaodyK6uXgpWhxi-DQ5OA-GFW12Gfwh0b3EDY,16177
|
|
12
|
+
examples/json_tasks_example.py,sha256=3ub2LLiC6hXpVEH1175QxCmfCD3Blfo3yoG85uV5CS8,5334
|
|
13
|
+
examples/nova_act_example.py,sha256=hZLpObVsiXKQzqGwMZVMf4A2j_z4TYE-YO9pgNmaKPk,836
|
|
14
|
+
examples/openai_example.py,sha256=I2vk_SJN9BkSRQCYRJfbtGJ-HJ2xzQj-lOjwqmLos5M,8234
|
|
15
|
+
examples/openai_simple_example.py,sha256=I42ytIwv0INgDO39pp1MOQSqsJz2YYH8GeNNBaUtq3A,1748
|
|
16
|
+
examples/query_builder_example.py,sha256=Q3lUBETHpu1aS2FXAO79ADYqCxOjMMMZNgCcFVapiII,3918
|
|
17
|
+
examples/quickstart.py,sha256=1VT39IRRhemsJgxi0O0gprdpcw7HB4pYO97GAYagIcg,3788
|
|
18
|
+
fleet/__init__.py,sha256=V2y7yMsloc56E4QdluakrVRiITQLmasxmAOBJ1oJ3cU,2365
|
|
19
|
+
fleet/base.py,sha256=0yYuMN0lBkrfTTZBt5NQp5112xWgziuWEk4GuHJB1wE,9189
|
|
20
|
+
fleet/client.py,sha256=_1SwqqmgrYkzqm50X27yUdpEqe--vqBPmTLOImg6sdA,28514
|
|
21
|
+
fleet/config.py,sha256=zd19st83NJdW9DdOq7Irpc0x-iUnMad0JOtAr_nD5DM,273
|
|
22
|
+
fleet/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
23
|
+
fleet/models.py,sha256=YMHFAgBF4OqoAOv-arHze3bPDDZ5DAzb3CXhVLF6pPw,9019
|
|
24
|
+
fleet/playwright.py,sha256=BmRvez5DUa0ttAQB084hPAyt9_8WxdzCGBGF-GZbTuQ,8593
|
|
25
|
+
fleet/tasks.py,sha256=w-0vVGfEuCWRHMEJ73SN141J7Lz2pD_0v-nNG4TyJTU,1645
|
|
26
|
+
fleet/types.py,sha256=eXeI8BFmiU5hln0PVvJbUZs7BSjl6wSqAtN9zaJT6yY,652
|
|
27
|
+
fleet/_async/__init__.py,sha256=AJWCnuo7XKja4yBb8fK2wX7ntciLXQrpzdRHwjTRP6M,62
|
|
28
|
+
fleet/_async/base.py,sha256=s0rYOtXsMJeitOvpa-Oh8ciLV226p_TIPp3fplzWvV4,9209
|
|
29
|
+
fleet/_async/client.py,sha256=1ooP-8uIXIpqeRlNR6JIdnOrzD4ZLxgd9Mi1qwsKRFo,10555
|
|
30
|
+
fleet/_async/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
31
|
+
fleet/_async/playwright.py,sha256=NXKcwJezl6jEV2CKBgeaB7zLshFWypgSA5GYXevDRX8,8908
|
|
32
|
+
fleet/_async/tasks.py,sha256=w-0vVGfEuCWRHMEJ73SN141J7Lz2pD_0v-nNG4TyJTU,1645
|
|
33
|
+
fleet/_async/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
fleet/_async/env/client.py,sha256=PQGKcvnVTr3o2KNsgQM7ZBqaCjimv-wEn-XiYGm44Ko,753
|
|
35
|
+
fleet/_async/instance/__init__.py,sha256=PtmJq8J8bh0SOQ2V55QURz5GJfobozwtQoqhaOk3_tI,515
|
|
36
|
+
fleet/_async/instance/base.py,sha256=3qUBuUR8OVS36LzdP6KyZzngtwPKYO09HoY6Ekxp-KA,1625
|
|
37
|
+
fleet/_async/instance/client.py,sha256=5fd_ABdlpmWIrDrOVnSs2XUUoXwcfvrV1LflLd7sqjw,5760
|
|
38
|
+
fleet/_async/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
fleet/_async/resources/base.py,sha256=ZFx2I9L7px-Z8LvbPUZJtbtajpqUn1yS1NFbZZutP9Q,668
|
|
40
|
+
fleet/_async/resources/browser.py,sha256=oldoSiymJ1lJkADhpUG81ViOBDNyppX1jSoEwe9-W94,1369
|
|
41
|
+
fleet/_async/resources/sqlite.py,sha256=d1wRLUIPIrKO6YaRaq3luPPQW_SjyRG1RJI67unu9jM,26633
|
|
42
|
+
fleet/_async/verifiers/__init__.py,sha256=Z2Ic77mw6-mhF5CmVrucmDnAGSTAtiejR_eZjTjPPA0,447
|
|
43
|
+
fleet/_async/verifiers/bundler.py,sha256=A4yR3wBOcVZYFAv87CD58QlJn6L4QXeilrasnVm8n74,26185
|
|
44
|
+
fleet/_async/verifiers/verifier.py,sha256=ImrpduuEZFEvBeiKBxGN-cHXNLz6hWJGipVM90qfjdA,12242
|
|
45
|
+
fleet/env/__init__.py,sha256=yYk657kVDnMWYbuPS_2o0kpVC99AYD85o5WG1jCaWSY,531
|
|
46
|
+
fleet/env/client.py,sha256=HwJ-xCg3oZMJ48nP9_EaHk4Rao-na9myPX9X5JxYMbo,644
|
|
47
|
+
fleet/instance/__init__.py,sha256=-Anms7Vw_FO8VBIvwnAnq4rQjwl_XNfAS-i7bypHMow,478
|
|
48
|
+
fleet/instance/base.py,sha256=OYqzBwZFfTX9wlBGSG5gljqj98NbiJeKIfFJ3uj5I4s,1587
|
|
49
|
+
fleet/instance/client.py,sha256=TgW2aGloF9qj_jloZQBKd62rRsrbBKPD7FmXXDdHBJU,5944
|
|
50
|
+
fleet/instance/models.py,sha256=ZTiue0YOuhuwX8jYfJAoCzGfqjLqqXRLqK1LVFhq6rQ,4183
|
|
51
|
+
fleet/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
+
fleet/resources/base.py,sha256=203gD54NP1IvjuSqFo-f7FvrkhtjChggtzrxJK7xf2E,667
|
|
53
|
+
fleet/resources/browser.py,sha256=OEqfNYPAptANza0T8K7UEn6nI7JnzlCICFMW8r2r83Q,1367
|
|
54
|
+
fleet/resources/mcp.py,sha256=55LBwhNDqSeFVCD0O3OP9m2DdJE-KEYRQiCnVyze8DM,1864
|
|
55
|
+
fleet/resources/sqlite.py,sha256=U0KneMzFKFrcuXcGrl4gkFPNoWST7_gWINphcA_UPJA,26251
|
|
56
|
+
fleet/verifiers/__init__.py,sha256=-dm2x0wC8UbfGcMCbPtjb0-LospGUhD-S3Pm4oha6BY,445
|
|
57
|
+
fleet/verifiers/bundler.py,sha256=A4yR3wBOcVZYFAv87CD58QlJn6L4QXeilrasnVm8n74,26185
|
|
58
|
+
fleet/verifiers/code.py,sha256=NJ4OLZnpqLkI1lXY7-5m2GuZklLxMzHUCnRMVyN2_OI,25
|
|
59
|
+
fleet/verifiers/db.py,sha256=tssmvJjDHuBIy8qlL_P5-UdmEFUw2DZcqLsWZ8ot3Xw,27766
|
|
60
|
+
fleet/verifiers/decorator.py,sha256=Q-KHhicnIYFwX7FX_VZguzNfu8ZslqNUeWxcS2CwNVY,3386
|
|
61
|
+
fleet/verifiers/sql_differ.py,sha256=dmiGCFXVMEMbAX519OjhVqgA8ZvhnvdmC1BVpL7QCF0,6490
|
|
62
|
+
fleet/verifiers/verifier.py,sha256=C5L24M7VBHWKaWlryC1EdW1g7keZBGzVSjgXC_SlpXA,12205
|
|
63
|
+
fleet_python-0.2.16.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
64
|
+
scripts/fix_sync_imports.py,sha256=BIQfnaOoQ7bwR1c-pDqH9ifN47W1bwL7OafWVXZNkuA,4368
|
|
65
|
+
scripts/unasync.py,sha256=--Fmaae47o-dZ1HYgX1c3Nvi-rMjcFymTRlJcWWnmpw,725
|
|
66
|
+
fleet_python-0.2.16.dist-info/METADATA,sha256=cntZGoC-jIhNPMXU18gUUGLsoM752--JWZS5mRjGyhg,3297
|
|
67
|
+
fleet_python-0.2.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
68
|
+
fleet_python-0.2.16.dist-info/top_level.txt,sha256=_3DSmTohvSDf3AIP_BYfGzhwO1ECFwuzg83X-wHCx3Y,23
|
|
69
|
+
fleet_python-0.2.16.dist-info/RECORD,,
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
examples/dsl_example.py,sha256=3Eu5924a8x61nuSGXqGz8XjPLNKKH8Ye7lSYHSvixtk,5361
|
|
2
|
-
examples/example.py,sha256=SAz5iPxca8n9MvnHVoWHI7GCaTgJwkegX78rMutF9gE,1082
|
|
3
|
-
examples/example_client.py,sha256=70HKEhz_Gb79YcvKQauCPdS08AAwjo9unt2dh1jN_Oo,1030
|
|
4
|
-
examples/example_sync.py,sha256=l8l-QOGTMnijiSK0mt5aofzXqbv-kKU3WCow4QS-7yg,976
|
|
5
|
-
examples/gemini_example.py,sha256=8mDXGGCaodyK6uXgpWhxi-DQ5OA-GFW12Gfwh0b3EDY,16177
|
|
6
|
-
examples/json_tasks_example.py,sha256=3ub2LLiC6hXpVEH1175QxCmfCD3Blfo3yoG85uV5CS8,5334
|
|
7
|
-
examples/nova_act_example.py,sha256=hZLpObVsiXKQzqGwMZVMf4A2j_z4TYE-YO9pgNmaKPk,836
|
|
8
|
-
examples/openai_example.py,sha256=I2vk_SJN9BkSRQCYRJfbtGJ-HJ2xzQj-lOjwqmLos5M,8234
|
|
9
|
-
examples/openai_simple_example.py,sha256=I42ytIwv0INgDO39pp1MOQSqsJz2YYH8GeNNBaUtq3A,1748
|
|
10
|
-
examples/quickstart.py,sha256=1VT39IRRhemsJgxi0O0gprdpcw7HB4pYO97GAYagIcg,3788
|
|
11
|
-
fleet/__init__.py,sha256=-EFlLzHmyJIUGSZ4_XIts6OhXIXGPyu5PgI9JxgDzTg,2165
|
|
12
|
-
fleet/base.py,sha256=E4gUv_eQEU4eSzTBy8gLGwl0s4t57OCTnsMYTKP3qI0,8782
|
|
13
|
-
fleet/client.py,sha256=XT61Tnq9O4B8eF01X2fFQYD4twwyKUrEK1UJawvZbUo,5583
|
|
14
|
-
fleet/config.py,sha256=9eWhJl81JinQJfZxD52t4Rgn5flnhhNqv2vGmddN2os,274
|
|
15
|
-
fleet/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
16
|
-
fleet/models.py,sha256=Jf6Zmk689TPXhTSnVENK_VCw0VsujWzEWsN3T29MQ0k,3713
|
|
17
|
-
fleet/playwright.py,sha256=BmRvez5DUa0ttAQB084hPAyt9_8WxdzCGBGF-GZbTuQ,8593
|
|
18
|
-
fleet/_async/__init__.py,sha256=AJWCnuo7XKja4yBb8fK2wX7ntciLXQrpzdRHwjTRP6M,62
|
|
19
|
-
fleet/_async/base.py,sha256=qXdaKthvrE4ZyELIapuFS63dHV6ulEYPDqddgK9qX1g,8801
|
|
20
|
-
fleet/_async/client.py,sha256=2ETcEUmpAdRToaUPmM4YGOPRXWwxoC4E9iM1Zk5BMwY,5842
|
|
21
|
-
fleet/_async/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
22
|
-
fleet/_async/playwright.py,sha256=2r4ywuv2ZqT0Qu3-k8A7V4YijeAOHnN8HiqJreLEYGI,8924
|
|
23
|
-
fleet/_async/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
fleet/_async/env/client.py,sha256=niS4uL8WY9G_lKyDhCqF5IdasxZJ3OCiiiJ5AG8_2a4,785
|
|
25
|
-
fleet/_async/instance/__init__.py,sha256=LC-1PGi8UyUHh4oQSyT9ZUg2K30a0gkhEytTf1vsKIU,535
|
|
26
|
-
fleet/_async/instance/base.py,sha256=3qUBuUR8OVS36LzdP6KyZzngtwPKYO09HoY6Ekxp-KA,1625
|
|
27
|
-
fleet/_async/instance/client.py,sha256=ZndvSeNxpdX8gIkLr7z6_IhKWDDxfcORtvq9evRIST0,5742
|
|
28
|
-
fleet/_async/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
-
fleet/_async/resources/base.py,sha256=ZFx2I9L7px-Z8LvbPUZJtbtajpqUn1yS1NFbZZutP9Q,668
|
|
30
|
-
fleet/_async/resources/browser.py,sha256=oldoSiymJ1lJkADhpUG81ViOBDNyppX1jSoEwe9-W94,1369
|
|
31
|
-
fleet/_async/resources/sqlite.py,sha256=_haVOT97ai1N6etYT-ZnhjILYunjK6XFjs_t8Xb8o2o,1503
|
|
32
|
-
fleet/env/__init__.py,sha256=yYk657kVDnMWYbuPS_2o0kpVC99AYD85o5WG1jCaWSY,531
|
|
33
|
-
fleet/env/client.py,sha256=HwJ-xCg3oZMJ48nP9_EaHk4Rao-na9myPX9X5JxYMbo,644
|
|
34
|
-
fleet/instance/__init__.py,sha256=Hr8xPPoqzKOViXZXWmaL6dQ7NOBn-GooTGzoIvGmiE4,514
|
|
35
|
-
fleet/instance/base.py,sha256=OYqzBwZFfTX9wlBGSG5gljqj98NbiJeKIfFJ3uj5I4s,1587
|
|
36
|
-
fleet/instance/client.py,sha256=GGBCf9Df1LxzwYXLPEbof-MJfGQs1BdZp7tvJR9Yi3I,5564
|
|
37
|
-
fleet/instance/models.py,sha256=ZTiue0YOuhuwX8jYfJAoCzGfqjLqqXRLqK1LVFhq6rQ,4183
|
|
38
|
-
fleet/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
-
fleet/resources/base.py,sha256=203gD54NP1IvjuSqFo-f7FvrkhtjChggtzrxJK7xf2E,667
|
|
40
|
-
fleet/resources/browser.py,sha256=hRNM0YMsVQUAraZGNi_B-KXxLpuddy4ntoEDFSw7czU,1295
|
|
41
|
-
fleet/resources/sqlite.py,sha256=sZVWsuq46ger-ta6PSlqcXGJG8iWpNQVj0CPmDNBXv8,1446
|
|
42
|
-
fleet/verifiers/__init__.py,sha256=mRMN8x0gDWFJ1MRLqdBtQw0gn_q8kDV3lMLyoiEf1yY,281
|
|
43
|
-
fleet/verifiers/code.py,sha256=NJ4OLZnpqLkI1lXY7-5m2GuZklLxMzHUCnRMVyN2_OI,25
|
|
44
|
-
fleet/verifiers/db.py,sha256=tssmvJjDHuBIy8qlL_P5-UdmEFUw2DZcqLsWZ8ot3Xw,27766
|
|
45
|
-
fleet/verifiers/sql_differ.py,sha256=dmiGCFXVMEMbAX519OjhVqgA8ZvhnvdmC1BVpL7QCF0,6490
|
|
46
|
-
fleet_python-0.2.13.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
47
|
-
scripts/fix_sync_imports.py,sha256=BIQfnaOoQ7bwR1c-pDqH9ifN47W1bwL7OafWVXZNkuA,4368
|
|
48
|
-
scripts/unasync.py,sha256=--Fmaae47o-dZ1HYgX1c3Nvi-rMjcFymTRlJcWWnmpw,725
|
|
49
|
-
fleet_python-0.2.13.dist-info/METADATA,sha256=JHCDePiOeKojRCtFXv7P4lVljs_pJyAl1alEjTabsA8,4358
|
|
50
|
-
fleet_python-0.2.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
51
|
-
fleet_python-0.2.13.dist-info/top_level.txt,sha256=_3DSmTohvSDf3AIP_BYfGzhwO1ECFwuzg83X-wHCx3Y,23
|
|
52
|
-
fleet_python-0.2.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|