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/_async/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 AsyncVerifierFunction
@@ -51,6 +50,7 @@ from .instance.client import ValidatorType
51
50
  from .resources.base import Resource
52
51
  from .resources.sqlite import AsyncSQLiteResource
53
52
  from .resources.browser import AsyncBrowserResource
53
+ from ..resources.mcp import MCPResource
54
54
 
55
55
  logger = logging.getLogger(__name__)
56
56
 
@@ -69,18 +69,18 @@ class AsyncEnv(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) -> AsyncInstanceClient:
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] = AsyncInstanceClient(
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 AsyncEnv(EnvironmentBase):
104
104
  def browser(self, name: str = "cdp") -> AsyncBrowserResource:
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 AsyncEnv(EnvironmentBase):
125
130
  return await _check_bundle_exists(self._load_client, bundle_hash)
126
131
 
127
132
  async 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 await _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 AsyncFleet:
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 = AsyncWrapper(
175
182
  api_key=api_key,
@@ -189,23 +196,27 @@ class AsyncFleet:
189
196
  response = await self.client.request("GET", f"/v1/env/{env_key}")
190
197
  return EnvironmentModel(**response.json())
191
198
 
192
- async def make(
193
- self, env_key: str, region: Optional[str] = None
194
- ) -> AsyncEnv:
199
+ async def make(self, env_key: str, region: Optional[str] = None) -> AsyncEnv:
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 = await 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
 
@@ -214,7 +225,7 @@ class AsyncFleet:
214
225
  return instance
215
226
 
216
227
  async def make_for_task(self, task: Task) -> AsyncEnv:
217
- return self.make(env_key=f"{task.env_id}:{task.version}")
228
+ return await self.make(env_key=f"{task.env_id}:{task.version}")
218
229
 
219
230
  async def instances(
220
231
  self, status: Optional[str] = None, region: Optional[str] = None
@@ -251,12 +262,14 @@ class AsyncFleet:
251
262
  return await _delete_instance(self.client, instance_id)
252
263
 
253
264
  async def load_tasks_from_file(self, filename: str) -> List[Task]:
254
- with open(filename, 'r', encoding='utf-8') as f:
265
+ with open(filename, "r", encoding="utf-8") as f:
255
266
  tasks_data = f.read()
256
267
 
257
268
  return self.load_task_array_from_string(tasks_data)
258
269
 
259
- async def load_task_array_from_string(self, serialized_tasks: List[Dict]) -> List[Task]:
270
+ async def load_task_array_from_string(
271
+ self, serialized_tasks: List[Dict]
272
+ ) -> List[Task]:
260
273
  tasks = []
261
274
 
262
275
  json_tasks = json.loads(serialized_tasks)
@@ -271,67 +284,101 @@ class AsyncFleet:
271
284
 
272
285
  async def load_task_from_json(self, task_json: Dict) -> Task:
273
286
  try:
274
- if 'verifier_id' in task_json and task_json['verifier_id']:
287
+ if "verifier_id" in task_json and task_json["verifier_id"]:
275
288
  verifier = self._create_verifier_from_data(
276
- verifier_id=task_json['verifier_id'],
277
- verifier_key=task_json['key'],
278
- verifier_code=task_json['verifier_func'],
279
- verifier_sha=task_json.get('verifier_sha', '')
289
+ verifier_id=task_json["verifier_id"],
290
+ verifier_key=task_json["key"],
291
+ verifier_code=task_json["verifier_func"],
292
+ verifier_sha=task_json.get("verifier_sha", ""),
280
293
  )
281
294
  except Exception as e:
282
295
  logger.warning(f"Failed to create verifier {task_json['key']}: {e}")
283
-
296
+
284
297
  task = Task(
285
- key=task_json['key'],
286
- prompt=task_json['prompt'],
287
- env_id=task_json['env_id'], # Use env_id from the data
288
- created_at=task_json['created_at'],
289
- version=task_json.get('version'),
290
- env_variables=task_json.get('env_variables', {}),
291
- verifier_func=task_json.get('verifier_func'), # Set verifier code
298
+ key=task_json["key"],
299
+ prompt=task_json["prompt"],
300
+ env_id=task_json["env_id"], # Use env_id from the data
301
+ created_at=task_json["created_at"],
302
+ version=task_json.get("version"),
303
+ env_variables=task_json.get("env_variables", {}),
304
+ verifier_func=task_json.get("verifier_func"), # Set verifier code
292
305
  verifier=verifier, # Use created verifier or None
293
- metadata=task_json.get('metadata', {}) # Default empty metadata
306
+ metadata=task_json.get("metadata", {}), # Default empty metadata
294
307
  )
295
308
  return task
296
309
 
297
- async def load_tasks(self, env_key: Optional[str] = None) -> List[Task]:
298
- """Load tasks for the authenticated team, optionally filtered by environment.
299
-
310
+ async def load_tasks(
311
+ self,
312
+ env_key: Optional[str] = None,
313
+ keys: Optional[List[str]] = None,
314
+ version: Optional[str] = None,
315
+ team_id: Optional[str] = None
316
+ ) -> List[Task]:
317
+ """Load tasks for the authenticated team, with optional filtering.
318
+
300
319
  Args:
301
320
  env_key: Optional environment key to filter tasks by
302
-
321
+ keys: Optional list of task keys to filter by
322
+ version: Optional version to filter tasks by (client-side filter)
323
+ team_id: Optional team_id to filter by (admin only)
324
+
303
325
  Returns:
304
326
  List[Task] containing Task objects
305
327
  """
306
328
  params = {}
307
329
  if env_key is not None:
308
330
  params["env_key"] = env_key
309
-
331
+ if keys is not None:
332
+ params["task_keys"] = keys
333
+ if team_id is not None:
334
+ params["team_id"] = team_id
335
+
310
336
  response = await self.client.request("GET", "/v1/tasks", params=params)
311
337
  task_list_response = TaskListResponse(**response.json())
312
-
338
+
313
339
  # Transform TaskResponse objects to Task objects
314
340
  tasks = []
315
341
  for task_response in task_list_response.tasks:
316
342
  # Create verifier function if verifier data is present
317
343
  verifier = None
318
344
  verifier_func = task_response.verifier_func
319
-
345
+
320
346
  if task_response.verifier:
321
- # Use verifier code from the embedded verifier object
322
- verifier_func = task_response.verifier.code
323
-
324
- # Create VerifierFunction from the embedded data
325
- try:
326
- verifier = await self._create_verifier_from_data(
327
- verifier_id=task_response.verifier.verifier_id,
328
- verifier_key=task_response.verifier.key,
329
- verifier_code=task_response.verifier.code,
330
- verifier_sha=task_response.verifier.sha256
331
- )
332
- except Exception as e:
333
- logger.warning(f"Failed to create verifier {task_response.verifier.key}: {e}")
334
-
347
+ embedded_code = task_response.verifier.code or ""
348
+ is_embedded_error = embedded_code.strip().startswith(
349
+ "<error loading code:"
350
+ )
351
+ if not is_embedded_error:
352
+ # Only override if the embedded code looks valid
353
+ verifier_func = embedded_code
354
+ # Create VerifierFunction from the embedded data
355
+ try:
356
+ verifier = await self._create_verifier_from_data(
357
+ verifier_id=task_response.verifier.verifier_id,
358
+ verifier_key=task_response.verifier.key,
359
+ verifier_code=embedded_code,
360
+ verifier_sha=task_response.verifier.sha256,
361
+ )
362
+ except Exception as e:
363
+ logger.warning(
364
+ f"Failed to create verifier {task_response.verifier.key}: {e}"
365
+ )
366
+ else:
367
+ # Fallback: try fetching by ID if embedded code failed to load
368
+ try:
369
+ logger.warning(
370
+ f"Embedded verifier code missing for {task_response.verifier.key} (NoSuchKey). "
371
+ f"Attempting to refetch by id {task_response.verifier.verifier_id}"
372
+ )
373
+ verifier = await self._load_verifier(
374
+ task_response.verifier.verifier_id
375
+ )
376
+ except Exception as e:
377
+ logger.warning(
378
+ f"Refetch by verifier id failed for {task_response.verifier.key}: {e}. "
379
+ "Leaving verifier unset."
380
+ )
381
+
335
382
  task = Task(
336
383
  key=task_response.key,
337
384
  prompt=task_response.prompt,
@@ -341,19 +388,25 @@ class AsyncFleet:
341
388
  env_variables=task_response.env_variables or {},
342
389
  verifier_func=verifier_func, # Set verifier code
343
390
  verifier=verifier, # Use created verifier or None
344
- metadata={} # Default empty metadata
391
+ metadata={}, # Default empty metadata
345
392
  )
346
393
  tasks.append(task)
347
-
394
+
395
+ # Apply client-side filtering for version if specified
396
+ if version is not None:
397
+ tasks = [task for task in tasks if task.version == version]
398
+
348
399
  return tasks
349
400
 
350
- async def export_tasks(self, env_key: Optional[str] = None, filename: Optional[str] = None):
401
+ async def export_tasks(
402
+ self, env_key: Optional[str] = None, filename: Optional[str] = None
403
+ ):
351
404
  """Export tasks for the authenticated team, optionally filtered by environment.
352
-
405
+
353
406
  Args:
354
407
  env_key: Optional environment key to filter tasks by
355
408
  filename: Optional filename to write tasks to. If not provided, defaults to 'tasks.json' or 'tasks_{env_key}.json'
356
-
409
+
357
410
  Returns:
358
411
  str: Path to the exported file if tasks were written, None if no tasks found
359
412
  """
@@ -365,38 +418,38 @@ class AsyncFleet:
365
418
  filename = f"tasks_{env_key}.json"
366
419
  else:
367
420
  filename = "tasks.json"
368
-
421
+
369
422
  # Convert tasks to serializable format
370
423
  tasks_data = []
371
424
  for task in tasks:
372
425
  task_dict = task.model_dump()
373
426
  # Remove non-serializable verifier object, keep verifier_func (code string)
374
- if 'verifier' in task_dict:
375
- task_dict.pop('verifier')
427
+ if "verifier" in task_dict:
428
+ task_dict.pop("verifier")
376
429
  tasks_data.append(task_dict)
377
-
430
+
378
431
  # Write to JSON file
379
- with open(filename, 'w', encoding='utf-8') as f:
432
+ with open(filename, "w", encoding="utf-8") as f:
380
433
  json.dump(tasks_data, f, indent=2, default=str)
381
-
434
+
382
435
  logger.info(f"Exported {len(tasks)} tasks to {filename}")
383
436
  return filename
384
437
  else:
385
438
  logger.info("No tasks found to export")
386
439
  return None
387
-
440
+
388
441
  async def import_tasks(self, filename: str):
389
442
  """Import tasks from a JSON file.
390
-
443
+
391
444
  Args:
392
445
  filename: Path to the JSON file of Task objects to import
393
-
446
+
394
447
  Returns:
395
448
  List[Task] containing imported Task objects
396
449
  """
397
- with open(filename, 'r', encoding='utf-8') as f:
450
+ with open(filename, "r", encoding="utf-8") as f:
398
451
  tasks_data = json.load(f)
399
-
452
+
400
453
  # Create tasks from the loaded data
401
454
  tasks = []
402
455
  for task_data in tasks_data:
@@ -413,12 +466,13 @@ class AsyncFleet:
413
466
  env_variables=task.env_variables or {},
414
467
  )
415
468
  try:
416
- response = await self.client.request("POST", "/v1/tasks", json=payload.model_dump())
469
+ response = await self.client.request(
470
+ "POST", "/v1/tasks", json=payload.model_dump()
471
+ )
417
472
  except Exception as e:
418
473
  logger.error(f"Failed to import task {task.key}: {e}")
419
474
  continue
420
475
 
421
-
422
476
  async def account(self) -> AccountResponse:
423
477
  """Get account information including instance limits and usage.
424
478
 
@@ -427,102 +481,62 @@ class AsyncFleet:
427
481
  """
428
482
  response = await self.client.request("GET", "/v1/account")
429
483
  return AccountResponse(**response.json())
430
-
484
+
431
485
  async def _create_verifier_from_data(
432
- self,
433
- verifier_id: str,
434
- verifier_key: str,
435
- verifier_code: str,
436
- verifier_sha: str
486
+ self, verifier_id: str, verifier_key: str, verifier_code: str, verifier_sha: str
437
487
  ) -> "AsyncVerifierFunction":
438
488
  """Create an AsyncVerifierFunction from verifier data.
439
-
489
+
440
490
  Args:
441
491
  verifier_id: The verifier ID
442
492
  verifier_key: The verifier key
443
493
  verifier_code: The verifier code
444
494
  verifier_sha: The verifier SHA256
445
-
495
+
446
496
  Returns:
447
497
  AsyncVerifierFunction created from the verifier code
448
498
  """
499
+ from ..tasks import verifier_from_string
449
500
  from .verifiers.verifier import AsyncVerifierFunction
450
501
 
451
- # Check if this is a new format verifier (has before/after parameters)
452
- if 'before: DatabaseSnapshot' in verifier_code and 'after: DatabaseSnapshot' in verifier_code:
453
- # Convert new format to old format
454
- verifier_code = convert_new_to_old_verifier(verifier_code)
455
- # Update function name since wrapper adds _wrapper suffix
456
- original_name = extract_function_name(verifier_code.split('\n')[0])
457
- if original_name and original_name.endswith('_wrapper'):
458
- function_name = original_name
459
- else:
460
- function_name = extract_function_name(verifier_code)
461
- else:
462
- # Extract function name from code
463
- function_name = extract_function_name(verifier_code)
464
-
465
- if not function_name:
466
- raise ValueError(f"Could not extract function name from verifier {verifier_id}")
467
-
468
- # Create a function object from the code
469
- # Import necessary classes for the namespace
470
- from ..verifiers.db import IgnoreConfig, DatabaseSnapshot
471
-
472
- # Create a namespace for the function
473
- namespace = {
474
- '__builtins__': __builtins__,
475
- 'Environment': object, # Placeholder, will be provided at runtime
476
- 'IgnoreConfig': IgnoreConfig,
477
- 'DatabaseSnapshot': DatabaseSnapshot,
478
- 'TASK_FAILED_SCORE': 0,
479
- 'TASK_SUCCESSFUL_SCORE': 1,
480
- }
481
-
482
- # Execute the code to define the function
483
- exec(verifier_code, namespace)
484
-
485
- # Get the function object
486
- if function_name not in namespace:
487
- raise ValueError(f"Function {function_name} not found in verifier code")
488
-
489
- func = namespace[function_name]
490
-
491
- # Create and return AsyncVerifierFunction with SHA if available
492
- # Since the function was created via exec, we need to provide the raw code
493
- verifier_func = AsyncVerifierFunction(
494
- func=func,
495
- key=verifier_key,
496
- extra_requirements=[],
502
+ # Use verifier_from_string to create the verifier
503
+ verifier_func = verifier_from_string(
504
+ verifier_func=verifier_code,
497
505
  verifier_id=verifier_id,
498
- sha256=verifier_sha, # Pass SHA directly to constructor
499
- raw_code=verifier_code # Provide raw code since func won't have extractable source
506
+ verifier_key=verifier_key,
507
+ sha256=verifier_sha,
500
508
  )
501
509
 
510
+ # Ensure we return an AsyncVerifierFunction
511
+ if not isinstance(verifier_func, AsyncVerifierFunction):
512
+ raise TypeError(
513
+ f"Expected AsyncVerifierFunction but got {type(verifier_func).__name__}"
514
+ )
515
+
502
516
  # Store the original verifier code for reference
503
517
  verifier_func._verifier_code = verifier_code
504
-
518
+
505
519
  return verifier_func
506
-
520
+
507
521
  async def _load_verifier(self, verifier_id: str) -> "AsyncVerifierFunction":
508
522
  """Load a verifier by ID and create an AsyncVerifierFunction.
509
-
523
+
510
524
  Args:
511
525
  verifier_id: The verifier ID to fetch
512
-
526
+
513
527
  Returns:
514
528
  AsyncVerifierFunction created from the verifier code
515
529
  """
516
530
  # Fetch verifier from API
517
531
  response = await self.client.request("GET", f"/v1/verifier/{verifier_id}")
518
532
  verifier_data = response.json()
519
-
533
+
520
534
  # Use the common method to create verifier
521
535
  return await self._create_verifier_from_data(
522
536
  verifier_id=verifier_id,
523
537
  verifier_key=verifier_data["key"],
524
538
  verifier_code=verifier_data["code"],
525
- verifier_sha=verifier_data.get("sha256", "")
539
+ verifier_sha=verifier_data.get("sha256", ""),
526
540
  )
527
541
 
528
542
 
@@ -567,23 +581,29 @@ async def _execute_verifier_remote(
567
581
  "timeout": timeout,
568
582
  "region": "us-west-1", # TODO: make configurable
569
583
  }
570
-
584
+
571
585
  # Add bundle data only if upload is needed
572
586
  if needs_upload:
573
587
  bundle_b64 = base64.b64encode(bundle_data).decode("utf-8")
574
588
  request_data["bundle"] = bundle_b64
575
-
589
+
576
590
  # Debug logging
577
- logger.debug(f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}")
591
+ logger.debug(
592
+ f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}"
593
+ )
578
594
  logger.debug(f"Request has bundle: {needs_upload}")
579
595
  logger.debug(f"Using client with base_url: {client.base_url}")
580
596
  logger.debug(f"Request data keys: {list(request_data.keys())}")
581
- logger.debug(f"Bundle size: {len(request_data.get('bundle', ''))} chars" if 'bundle' in request_data else "No bundle")
597
+ logger.debug(
598
+ f"Bundle size: {len(request_data.get('bundle', ''))} chars"
599
+ if "bundle" in request_data
600
+ else "No bundle"
601
+ )
582
602
 
583
603
  # Note: This should be called on the instance URL, not the orchestrator
584
604
  # The instance has manager URLs for verifier execution
585
605
  response = await client.request("POST", "/v1/verifiers/execute", json=request_data)
586
-
606
+
587
607
  # Debug the response
588
608
  response_json = response.json()
589
609
  logger.debug(f"Verifier execute response: {response_json}")
@@ -6,9 +6,11 @@ from typing import List, Optional
6
6
  async def make_async(env_key: str, region: Optional[str] = None) -> AsyncEnv:
7
7
  return await AsyncFleet().make(env_key, region=region)
8
8
 
9
+
9
10
  async def make_for_task_async(task: Task) -> AsyncEnv:
10
11
  return await AsyncFleet().make_for_task(task)
11
12
 
13
+
12
14
  async def list_envs_async() -> List[EnvironmentModel]:
13
15
  return await AsyncFleet().list_envs()
14
16
 
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from .client import AsyncFleet
6
+ from ..config import DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT
7
+
8
+
9
+ _default_client: Optional[AsyncFleet] = None
10
+
11
+
12
+ def get_client() -> AsyncFleet:
13
+ """Get the global default AsyncFleet client, creating it if needed."""
14
+ global _default_client
15
+ if _default_client is None:
16
+ _default_client = AsyncFleet()
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
+ ) -> AsyncFleet:
26
+ """Configure the global default AsyncFleet client.
27
+
28
+ Returns the configured client instance.
29
+ """
30
+ global _default_client
31
+ _default_client = AsyncFleet(
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
@@ -130,7 +130,7 @@ class AsyncInstanceClient:
130
130
 
131
131
  async def _load_resources(self) -> None:
132
132
  if self._resources is None:
133
- response = await self.client.request("GET", "/resources")
133
+ response = await self.client.request("GET", "/resources", timeout=1.0)
134
134
  if response.status_code != 200:
135
135
  self._resources = []
136
136
  return