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.
Files changed (70) hide show
  1. examples/export_tasks.py +16 -5
  2. examples/export_tasks_filtered.py +245 -0
  3. examples/fetch_tasks.py +230 -0
  4. examples/import_tasks.py +140 -8
  5. examples/iterate_verifiers.py +725 -0
  6. fleet/__init__.py +128 -5
  7. fleet/_async/__init__.py +27 -3
  8. fleet/_async/base.py +24 -9
  9. fleet/_async/client.py +938 -41
  10. fleet/_async/env/client.py +60 -3
  11. fleet/_async/instance/client.py +52 -7
  12. fleet/_async/models.py +15 -0
  13. fleet/_async/resources/api.py +200 -0
  14. fleet/_async/resources/sqlite.py +1801 -46
  15. fleet/_async/tasks.py +122 -25
  16. fleet/_async/verifiers/bundler.py +22 -21
  17. fleet/_async/verifiers/verifier.py +25 -19
  18. fleet/agent/__init__.py +32 -0
  19. fleet/agent/gemini_cua/Dockerfile +45 -0
  20. fleet/agent/gemini_cua/__init__.py +10 -0
  21. fleet/agent/gemini_cua/agent.py +759 -0
  22. fleet/agent/gemini_cua/mcp/main.py +108 -0
  23. fleet/agent/gemini_cua/mcp_server/__init__.py +5 -0
  24. fleet/agent/gemini_cua/mcp_server/main.py +105 -0
  25. fleet/agent/gemini_cua/mcp_server/tools.py +178 -0
  26. fleet/agent/gemini_cua/requirements.txt +5 -0
  27. fleet/agent/gemini_cua/start.sh +30 -0
  28. fleet/agent/orchestrator.py +854 -0
  29. fleet/agent/types.py +49 -0
  30. fleet/agent/utils.py +34 -0
  31. fleet/base.py +34 -9
  32. fleet/cli.py +1061 -0
  33. fleet/client.py +1060 -48
  34. fleet/config.py +1 -1
  35. fleet/env/__init__.py +16 -0
  36. fleet/env/client.py +60 -3
  37. fleet/eval/__init__.py +15 -0
  38. fleet/eval/uploader.py +231 -0
  39. fleet/exceptions.py +8 -0
  40. fleet/instance/client.py +53 -8
  41. fleet/instance/models.py +1 -0
  42. fleet/models.py +303 -0
  43. fleet/proxy/__init__.py +25 -0
  44. fleet/proxy/proxy.py +453 -0
  45. fleet/proxy/whitelist.py +244 -0
  46. fleet/resources/api.py +200 -0
  47. fleet/resources/sqlite.py +1845 -46
  48. fleet/tasks.py +113 -20
  49. fleet/utils/__init__.py +7 -0
  50. fleet/utils/http_logging.py +178 -0
  51. fleet/utils/logging.py +13 -0
  52. fleet/utils/playwright.py +440 -0
  53. fleet/verifiers/bundler.py +22 -21
  54. fleet/verifiers/db.py +985 -1
  55. fleet/verifiers/decorator.py +1 -1
  56. fleet/verifiers/verifier.py +25 -19
  57. {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/METADATA +28 -1
  58. fleet_python-0.2.105.dist-info/RECORD +115 -0
  59. {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/WHEEL +1 -1
  60. fleet_python-0.2.105.dist-info/entry_points.txt +2 -0
  61. tests/test_app_method.py +85 -0
  62. tests/test_expect_exactly.py +4148 -0
  63. tests/test_expect_only.py +2593 -0
  64. tests/test_instance_dispatch.py +607 -0
  65. tests/test_sqlite_resource_dual_mode.py +263 -0
  66. tests/test_sqlite_shared_memory_behavior.py +117 -0
  67. fleet_python-0.2.66b2.dist-info/RECORD +0 -81
  68. tests/test_verifier_security.py +0 -427
  69. {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/licenses/LICENSE +0 -0
  70. {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.1.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.1.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": "1.0.0",
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
- logger = logging.getLogger(__name__)
51
- logger.debug(f"Headers being sent: {headers}")
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: