fleet-python 0.2.28__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 +194 -127
  23. fleet/_async/env/client.py +5 -1
  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 +71 -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 +176 -136
  36. fleet/config.py +3 -2
  37. fleet/env/__init__.py +10 -1
  38. fleet/env/client.py +5 -1
  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 +197 -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.28.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.28.dist-info/RECORD +0 -70
  59. {fleet_python-0.2.28.dist-info → fleet_python-0.2.32.dist-info}/WHEEL +0 -0
  60. {fleet_python-0.2.28.dist-info → fleet_python-0.2.32.dist-info}/licenses/LICENSE +0 -0
  61. {fleet_python-0.2.28.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
 
@@ -213,6 +224,9 @@ class Fleet:
213
224
  instance.instance.load()
214
225
  return instance
215
226
 
227
+ def make_for_task(self, task: Task) -> SyncEnv:
228
+ return self.make(env_key=f"{task.env_id}:{task.version}")
229
+
216
230
  def instances(
217
231
  self, status: Optional[str] = None, region: Optional[str] = None
218
232
  ) -> List[SyncEnv]:
@@ -240,51 +254,114 @@ class Fleet:
240
254
  def execute_verifier_remote(
241
255
  self, bundle_data: bytes, args: tuple, kwargs: dict, timeout: Optional[int] = 30
242
256
  ) -> VerifiersExecuteResponse:
243
- return _execute_verifier_remote(
244
- self.client, bundle_data, args, kwargs, timeout
245
- )
257
+ return _execute_verifier_remote(self.client, bundle_data, args, kwargs, timeout)
246
258
 
247
259
  def delete(self, instance_id: str) -> InstanceResponse:
248
260
  return _delete_instance(self.client, instance_id)
249
261
 
262
+ def load_tasks_from_file(self, filename: str) -> List[Task]:
263
+ with open(filename, "r", encoding="utf-8") as f:
264
+ tasks_data = f.read()
265
+
266
+ return self.load_task_array_from_string(tasks_data)
267
+
268
+ def load_task_array_from_string(self, serialized_tasks: List[Dict]) -> List[Task]:
269
+ tasks = []
270
+
271
+ json_tasks = json.loads(serialized_tasks)
272
+ for json_task in json_tasks:
273
+ parsed_task = self.load_task_from_json(json_task)
274
+ tasks.append(parsed_task)
275
+ return tasks
276
+
277
+ def load_task_from_string(self, task_string: str) -> Task:
278
+ task_json = json.loads(task_string)
279
+ return self.load_task_from_json(task_json)
280
+
281
+ def load_task_from_json(self, task_json: Dict) -> Task:
282
+ try:
283
+ if "verifier_id" in task_json and task_json["verifier_id"]:
284
+ verifier = self._create_verifier_from_data(
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", ""),
289
+ )
290
+ except Exception as e:
291
+ logger.warning(f"Failed to create verifier {task_json['key']}: {e}")
292
+
293
+ task = Task(
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
301
+ verifier=verifier, # Use created verifier or None
302
+ metadata=task_json.get("metadata", {}), # Default empty metadata
303
+ )
304
+ return task
305
+
250
306
  def load_tasks(self, env_key: Optional[str] = None) -> List[Task]:
251
307
  """Load tasks for the authenticated team, optionally filtered by environment.
252
-
308
+
253
309
  Args:
254
310
  env_key: Optional environment key to filter tasks by
255
-
311
+
256
312
  Returns:
257
313
  List[Task] containing Task objects
258
314
  """
259
315
  params = {}
260
316
  if env_key is not None:
261
317
  params["env_key"] = env_key
262
-
318
+
263
319
  response = self.client.request("GET", "/v1/tasks", params=params)
264
320
  task_list_response = TaskListResponse(**response.json())
265
-
321
+
266
322
  # Transform TaskResponse objects to Task objects
267
323
  tasks = []
268
324
  for task_response in task_list_response.tasks:
269
325
  # Create verifier function if verifier data is present
270
326
  verifier = None
271
327
  verifier_func = task_response.verifier_func
272
-
328
+
273
329
  if task_response.verifier:
274
- # Use verifier code from the embedded verifier object
275
- verifier_func = task_response.verifier.code
276
-
277
- # Create VerifierFunction from the embedded data
278
- try:
279
- verifier = self._create_verifier_from_data(
280
- verifier_id=task_response.verifier.verifier_id,
281
- verifier_key=task_response.verifier.key,
282
- verifier_code=task_response.verifier.code,
283
- verifier_sha=task_response.verifier.sha256
284
- )
285
- except Exception as e:
286
- logger.warning(f"Failed to create verifier {task_response.verifier.key}: {e}")
287
-
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
+
288
365
  task = Task(
289
366
  key=task_response.key,
290
367
  prompt=task_response.prompt,
@@ -294,19 +371,21 @@ class Fleet:
294
371
  env_variables=task_response.env_variables or {},
295
372
  verifier_func=verifier_func, # Set verifier code
296
373
  verifier=verifier, # Use created verifier or None
297
- metadata={} # Default empty metadata
374
+ metadata={}, # Default empty metadata
298
375
  )
299
376
  tasks.append(task)
300
-
377
+
301
378
  return tasks
302
379
 
303
- 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
+ ):
304
383
  """Export tasks for the authenticated team, optionally filtered by environment.
305
-
384
+
306
385
  Args:
307
386
  env_key: Optional environment key to filter tasks by
308
387
  filename: Optional filename to write tasks to. If not provided, defaults to 'tasks.json' or 'tasks_{env_key}.json'
309
-
388
+
310
389
  Returns:
311
390
  str: Path to the exported file if tasks were written, None if no tasks found
312
391
  """
@@ -318,38 +397,38 @@ class Fleet:
318
397
  filename = f"tasks_{env_key}.json"
319
398
  else:
320
399
  filename = "tasks.json"
321
-
400
+
322
401
  # Convert tasks to serializable format
323
402
  tasks_data = []
324
403
  for task in tasks:
325
404
  task_dict = task.model_dump()
326
405
  # Remove non-serializable verifier object, keep verifier_func (code string)
327
- if 'verifier' in task_dict:
328
- task_dict.pop('verifier')
406
+ if "verifier" in task_dict:
407
+ task_dict.pop("verifier")
329
408
  tasks_data.append(task_dict)
330
-
409
+
331
410
  # Write to JSON file
332
- with open(filename, 'w', encoding='utf-8') as f:
411
+ with open(filename, "w", encoding="utf-8") as f:
333
412
  json.dump(tasks_data, f, indent=2, default=str)
334
-
413
+
335
414
  logger.info(f"Exported {len(tasks)} tasks to {filename}")
336
415
  return filename
337
416
  else:
338
417
  logger.info("No tasks found to export")
339
418
  return None
340
-
419
+
341
420
  def import_tasks(self, filename: str):
342
421
  """Import tasks from a JSON file.
343
-
422
+
344
423
  Args:
345
424
  filename: Path to the JSON file of Task objects to import
346
-
425
+
347
426
  Returns:
348
427
  List[Task] containing imported Task objects
349
428
  """
350
- with open(filename, 'r', encoding='utf-8') as f:
429
+ with open(filename, "r", encoding="utf-8") as f:
351
430
  tasks_data = json.load(f)
352
-
431
+
353
432
  # Create tasks from the loaded data
354
433
  tasks = []
355
434
  for task_data in tasks_data:
@@ -366,12 +445,13 @@ class Fleet:
366
445
  env_variables=task.env_variables or {},
367
446
  )
368
447
  try:
369
- 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
+ )
370
451
  except Exception as e:
371
452
  logger.error(f"Failed to import task {task.key}: {e}")
372
453
  continue
373
454
 
374
-
375
455
  def account(self) -> AccountResponse:
376
456
  """Get account information including instance limits and usage.
377
457
 
@@ -380,108 +460,62 @@ class Fleet:
380
460
  """
381
461
  response = self.client.request("GET", "/v1/account")
382
462
  return AccountResponse(**response.json())
383
-
463
+
384
464
  def _create_verifier_from_data(
385
- self,
386
- verifier_id: str,
387
- verifier_key: str,
388
- verifier_code: str,
389
- verifier_sha: str
465
+ self, verifier_id: str, verifier_key: str, verifier_code: str, verifier_sha: str
390
466
  ) -> "SyncVerifierFunction":
391
- """Create an AsyncVerifierFunction from verifier data.
392
-
467
+ """Create a SyncVerifierFunction from verifier data.
468
+
393
469
  Args:
394
470
  verifier_id: The verifier ID
395
471
  verifier_key: The verifier key
396
472
  verifier_code: The verifier code
397
473
  verifier_sha: The verifier SHA256
398
-
474
+
399
475
  Returns:
