fleet-python 0.2.62__py3-none-any.whl → 0.2.63__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.

examples/import_tasks.py CHANGED
@@ -2,12 +2,194 @@ import asyncio
2
2
  import argparse
3
3
  import json
4
4
  import sys
5
+ from collections import defaultdict
6
+ from typing import Dict, List, Tuple
5
7
  import fleet
8
+ from fleet._async.tasks import Task
6
9
  from dotenv import load_dotenv
7
10
 
8
11
  load_dotenv()
9
12
 
10
13
 
14
+ async def run_verifier_sanity_check(
15
+ tasks: List[Task],
16
+ client: fleet.AsyncFleet,
17
+ ) -> Tuple[bool, Dict[str, str]]:
18
+ """
19
+ Run sanity check by spinning up instances and running verifiers.
20
+
21
+ Args:
22
+ tasks: List of Task objects to verify
23
+ client: AsyncFleet client instance
24
+
25
+ Returns:
26
+ Tuple of (all_passed, error_dict) where error_dict maps task_key to error message
27
+ """
28
+ print("\n" + "=" * 60)
29
+ print("Running verifier sanity check...")
30
+ print("=" * 60)
31
+
32
+ # Group tasks by env_key×env_version×data_key×data_version
33
+ instance_groups = defaultdict(list)
34
+ for task in tasks:
35
+ # Build the instance key
36
+ env_key = task.env_id or ""
37
+ env_version = task.version or ""
38
+ data_key = task.data_id or ""
39
+ data_version = task.data_version or ""
40
+
41
+ instance_key = f"{env_key}×{env_version}×{data_key}×{data_version}"
42
+ instance_groups[instance_key].append(task)
43
+
44
+ print(f"\nFound {len(instance_groups)} unique environment/data combinations:")
45
+ for instance_key, group_tasks in instance_groups.items():
46
+ print(f" {instance_key}: {len(group_tasks)} task(s)")
47
+
48
+ # Create all instances in parallel
49
+ print(f"\nCreating {len(instance_groups)} instance(s) in parallel...")
50
+ instance_map = {}
51
+
52
+ async def create_instance(instance_key: str) -> Tuple[str, object]:
53
+ """Create a single instance."""
54
+ try:
55
+ env_key, env_version, data_key, data_version = instance_key.split("×")
56
+
57
+ # Build env_key_str and data_key_str
58
+ if env_version:
59
+ env_key_str = f"{env_key}:{env_version}"
60
+ else:
61
+ env_key_str = env_key
62
+
63
+ if data_key and data_version:
64
+ data_key_str = f"{data_key}:{data_version}"
65
+ elif data_key:
66
+ data_key_str = data_key
67
+ else:
68
+ data_key_str = None
69
+
70
+ print(
71
+ f" Creating instance: {env_key_str}"
72
+ + (f" with data {data_key_str}" if data_key_str else "")
73
+ )
74
+ env = await client.make(env_key=env_key_str, data_key=data_key_str)
75
+ return instance_key, env
76
+ except Exception as e:
77
+ print(f" ✗ Failed to create instance for {instance_key}: {e}")
78
+ return instance_key, None
79
+
80
+ # Create instances concurrently
81
+ instance_results = await asyncio.gather(
82
+ *[create_instance(key) for key in instance_groups.keys()],
83
+ return_exceptions=True,
84
+ )
85
+
86
+ for result in instance_results:
87
+ if isinstance(result, Exception):
88
+ print(f" ✗ Exception creating instance: {result}")
89
+ return False, {"__instance_creation__": str(result)}
90
+ instance_key, env = result
91
+ if env is None:
92
+ return False, {instance_key: "Failed to create instance"}
93
+ instance_map[instance_key] = env
94
+
95
+ print(f"✓ Created {len(instance_map)} instance(s)")
96
+
97
+ # Run all verifiers in parallel with concurrency limit
98
+ max_concurrent_verifiers = 5 # Limit concurrent verifier executions
99
+ print(
100
+ f"\nRunning {len(tasks)} verifier(s) in parallel (max {max_concurrent_verifiers} concurrent)..."
101
+ )
102
+ errors = {}
103
+ semaphore = asyncio.Semaphore(max_concurrent_verifiers)
104
+
105
+ async def run_single_verifier(task, instance_key: str) -> Tuple[str, bool, str]:
106
+ """Run a single verifier and return (task_key, success, error_message)."""
107
+ async with semaphore:
108
+ try:
109
+ env = instance_map[instance_key]
110
+ task_key = task.key
111
+
112
+ # Run the verifier
113
+ if task.verifier is None:
114
+ return task_key, False, "No verifier found"
115
+
116
+ result = await task.verify_async(env)
117
+
118
+ # For sanity check: we expect verifiers to return 0.0 (TASK_FAILED_SCORE)
119
+ # since we're running on fresh instances with no task completion.
120
+ # This confirms the verifier runs without errors.
121
+ if isinstance(result, float):
122
+ if result == 0.0:
123
+ print(f" ✓ {task_key}: {result:.2f} (correctly returns 0.0)")
124
+ return task_key, True, ""
125
+ else:
126
+ print(
127
+ f" ⚠ {task_key}: {result:.2f} (expected 0.0 on fresh instance)"
128
+ )
129
+ return (
130
+ task_key,
131
+ False,
132
+ f"Expected 0.0 but got {result:.2f} on fresh instance",
133
+ )
134
+ else:
135
+ # Non-float result - verifier ran but didn't return expected type
136
+ print(f" ⚠ {task_key}: {result} (expected float 0.0)")
137
+ return (
138
+ task_key,
139
+ False,
140
+ f"Expected float 0.0 but got {type(result).__name__}: {result}",
141
+ )
142
+
143
+ except Exception as e:
144
+ task_key = task.key
145
+ error_msg = f"{type(e).__name__}: {str(e)}"
146
+ print(f" ✗ {task_key}: {error_msg}")
147
+ return task_key, False, error_msg
148
+
149
+ # Run verifiers concurrently with semaphore
150
+ verifier_results = await asyncio.gather(
151
+ *[
152
+ run_single_verifier(task, instance_key)
153
+ for instance_key, group_tasks in instance_groups.items()
154
+ for task in group_tasks
155
+ ],
156
+ return_exceptions=True,
157
+ )
158
+
159
+ # Process results
160
+ for result in verifier_results:
161
+ if isinstance(result, Exception):
162
+ print(f" ✗ Exception running verifier: {result}")
163
+ errors["__verifier_exception__"] = str(result)
164
+ else:
165
+ task_key, success, error_msg = result
166
+ if not success:
167
+ errors[task_key] = error_msg
168
+
169
+ # Clean up instances
170
+ print(f"\nCleaning up {len(instance_map)} instance(s)...")
171
+ cleanup_tasks = [env.close() for env in instance_map.values()]
172
+ await asyncio.gather(*cleanup_tasks, return_exceptions=True)
173
+ print("✓ Cleanup complete")
174
+
175
+ # Summary
176
+ passed_count = len(tasks) - len(errors)
177
+ print("\n" + "=" * 60)
178
+ print(f"Sanity check complete: {passed_count}/{len(tasks)} passed")
179
+
180
+ if errors:
181
+ print(f"\n✗ {len(errors)} verifier(s) failed:")
182
+ for task_key, error_msg in list(errors.items())[:10]:
183
+ print(f" - {task_key}: {error_msg}")
184
+ if len(errors) > 10:
185
+ print(f" ... and {len(errors) - 10} more")
186
+ print("\nFix the verifiers and try again.")
187
+ return False, errors
188
+ else:
189
+ print("✓ All verifiers passed!")
190
+ return True, {}
191
+
192
+
11
193
  async def main():
