fleet-python 0.2.66b4__tar.gz → 0.2.67__tar.gz
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.
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/PKG-INFO +1 -1
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/export_tasks.py +11 -1
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/import_tasks.py +15 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/client.py +4 -2
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/models.py +3 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/tasks.py +38 -25
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/client.py +4 -2
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/models.py +3 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/tasks.py +33 -24
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet_python.egg-info/PKG-INFO +1 -1
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet_python.egg-info/SOURCES.txt +1 -3
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/pyproject.toml +1 -1
- fleet_python-0.2.66b4/fleet/verifiers/parsing.py +0 -106
- fleet_python-0.2.66b4/tests/test_verifier_security.py +0 -427
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/LICENSE +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/README.md +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/diff_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/dsl_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/exampleResume.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_account.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_action_log.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_client.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_mcp_anthropic.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_mcp_openai.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_sync.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_task.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_tasks.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/example_verifier.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/gemini_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/json_tasks_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/nova_act_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/openai_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/openai_simple_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/query_builder_example.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/quickstart.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/examples/test_cdp_logging.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/base.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/env/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/env/client.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/exceptions.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/global_client.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/instance/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/instance/base.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/instance/client.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/resources/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/resources/base.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/resources/browser.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/resources/mcp.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/resources/sqlite.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/verifiers/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/verifiers/bundler.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/_async/verifiers/verifier.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/base.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/config.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/env/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/env/client.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/exceptions.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/global_client.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/instance/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/instance/base.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/instance/client.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/instance/models.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/resources/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/resources/base.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/resources/browser.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/resources/mcp.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/resources/sqlite.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/types.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/bundler.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/code.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/db.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/decorator.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/parse.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/sql_differ.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet/verifiers/verifier.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet_python.egg-info/dependency_links.txt +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet_python.egg-info/requires.txt +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/fleet_python.egg-info/top_level.txt +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/scripts/fix_sync_imports.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/scripts/unasync.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/setup.cfg +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/tests/__init__.py +0 -0
- {fleet_python-0.2.66b4 → fleet_python-0.2.67}/tests/test_verifier_from_string.py +0 -0
|
@@ -27,6 +27,12 @@ def main():
|
|
|
27
27
|
help="Optional task project key to filter tasks",
|
|
28
28
|
default=None,
|
|
29
29
|
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--env-key",
|
|
32
|
+
"-e",
|
|
33
|
+
help="Optional environment key to filter tasks",
|
|
34
|
+
default=None,
|
|
35
|
+
)
|
|
30
36
|
parser.add_argument(
|
|
31
37
|
"--output",
|
|
32
38
|
"-o",
|
|
@@ -42,12 +48,13 @@ def main():
|
|
|
42
48
|
args.project_key is not None,
|
|
43
49
|
args.task_keys is not None,
|
|
44
50
|
args.task_project_key is not None,
|
|
51
|
+
args.env_key is not None,
|
|
45
52
|
]
|
|
46
53
|
)
|
|
47
54
|
|
|
48
55
|
if filters_specified > 1:
|
|
49
56
|
parser.error(
|
|
50
|
-
"Cannot specify multiple filters. Use only one of --project-key, --task-keys,
|
|
57
|
+
"Cannot specify multiple filters. Use only one of --project-key, --task-keys, --task-project-key, or --env-key."
|
|
51
58
|
)
|
|
52
59
|
|
|
53
60
|
# Get account info
|
|
@@ -66,6 +73,9 @@ def main():
|
|
|
66
73
|
elif args.task_project_key:
|
|
67
74
|
print(f"Loading tasks from task project: {args.task_project_key}")
|
|
68
75
|
tasks = fleet.load_tasks(task_project_key=args.task_project_key)
|
|
76
|
+
elif args.env_key:
|
|
77
|
+
print(f"Loading tasks from environment: {args.env_key}")
|
|
78
|
+
tasks = fleet.load_tasks(env_key=args.env_key)
|
|
69
79
|
else:
|
|
70
80
|
print("Loading all tasks")
|
|
71
81
|
tasks = fleet.load_tasks()
|
|
@@ -210,9 +210,18 @@ async def main():
|
|
|
210
210
|
action="store_true",
|
|
211
211
|
help="Skip the verifier sanity check (not recommended)",
|
|
212
212
|
)
|
|
213
|
+
parser.add_argument(
|
|
214
|
+
"--sanity-check-only",
|
|
215
|
+
action="store_true",
|
|
216
|
+
help="Only run the sanity check without importing tasks",
|
|
217
|
+
)
|
|
213
218
|
|
|
214
219
|
args = parser.parse_args()
|
|
215
220
|
|
|
221
|
+
# Validate conflicting flags
|
|
222
|
+
if args.skip_sanity_check and args.sanity_check_only:
|
|
223
|
+
parser.error("Cannot use --skip-sanity-check and --sanity-check-only together")
|
|
224
|
+
|
|
216
225
|
# Load and parse the JSON file
|
|
217
226
|
try:
|
|
218
227
|
with open(args.json_file, "r", encoding="utf-8") as f:
|
|
@@ -286,6 +295,12 @@ async def main():
|
|
|
286
295
|
success, errors = await run_verifier_sanity_check(tasks, client)
|
|
287
296
|
if not success:
|
|
288
297
|
sys.exit(1)
|
|
298
|
+
|
|
299
|
+
# If only doing sanity check, exit successfully here
|
|
300
|
+
if args.sanity_check_only:
|
|
301
|
+
print("\n✓ Sanity check complete! (--sanity-check-only)")
|
|
302
|
+
print("Tasks are ready to import.")
|
|
303
|
+
sys.exit(0)
|
|
289
304
|
else:
|
|
290
305
|
print("\n⚠️ Skipping sanity check (--skip-sanity-check)")
|
|
291
306
|
|
|
@@ -553,7 +553,7 @@ class AsyncFleet:
|
|
|
553
553
|
env_variables=task_response.env_variables or {},
|
|
554
554
|
verifier_func=verifier_func, # Set verifier code
|
|
555
555
|
verifier=verifier, # Use created verifier or None
|
|
556
|
-
metadata={},
|
|
556
|
+
metadata=task_response.metadata or {},
|
|
557
557
|
output_json_schema=getattr(task_response, "output_json_schema", None), # Get output_json_schema if available
|
|
558
558
|
)
|
|
559
559
|
tasks.append(task)
|
|
@@ -708,6 +708,7 @@ class AsyncFleet:
|
|
|
708
708
|
task_key: str,
|
|
709
709
|
prompt: Optional[str] = None,
|
|
710
710
|
verifier_code: Optional[str] = None,
|
|
711
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
711
712
|
) -> TaskResponse:
|
|
712
713
|
"""Update an existing task.
|
|
713
714
|
|
|
@@ -715,11 +716,12 @@ class AsyncFleet:
|
|
|
715
716
|
task_key: The key of the task to update
|
|
716
717
|
prompt: New prompt text for the task (optional)
|
|
717
718
|
verifier_code: Python code for task verification (optional)
|
|
719
|
+
metadata: Additional metadata for the task (optional)
|
|
718
720
|
|
|
719
721
|
Returns:
|
|
720
722
|
TaskResponse containing the updated task details
|
|
721
723
|
"""
|
|
722
|
-
payload = TaskUpdateRequest(prompt=prompt, verifier_code=verifier_code)
|
|
724
|
+
payload = TaskUpdateRequest(prompt=prompt, verifier_code=verifier_code, metadata=metadata)
|
|
723
725
|
response = await self.client.request(
|
|
724
726
|
"PUT", f"/v1/tasks/{task_key}", json=payload.model_dump(exclude_none=True)
|
|
725
727
|
)
|
|
@@ -155,12 +155,14 @@ class TaskRequest(BaseModel):
|
|
|
155
155
|
verifier_id: Optional[str] = Field(None, title="Verifier Id")
|
|
156
156
|
version: Optional[str] = Field(None, title="Version")
|
|
157
157
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
158
|
+
metadata: Optional[Dict[str, Any]] = Field(None, title="Metadata")
|
|
158
159
|
output_json_schema: Optional[Dict[str, Any]] = Field(None, title="Output Json Schema")
|
|
159
160
|
|
|
160
161
|
|
|
161
162
|
class TaskUpdateRequest(BaseModel):
|
|
162
163
|
prompt: Optional[str] = Field(None, title="Prompt")
|
|
163
164
|
verifier_code: Optional[str] = Field(None, title="Verifier Code")
|
|
165
|
+
metadata: Optional[Dict[str, Any]] = Field(None, title="Metadata")
|
|
164
166
|
|
|
165
167
|
|
|
166
168
|
class VerifierData(BaseModel):
|
|
@@ -186,6 +188,7 @@ class TaskResponse(BaseModel):
|
|
|
186
188
|
data_version: Optional[str] = Field(None, title="Data Version")
|
|
187
189
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
188
190
|
verifier: Optional[VerifierData] = Field(None, title="Verifier")
|
|
191
|
+
metadata: Optional[Dict[str, Any]] = Field(None, title="Metadata")
|
|
189
192
|
output_json_schema: Optional[Dict[str, Any]] = Field(None, title="Output Json Schema")
|
|
190
193
|
|
|
191
194
|
|
|
@@ -209,17 +209,19 @@ class Task(BaseModel):
|
|
|
209
209
|
)
|
|
210
210
|
self.verifier = verifier
|
|
211
211
|
|
|
212
|
-
async def make_env(
|
|
212
|
+
async def make_env(
|
|
213
|
+
self,
|
|
214
|
+
region: Optional[str] = None,
|
|
215
|
+
image_type: Optional[str] = None,
|
|
216
|
+
ttl_seconds: Optional[int] = None,
|
|
217
|
+
):
|
|
213
218
|
"""Create an environment instance for this task's environment.
|
|
214
219
|
|
|
215
|
-
Uses the task's env_id (and version if present) to create the env.
|
|
220
|
+
Alias for make() method. Uses the task's env_id (and version if present) to create the env.
|
|
216
221
|
"""
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
from .client import AsyncFleet
|
|
221
|
-
|
|
222
|
-
return await AsyncFleet().make(env_key=self.env_key, region=region)
|
|
222
|
+
return await self.make(
|
|
223
|
+
region=region, image_type=image_type, ttl_seconds=ttl_seconds
|
|
224
|
+
)
|
|
223
225
|
|
|
224
226
|
async def make(
|
|
225
227
|
self,
|
|
@@ -279,14 +281,17 @@ def verifier_from_string(
|
|
|
279
281
|
"""
|
|
280
282
|
try:
|
|
281
283
|
import inspect
|
|
284
|
+
import re
|
|
282
285
|
from .verifiers.verifier import AsyncVerifierFunction
|
|
283
286
|
from fleet.verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
|
|
284
287
|
from fleet.verifiers.db import IgnoreConfig
|
|
285
|
-
from fleet.verifiers.parsing import parse_and_validate_verifier
|
|
286
288
|
|
|
287
|
-
#
|
|
288
|
-
#
|
|
289
|
-
|
|
289
|
+
# Strip @verifier decorator if present to avoid double-wrapping
|
|
290
|
+
# Remove lines like: @verifier(key="...")
|
|
291
|
+
cleaned_code = re.sub(r"@verifier\([^)]*\)\s*\n", "", verifier_func)
|
|
292
|
+
# Also remove the verifier import if present
|
|
293
|
+
cleaned_code = re.sub(r"from fleet import.*verifier.*\n", "", cleaned_code)
|
|
294
|
+
cleaned_code = re.sub(r"import.*verifier.*\n", "", cleaned_code)
|
|
290
295
|
|
|
291
296
|
# Create a local namespace for executing the code
|
|
292
297
|
local_namespace = {
|
|
@@ -296,18 +301,20 @@ def verifier_from_string(
|
|
|
296
301
|
"Environment": object, # Add Environment type if needed
|
|
297
302
|
}
|
|
298
303
|
|
|
299
|
-
# Execute the verifier code in the namespace
|
|
300
|
-
|
|
301
|
-
exec(verifier_func, globals(), local_namespace)
|
|
304
|
+
# Execute the cleaned verifier code in the namespace
|
|
305
|
+
exec(cleaned_code, globals(), local_namespace)
|
|
302
306
|
|
|
303
|
-
#
|
|
304
|
-
|
|
307
|
+
# Find the function that was defined (not imported)
|
|
308
|
+
# Functions defined via exec have co_filename == '<string>'
|
|
309
|
+
# Imported functions have their actual module file path
|
|
310
|
+
func_obj = None
|
|
311
|
+
for name, obj in local_namespace.items():
|
|
312
|
+
if inspect.isfunction(obj) and obj.__code__.co_filename == "<string>":
|
|
313
|
+
func_obj = obj
|
|
314
|
+
break
|
|
305
315
|
|
|
306
316
|
if func_obj is None:
|
|
307
|
-
raise ValueError(
|
|
308
|
-
|
|
309
|
-
if not inspect.isfunction(func_obj):
|
|
310
|
-
raise ValueError(f"'{func_name}' is not a function")
|
|
317
|
+
raise ValueError("No function found in verifier code")
|
|
311
318
|
|
|
312
319
|
# Create an AsyncVerifierFunction instance with raw code
|
|
313
320
|
verifier_instance = AsyncVerifierFunction(
|
|
@@ -381,7 +388,7 @@ async def load_tasks(
|
|
|
381
388
|
|
|
382
389
|
|
|
383
390
|
async def update_task(
|
|
384
|
-
task_key: str, prompt: Optional[str] = None, verifier_code: Optional[str] = None
|
|
391
|
+
task_key: str, prompt: Optional[str] = None, verifier_code: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None
|
|
385
392
|
):
|
|
386
393
|
"""Convenience function to update an existing task.
|
|
387
394
|
|
|
@@ -389,6 +396,7 @@ async def update_task(
|
|
|
389
396
|
task_key: The key of the task to update
|
|
390
397
|
prompt: New prompt text for the task (optional)
|
|
391
398
|
verifier_code: Python code for task verification (optional)
|
|
399
|
+
metadata: Additional metadata for the task (optional)
|
|
392
400
|
|
|
393
401
|
Returns:
|
|
394
402
|
TaskResponse containing the updated task details
|
|
@@ -396,16 +404,19 @@ async def update_task(
|
|
|
396
404
|
Examples:
|
|
397
405
|
response = await fleet.update_task("my-task", prompt="New prompt text")
|
|
398
406
|
response = await fleet.update_task("my-task", verifier_code="def verify(env): return True")
|
|
407
|
+
response = await fleet.update_task("my-task", metadata={"seed": 42, "story": "Updated story"})
|
|
399
408
|
"""
|
|
400
409
|
from .global_client import get_client
|
|
401
410
|
|
|
402
411
|
client = get_client()
|
|
403
412
|
return await client.update_task(
|
|
404
|
-
task_key=task_key, prompt=prompt, verifier_code=verifier_code
|
|
413
|
+
task_key=task_key, prompt=prompt, verifier_code=verifier_code, metadata=metadata
|
|
405
414
|
)
|
|
406
415
|
|
|
407
416
|
|
|
408
|
-
async def get_task(
|
|
417
|
+
async def get_task(
|
|
418
|
+
task_key: str, version_id: Optional[str] = None, team_id: Optional[str] = None
|
|
419
|
+
):
|
|
409
420
|
"""Convenience function to get a task by key and optional version.
|
|
410
421
|
|
|
411
422
|
Args:
|
|
@@ -424,7 +435,9 @@ async def get_task(task_key: str, version_id: Optional[str] = None, team_id: Opt
|
|
|
424
435
|
from .global_client import get_client
|
|
425
436
|
|
|
426
437
|
client = get_client()
|
|
427
|
-
return await client.get_task(
|
|
438
|
+
return await client.get_task(
|
|
439
|
+
task_key=task_key, version_id=version_id, team_id=team_id
|
|
440
|
+
)
|
|
428
441
|
|
|
429
442
|
|
|
430
443
|
async def import_task(task: Task, project_key: Optional[str] = None):
|
|
@@ -559,7 +559,7 @@ class Fleet:
|
|
|
559
559
|
env_variables=task_response.env_variables or {},
|
|
560
560
|
verifier_func=verifier_func, # Set verifier code
|
|
561
561
|
verifier=verifier, # Use created verifier or None
|
|
562
|
-
metadata={},
|
|
562
|
+
metadata=task_response.metadata or {},
|
|
563
563
|
output_json_schema=getattr(task_response, "output_json_schema", None), # Get output_json_schema if available
|
|
564
564
|
)
|
|
565
565
|
tasks.append(task)
|
|
@@ -706,6 +706,7 @@ class Fleet:
|
|
|
706
706
|
task_key: str,
|
|
707
707
|
prompt: Optional[str] = None,
|
|
708
708
|
verifier_code: Optional[str] = None,
|
|
709
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
709
710
|
) -> TaskResponse:
|
|
710
711
|
"""Update an existing task.
|
|
711
712
|
|
|
@@ -713,11 +714,12 @@ class Fleet:
|
|
|
713
714
|
task_key: The key of the task to update
|
|
714
715
|
prompt: New prompt text for the task (optional)
|
|
715
716
|
verifier_code: Python code for task verification (optional)
|
|
717
|
+
metadata: Additional metadata for the task (optional)
|
|
716
718
|
|
|
717
719
|
Returns:
|
|
718
720
|
TaskResponse containing the updated task details
|
|
719
721
|
"""
|
|
720
|
-
payload = TaskUpdateRequest(prompt=prompt, verifier_code=verifier_code)
|
|
722
|
+
payload = TaskUpdateRequest(prompt=prompt, verifier_code=verifier_code, metadata=metadata)
|
|
721
723
|
response = self.client.request(
|
|
722
724
|
"PUT", f"/v1/tasks/{task_key}", json=payload.model_dump(exclude_none=True)
|
|
723
725
|
)
|
|
@@ -158,6 +158,7 @@ class TaskRequest(BaseModel):
|
|
|
158
158
|
verifier_id: Optional[str] = Field(None, title="Verifier Id")
|
|
159
159
|
version: Optional[str] = Field(None, title="Version")
|
|
160
160
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
161
|
+
metadata: Optional[Dict[str, Any]] = Field(None, title="Metadata")
|
|
161
162
|
output_json_schema: Optional[Dict[str, Any]] = Field(
|
|
162
163
|
None, title="Output Json Schema"
|
|
163
164
|
)
|
|
@@ -166,6 +167,7 @@ class TaskRequest(BaseModel):
|
|
|
166
167
|
class TaskUpdateRequest(BaseModel):
|
|
167
168
|
prompt: Optional[str] = Field(None, title="Prompt")
|
|
168
169
|
verifier_code: Optional[str] = Field(None, title="Verifier Code")
|
|
170
|
+
metadata: Optional[Dict[str, Any]] = Field(None, title="Metadata")
|
|
169
171
|
|
|
170
172
|
|
|
171
173
|
class VerifierData(BaseModel):
|
|
@@ -191,6 +193,7 @@ class TaskResponse(BaseModel):
|
|
|
191
193
|
data_version: Optional[str] = Field(None, title="Data Version")
|
|
192
194
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
193
195
|
verifier: Optional[VerifierData] = Field(None, title="Verifier")
|
|
196
|
+
metadata: Optional[Dict[str, Any]] = Field(None, title="Metadata")
|
|
194
197
|
output_json_schema: Optional[Dict[str, Any]] = Field(
|
|
195
198
|
None, title="Output Json Schema"
|
|
196
199
|
)
|
|
@@ -202,17 +202,17 @@ class Task(BaseModel):
|
|
|
202
202
|
)
|
|
203
203
|
self.verifier = verifier
|
|
204
204
|
|
|
205
|
-
def make_env(
|
|
205
|
+
def make_env(
|
|
206
|
+
self,
|
|
207
|
+
region: Optional[str] = None,
|
|
208
|
+
image_type: Optional[str] = None,
|
|
209
|
+
ttl_seconds: Optional[int] = None,
|
|
210
|
+
):
|
|
206
211
|
"""Create an environment instance for this task's environment.
|
|
207
212
|
|
|
208
|
-
Uses the task's env_id (and version if present) to create the env.
|
|
213
|
+
Alias for make() method. Uses the task's env_id (and version if present) to create the env.
|
|
209
214
|
"""
|
|
210
|
-
|
|
211
|
-
raise ValueError("Task has no env_id defined")
|
|
212
|
-
# Deferred import to avoid circular dependencies
|
|
213
|
-
from .client import Fleet
|
|
214
|
-
|
|
215
|
-
return Fleet().make(env_key=self.env_key, region=region)
|
|
215
|
+
return self.make(region=region, image_type=image_type, ttl_seconds=ttl_seconds)
|
|
216
216
|
|
|
217
217
|
def make(
|
|
218
218
|
self,
|
|
@@ -272,14 +272,17 @@ def verifier_from_string(
|
|
|
272
272
|
"""
|
|
273
273
|
try:
|
|
274
274
|
import inspect
|
|
275
|
+
import re
|
|
275
276
|
from .verifiers import SyncVerifierFunction
|
|
276
277
|
from .verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
|
|
277
278
|
from .verifiers.db import IgnoreConfig
|
|
278
|
-
from .verifiers.parsing import parse_and_validate_verifier
|
|
279
279
|
|
|
280
|
-
#
|
|
281
|
-
#
|
|
282
|
-
|
|
280
|
+
# Strip @verifier decorator if present to avoid double-wrapping
|
|
281
|
+
# Remove lines like: @verifier(key="...")
|
|
282
|
+
cleaned_code = re.sub(r"@verifier\([^)]*\)\s*\n", "", verifier_func)
|
|
283
|
+
# Also remove the verifier import if present
|
|
284
|
+
cleaned_code = re.sub(r"from fleet import.*verifier.*\n", "", cleaned_code)
|
|
285
|
+
cleaned_code = re.sub(r"import.*verifier.*\n", "", cleaned_code)
|
|
283
286
|
|
|
284
287
|
# Create a globals namespace with all required imports
|
|
285
288
|
exec_globals = globals().copy()
|
|
@@ -295,18 +298,20 @@ def verifier_from_string(
|
|
|
295
298
|
# Create a local namespace for executing the code
|
|
296
299
|
local_namespace = {}
|
|
297
300
|
|
|
298
|
-
# Execute the verifier code in the namespace
|
|
299
|
-
|
|
300
|
-
exec(verifier_func, exec_globals, local_namespace)
|
|
301
|
+
# Execute the cleaned verifier code in the namespace
|
|
302
|
+
exec(cleaned_code, exec_globals, local_namespace)
|
|
301
303
|
|
|
302
|
-
#
|
|
303
|
-
|
|
304
|
+
# Find the function that was defined (not imported)
|
|
305
|
+
# Functions defined via exec have co_filename == '<string>'
|
|
306
|
+
# Imported functions have their actual module file path
|
|
307
|
+
func_obj = None
|
|
308
|
+
for name, obj in local_namespace.items():
|
|
309
|
+
if inspect.isfunction(obj) and obj.__code__.co_filename == "<string>":
|
|
310
|
+
func_obj = obj
|
|
311
|
+
break
|
|
304
312
|
|
|
305
313
|
if func_obj is None:
|
|
306
|
-
raise ValueError(
|
|
307
|
-
|
|
308
|
-
if not inspect.isfunction(func_obj):
|
|
309
|
-
raise ValueError(f"'{func_name}' is not a function")
|
|
314
|
+
raise ValueError("No function found in verifier code")
|
|
310
315
|
|
|
311
316
|
# Create an SyncVerifierFunction instance with raw code
|
|
312
317
|
verifier_instance = SyncVerifierFunction(
|
|
@@ -384,7 +389,7 @@ def load_tasks(
|
|
|
384
389
|
|
|
385
390
|
|
|
386
391
|
def update_task(
|
|
387
|
-
task_key: str, prompt: Optional[str] = None, verifier_code: Optional[str] = None
|
|
392
|
+
task_key: str, prompt: Optional[str] = None, verifier_code: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None
|
|
388
393
|
):
|
|
389
394
|
"""Convenience function to update an existing task.
|
|
390
395
|
|
|
@@ -392,6 +397,7 @@ def update_task(
|
|
|
392
397
|
task_key: The key of the task to update
|
|
393
398
|
prompt: New prompt text for the task (optional)
|
|
394
399
|
verifier_code: Python code for task verification (optional)
|
|
400
|
+
metadata: Additional metadata for the task (optional)
|
|
395
401
|
|
|
396
402
|
Returns:
|
|
397
403
|
TaskResponse containing the updated task details
|
|
@@ -399,16 +405,19 @@ def update_task(
|
|
|
399
405
|
Examples:
|
|
400
406
|
response = fleet.update_task("my-task", prompt="New prompt text")
|
|
401
407
|
response = fleet.update_task("my-task", verifier_code="def verify(env): return True")
|
|
408
|
+
response = fleet.update_task("my-task", metadata={"seed": 42, "story": "Updated story"})
|
|
402
409
|
"""
|
|
403
410
|
from .global_client import get_client
|
|
404
411
|
|
|
405
412
|
client = get_client()
|
|
406
413
|
return client.update_task(
|
|
407
|
-
task_key=task_key, prompt=prompt, verifier_code=verifier_code
|
|
414
|
+
task_key=task_key, prompt=prompt, verifier_code=verifier_code, metadata=metadata
|
|
408
415
|
)
|
|
409
416
|
|
|
410
417
|
|
|
411
|
-
def get_task(
|
|
418
|
+
def get_task(
|
|
419
|
+
task_key: str, version_id: Optional[str] = None, team_id: Optional[str] = None
|
|
420
|
+
):
|
|
412
421
|
"""Convenience function to get a task by key and optional version.
|
|
413
422
|
|
|
414
423
|
Args:
|
|
@@ -70,7 +70,6 @@ fleet/verifiers/code.py
|
|
|
70
70
|
fleet/verifiers/db.py
|
|
71
71
|
fleet/verifiers/decorator.py
|
|
72
72
|
fleet/verifiers/parse.py
|
|
73
|
-
fleet/verifiers/parsing.py
|
|
74
73
|
fleet/verifiers/sql_differ.py
|
|
75
74
|
fleet/verifiers/verifier.py
|
|
76
75
|
fleet_python.egg-info/PKG-INFO
|
|
@@ -81,5 +80,4 @@ fleet_python.egg-info/top_level.txt
|
|
|
81
80
|
scripts/fix_sync_imports.py
|
|
82
81
|
scripts/unasync.py
|
|
83
82
|
tests/__init__.py
|
|
84
|
-
tests/test_verifier_from_string.py
|
|
85
|
-
tests/test_verifier_security.py
|
|
83
|
+
tests/test_verifier_from_string.py
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"""Verifier code parsing and validation utilities."""
|
|
2
|
-
|
|
3
|
-
import ast
|
|
4
|
-
from typing import Set
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def parse_and_validate_verifier(code: str) -> str:
|
|
8
|
-
"""Parse and validate verifier code, returning the first function name.
|
|
9
|
-
|
|
10
|
-
This function ensures that the verifier code only contains safe declarative
|
|
11
|
-
statements and does not execute arbitrary code during import.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
code: Python code string containing the verifier function
|
|
15
|
-
|
|
16
|
-
Returns:
|
|
17
|
-
Name of the first function found in the code
|
|
18
|
-
|
|
19
|
-
Raises:
|
|
20
|
-
ValueError: If code is invalid or contains unsafe statements
|
|
21
|
-
SyntaxError: If code has syntax errors
|
|
22
|
-
"""
|
|
23
|
-
try:
|
|
24
|
-
tree = ast.parse(code)
|
|
25
|
-
except SyntaxError as e:
|
|
26
|
-
raise SyntaxError(f"Syntax error in verifier code: {e}")
|
|
27
|
-
|
|
28
|
-
first_function_name = None
|
|
29
|
-
|
|
30
|
-
for node in tree.body:
|
|
31
|
-
# Check for function definitions
|
|
32
|
-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
33
|
-
# Validate that decorators don't contain function calls
|
|
34
|
-
for decorator in node.decorator_list:
|
|
35
|
-
if _contains_call(decorator):
|
|
36
|
-
raise ValueError(
|
|
37
|
-
f"Line {node.lineno}: Function decorators with function calls "
|
|
38
|
-
f"are not allowed. Decorators execute during import and could "
|
|
39
|
-
f"run arbitrary code."
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
if first_function_name is None:
|
|
43
|
-
first_function_name = node.name
|
|
44
|
-
continue
|
|
45
|
-
|
|
46
|
-
# Allow imports
|
|
47
|
-
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
48
|
-
continue
|
|
49
|
-
|
|
50
|
-
# Allow class definitions
|
|
51
|
-
if isinstance(node, ast.ClassDef):
|
|
52
|
-
continue
|
|
53
|
-
|
|
54
|
-
# Allow docstrings and other expression statements (but not calls)
|
|
55
|
-
if isinstance(node, ast.Expr):
|
|
56
|
-
if isinstance(node.value, ast.Constant):
|
|
57
|
-
# Docstring or constant expression - safe
|
|
58
|
-
continue
|
|
59
|
-
else:
|
|
60
|
-
# Check if it's a call or other dangerous expression
|
|
61
|
-
raise ValueError(
|
|
62
|
-
f"Line {node.lineno}: Expression statements that are not "
|
|
63
|
-
f"constants are not allowed at module level. Found: {ast.dump(node.value)}"
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
# Allow variable assignments, but check the value
|
|
67
|
-
if isinstance(node, (ast.Assign, ast.AnnAssign)):
|
|
68
|
-
# Check if the assignment value contains any function calls
|
|
69
|
-
if _contains_call(node.value if isinstance(node, ast.AnnAssign) else node.value):
|
|
70
|
-
raise ValueError(
|
|
71
|
-
f"Line {node.lineno}: Variable assignments with function calls "
|
|
72
|
-
f"are not allowed at module level. This prevents arbitrary code "
|
|
73
|
-
f"execution during import."
|
|
74
|
-
)
|
|
75
|
-
continue
|
|
76
|
-
|
|
77
|
-
# If we get here, it's an unsupported statement type
|
|
78
|
-
raise ValueError(
|
|
79
|
-
f"Line {node.lineno}: Unsupported statement type at module level: "
|
|
80
|
-
f"{node.__class__.__name__}. Only imports, function/class definitions, "
|
|
81
|
-
f"and constant assignments are allowed."
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
if first_function_name is None:
|
|
85
|
-
raise ValueError("No function found in verifier code")
|
|
86
|
-
|
|
87
|
-
return first_function_name
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _contains_call(node: ast.AST) -> bool:
|
|
91
|
-
"""Recursively check if an AST node contains any Call nodes.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
node: AST node to check
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
True if the node or any of its children is a Call node
|
|
98
|
-
"""
|
|
99
|
-
if isinstance(node, ast.Call):
|
|
100
|
-
return True
|
|
101
|
-
|
|
102
|
-
for child in ast.walk(node):
|
|
103
|
-
if isinstance(child, ast.Call):
|
|
104
|
-
return True
|
|
105
|
-
|
|
106
|
-
return False
|