fleet-python 0.2.29__py3-none-any.whl → 0.2.32__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 (61) hide show
  1. examples/diff_example.py +30 -20
  2. examples/dsl_example.py +12 -7
  3. examples/example.py +4 -4
  4. examples/example_account.py +8 -0
  5. examples/example_action_log.py +2 -2
  6. examples/example_client.py +2 -2
  7. examples/example_mcp_anthropic.py +8 -5
  8. examples/example_mcp_openai.py +2 -2
  9. examples/example_sync.py +4 -4
  10. examples/example_task.py +16 -6
  11. examples/example_tasks.py +3 -6
  12. examples/example_verifier.py +16 -3
  13. examples/gemini_example.py +6 -6
  14. examples/json_tasks_example.py +2 -2
  15. examples/nova_act_example.py +2 -2
  16. examples/openai_example.py +3 -3
  17. examples/openai_simple_example.py +3 -3
  18. examples/query_builder_example.py +11 -7
  19. fleet/__init__.py +60 -5
  20. fleet/_async/__init__.py +258 -1
  21. fleet/_async/base.py +2 -1
  22. fleet/_async/client.py +164 -144
  23. fleet/_async/env/client.py +2 -0
  24. fleet/_async/global_client.py +43 -0
  25. fleet/_async/instance/client.py +1 -1
  26. fleet/_async/models.py +172 -171
  27. fleet/_async/resources/base.py +1 -1
  28. fleet/_async/resources/mcp.py +55 -0
  29. fleet/_async/resources/sqlite.py +141 -130
  30. fleet/_async/tasks.py +69 -16
  31. fleet/_async/verifiers/__init__.py +2 -2
  32. fleet/_async/verifiers/bundler.py +18 -14
  33. fleet/_async/verifiers/verifier.py +77 -71
  34. fleet/base.py +2 -1
  35. fleet/client.py +145 -158
  36. fleet/config.py +3 -2
  37. fleet/env/__init__.py +9 -1
  38. fleet/env/client.py +3 -0
  39. fleet/global_client.py +43 -0
  40. fleet/instance/__init__.py +1 -1
  41. fleet/instance/client.py +2 -4
  42. fleet/models.py +172 -171
  43. fleet/resources/base.py +1 -1
  44. fleet/resources/mcp.py +27 -33
  45. fleet/resources/sqlite.py +136 -131
  46. fleet/tasks.py +195 -16
  47. fleet/types.py +1 -1
  48. fleet/verifiers/__init__.py +2 -2
  49. fleet/verifiers/bundler.py +18 -14
  50. fleet/verifiers/code.py +1 -1
  51. fleet/verifiers/decorator.py +25 -34
  52. fleet/verifiers/parse.py +98 -68
  53. fleet/verifiers/verifier.py +77 -78
  54. {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/METADATA +9 -9
  55. fleet_python-0.2.32.dist-info/RECORD +74 -0
  56. scripts/fix_sync_imports.py +87 -59
  57. scripts/unasync.py +10 -9
  58. fleet_python-0.2.29.dist-info/RECORD +0 -70
  59. {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/WHEEL +0 -0
  60. {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/licenses/LICENSE +0 -0
  61. {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/top_level.txt +0 -0
fleet/client.py CHANGED
@@ -34,7 +34,6 @@ from .models import (
34
34
  TaskRequest,
35
35
  )
36
36
  from .tasks import Task
37
- from .verifiers.parse import extract_function_name, convert_new_to_old_verifier
38
37
 
39
38
  if TYPE_CHECKING:
40
39
  from .verifiers import SyncVerifierFunction
@@ -51,6 +50,7 @@ from .instance.client import ValidatorType
51
50
  from .resources.base import Resource
52
51
  from .resources.sqlite import SQLiteResource
53
52
  from .resources.browser import BrowserResource
53
+ from .resources.mcp import MCPResource
54
54
 
55
55
  logger = logging.getLogger(__name__)
56
56
 
@@ -69,18 +69,18 @@ class SyncEnv(EnvironmentBase):
69
69
  self.manager_url, self._client.httpx_client if self._client else None
70
70
  )
71
71
  return self._instance
72
-
72
+
73
73
  def app(self, name: str) -> InstanceClient:
74
74
  if name not in self._apps:
75
75
  # Extract base URL by removing the current app path (e.g., /sentry/api/v1/env)
76
76
  # manager_url looks like: https://xxx.fleetai.com/sentry/api/v1/env
77
- base_url = self.manager_url.split('/api/v1/env')[0]
77
+ base_url = self.manager_url.split("/api/v1/env")[0]
78
78
  # Remove the current app name (e.g., /sentry) to get the root
79
- if '/' in base_url:
80
- parts = base_url.rsplit('/', 1)
79
+ if "/" in base_url:
80
+ parts = base_url.rsplit("/", 1)
81
81
  if len(parts) == 2 and parts[0] != "https:/":
82
82
  base_url = parts[0]
83
-
83
+
84
84
  self._apps[name] = InstanceClient(
85
85
  f"{base_url}/{name}/api/v1/env",
86
86
  self._client.httpx_client if self._client else None,
@@ -104,6 +104,11 @@ class SyncEnv(EnvironmentBase):
104
104
  def browser(self, name: str = "cdp") -> BrowserResource:
105
105
  return self.instance.browser(name)
106
106
 
107
+ @property
108
+ def mcp(self) -> MCPResource:
109
+ mcp_url = f"{self.urls.root}mcp"
110
+ return MCPResource(url=mcp_url, env_key=self.env_key)
111
+
107
112
  def state(self, uri: str) -> Resource:
108
113
  return self.instance.state(uri)
109
114
 
@@ -125,28 +130,28 @@ class SyncEnv(EnvironmentBase):
125
130
  return _check_bundle_exists(self._load_client, bundle_hash)
126
131
 
127
132
  def execute_verifier_remote(
128
- self,
129
- bundle_data: bytes,
133
+ self,
134
+ bundle_data: bytes,
130
135
  bundle_sha: str,
131
136
  key: str,
132
137
  function_name: str,
133
- args: tuple,
138
+ args: tuple,
134
139
  args_array: list,
135
- kwargs: dict,
140
+ kwargs: dict,
136
141
  timeout: Optional[int] = 30,
137
142
  needs_upload: bool = True,
138
143
  ) -> VerifiersExecuteResponse:
139
144
  return _execute_verifier_remote(
140
- self._load_client,
141
- bundle_data,
145
+ self._load_client,
146
+ bundle_data,
142
147
  bundle_sha,
143
148
  key,
144
149
  function_name,
145
- args,
150
+ args,
146
151
  args_array,
147
- kwargs,
152
+ kwargs,
148
153
  timeout,
149
- needs_upload
154
+ needs_upload,
150
155
  )
151
156
 
152
157
  def __getstate__(self):
@@ -170,6 +175,8 @@ class Fleet:
170
175
  ):
171
176
  if api_key is None:
172
177
  api_key = os.getenv("FLEET_API_KEY")
178
+ if base_url is None:
179
+ base_url = os.getenv("FLEET_BASE_URL")
173
180
  self._httpx_client = httpx_client or default_httpx_client(max_retries, timeout)
174
181
  self.client = SyncWrapper(
175
182
  api_key=api_key,
@@ -189,23 +196,27 @@ class Fleet:
189
196
  response = self.client.request("GET", f"/v1/env/{env_key}")
190
197
  return EnvironmentModel(**response.json())
191
198
 
192
- def make(
193
- self, env_key: str, region: Optional[str] = None
194
- ) -> SyncEnv:
199
+ def make(self, env_key: str, region: Optional[str] = None) -> SyncEnv:
195
200
  if ":" in env_key:
196
201
  env_key_part, version = env_key.split(":", 1)
197
- if not version.startswith("v") and len(version) != 0 and version[0].isdigit():
202
+ if (
203
+ not version.startswith("v")
204
+ and len(version) != 0
205
+ and version[0].isdigit()
206
+ ):
198
207
  version = f"v{version}"
199
208
  else:
200
209
  env_key_part = env_key
201
210
  version = None
202
211
 
203
- request = InstanceRequest(env_key=env_key_part, version=version, region=region, created_from="sdk")
212
+ request = InstanceRequest(
213
+ env_key=env_key_part, version=version, region=region, created_from="sdk"
214
+ )
204
215
  region_base_url = REGION_BASE_URL.get(region)
205
216
  response = self.client.request(
206
217
  "POST",
207
218
  "/v1/env/instances",
208
- json=request.model_dump(),
219
+ json=request.model_dump(exclude_none=True),
209
220
  base_url=region_base_url,
210
221
  )
211
222
 
@@ -243,15 +254,13 @@ class Fleet:
243
254
  def execute_verifier_remote(
244
255
  self, bundle_data: bytes, args: tuple, kwargs: dict, timeout: Optional[int] = 30
245
256
  ) -> VerifiersExecuteResponse:
246
- return _execute_verifier_remote(
247
- self.client, bundle_data, args, kwargs, timeout
248
- )
257
+ return _execute_verifier_remote(self.client, bundle_data, args, kwargs, timeout)
249
258
 
250
259
  def delete(self, instance_id: str) -> InstanceResponse:
251
260
  return _delete_instance(self.client, instance_id)
252
-
261
+
253
262
  def load_tasks_from_file(self, filename: str) -> List[Task]:
254
- with open(filename, 'r', encoding='utf-8') as f:
263
+ with open(filename, "r", encoding="utf-8") as f:
255
264
  tasks_data = f.read()
256
265
 
257
266
  return self.load_task_array_from_string(tasks_data)
@@ -270,38 +279,36 @@ class Fleet:
270
279
  return self.load_task_from_json(task_json)
271
280
 
272
281
  def load_task_from_json(self, task_json: Dict) -> Task:
273
- verifier = None
274
282
  try:
275
- if 'verifier_id' in task_json and task_json['verifier_id']:
283
+ if "verifier_id" in task_json and task_json["verifier_id"]:
276
284
  verifier = self._create_verifier_from_data(
277
- verifier_id=task_json['verifier_id'],
278
- verifier_key=task_json['key'],
279
- verifier_code=task_json['verifier_func'],
280
- verifier_sha=task_json.get('verifier_sha', '')
285
+ verifier_id=task_json["verifier_id"],
286
+ verifier_key=task_json["key"],
287
+ verifier_code=task_json["verifier_func"],
288
+ verifier_sha=task_json.get("verifier_sha", ""),
281
289
  )
282
290
  except Exception as e:
283
291
  logger.warning(f"Failed to create verifier {task_json['key']}: {e}")
284
-
292
+
285
293
  task = Task(
286
- key=task_json['key'],
287
- prompt=task_json['prompt'],
288
- env_id=task_json['env_id'], # Use env_id from the data
289
- created_at=task_json['created_at'],
290
- version=task_json.get('version'),
291
- env_variables=task_json.get('env_variables', {}),
292
- verifier_func=task_json.get('verifier_func'), # Set verifier code
294
+ key=task_json["key"],
295
+ prompt=task_json["prompt"],
296
+ env_id=task_json["env_id"], # Use env_id from the data
297
+ created_at=task_json["created_at"],
298
+ version=task_json.get("version"),
299
+ env_variables=task_json.get("env_variables", {}),
300
+ verifier_func=task_json.get("verifier_func"), # Set verifier code
293
301
  verifier=verifier, # Use created verifier or None
294
- metadata=task_json.get('metadata', {}) # Default empty metadata
302
+ metadata=task_json.get("metadata", {}), # Default empty metadata
295
303
  )
296
304
  return task
297
305
 
298
-
299
- def load_tasks(self, env_key: Optional[str] = None, task_keys: Optional[List[str]] = None) -> List[Task]:
306
+ def load_tasks(self, env_key: Optional[str] = None) -> List[Task]:
300
307
  """Load tasks for the authenticated team, optionally filtered by environment.
301
-
308
+
302
309
  Args:
303
310
  env_key: Optional environment key to filter tasks by
304
-
311
+
305
312
  Returns:
306
313
  List[Task] containing Task objects
307
314
  """
@@ -309,34 +316,52 @@ class Fleet:
309
316
  if env_key is not None:
310
317
  params["env_key"] = env_key
311
318
 
312
- if task_keys is not None:
313
- params["task_keys"] = task_keys
314
-
315
319
  response = self.client.request("GET", "/v1/tasks", params=params)
316
320
  task_list_response = TaskListResponse(**response.json())
317
-
321
+
318
322
  # Transform TaskResponse objects to Task objects
319
323
  tasks = []
320
324
  for task_response in task_list_response.tasks:
321
325
  # Create verifier function if verifier data is present
322
326
  verifier = None
323
327
  verifier_func = task_response.verifier_func
324
-
328
+
325
329
  if task_response.verifier:
326
- # Use verifier code from the embedded verifier object
327
- verifier_func = task_response.verifier.code
328
-
329
- # Create VerifierFunction from the embedded data
330
- try:
331
- verifier = self._create_verifier_from_data(
332
- verifier_id=task_response.verifier.verifier_id,
333
- verifier_key=task_response.verifier.key,
334
- verifier_code=task_response.verifier.code,
335
- verifier_sha=task_response.verifier.sha256
336
- )
337
- except Exception as e:
338
- logger.warning(f"Failed to create verifier {task_response.verifier.key}: {e}")
339
-
330
+ embedded_code = task_response.verifier.code or ""
331
+ is_embedded_error = embedded_code.strip().startswith(
332
+ "<error loading code:"
333
+ )
334
+ if not is_embedded_error:
335
+ # Only override if the embedded code looks valid
336
+ verifier_func = embedded_code
337
+ # Create VerifierFunction from the embedded data
338
+ try:
339
+ verifier = self._create_verifier_from_data(
340
+ verifier_id=task_response.verifier.verifier_id,
341
+ verifier_key=task_response.verifier.key,
342
+ verifier_code=embedded_code,
343
+ verifier_sha=task_response.verifier.sha256,
344
+ )
345
+ except Exception as e:
346
+ logger.warning(
347
+ f"Failed to create verifier {task_response.verifier.key}: {e}"
348
+ )
349
+ else:
350
+ # Fallback: try fetching by ID if embedded code failed to load
351
+ try:
352
+ logger.warning(
353
+ f"Embedded verifier code missing for {task_response.verifier.key} (NoSuchKey). "
354
+ f"Attempting to refetch by id {task_response.verifier.verifier_id}"
355
+ )
356
+ verifier = self._load_verifier(
357
+ task_response.verifier.verifier_id
358
+ )
359
+ except Exception as e:
360
+ logger.warning(
361
+ f"Refetch by verifier id failed for {task_response.verifier.key}: {e}. "
362
+ "Leaving verifier unset."
363
+ )
364
+
340
365
  task = Task(
341
366
  key=task_response.key,
342
367
  prompt=task_response.prompt,
@@ -346,21 +371,21 @@ class Fleet:
346
371
  env_variables=task_response.env_variables or {},
347
372
  verifier_func=verifier_func, # Set verifier code
348
373
  verifier=verifier, # Use created verifier or None
349
- verifier_id=task_response.verifier.verifier_id,
350
- verifier_sha=task_response.verifier.sha256,
351
- metadata={} # Default empty metadata
374
+ metadata={}, # Default empty metadata
352
375
  )
353
376
  tasks.append(task)
354
-
377
+
355
378
  return tasks
356
379
 
357
- def export_tasks(self, env_key: Optional[str] = None, filename: Optional[str] = None):
380
+ def export_tasks(
381
+ self, env_key: Optional[str] = None, filename: Optional[str] = None
382
+ ):
358
383
  """Export tasks for the authenticated team, optionally filtered by environment.
359
-
384
+
360
385
  Args:
361
386
  env_key: Optional environment key to filter tasks by
362
387
  filename: Optional filename to write tasks to. If not provided, defaults to 'tasks.json' or 'tasks_{env_key}.json'
363
-
388
+
364
389
  Returns:
365
390
  str: Path to the exported file if tasks were written, None if no tasks found
366
391
  """
@@ -372,38 +397,38 @@ class Fleet:
372
397
  filename = f"tasks_{env_key}.json"
373
398
  else:
374
399
  filename = "tasks.json"
375
-
400
+
376
401
  # Convert tasks to serializable format
377
402
  tasks_data = []
378
403
  for task in tasks:
379
404
  task_dict = task.model_dump()
380
405
  # Remove non-serializable verifier object, keep verifier_func (code string)
381
- if 'verifier' in task_dict:
382
- task_dict.pop('verifier')
406
+ if "verifier" in task_dict:
407
+ task_dict.pop("verifier")
383
408
  tasks_data.append(task_dict)
384
-
409
+
385
410
  # Write to JSON file
386
- with open(filename, 'w', encoding='utf-8') as f:
411
+ with open(filename, "w", encoding="utf-8") as f:
387
412
  json.dump(tasks_data, f, indent=2, default=str)
388
-
413
+
389
414
  logger.info(f"Exported {len(tasks)} tasks to {filename}")
390
415
  return filename
391
416
  else:
392
417
  logger.info("No tasks found to export")
393
418
  return None
394
-
419
+
395
420
  def import_tasks(self, filename: str):
396
421
  """Import tasks from a JSON file.
397
-
422
+
398
423
  Args:
399
424
  filename: Path to the JSON file of Task objects to import
400
-
425
+
401
426
  Returns:
402
427
  List[Task] containing imported Task objects
403
428
  """
404
- with open(filename, 'r', encoding='utf-8') as f:
429
+ with open(filename, "r", encoding="utf-8") as f:
405
430
  tasks_data = json.load(f)
406
-
431
+
407
432
  # Create tasks from the loaded data
408
433
  tasks = []
409
434
  for task_data in tasks_data:
@@ -420,7 +445,9 @@ class Fleet:
420
445
  env_variables=task.env_variables or {},
421
446
  )
422
447
  try:
423
- response = self.client.request("POST", "/v1/tasks", json=payload.model_dump())
448
+ response = self.client.request(
449
+ "POST", "/v1/tasks", json=payload.model_dump()
450
+ )
424
451
  except Exception as e:
425
452
  logger.error(f"Failed to import task {task.key}: {e}")
426
453
  continue
@@ -433,108 +460,62 @@ class Fleet:
433
460
  """
434
461
  response = self.client.request("GET", "/v1/account")
435
462
  return AccountResponse(**response.json())
436
-
463
+
437
464
  def _create_verifier_from_data(
438
- self,
439
- verifier_id: str,
440
- verifier_key: str,
441
- verifier_code: str,
442
- verifier_sha: str
465
+ self, verifier_id: str, verifier_key: str, verifier_code: str, verifier_sha: str
443
466
  ) -> "SyncVerifierFunction":
444
- """Create an AsyncVerifierFunction from verifier data.
445
-
467
+ """Create a SyncVerifierFunction from verifier data.
468
+
446
469
  Args:
447
470
  verifier_id: The verifier ID
448
471
  verifier_key: The verifier key
449
472
  verifier_code: The verifier code
450
473
  verifier_sha: The verifier SHA256
451
-
474
+
452
475
  Returns:
453
- AsyncVerifierFunction created from the verifier code
476
+ SyncVerifierFunction created from the verifier code
454
477
  """
478
+ from .tasks import verifier_from_string
455
479
  from .verifiers.verifier import SyncVerifierFunction
456
-
457
- # Convert async verifier code to sync
458
- if 'async def' in verifier_code:
459
- verifier_code = verifier_code.replace('async def', 'def')
460
- if 'await ' in verifier_code:
461
- verifier_code = verifier_code.replace('await ', '')
462
-
463
- # Check if this is a new format verifier (has before/after parameters)
464
- if 'before: DatabaseSnapshot' in verifier_code and 'after: DatabaseSnapshot' in verifier_code:
465
- # Convert new format to old format
466
- verifier_code = convert_new_to_old_verifier(verifier_code)
467
- # Update function name since wrapper adds _wrapper suffix
468
- original_name = extract_function_name(verifier_code.split('\n')[0])
469
- if original_name and original_name.endswith('_wrapper'):
470
- function_name = original_name
471
- else:
472
- function_name = extract_function_name(verifier_code)
473
- else:
474
- # Extract function name from code
475
- function_name = extract_function_name(verifier_code)
476
-
477
- if not function_name:
478
- raise ValueError(f"Could not extract function name from verifier {verifier_id}")
479
-
480
- # Create a function object from the code
481
- # Import necessary classes for the namespace
482
- from .verifiers.db import IgnoreConfig, DatabaseSnapshot
483
-
484
- # Create a namespace for the function
485
- namespace = {
486
- '__builtins__': __builtins__,
487
- 'Environment': object, # Placeholder, will be provided at runtime
488
- 'IgnoreConfig': IgnoreConfig,
489
- 'DatabaseSnapshot': DatabaseSnapshot,
490
- 'TASK_FAILED_SCORE': 0,
491
- 'TASK_SUCCESSFUL_SCORE': 1,
492
- }
493
-
494
- # Execute the code to define the function
495
- exec(verifier_code, namespace)
496
480
 
497
- # Get the function object
498
- if function_name not in namespace:
499
- raise ValueError(f"Function {function_name} not found in verifier code")
500
-
501
- func = namespace[function_name]
502
-
503
- # Create and return AsyncVerifierFunction with SHA if available
504
- # Since the function was created via exec, we need to provide the raw code
505
- verifier_func = SyncVerifierFunction(
506
- func=func,
507
- key=verifier_key,
508
- extra_requirements=[],
481
+ # Use verifier_from_string to create the verifier
482
+ verifier_func = verifier_from_string(
483
+ verifier_func=verifier_code,
509
484
  verifier_id=verifier_id,
510
- sha256=verifier_sha, # Pass SHA directly to constructor
511
- raw_code=verifier_code # Provide raw code since func won't have extractable source
485
+ verifier_key=verifier_key,
486
+ sha256=verifier_sha,
512
487
  )
513
488
 
489
+ # Ensure we return a SyncVerifierFunction
490
+ if not isinstance(verifier_func, SyncVerifierFunction):
491
+ raise TypeError(
492
+ f"Expected SyncVerifierFunction but got {type(verifier_func).__name__}"
493
+ )
494
+
514
495
  # Store the original verifier code for reference
515
496
  verifier_func._verifier_code = verifier_code
516
-
497
+
517
498
  return verifier_func
518
-
499
+
519
500
  def _load_verifier(self, verifier_id: str) -> "SyncVerifierFunction":
520
501
  """Load a verifier by ID and create an AsyncVerifierFunction.
521
-
502
+
522
503
  Args:
523
504
  verifier_id: The verifier ID to fetch
524
-
505
+
525
506
  Returns:
526
507
  AsyncVerifierFunction created from the verifier code
527
508
  """
528
509
  # Fetch verifier from API
529
510
  response = self.client.request("GET", f"/v1/verifier/{verifier_id}")
530
511
  verifier_data = response.json()
531
-
512
+
532
513
  # Use the common method to create verifier
533
514
  return self._create_verifier_from_data(
534
515
  verifier_id=verifier_id,
535
516
  verifier_key=verifier_data["key"],
536
517
  verifier_code=verifier_data["code"],
537
- verifier_sha=verifier_data.get("sha256", "")
518
+ verifier_sha=verifier_data.get("sha256", ""),
538
519
  )
539
520
 
540
521
 
@@ -579,23 +560,29 @@ def _execute_verifier_remote(
579
560
  "timeout": timeout,
580
561
  "region": "us-west-1", # TODO: make configurable
581
562
  }
582
-
563
+
583
564
  # Add bundle data only if upload is needed
584
565
  if needs_upload:
585
566
  bundle_b64 = base64.b64encode(bundle_data).decode("utf-8")
586
567
  request_data["bundle"] = bundle_b64
587
-
568
+
588
569
  # Debug logging
589
- logger.debug(f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}")
570
+ logger.debug(
571
+ f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}"
572
+ )
590
573
  logger.debug(f"Request has bundle: {needs_upload}")
591
574
  logger.debug(f"Using client with base_url: {client.base_url}")
592
575
  logger.debug(f"Request data keys: {list(request_data.keys())}")
593
- logger.debug(f"Bundle size: {len(request_data.get('bundle', ''))} chars" if 'bundle' in request_data else "No bundle")
576
+ logger.debug(
577
+ f"Bundle size: {len(request_data.get('bundle', ''))} chars"
578
+ if "bundle" in request_data
579
+ else "No bundle"
580
+ )
594
581
 
595
582
  # Note: This should be called on the instance URL, not the orchestrator
596
583
  # The instance has manager URLs for verifier execution
597
584
  response = client.request("POST", "/v1/verifiers/execute", json=request_data)
598
-
585
+
599
586
  # Debug the response
600
587
  response_json = response.json()
601
588
  logger.debug(f"Verifier execute response: {response_json}")
fleet/config.py CHANGED
@@ -1,9 +1,10 @@
1
- DEFAULT_MAX_RETRIES = 5
2
- DEFAULT_TIMEOUT = 60.0
1
+ DEFAULT_MAX_RETRIES = 3
2
+ DEFAULT_TIMEOUT = 180.0
3
3
 
4
4
  GLOBAL_BASE_URL = "https://orchestrator.fleetai.com"
5
5
  REGION_BASE_URL = {
6
6
  "us-west-1": "https://us-west-1.fleetai.com",
7
7
  "us-east-1": "https://us-east-1.fleetai.com",
8
8
  "eu-west-2": "https://eu-west-2.fleetai.com",
9
+ "staging": "https://staging.fleetai.com",
9
10
  }
fleet/env/__init__.py CHANGED
@@ -1,6 +1,14 @@
1
1
  """Fleet env module - convenience functions for environment management."""
2
2
 
3
- from .client import make, make_for_task, list_envs, list_regions, get, list_instances, account
3
+ from .client import (
4
+ make,
5
+ make_for_task,
6
+ list_envs,
7
+ list_regions,
8
+ get,
9
+ list_instances,
10
+ account,
11
+ )
4
12
 
5
13
  # Import async versions from _async
6
14
  from .._async.env.client import (
fleet/env/client.py CHANGED
@@ -6,12 +6,15 @@ from typing import List, Optional
6
6
  def make(env_key: str, region: Optional[str] = None) -> SyncEnv:
7
7
  return Fleet().make(env_key, region=region)
8
8
 
9
+
9
10
  def make_for_task(task: Task) -> SyncEnv:
10
11
  return Fleet().make_for_task(task)
11
12
 
13
+
12
14
  def list_envs() -> List[EnvironmentModel]:
13
15
  return Fleet().list_envs()
14
16
 
17
+
15
18
  def list_regions() -> List[str]:
16
19
  return Fleet().list_regions()
17
20
 
fleet/global_client.py ADDED
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from .client import Fleet
6
+ from .config import DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT
7
+
8
+
9
+ _default_client: Optional[Fleet] = None
10
+
11
+
12
+ def get_client() -> Fleet:
13
+ """Get the global default AsyncFleet client, creating it if needed."""
14
+ global _default_client
15
+ if _default_client is None:
16
+ _default_client = Fleet()
17
+ return _default_client
18
+
19
+
20
+ def configure(
21
+ api_key: Optional[str] = None,
22
+ base_url: Optional[str] = None,
23
+ max_retries: int = DEFAULT_MAX_RETRIES,
24
+ timeout: float = DEFAULT_TIMEOUT,
25
+ ) -> Fleet:
26
+ """Configure the global default AsyncFleet client.
27
+
28
+ Returns the configured client instance.
29
+ """
30
+ global _default_client
31
+ _default_client = Fleet(
32
+ api_key=api_key,
33
+ base_url=base_url,
34
+ max_retries=max_retries,
35
+ timeout=timeout,
36
+ )
37
+ return _default_client
38
+
39
+
40
+ def reset_client() -> None:
41
+ """Reset the global default client. A new one will be created on next access."""
42
+ global _default_client
43
+ _default_client = None
@@ -19,5 +19,5 @@ __all__ = [
19
19
  "ChromeStartRequest",
20
20
  "ChromeStartResponse",
21
21
  "ChromeStatusResponse",
22
- "ExecuteFunctionResponse"
22
+ "ExecuteFunctionResponse",
23
23
  ]
fleet/instance/client.py CHANGED
@@ -63,9 +63,7 @@ class InstanceClient:
63
63
  def load(self) -> None:
64
64
  self._load_resources()
65
65
 
66
- def reset(
67
- self, reset_request: Optional[ResetRequest] = None
68
- ) -> ResetResponse:
66
+ def reset(self, reset_request: Optional[ResetRequest] = None) -> ResetResponse:
69
67
  response = self.client.request(
70
68
  "POST", "/reset", json=reset_request.model_dump() if reset_request else None
71
69
  )
@@ -130,7 +128,7 @@ class InstanceClient:
130
128
 
131
129
  def _load_resources(self) -> None:
132
130
  if self._resources is None:
133
- response = self.client.request("GET", "/resources")
131
+ response = self.client.request("GET", "/resources", timeout=1.0)
134
132
  if response.status_code != 200:
135
133
  self._resources = []
136
134
  return