fleet-python 0.2.66b2__py3-none-any.whl → 0.2.105__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.
- examples/export_tasks.py +16 -5
- examples/export_tasks_filtered.py +245 -0
- examples/fetch_tasks.py +230 -0
- examples/import_tasks.py +140 -8
- examples/iterate_verifiers.py +725 -0
- fleet/__init__.py +128 -5
- fleet/_async/__init__.py +27 -3
- fleet/_async/base.py +24 -9
- fleet/_async/client.py +938 -41
- fleet/_async/env/client.py +60 -3
- fleet/_async/instance/client.py +52 -7
- fleet/_async/models.py +15 -0
- fleet/_async/resources/api.py +200 -0
- fleet/_async/resources/sqlite.py +1801 -46
- fleet/_async/tasks.py +122 -25
- fleet/_async/verifiers/bundler.py +22 -21
- fleet/_async/verifiers/verifier.py +25 -19
- fleet/agent/__init__.py +32 -0
- fleet/agent/gemini_cua/Dockerfile +45 -0
- fleet/agent/gemini_cua/__init__.py +10 -0
- fleet/agent/gemini_cua/agent.py +759 -0
- fleet/agent/gemini_cua/mcp/main.py +108 -0
- fleet/agent/gemini_cua/mcp_server/__init__.py +5 -0
- fleet/agent/gemini_cua/mcp_server/main.py +105 -0
- fleet/agent/gemini_cua/mcp_server/tools.py +178 -0
- fleet/agent/gemini_cua/requirements.txt +5 -0
- fleet/agent/gemini_cua/start.sh +30 -0
- fleet/agent/orchestrator.py +854 -0
- fleet/agent/types.py +49 -0
- fleet/agent/utils.py +34 -0
- fleet/base.py +34 -9
- fleet/cli.py +1061 -0
- fleet/client.py +1060 -48
- fleet/config.py +1 -1
- fleet/env/__init__.py +16 -0
- fleet/env/client.py +60 -3
- fleet/eval/__init__.py +15 -0
- fleet/eval/uploader.py +231 -0
- fleet/exceptions.py +8 -0
- fleet/instance/client.py +53 -8
- fleet/instance/models.py +1 -0
- fleet/models.py +303 -0
- fleet/proxy/__init__.py +25 -0
- fleet/proxy/proxy.py +453 -0
- fleet/proxy/whitelist.py +244 -0
- fleet/resources/api.py +200 -0
- fleet/resources/sqlite.py +1845 -46
- fleet/tasks.py +113 -20
- fleet/utils/__init__.py +7 -0
- fleet/utils/http_logging.py +178 -0
- fleet/utils/logging.py +13 -0
- fleet/utils/playwright.py +440 -0
- fleet/verifiers/bundler.py +22 -21
- fleet/verifiers/db.py +985 -1
- fleet/verifiers/decorator.py +1 -1
- fleet/verifiers/verifier.py +25 -19
- {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/METADATA +28 -1
- fleet_python-0.2.105.dist-info/RECORD +115 -0
- {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/WHEEL +1 -1
- fleet_python-0.2.105.dist-info/entry_points.txt +2 -0
- tests/test_app_method.py +85 -0
- tests/test_expect_exactly.py +4148 -0
- tests/test_expect_only.py +2593 -0
- tests/test_instance_dispatch.py +607 -0
- tests/test_sqlite_resource_dual_mode.py +263 -0
- tests/test_sqlite_shared_memory_behavior.py +117 -0
- fleet_python-0.2.66b2.dist-info/RECORD +0 -81
- tests/test_verifier_security.py +0 -427
- {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/top_level.txt +0 -0
fleet/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
"""Fleet Python SDK - Environment-based AI agent interactions."""
|
|
16
16
|
|
|
17
|
-
from typing import Optional, List
|
|
17
|
+
from typing import Any, Optional, List
|
|
18
18
|
|
|
19
19
|
from .exceptions import (
|
|
20
20
|
FleetError,
|
|
@@ -24,9 +24,9 @@ from .exceptions import (
|
|
|
24
24
|
FleetInstanceLimitError,
|
|
25
25
|
FleetConfigurationError,
|
|
26
26
|
)
|
|
27
|
-
from .client import Fleet, SyncEnv
|
|
28
|
-
from ._async.client import AsyncFleet, AsyncEnv
|
|
29
|
-
from .models import InstanceResponse, Environment
|
|
27
|
+
from .client import Fleet, SyncEnv, Session
|
|
28
|
+
from ._async.client import AsyncFleet, AsyncEnv, AsyncSession
|
|
29
|
+
from .models import InstanceResponse, Environment, Run
|
|
30
30
|
from .instance.models import Resource, ResetResponse
|
|
31
31
|
|
|
32
32
|
# Import sync verifiers with explicit naming
|
|
@@ -73,7 +73,7 @@ from . import env
|
|
|
73
73
|
from . import global_client as _global_client
|
|
74
74
|
from ._async import global_client as _async_global_client
|
|
75
75
|
|
|
76
|
-
__version__ = "0.
|
|
76
|
+
__version__ = "0.2.103"
|
|
77
77
|
|
|
78
78
|
__all__ = [
|
|
79
79
|
# Core classes
|
|
@@ -86,6 +86,7 @@ __all__ = [
|
|
|
86
86
|
"SyncEnv",
|
|
87
87
|
"Resource",
|
|
88
88
|
"ResetResponse",
|
|
89
|
+
"Run",
|
|
89
90
|
# Task models
|
|
90
91
|
"Task",
|
|
91
92
|
"VerifierFunction",
|
|
@@ -121,6 +122,14 @@ __all__ = [
|
|
|
121
122
|
"import_tasks_async",
|
|
122
123
|
"get_task",
|
|
123
124
|
"get_task_async",
|
|
125
|
+
# Session helpers
|
|
126
|
+
"session",
|
|
127
|
+
"session_async",
|
|
128
|
+
"Session",
|
|
129
|
+
"AsyncSession",
|
|
130
|
+
# Job helpers
|
|
131
|
+
"job",
|
|
132
|
+
"job_async",
|
|
124
133
|
# Version
|
|
125
134
|
"__version__",
|
|
126
135
|
]
|
|
@@ -161,3 +170,117 @@ def reset_client():
|
|
|
161
170
|
"""Reset both sync and async global clients."""
|
|
162
171
|
_global_client.reset_client()
|
|
163
172
|
_async_global_client.reset_client()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def session(
|
|
176
|
+
session_id: Optional[str] = None,
|
|
177
|
+
job_id: Optional[str] = None,
|
|
178
|
+
config: Optional[Any] = None,
|
|
179
|
+
model: Optional[str] = None,
|
|
180
|
+
task_key: Optional[str] = None,
|
|
181
|
+
instance_id: Optional[str] = None,
|
|
182
|
+
) -> Session:
|
|
183
|
+
"""Start a new session for logging agent interactions (sync).
|
|
184
|
+
|
|
185
|
+
This returns a Session object. The session is created on the backend
|
|
186
|
+
when you call log() for the first time.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
session_id: Optional existing session ID to resume
|
|
190
|
+
job_id: Optional job ID to associate with the session
|
|
191
|
+
config: Optional config object (e.g., GenerateContentConfig) to log
|
|
192
|
+
model: Optional model name to log
|
|
193
|
+
task_key: Optional Fleet task key
|
|
194
|
+
instance_id: Optional Fleet instance ID
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Session object with log(), complete(), and fail() methods
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
session = fleet.session(config=config, model="gpt-4", task_key="task_123")
|
|
201
|
+
session.log(history, response)
|
|
202
|
+
session.complete()
|
|
203
|
+
"""
|
|
204
|
+
client = _global_client.get_client()
|
|
205
|
+
return client.start_session(
|
|
206
|
+
session_id=session_id,
|
|
207
|
+
job_id=job_id,
|
|
208
|
+
config=config,
|
|
209
|
+
model=model,
|
|
210
|
+
task_key=task_key,
|
|
211
|
+
instance_id=instance_id,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def session_async(
|
|
216
|
+
session_id: Optional[str] = None,
|
|
217
|
+
job_id: Optional[str] = None,
|
|
218
|
+
config: Optional[Any] = None,
|
|
219
|
+
model: Optional[str] = None,
|
|
220
|
+
task_key: Optional[str] = None,
|
|
221
|
+
instance_id: Optional[str] = None,
|
|
222
|
+
) -> AsyncSession:
|
|
223
|
+
"""Start a new session for logging agent interactions (async).
|
|
224
|
+
|
|
225
|
+
This returns an AsyncSession object. The session is created on the backend
|
|
226
|
+
when you call log() for the first time.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
session_id: Optional existing session ID to resume
|
|
230
|
+
job_id: Optional job ID to associate with the session
|
|
231
|
+
config: Optional config object (e.g., GenerateContentConfig) to log
|
|
232
|
+
model: Optional model name to log
|
|
233
|
+
task_key: Optional Fleet task key
|
|
234
|
+
instance_id: Optional Fleet instance ID
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
AsyncSession object with log(), complete(), and fail() methods
|
|
238
|
+
|
|
239
|
+
Example:
|
|
240
|
+
session = fleet.session_async(config=config, model="gpt-4", task_key="task_123")
|
|
241
|
+
await session.log(history, response)
|
|
242
|
+
await session.complete()
|
|
243
|
+
"""
|
|
244
|
+
client = _async_global_client.get_client()
|
|
245
|
+
return client.start_session(
|
|
246
|
+
session_id=session_id,
|
|
247
|
+
job_id=job_id,
|
|
248
|
+
config=config,
|
|
249
|
+
model=model,
|
|
250
|
+
task_key=task_key,
|
|
251
|
+
instance_id=instance_id,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def job(name: Optional[str] = None) -> str:
|
|
256
|
+
"""Create a new trace job (sync).
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
name: Name of the job (generated server-side if not provided)
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
The job_id string
|
|
263
|
+
|
|
264
|
+
Example:
|
|
265
|
+
job_id = fleet.job("my-agent-run")
|
|
266
|
+
session = fleet.session(job_id=job_id, ...)
|
|
267
|
+
"""
|
|
268
|
+
client = _global_client.get_client()
|
|
269
|
+
return client.trace_job(name=name)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def job_async(name: Optional[str] = None) -> str:
|
|
273
|
+
"""Create a new trace job (async).
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
name: Name of the job (generated server-side if not provided)
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
The job_id string
|
|
280
|
+
|
|
281
|
+
Example:
|
|
282
|
+
job_id = await fleet.job_async("my-agent-run")
|
|
283
|
+
session = await fleet.session_async(job_id=job_id, ...)
|
|
284
|
+
"""
|
|
285
|
+
client = _async_global_client.get_client()
|
|
286
|
+
return await client.trace_job(name=name)
|
fleet/_async/__init__.py
CHANGED
|
@@ -24,8 +24,8 @@ from ..exceptions import (
|
|
|
24
24
|
FleetInstanceLimitError,
|
|
25
25
|
FleetConfigurationError,
|
|
26
26
|
)
|
|
27
|
-
from .client import AsyncFleet, AsyncEnv
|
|
28
|
-
from ..models import InstanceResponse, Environment, AccountResponse
|
|
27
|
+
from .client import AsyncFleet, AsyncEnv, AsyncSession
|
|
28
|
+
from ..models import InstanceResponse, Environment, AccountResponse, Run
|
|
29
29
|
from ..instance.models import Resource, ResetResponse
|
|
30
30
|
|
|
31
31
|
# Import async verifiers
|
|
@@ -44,7 +44,7 @@ from ..types import VerifierFunction
|
|
|
44
44
|
from .. import env
|
|
45
45
|
from . import global_client as _async_global_client
|
|
46
46
|
|
|
47
|
-
__version__ = "0.
|
|
47
|
+
__version__ = "0.2.103"
|
|
48
48
|
|
|
49
49
|
__all__ = [
|
|
50
50
|
# Core classes
|
|
@@ -54,6 +54,7 @@ __all__ = [
|
|
|
54
54
|
"InstanceResponse",
|
|
55
55
|
"Resource",
|
|
56
56
|
"ResetResponse",
|
|
57
|
+
"Run",
|
|
57
58
|
# Task models
|
|
58
59
|
"Task",
|
|
59
60
|
"VerifierFunction",
|
|
@@ -90,6 +91,7 @@ __all__ = [
|
|
|
90
91
|
"import_tasks",
|
|
91
92
|
"account",
|
|
92
93
|
"get_task",
|
|
94
|
+
"list_runs",
|
|
93
95
|
# Version
|
|
94
96
|
"__version__",
|
|
95
97
|
]
|
|
@@ -255,6 +257,28 @@ async def get_task(task_key: str, version_id: Optional[str] = None):
|
|
|
255
257
|
)
|
|
256
258
|
|
|
257
259
|
|
|
260
|
+
async def list_runs(
|
|
261
|
+
profile_id: Optional[str] = None, status: Optional[str] = "active"
|
|
262
|
+
) -> List[Run]:
|
|
263
|
+
"""List all runs (groups of instances by run_id) with aggregated statistics.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
profile_id: Optional profile ID to filter runs by (use "self" for your own profile)
|
|
267
|
+
status: Filter by run status - "active" (default), "inactive", or "all"
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
List[Run] containing run information with instance counts and timestamps
|
|
271
|
+
|
|
272
|
+
Example:
|
|
273
|
+
runs = await fleet.list_runs()
|
|
274
|
+
my_runs = await fleet.list_runs(profile_id="self")
|
|
275
|
+
all_runs = await fleet.list_runs(status="all")
|
|
276
|
+
"""
|
|
277
|
+
return await _async_global_client.get_client().list_runs(
|
|
278
|
+
profile_id=profile_id, status=status
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
|
|
258
282
|
def configure(
|
|
259
283
|
api_key: Optional[str] = None,
|
|
260
284
|
base_url: Optional[str] = None,
|
fleet/_async/base.py
CHANGED
|
@@ -2,6 +2,8 @@ import httpx
|
|
|
2
2
|
from typing import Dict, Any, Optional
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
5
7
|
|
|
6
8
|
from ..models import InstanceResponse
|
|
7
9
|
from ..config import GLOBAL_BASE_URL
|
|
@@ -20,6 +22,12 @@ from .exceptions import (
|
|
|
20
22
|
FleetPermissionError,
|
|
21
23
|
)
|
|
22
24
|
|
|
25
|
+
# Import version
|
|
26
|
+
try:
|
|
27
|
+
from .. import __version__
|
|
28
|
+
except ImportError:
|
|
29
|
+
__version__ = "0.2.103"
|
|
30
|
+
|
|
23
31
|
logger = logging.getLogger(__name__)
|
|
24
32
|
|
|
25
33
|
|
|
@@ -38,17 +46,20 @@ class BaseWrapper:
|
|
|
38
46
|
base_url = GLOBAL_BASE_URL
|
|
39
47
|
self.base_url = base_url
|
|
40
48
|
|
|
41
|
-
def get_headers(self) -> Dict[str, str]:
|
|
49
|
+
def get_headers(self, request_id: Optional[str] = None) -> Dict[str, str]:
|
|
42
50
|
headers: Dict[str, str] = {
|
|
43
51
|
"X-Fleet-SDK-Language": "Python",
|
|
44
|
-
"X-Fleet-SDK-Version":
|
|
52
|
+
"X-Fleet-SDK-Version": __version__,
|
|
45
53
|
}
|
|
46
54
|
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
47
|
-
# Debug log
|
|
48
|
-
import logging
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
# Add request ID for idempotency (persists across retries)
|
|
57
|
+
if request_id:
|
|
58
|
+
headers["X-Request-ID"] = request_id
|
|
59
|
+
|
|
60
|
+
# Add timestamp for all requests
|
|
61
|
+
headers["X-Request-Timestamp"] = str(int(time.time() * 1000))
|
|
62
|
+
|
|
52
63
|
return headers
|
|
53
64
|
|
|
54
65
|
|
|
@@ -67,11 +78,14 @@ class AsyncWrapper(BaseWrapper):
|
|
|
67
78
|
**kwargs,
|
|
68
79
|
) -> httpx.Response:
|
|
69
80
|
base_url = base_url or self.base_url
|
|
81
|
+
# Generate unique request ID that persists across retries
|
|
82
|
+
request_id = str(uuid.uuid4())
|
|
83
|
+
|
|
70
84
|
try:
|
|
71
85
|
response = await self.httpx_client.request(
|
|
72
86
|
method,
|
|
73
87
|
f"{base_url}{url}",
|
|
74
|
-
headers=self.get_headers(),
|
|
88
|
+
headers=self.get_headers(request_id=request_id),
|
|
75
89
|
params=params,
|
|
76
90
|
json=json,
|
|
77
91
|
**kwargs,
|
|
@@ -93,8 +107,9 @@ class AsyncWrapper(BaseWrapper):
|
|
|
93
107
|
|
|
94
108
|
# Debug log 500 errors
|
|
95
109
|
if status_code == 500:
|
|
96
|
-
logger.error(f"Got 500 error from {response.url}")
|
|
97
|
-
logger.error(f"Response text: {response.text}")
|
|
110
|
+
# logger.error(f"Got 500 error from {response.url}")
|
|
111
|
+
# logger.error(f"Response text: {response.text}")
|
|
112
|
+
pass
|
|
98
113
|
|
|
99
114
|
# Try to parse error response as JSON
|
|
100
115
|
try:
|