12
194
  parser = argparse.ArgumentParser(description="Import tasks from a JSON file")
13
195
  parser.add_argument("json_file", help="Path to the JSON file containing tasks")
@@ -23,6 +205,11 @@ async def main():
23
205
  action="store_true",
24
206
  help="Skip confirmation prompt and import automatically",
25
207
  )
208
+ parser.add_argument(
209
+ "--skip-sanity-check",
210
+ action="store_true",
211
+ help="Skip the verifier sanity check (not recommended)",
212
+ )
26
213
 
27
214
  args = parser.parse_args()
28
215
 
@@ -47,7 +234,7 @@ async def main():
47
234
  task_keys.append(task_key)
48
235
  else:
49
236
  task_keys.append("(no key)")
50
-
237
+
51
238
  # Check for verifier_func
52
239
  verifier_code = task_data.get("verifier_func") or task_data.get("verifier_code")
53
240
  if not verifier_code:
@@ -78,6 +265,30 @@ async def main():
78
265
  else:
79
266
  print("\nProject key: (none)")
80
267
 
268
+ # Load tasks as Task objects
269
+ client = fleet.AsyncFleet()
270
+ tasks = []
271
+ print("\nLoading tasks...")
272
+ for task_data in tasks_data:
273
+ try:
274
+ task = await client.load_task_from_json(
275
+ task_data, raise_on_verifier_error=True
276
+ )
277
+ tasks.append(task)
278
+ except Exception as e:
279
+ task_key = task_data.get("key") or task_data.get("id", "unknown")
280
+ print(f"✗ Failed to load task {task_key}: {e}")
281
+ sys.exit(1)
282
+ print(f"✓ Loaded {len(tasks)} tasks")
283
+
284
+ # Run sanity check (unless skipped)
285
+ if not args.skip_sanity_check:
286
+ success, errors = await run_verifier_sanity_check(tasks, client)
287
+ if not success:
288
+ sys.exit(1)
289
+ else:
290
+ print("\n⚠️ Skipping sanity check (--skip-sanity-check)")
291
+
81
292
  # Confirmation prompt (unless --yes flag is provided)
82
293
  if not args.yes:
83
294
  print("\n" + "=" * 60)
fleet/_async/tasks.py CHANGED
@@ -274,10 +274,18 @@ def verifier_from_string(
274
274
  """
275
275
  try:
276
276
  import inspect
277
+ import re
277
278
  from .verifiers.verifier import AsyncVerifierFunction
278
279
  from fleet.verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
279
280
  from fleet.verifiers.db import IgnoreConfig
280
281
 
282
+ # Strip @verifier decorator if present to avoid double-wrapping
283
+ # Remove lines like: @verifier(key="...")
284
+ cleaned_code = re.sub(r'@verifier\([^)]*\)\s*\n', '', verifier_func)
285
+ # Also remove the verifier import if present
286
+ cleaned_code = re.sub(r'from fleet import.*verifier.*\n', '', cleaned_code)
287
+ cleaned_code = re.sub(r'import.*verifier.*\n', '', cleaned_code)
288
+
281
289
  # Create a local namespace for executing the code
282
290
  local_namespace = {
283
291
  "TASK_SUCCESSFUL_SCORE": TASK_SUCCESSFUL_SCORE,
@@ -286,8 +294,8 @@ def verifier_from_string(
286
294
  "Environment": object, # Add Environment type if needed
287
295
  }
288
296
 
289
- # Execute the verifier code in the namespace
290
- exec(verifier_func, globals(), local_namespace)
297
+ # Execute the cleaned verifier code in the namespace
298
+ exec(cleaned_code, globals(), local_namespace)
291
299
 
292
300
  # Find the function that was defined (not imported)
293
301
  # Functions defined via exec have co_filename == '<string>'
fleet/tasks.py CHANGED
@@ -265,10 +265,18 @@ def verifier_from_string(
265
265
  """
266
266
  try:
267
267
  import inspect
268
+ import re
268
269
  from .verifiers import SyncVerifierFunction
269
270
  from .verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
270
271
  from .verifiers.db import IgnoreConfig
271
272
 
273
+ # Strip @verifier decorator if present to avoid double-wrapping
274
+ # Remove lines like: @verifier(key="...")
275
+ cleaned_code = re.sub(r'@verifier\([^)]*\)\s*\n', '', verifier_func)
276
+ # Also remove the verifier import if present
277
+ cleaned_code = re.sub(r'from fleet import.*verifier.*\n', '', cleaned_code)
278
+ cleaned_code = re.sub(r'import.*verifier.*\n', '', cleaned_code)
279
+
272
280
  # Create a globals namespace with all required imports
273
281
  exec_globals = globals().copy()
274
282
  exec_globals.update(
@@ -283,8 +291,8 @@ def verifier_from_string(
283
291
  # Create a local namespace for executing the code
284
292
  local_namespace = {}
285
293
 
286
- # Execute the verifier code in the namespace
287
- exec(verifier_func, exec_globals, local_namespace)
294
+ # Execute the cleaned verifier code in the namespace
295
+ exec(cleaned_code, exec_globals, local_namespace)
288
296
 
289
297
  # Find the function that was defined (not imported)
290
298
  # Functions defined via exec have co_filename == '<string>'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.62
3
+ Version: 0.2.63
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -13,7 +13,7 @@ examples/example_tasks.py,sha256=xTL8UWVAuolSX6swskfrAcmDrLIzn45dJ7YPWCwoEBU,514
13
13
  examples/example_verifier.py,sha256=0vwNITIG3m4CkSPwIxNXcGx9TqrxEsCGqK2A8keKZMM,2392
14
14
  examples/export_tasks.py,sha256=cJ_8xND7Q3IOM1JfJPR-DH3aLfHo_KmKJeO-1IVUFrQ,3237
15
15
  examples/gemini_example.py,sha256=qj9WDazQTYNiRHNeUg9Tjkp33lJMwbx8gDfpFe1sDQo,16180
16
- examples/import_tasks.py,sha256=pb60dOXnOn-G1INQxV1wujeHmmY2oWz9Ddj9SbMm1pk,3139
16
+ examples/import_tasks.py,sha256=Duh7T0HUuqsYUZ6LK2AXF3eP0zfSj1izkI5-1p09d9w,11041
17
17
  examples/json_tasks_example.py,sha256=CYPESGGtOo0fmsDdLidujTfsE4QlJHw7rOhyVqPJ_Ls,5329
18
18
  examples/nova_act_example.py,sha256=rH23Lp74Okf0rn8ynMdWjK2aviEf5NLPH4k_53Pyxho,831
19
19
  examples/openai_example.py,sha256=dEWERrTEP5xBiGkLkQjBQGd2NqoxX6gcW6XteBPsWFQ,8231
@@ -28,7 +28,7 @@ fleet/config.py,sha256=uY02ZKxVoXqVDta-0IMWaYJeE1CTXF_fA9NI6QUutmU,319
28
28
  fleet/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
29
29
  fleet/global_client.py,sha256=frrDAFNM2ywN0JHLtlm9qbE1dQpnQJsavJpb7xSR_bU,1072
30
30
  fleet/models.py,sha256=-8WG10SMkXUljRqD_8pfajCr4PS9qYDVLAZ8RxdJMb0,13392
31
- fleet/tasks.py,sha256=STR34InS14rMq1DFPsCeAASarsvUqf_ODK_jitT8ORU,16914
31
+ fleet/tasks.py,sha256=PJvq9JAduk88oVv3c9e4P7pwIKpO0Zxym4TFJpQbiSc,17349
32
32
  fleet/types.py,sha256=L4Y82xICf1tzyCLqhLYUgEoaIIS5h9T05TyFNHSWs3s,652
33
33
  fleet/_async/__init__.py,sha256=Wi8Tjj-Lfnxi4cPOkAxh2lynnpEBNni6mI6Iq80uOro,8054
34
34
  fleet/_async/base.py,sha256=oisVTQsx0M_yTmyQJc3oij63uKZ97MHz-xYFsWXxQE8,9202
@@ -36,7 +36,7 @@ fleet/_async/client.py,sha256=Uu9o8LpUce8vOoFxqG0RDe4VtJpu1BJGIFbVecuO71g,32947
36
36
  fleet/_async/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
37
37
  fleet/_async/global_client.py,sha256=4WskpLHbsDEgWW7hXMD09W-brkp4euy8w2ZJ88594rQ,1103
38
38
  fleet/_async/models.py,sha256=6x3IPYuWz1v6zWjujqgzK2CpR2HB5Rme4LQFLyvUDXE,13164
39
- fleet/_async/tasks.py,sha256=LiDDIrmT9mIqlUna-I903n4yVpLxbmxUSv4lPdaBcDE,16942
39
+ fleet/_async/tasks.py,sha256=K7xDIPO_ZHga_1ShssIiCuMe8_sx1fWU9a5zQwm_8d0,17377
40
40
  fleet/_async/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  fleet/_async/env/client.py,sha256=C5WG5Ir_McXaFPZNdkQjj0w4V7xMIcw3QyVP5g-3kVk,1237
42
42
  fleet/_async/instance/__init__.py,sha256=PtmJq8J8bh0SOQ2V55QURz5GJfobozwtQoqhaOk3_tI,515
@@ -69,12 +69,12 @@ fleet/verifiers/decorator.py,sha256=nAP3O8szXu7md_kpwpz91hGSUNEVLYjwZQZTkQlV1DM,
69
69
  fleet/verifiers/parse.py,sha256=qz9AfJrTbjlg-LU-lE8Ciqi7Yt2a8-cs17FdpjTLhMk,8550
70
70
  fleet/verifiers/sql_differ.py,sha256=TqTLWyK3uOyLbitT6HYzYEzuSFC39wcyhgk3rcm__k8,6525
71
71
  fleet/verifiers/verifier.py,sha256=_lcxXVm8e0xRrK2gNJy9up7pW1zOkPRY5n5lQ85S8jg,14197
72
- fleet_python-0.2.62.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
72
+ fleet_python-0.2.63.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
73
73
  scripts/fix_sync_imports.py,sha256=X9fWLTpiPGkSHsjyQUDepOJkxOqw1DPj7nd8wFlFqLQ,8368
74
74
  scripts/unasync.py,sha256=vWVQxRWX8SRZO5cmzEhpvnG_REhCWXpidIGIpWmEcvI,696
75
75
  tests/__init__.py,sha256=Re1SdyxH8NfyL1kjhi7SQkGP1mYeWB-D6UALqdIMd8I,35
76
76
  tests/test_verifier_from_string.py,sha256=Lxi3TpFHFb-hG4-UhLKZJkqo84ax9YJY8G6beO-1erM,13581
77
- fleet_python-0.2.62.dist-info/METADATA,sha256=pkly98qTBJVHA9jkevvz_v3UKZyGa2-_RFVlbVGLlSg,3304
78
- fleet_python-0.2.62.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
- fleet_python-0.2.62.dist-info/top_level.txt,sha256=qb1zIbtEktyhRFZdqVytwg54l64qtoZL0wjHB4bUg3c,29
80
- fleet_python-0.2.62.dist-info/RECORD,,
77
+ fleet_python-0.2.63.dist-info/METADATA,sha256=Qe5K9wBiRkdNI6PUnExcW3zQDG_MjpOvVfnpNAB1gJo,3304
78
+ fleet_python-0.2.63.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
+ fleet_python-0.2.63.dist-info/top_level.txt,sha256=qb1zIbtEktyhRFZdqVytwg54l64qtoZL0wjHB4bUg3c,29
80
+ fleet_python-0.2.63.dist-info/RECORD,,