400
- AsyncVerifierFunction created from the verifier code
476
+ SyncVerifierFunction created from the verifier code
401
477
  """
478
+ from .tasks import verifier_from_string
402
479
  from .verifiers.verifier import SyncVerifierFunction
403
-
404
- # Convert async verifier code to sync
405
- if 'async def' in verifier_code:
406
- verifier_code = verifier_code.replace('async def', 'def')
407
- if 'await ' in verifier_code:
408
- verifier_code = verifier_code.replace('await ', '')
409
-
410
- # Check if this is a new format verifier (has before/after parameters)
411
- if 'before: DatabaseSnapshot' in verifier_code and 'after: DatabaseSnapshot' in verifier_code:
412
- # Convert new format to old format
413
- verifier_code = convert_new_to_old_verifier(verifier_code)
414
- # Update function name since wrapper adds _wrapper suffix
415
- original_name = extract_function_name(verifier_code.split('\n')[0])
416
- if original_name and original_name.endswith('_wrapper'):
417
- function_name = original_name
418
- else:
419
- function_name = extract_function_name(verifier_code)
420
- else:
421
- # Extract function name from code
422
- function_name = extract_function_name(verifier_code)
423
-
424
- if not function_name:
425
- raise ValueError(f"Could not extract function name from verifier {verifier_id}")
426
-
427
- # Create a function object from the code
428
- # Import necessary classes for the namespace
429
- from .verifiers.db import IgnoreConfig, DatabaseSnapshot
430
480
 
431
- # Create a namespace for the function
432
- namespace = {
433
- '__builtins__': __builtins__,
434
- 'Environment': object, # Placeholder, will be provided at runtime
435
- 'IgnoreConfig': IgnoreConfig,
436
- 'DatabaseSnapshot': DatabaseSnapshot,
437
- 'TASK_FAILED_SCORE': 0,
438
- 'TASK_SUCCESSFUL_SCORE': 1,
439
- }
440
-
441
- # Execute the code to define the function
442
- exec(verifier_code, namespace)
443
-
444
- # Get the function object
445
- if function_name not in namespace:
446
- raise ValueError(f"Function {function_name} not found in verifier code")
447
-
448
- func = namespace[function_name]
449
-
450
- # Create and return AsyncVerifierFunction with SHA if available
451
- # Since the function was created via exec, we need to provide the raw code
452
- verifier_func = SyncVerifierFunction(
453
- func=func,
454
- key=verifier_key,
455
- extra_requirements=[],
481
+ # Use verifier_from_string to create the verifier
482
+ verifier_func = verifier_from_string(
483
+ verifier_func=verifier_code,
456
484
  verifier_id=verifier_id,
457
- sha256=verifier_sha, # Pass SHA directly to constructor
458
- raw_code=verifier_code # Provide raw code since func won't have extractable source
485
+ verifier_key=verifier_key,
486
+ sha256=verifier_sha,
459
487
  )
460
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
+
461
495
  # Store the original verifier code for reference
462
496
  verifier_func._verifier_code = verifier_code
463
-
497
+
464
498
  return verifier_func
465
-
499
+
466
500
  def _load_verifier(self, verifier_id: str) -> "SyncVerifierFunction":
467
501
  """Load a verifier by ID and create an AsyncVerifierFunction.
468
-
502
+
469
503
  Args:
470
504
  verifier_id: The verifier ID to fetch
471
-
505
+
472
506
  Returns:
473
507
  AsyncVerifierFunction created from the verifier code
474
508
  """
475
509
  # Fetch verifier from API
476
510
  response = self.client.request("GET", f"/v1/verifier/{verifier_id}")
477
511
  verifier_data = response.json()
478
-
512
+
479
513
  # Use the common method to create verifier
480
514
  return self._create_verifier_from_data(
481
515
  verifier_id=verifier_id,
482
516
  verifier_key=verifier_data["key"],
483
517
  verifier_code=verifier_data["code"],
484
- verifier_sha=verifier_data.get("sha256", "")
518
+ verifier_sha=verifier_data.get("sha256", ""),
485
519
  )
486
520
 
487
521
 
@@ -526,23 +560,29 @@ def _execute_verifier_remote(
526
560
  "timeout": timeout,
527
561
  "region": "us-west-1", # TODO: make configurable
528
562
  }
529
-
563
+
530
564
  # Add bundle data only if upload is needed
531
565
  if needs_upload:
532
566
  bundle_b64 = base64.b64encode(bundle_data).decode("utf-8")
533
567
  request_data["bundle"] = bundle_b64
534
-
568
+
535
569
  # Debug logging
536
- 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
+ )
537
573
  logger.debug(f"Request has bundle: {needs_upload}")
538
574
  logger.debug(f"Using client with base_url: {client.base_url}")
539
575
  logger.debug(f"Request data keys: {list(request_data.keys())}")
540
- 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
+ )
541
581
 
542
582
  # Note: This should be called on the instance URL, not the orchestrator
543
583
  # The instance has manager URLs for verifier execution
544
584
  response = client.request("POST", "/v1/verifiers/execute", json=request_data)
545
-
585
+
546
586
  # Debug the response
547
587
  response_json = response.json()
548
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, 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 (
@@ -14,6 +22,7 @@ from .._async.env.client import (
14
22
 
15
23
  __all__ = [
16
24
  "make",
25
+ "make_for_task",
17
26
  "list_envs",
18
27
  "list_regions",
19
28
  "list_instances",
fleet/env/client.py CHANGED
@@ -1,4 +1,4 @@
1
- from ..client import Fleet, SyncEnv
1
+ from ..client import Fleet, SyncEnv, Task
2
2
  from ..models import Environment as EnvironmentModel, AccountResponse
3
3
  from typing import List, Optional
4
4
 
@@ -7,6 +7,10 @@ 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:
11
+ return Fleet().make_for_task(task)
12
+
13
+
10
14
  def list_envs() -> List[EnvironmentModel]:
11
15
  return Fleet().list_envs()
12
16
 
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