fleet-python 0.2.53__py3-none-any.whl → 0.2.55__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/export_tasks.py +76 -0
- examples/import_tasks.py +102 -0
- fleet/__init__.py +15 -2
- fleet/_async/__init__.py +16 -0
- fleet/_async/client.py +57 -6
- fleet/_async/tasks.py +60 -0
- fleet/client.py +61 -8
- fleet/tasks.py +63 -3
- {fleet_python-0.2.53.dist-info → fleet_python-0.2.55.dist-info}/METADATA +1 -1
- {fleet_python-0.2.53.dist-info → fleet_python-0.2.55.dist-info}/RECORD +13 -11
- {fleet_python-0.2.53.dist-info → fleet_python-0.2.55.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.53.dist-info → fleet_python-0.2.55.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.53.dist-info → fleet_python-0.2.55.dist-info}/top_level.txt +0 -0
examples/export_tasks.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import fleet
|
|
4
|
+
from dotenv import load_dotenv
|
|
5
|
+
|
|
6
|
+
load_dotenv()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
parser = argparse.ArgumentParser(description="Export tasks to a JSON file")
|
|
11
|
+
parser.add_argument(
|
|
12
|
+
"--project-key",
|
|
13
|
+
"-p",
|
|
14
|
+
help="Optional project key to filter tasks",
|
|
15
|
+
default=None,
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"--output",
|
|
19
|
+
"-o",
|
|
20
|
+
help="Output JSON filename (defaults to {team_id}.json)",
|
|
21
|
+
default=None,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
|
|
26
|
+
# Get account info
|
|
27
|
+
account = fleet.env.account()
|
|
28
|
+
print(f"Exporting from team: {account.team_name}")
|
|
29
|
+
|
|
30
|
+
# Load tasks
|
|
31
|
+
if args.project_key:
|
|
32
|
+
print(f"Loading tasks from project: {args.project_key}")
|
|
33
|
+
tasks = fleet.load_tasks(project_key=args.project_key)
|
|
34
|
+
else:
|
|
35
|
+
print("Loading all tasks")
|
|
36
|
+
tasks = fleet.load_tasks()
|
|
37
|
+
|
|
38
|
+
print(f"\nFound {len(tasks)} task(s)")
|
|
39
|
+
|
|
40
|
+
# Validate that all tasks have verifier_func
|
|
41
|
+
print("Validating tasks have verifier_func...")
|
|
42
|
+
missing_verifier = []
|
|
43
|
+
for task in tasks:
|
|
44
|
+
if not task.verifier_func:
|
|
45
|
+
missing_verifier.append(task.key)
|
|
46
|
+
|
|
47
|
+
if missing_verifier:
|
|
48
|
+
print(f"\n✗ Error: {len(missing_verifier)} task(s) missing verifier_func:")
|
|
49
|
+
for key in missing_verifier[:10]: # Show first 10
|
|
50
|
+
print(f" - {key}")
|
|
51
|
+
if len(missing_verifier) > 10:
|
|
52
|
+
print(f" ... and {len(missing_verifier) - 10} more")
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"All tasks must have a verifier_func. Cannot export tasks without verifiers."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
print("✓ All tasks have verifier_func")
|
|
58
|
+
|
|
59
|
+
# Determine output filename
|
|
60
|
+
output_file = args.output or f"{account.team_id}.json"
|
|
61
|
+
|
|
62
|
+
# Export to JSON
|
|
63
|
+
print(f"\nExporting to: {output_file}")
|
|
64
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
65
|
+
json.dump(
|
|
66
|
+
[task.model_dump() for task in tasks],
|
|
67
|
+
f,
|
|
68
|
+
indent=2,
|
|
69
|
+
ensure_ascii=False,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
print(f"✓ Successfully exported {len(tasks)} task(s) to {output_file}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
main()
|
examples/import_tasks.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
import fleet
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
|
|
8
|
+
load_dotenv()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def main():
|
|
12
|
+
parser = argparse.ArgumentParser(description="Import tasks from a JSON file")
|
|
13
|
+
parser.add_argument("json_file", help="Path to the JSON file containing tasks")
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"--project-key",
|
|
16
|
+
"-p",
|
|
17
|
+
help="Optional project key to associate with the tasks",
|
|
18
|
+
default=None,
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--yes",
|
|
22
|
+
"-y",
|
|
23
|
+
action="store_true",
|
|
24
|
+
help="Skip confirmation prompt and import automatically",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
args = parser.parse_args()
|
|
28
|
+
|
|
29
|
+
# Load and parse the JSON file
|
|
30
|
+
try:
|
|
31
|
+
with open(args.json_file, "r", encoding="utf-8") as f:
|
|
32
|
+
tasks_data = json.load(f)
|
|
33
|
+
except FileNotFoundError:
|
|
34
|
+
print(f"Error: File '{args.json_file}' not found")
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
except json.JSONDecodeError as e:
|
|
37
|
+
print(f"Error: Invalid JSON in '{args.json_file}': {e}")
|
|
38
|
+
sys.exit(1)
|
|
39
|
+
|
|
40
|
+
# Extract task information and validate verifier_func
|
|
41
|
+
task_count = len(tasks_data)
|
|
42
|
+
task_keys = []
|
|
43
|
+
missing_verifier = []
|
|
44
|
+
for task_data in tasks_data:
|
|
45
|
+
task_key = task_data.get("key") or task_data.get("id")
|
|
46
|
+
if task_key:
|
|
47
|
+
task_keys.append(task_key)
|
|
48
|
+
else:
|
|
49
|
+
task_keys.append("(no key)")
|
|
50
|
+
|
|
51
|
+
# Check for verifier_func
|
|
52
|
+
verifier_code = task_data.get("verifier_func") or task_data.get("verifier_code")
|
|
53
|
+
if not verifier_code:
|
|
54
|
+
missing_verifier.append(task_key or "(no key)")
|
|
55
|
+
|
|
56
|
+
# Validate all tasks have verifier_func
|
|
57
|
+
if missing_verifier:
|
|
58
|
+
print(f"✗ Error: {len(missing_verifier)} task(s) missing verifier_func:")
|
|
59
|
+
for key in missing_verifier[:10]: # Show first 10
|
|
60
|
+
print(f" - {key}")
|
|
61
|
+
if len(missing_verifier) > 10:
|
|
62
|
+
print(f" ... and {len(missing_verifier) - 10} more")
|
|
63
|
+
print("\nAll tasks must have a verifier_func to be imported.")
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
|
|
66
|
+
# Get account info
|
|
67
|
+
account = await fleet.env.account_async()
|
|
68
|
+
|
|
69
|
+
# Print summary
|
|
70
|
+
print(f"Importing to team: {account.team_name}")
|
|
71
|
+
print(f"\nFound {task_count} task(s) in '{args.json_file}':")
|
|
72
|
+
print("\nTask keys:")
|
|
73
|
+
for i, key in enumerate(task_keys, 1):
|
|
74
|
+
print(f" {i}. {key}")
|
|
75
|
+
|
|
76
|
+
if args.project_key:
|
|
77
|
+
print(f"\nProject key: {args.project_key}")
|
|
78
|
+
else:
|
|
79
|
+
print("\nProject key: (none)")
|
|
80
|
+
|
|
81
|
+
# Confirmation prompt (unless --yes flag is provided)
|
|
82
|
+
if not args.yes:
|
|
83
|
+
print("\n" + "=" * 60)
|
|
84
|
+
response = input("Type 'YES' to proceed with import: ")
|
|
85
|
+
if response != "YES":
|
|
86
|
+
print("Import cancelled.")
|
|
87
|
+
sys.exit(0)
|
|
88
|
+
|
|
89
|
+
# Import tasks
|
|
90
|
+
print("\nImporting tasks...")
|
|
91
|
+
try:
|
|
92
|
+
results = await fleet.import_tasks_async(
|
|
93
|
+
args.json_file, project_key=args.project_key
|
|
94
|
+
)
|
|
95
|
+
print(f"\n✓ Successfully imported {len(results)} task(s)")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
print(f"\n✗ Error importing tasks: {e}")
|
|
98
|
+
sys.exit(1)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
asyncio.run(main())
|
fleet/__init__.py
CHANGED
|
@@ -51,10 +51,17 @@ from ._async.tasks import (
|
|
|
51
51
|
Task,
|
|
52
52
|
load_tasks as load_tasks_async,
|
|
53
53
|
load_tasks_from_file as load_tasks_from_file_async,
|
|
54
|
+
import_task as import_task_async,
|
|
55
|
+
import_tasks as import_tasks_async,
|
|
54
56
|
)
|
|
55
57
|
|
|
56
|
-
# Import sync
|
|
57
|
-
from .tasks import
|
|
58
|
+
# Import sync task functions
|
|
59
|
+
from .tasks import (
|
|
60
|
+
load_tasks,
|
|
61
|
+
load_tasks_from_file,
|
|
62
|
+
import_task,
|
|
63
|
+
import_tasks,
|
|
64
|
+
)
|
|
58
65
|
|
|
59
66
|
# Import shared types
|
|
60
67
|
from .types import VerifierFunction
|
|
@@ -104,6 +111,12 @@ __all__ = [
|
|
|
104
111
|
# Module-level functions (async is default)
|
|
105
112
|
"load_tasks",
|
|
106
113
|
"load_tasks_async",
|
|
114
|
+
"load_tasks_from_file",
|
|
115
|
+
"load_tasks_from_file_async",
|
|
116
|
+
"import_task",
|
|
117
|
+
"import_task_async",
|
|
118
|
+
"import_tasks",
|
|
119
|
+
"import_tasks_async",
|
|
107
120
|
# Version
|
|
108
121
|
"__version__",
|
|
109
122
|
]
|
fleet/_async/__init__.py
CHANGED
|
@@ -86,6 +86,7 @@ __all__ = [
|
|
|
86
86
|
"load_task_from_string",
|
|
87
87
|
"load_task_from_json",
|
|
88
88
|
"export_tasks",
|
|
89
|
+
"import_task",
|
|
89
90
|
"import_tasks",
|
|
90
91
|
"account",
|
|
91
92
|
# Version
|
|
@@ -193,6 +194,21 @@ async def export_tasks(
|
|
|
193
194
|
return await _async_global_client.get_client().export_tasks(env_key, filename)
|
|
194
195
|
|
|
195
196
|
|
|
197
|
+
async def import_task(task, project_key: Optional[str] = None):
|
|
198
|
+
"""Import a single task.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
task: Task object to import
|
|
202
|
+
project_key: Optional project key to associate with the task
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
task = fleet.Task(key="my-task", prompt="Do something", env_id="my-env")
|
|
206
|
+
await fleet.import_task(task)
|
|
207
|
+
await fleet.import_task(task, project_key="my-project")
|
|
208
|
+
"""
|
|
209
|
+
return await _async_global_client.get_client().import_single_task(task, project_key)
|
|
210
|
+
|
|
211
|
+
|
|
196
212
|
async def import_tasks(filename: str, project_key: Optional[str] = None):
|
|
197
213
|
"""Import tasks from a JSON file.
|
|
198
214
|
|
fleet/_async/client.py
CHANGED
|
@@ -328,7 +328,9 @@ class AsyncFleet:
|
|
|
328
328
|
task_json = json.loads(task_string)
|
|
329
329
|
return await self.load_task_from_json(task_json)
|
|
330
330
|
|
|
331
|
-
async def load_task_from_json(
|
|
331
|
+
async def load_task_from_json(
|
|
332
|
+
self, task_json: Dict, raise_on_verifier_error: bool = False
|
|
333
|
+
) -> Task:
|
|
332
334
|
verifier = None
|
|
333
335
|
verifier_code = task_json.get("verifier_func") or task_json.get("verifier_code")
|
|
334
336
|
|
|
@@ -356,9 +358,11 @@ class AsyncFleet:
|
|
|
356
358
|
verifier_sha=task_json.get("verifier_sha", ""),
|
|
357
359
|
)
|
|
358
360
|
except Exception as e:
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
361
|
+
error_msg = f"Failed to create verifier {task_json.get('key', task_json.get('id'))}: {e}"
|
|
362
|
+
if raise_on_verifier_error:
|
|
363
|
+
raise ValueError(error_msg) from e
|
|
364
|
+
else:
|
|
365
|
+
logger.warning(error_msg)
|
|
362
366
|
|
|
363
367
|
task = Task(
|
|
364
368
|
key=task_json.get("key", task_json.get("id")),
|
|
@@ -368,6 +372,8 @@ class AsyncFleet:
|
|
|
368
372
|
), # Use env_id or fallback to env_key
|
|
369
373
|
created_at=task_json.get("created_at"),
|
|
370
374
|
version=task_json.get("version"),
|
|
375
|
+
data_id=task_json.get("data_id"),
|
|
376
|
+
data_version=task_json.get("data_version"),
|
|
371
377
|
env_variables=task_json.get("env_variables", {}),
|
|
372
378
|
verifier_func=verifier_code, # Set verifier code
|
|
373
379
|
verifier=verifier, # Use created verifier or None
|
|
@@ -382,6 +388,8 @@ class AsyncFleet:
|
|
|
382
388
|
version: Optional[str] = None,
|
|
383
389
|
team_id: Optional[str] = None,
|
|
384
390
|
project_key: Optional[str] = None,
|
|
391
|
+
data_id: Optional[str] = None,
|
|
392
|
+
data_version: Optional[str] = None,
|
|
385
393
|
) -> List[Task]:
|
|
386
394
|
"""Load tasks for the authenticated team, with optional filtering.
|
|
387
395
|
|
|
@@ -390,6 +398,9 @@ class AsyncFleet:
|
|
|
390
398
|
keys: Optional list of task keys to filter by
|
|
391
399
|
version: Optional version to filter tasks by (client-side filter)
|
|
392
400
|
team_id: Optional team_id to filter by (admin only)
|
|
401
|
+
project_key: Optional project key to filter tasks by
|
|
402
|
+
data_id: Optional data identifier to filter tasks by
|
|
403
|
+
data_version: Optional data version to filter tasks by
|
|
393
404
|
|
|
394
405
|
Returns:
|
|
395
406
|
List[Task] containing Task objects
|
|
@@ -403,6 +414,10 @@ class AsyncFleet:
|
|
|
403
414
|
params["team_id"] = team_id
|
|
404
415
|
if project_key is not None:
|
|
405
416
|
params["project_key"] = project_key
|
|
417
|
+
if data_id is not None:
|
|
418
|
+
params["data_id"] = data_id
|
|
419
|
+
if data_version is not None:
|
|
420
|
+
params["data_version"] = data_version
|
|
406
421
|
|
|
407
422
|
response = await self.client.request("GET", "/v1/tasks", params=params)
|
|
408
423
|
task_list_response = TaskListResponse(**response.json())
|
|
@@ -507,6 +522,8 @@ class AsyncFleet:
|
|
|
507
522
|
env_id=task_response.environment_id, # Map environment_id -> env_id
|
|
508
523
|
created_at=task_response.created_at,
|
|
509
524
|
version=task_response.version,
|
|
525
|
+
data_id=getattr(task_response, "data_id", None), # Get data_id if available
|
|
526
|
+
data_version=getattr(task_response, "data_version", None), # Get data_version if available
|
|
510
527
|
env_variables=task_response.env_variables or {},
|
|
511
528
|
verifier_func=verifier_func, # Set verifier code
|
|
512
529
|
verifier=verifier, # Use created verifier or None
|
|
@@ -517,6 +534,14 @@ class AsyncFleet:
|
|
|
517
534
|
# Apply client-side filtering for version if specified
|
|
518
535
|
if version is not None:
|
|
519
536
|
tasks = [task for task in tasks if task.version == version]
|
|
537
|
+
|
|
538
|
+
# Apply client-side filtering for data_id if specified
|
|
539
|
+
if data_id is not None:
|
|
540
|
+
tasks = [task for task in tasks if task.data_id == data_id]
|
|
541
|
+
|
|
542
|
+
# Apply client-side filtering for data_version if specified
|
|
543
|
+
if data_version is not None:
|
|
544
|
+
tasks = [task for task in tasks if task.data_version == data_version]
|
|
520
545
|
|
|
521
546
|
return tasks
|
|
522
547
|
|
|
@@ -571,6 +596,13 @@ class AsyncFleet:
|
|
|
571
596
|
Response from the API, or None if the import failed
|
|
572
597
|
"""
|
|
573
598
|
try:
|
|
599
|
+
# Validate that verifier_func exists
|
|
600
|
+
if not task.verifier_func:
|
|
601
|
+
raise ValueError(
|
|
602
|
+
f"Task {task.key} is missing verifier_func. "
|
|
603
|
+
"All tasks must have a verifier_func to be imported."
|
|
604
|
+
)
|
|
605
|
+
|
|
574
606
|
params = {}
|
|
575
607
|
if project_key:
|
|
576
608
|
params["project_key"] = project_key
|
|
@@ -591,14 +623,33 @@ class AsyncFleet:
|
|
|
591
623
|
|
|
592
624
|
Returns:
|
|
593
625
|
List[Task] containing imported Task objects
|
|
626
|
+
|
|
627
|
+
Raises:
|
|
628
|
+
ValueError: If any task is missing verifier_func or has invalid verifier code
|
|
594
629
|
"""
|
|
595
630
|
with open(filename, "r", encoding="utf-8") as f:
|
|
596
631
|
tasks_data = json.load(f)
|
|
597
632
|
|
|
598
|
-
# Create tasks from the loaded data
|
|
633
|
+
# Create tasks from the loaded data using load_task_from_json
|
|
634
|
+
# This will validate and create verifiers properly
|
|
599
635
|
tasks = []
|
|
600
636
|
for task_data in tasks_data:
|
|
601
|
-
|
|
637
|
+
# Validate that verifier_func exists
|
|
638
|
+
verifier_code = task_data.get("verifier_func") or task_data.get(
|
|
639
|
+
"verifier_code"
|
|
640
|
+
)
|
|
641
|
+
if not verifier_code:
|
|
642
|
+
task_key = task_data.get("key", task_data.get("id", "unknown"))
|
|
643
|
+
raise ValueError(
|
|
644
|
+
f"Task {task_key} is missing verifier_func. "
|
|
645
|
+
"All tasks must have a verifier_func to be imported."
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Use load_task_from_json to properly create and validate the task
|
|
649
|
+
# Pass raise_on_verifier_error=True to fail fast on invalid verifier code
|
|
650
|
+
task = await self.load_task_from_json(
|
|
651
|
+
task_data, raise_on_verifier_error=True
|
|
652
|
+
)
|
|
602
653
|
tasks.append(task)
|
|
603
654
|
|
|
604
655
|
# Use semaphore to limit concurrency to 20
|
fleet/_async/tasks.py
CHANGED
|
@@ -25,6 +25,8 @@ class Task(BaseModel):
|
|
|
25
25
|
)
|
|
26
26
|
created_at: Optional[datetime] = Field(None, description="Task creation timestamp")
|
|
27
27
|
version: Optional[str] = Field(None, description="Task version")
|
|
28
|
+
data_id: Optional[str] = Field(None, description="Data identifier")
|
|
29
|
+
data_version: Optional[str] = Field(None, description="Data version")
|
|
28
30
|
verifier_func: Optional[str] = Field(None, description="Verifier function code")
|
|
29
31
|
verifier: Optional[Any] = Field(
|
|
30
32
|
None,
|
|
@@ -58,6 +60,15 @@ class Task(BaseModel):
|
|
|
58
60
|
return f"{self.env_id}:{self.version}"
|
|
59
61
|
return self.env_id
|
|
60
62
|
|
|
63
|
+
@property
|
|
64
|
+
def data_key(self) -> Optional[str]:
|
|
65
|
+
"""Get the data key combining data_id and data_version."""
|
|
66
|
+
if self.data_id and self.data_version:
|
|
67
|
+
return f"{self.data_id}:{self.data_version}"
|
|
68
|
+
elif self.data_id:
|
|
69
|
+
return self.data_id
|
|
70
|
+
return None
|
|
71
|
+
|
|
61
72
|
class Config:
|
|
62
73
|
"""Pydantic model configuration."""
|
|
63
74
|
|
|
@@ -282,6 +293,8 @@ async def load_tasks(
|
|
|
282
293
|
version: Optional[str] = None,
|
|
283
294
|
team_id: Optional[str] = None,
|
|
284
295
|
project_key: Optional[str] = None,
|
|
296
|
+
data_id: Optional[str] = None,
|
|
297
|
+
data_version: Optional[str] = None,
|
|
285
298
|
) -> List[Task]:
|
|
286
299
|
"""Convenience function to load tasks with optional filtering.
|
|
287
300
|
|
|
@@ -290,11 +303,15 @@ async def load_tasks(
|
|
|
290
303
|
keys: Optional list of task keys to filter by
|
|
291
304
|
version: Optional version to filter tasks by
|
|
292
305
|
team_id: Optional team_id to filter by (admin only)
|
|
306
|
+
project_key: Optional project key to filter tasks by
|
|
307
|
+
data_id: Optional data identifier to filter tasks by
|
|
308
|
+
data_version: Optional data version to filter tasks by
|
|
293
309
|
|
|
294
310
|
Examples:
|
|
295
311
|
tasks = await fleet.load_tasks(env_key="fira")
|
|
296
312
|
tasks = await fleet.load_tasks(keys=["task1", "task2"])
|
|
297
313
|
tasks = await fleet.load_tasks(env_key="fira", version="v1.0")
|
|
314
|
+
tasks = await fleet.load_tasks(data_id="my-data", data_version="v1.0")
|
|
298
315
|
"""
|
|
299
316
|
# Use the global client by default so users can pre-configure it once
|
|
300
317
|
from .global_client import get_client
|
|
@@ -306,6 +323,8 @@ async def load_tasks(
|
|
|
306
323
|
version=version,
|
|
307
324
|
team_id=team_id,
|
|
308
325
|
project_key=project_key,
|
|
326
|
+
data_id=data_id,
|
|
327
|
+
data_version=data_version,
|
|
309
328
|
)
|
|
310
329
|
|
|
311
330
|
|
|
@@ -332,3 +351,44 @@ async def update_task(
|
|
|
332
351
|
return await client.update_task(
|
|
333
352
|
task_key=task_key, prompt=prompt, verifier_code=verifier_code
|
|
334
353
|
)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
async def import_task(task: Task, project_key: Optional[str] = None):
|
|
357
|
+
"""Convenience function to import a single task.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
task: Task object to import
|
|
361
|
+
project_key: Optional project key to associate with the task
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Response from the API, or None if the import failed
|
|
365
|
+
|
|
366
|
+
Examples:
|
|
367
|
+
task = fleet.Task(key="my-task", prompt="Do something", env_id="my-env")
|
|
368
|
+
response = await fleet.import_task(task)
|
|
369
|
+
response = await fleet.import_task(task, project_key="my-project")
|
|
370
|
+
"""
|
|
371
|
+
from .global_client import get_client
|
|
372
|
+
|
|
373
|
+
client = get_client()
|
|
374
|
+
return await client.import_single_task(task, project_key=project_key)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
async def import_tasks(filename: str, project_key: Optional[str] = None):
|
|
378
|
+
"""Convenience function to import tasks from a JSON file.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
filename: Path to the JSON file of Task objects to import
|
|
382
|
+
project_key: Optional project key to associate with the tasks
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
List of responses from the API for successfully imported tasks
|
|
386
|
+
|
|
387
|
+
Examples:
|
|
388
|
+
responses = await fleet.import_tasks("tasks.json")
|
|
389
|
+
responses = await fleet.import_tasks("tasks.json", project_key="my-project")
|
|
390
|
+
"""
|
|
391
|
+
from .global_client import get_client
|
|
392
|
+
|
|
393
|
+
client = get_client()
|
|
394
|
+
return await client.import_tasks(filename, project_key=project_key)
|
fleet/client.py
CHANGED
|
@@ -326,7 +326,9 @@ class Fleet:
|
|
|
326
326
|
task_json = json.loads(task_string)
|
|
327
327
|
return self.load_task_from_json(task_json)
|
|
328
328
|
|
|
329
|
-
def load_task_from_json(
|
|
329
|
+
def load_task_from_json(
|
|
330
|
+
self, task_json: Dict, raise_on_verifier_error: bool = False
|
|
331
|
+
) -> Task:
|
|
330
332
|
verifier = None
|
|
331
333
|
verifier_code = task_json.get("verifier_func") or task_json.get("verifier_code")
|
|
332
334
|
|
|
@@ -354,9 +356,11 @@ class Fleet:
|
|
|
354
356
|
verifier_sha=task_json.get("verifier_sha", ""),
|
|
355
357
|
)
|
|
356
358
|
except Exception as e:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
359
|
+
error_msg = f"Failed to create verifier {task_json.get('key', task_json.get('id'))}: {e}"
|
|
360
|
+
if raise_on_verifier_error:
|
|
361
|
+
raise ValueError(error_msg) from e
|
|
362
|
+
else:
|
|
363
|
+
logger.warning(error_msg)
|
|
360
364
|
|
|
361
365
|
task = Task(
|
|
362
366
|
key=task_json.get("key", task_json.get("id")),
|
|
@@ -366,6 +370,8 @@ class Fleet:
|
|
|
366
370
|
), # Use env_id or fallback to env_key
|
|
367
371
|
created_at=task_json.get("created_at"),
|
|
368
372
|
version=task_json.get("version"),
|
|
373
|
+
data_id=task_json.get("data_id"),
|
|
374
|
+
data_version=task_json.get("data_version"),
|
|
369
375
|
env_variables=task_json.get("env_variables", {}),
|
|
370
376
|
verifier_func=verifier_code, # Set verifier code
|
|
371
377
|
verifier=verifier, # Use created verifier or None
|
|
@@ -380,6 +386,8 @@ class Fleet:
|
|
|
380
386
|
version: Optional[str] = None,
|
|
381
387
|
team_id: Optional[str] = None,
|
|
382
388
|
project_key: Optional[str] = None,
|
|
389
|
+
data_id: Optional[str] = None,
|
|
390
|
+
data_version: Optional[str] = None,
|
|
383
391
|
) -> List[Task]:
|
|
384
392
|
"""Load tasks for the authenticated team, with optional filtering.
|
|
385
393
|
|
|
@@ -388,6 +396,9 @@ class Fleet:
|
|
|
388
396
|
keys: Optional list of task keys to filter by
|
|
389
397
|
version: Optional version to filter tasks by (client-side filter)
|
|
390
398
|
team_id: Optional team_id to filter by (admin only)
|
|
399
|
+
project_key: Optional project key to filter tasks by
|
|
400
|
+
data_id: Optional data identifier to filter tasks by
|
|
401
|
+
data_version: Optional data version to filter tasks by
|
|
391
402
|
|
|
392
403
|
Returns:
|
|
393
404
|
List[Task] containing Task objects
|
|
@@ -401,6 +412,10 @@ class Fleet:
|
|
|
401
412
|
params["team_id"] = team_id
|
|
402
413
|
if project_key is not None:
|
|
403
414
|
params["project_key"] = project_key
|
|
415
|
+
if data_id is not None:
|
|
416
|
+
params["data_id"] = data_id
|
|
417
|
+
if data_version is not None:
|
|
418
|
+
params["data_version"] = data_version
|
|
404
419
|
|
|
405
420
|
response = self.client.request("GET", "/v1/tasks", params=params)
|
|
406
421
|
task_list_response = TaskListResponse(**response.json())
|
|
@@ -513,6 +528,8 @@ class Fleet:
|
|
|
513
528
|
env_id=task_response.environment_id, # Map environment_id -> env_id
|
|
514
529
|
created_at=task_response.created_at,
|
|
515
530
|
version=task_response.version,
|
|
531
|
+
data_id=getattr(task_response, "data_id", None), # Get data_id if available
|
|
532
|
+
data_version=getattr(task_response, "data_version", None), # Get data_version if available
|
|
516
533
|
env_variables=task_response.env_variables or {},
|
|
517
534
|
verifier_func=verifier_func, # Set verifier code
|
|
518
535
|
verifier=verifier, # Use created verifier or None
|
|
@@ -523,6 +540,14 @@ class Fleet:
|
|
|
523
540
|
# Apply client-side filtering for version if specified
|
|
524
541
|
if version is not None:
|
|
525
542
|
tasks = [task for task in tasks if task.version == version]
|
|
543
|
+
|
|
544
|
+
# Apply client-side filtering for data_id if specified
|
|
545
|
+
if data_id is not None:
|
|
546
|
+
tasks = [task for task in tasks if task.data_id == data_id]
|
|
547
|
+
|
|
548
|
+
# Apply client-side filtering for data_version if specified
|
|
549
|
+
if data_version is not None:
|
|
550
|
+
tasks = [task for task in tasks if task.data_version == data_version]
|
|
526
551
|
|
|
527
552
|
return tasks
|
|
528
553
|
|
|
@@ -577,10 +602,19 @@ class Fleet:
|
|
|
577
602
|
Response from the API, or None if the import failed
|
|
578
603
|
"""
|
|
579
604
|
try:
|
|
605
|
+
# Validate that verifier_func exists
|
|
606
|
+
if not task.verifier_func:
|
|
607
|
+
raise ValueError(
|
|
608
|
+
f"Task {task.key} is missing verifier_func. "
|
|
609
|
+
"All tasks must have a verifier_func to be imported."
|
|
610
|
+
)
|
|
611
|
+
|
|
580
612
|
params = {}
|
|
581
613
|
if project_key:
|
|
582
614
|
params["project_key"] = project_key
|
|
583
|
-
response = self.client.request(
|
|
615
|
+
response = self.client.request(
|
|
616
|
+
"POST", "/v1/tasks", json=task.model_dump(), params=params
|
|
617
|
+
)
|
|
584
618
|
return response
|
|
585
619
|
except Exception as e:
|
|
586
620
|
logger.error(f"Failed to import task {task.key}: {e}")
|
|
@@ -595,19 +629,38 @@ class Fleet:
|
|
|
595
629
|
|
|
596
630
|
Returns:
|
|
597
631
|
List[Task] containing imported Task objects
|
|
632
|
+
|
|
633
|
+
Raises:
|
|
634
|
+
ValueError: If any task is missing verifier_func or has invalid verifier code
|
|
598
635
|
"""
|
|
599
636
|
with open(filename, "r", encoding="utf-8") as f:
|
|
600
637
|
tasks_data = json.load(f)
|
|
601
638
|
|
|
602
|
-
# Create tasks from the loaded data
|
|
639
|
+
# Create tasks from the loaded data using load_task_from_json
|
|
640
|
+
# This will validate and create verifiers properly
|
|
603
641
|
tasks = []
|
|
604
642
|
for task_data in tasks_data:
|
|
605
|
-
|
|
643
|
+
# Validate that verifier_func exists
|
|
644
|
+
verifier_code = task_data.get("verifier_func") or task_data.get(
|
|
645
|
+
"verifier_code"
|
|
646
|
+
)
|
|
647
|
+
if not verifier_code:
|
|
648
|
+
task_key = task_data.get("key", task_data.get("id", "unknown"))
|
|
649
|
+
raise ValueError(
|
|
650
|
+
f"Task {task_key} is missing verifier_func. "
|
|
651
|
+
"All tasks must have a verifier_func to be imported."
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
# Use load_task_from_json to properly create and validate the task
|
|
655
|
+
# Pass raise_on_verifier_error=True to fail fast on invalid verifier code
|
|
656
|
+
task = self.load_task_from_json(task_data, raise_on_verifier_error=True)
|
|
606
657
|
tasks.append(task)
|
|
607
658
|
|
|
608
659
|
# Use ThreadPoolExecutor to parallelize the imports with max 20 workers
|
|
609
660
|
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
|
|
610
|
-
responses = list(
|
|
661
|
+
responses = list(
|
|
662
|
+
executor.map(lambda t: self.import_single_task(t, project_key), tasks)
|
|
663
|
+
)
|
|
611
664
|
|
|
612
665
|
# Filter out None values (failed imports)
|
|
613
666
|
return [r for r in responses if r is not None]
|
fleet/tasks.py
CHANGED
|
@@ -26,6 +26,8 @@ class Task(BaseModel):
|
|
|
26
26
|
)
|
|
27
27
|
created_at: Optional[datetime] = Field(None, description="Task creation timestamp")
|
|
28
28
|
version: Optional[str] = Field(None, description="Task version")
|
|
29
|
+
data_id: Optional[str] = Field(None, description="Data identifier")
|
|
30
|
+
data_version: Optional[str] = Field(None, description="Data version")
|
|
29
31
|
verifier_func: Optional[str] = Field(None, description="Verifier function code")
|
|
30
32
|
verifier: Optional[Any] = Field(
|
|
31
33
|
None,
|
|
@@ -59,6 +61,15 @@ class Task(BaseModel):
|
|
|
59
61
|
return f"{self.env_id}:{self.version}"
|
|
60
62
|
return self.env_id
|
|
61
63
|
|
|
64
|
+
@property
|
|
65
|
+
def data_key(self) -> Optional[str]:
|
|
66
|
+
"""Get the data key combining data_id and data_version."""
|
|
67
|
+
if self.data_id and self.data_version:
|
|
68
|
+
return f"{self.data_id}:{self.data_version}"
|
|
69
|
+
elif self.data_id:
|
|
70
|
+
return self.data_id
|
|
71
|
+
return None
|
|
72
|
+
|
|
62
73
|
class Config:
|
|
63
74
|
"""Pydantic model configuration."""
|
|
64
75
|
|
|
@@ -285,6 +296,8 @@ def load_tasks(
|
|
|
285
296
|
version: Optional[str] = None,
|
|
286
297
|
team_id: Optional[str] = None,
|
|
287
298
|
project_key: Optional[str] = None,
|
|
299
|
+
data_id: Optional[str] = None,
|
|
300
|
+
data_version: Optional[str] = None,
|
|
288
301
|
) -> List[Task]:
|
|
289
302
|
"""Convenience function to load tasks with optional filtering.
|
|
290
303
|
|
|
@@ -293,11 +306,15 @@ def load_tasks(
|
|
|
293
306
|
keys: Optional list of task keys to filter by
|
|
294
307
|
version: Optional version to filter tasks by
|
|
295
308
|
team_id: Optional team_id to filter by (admin only)
|
|
309
|
+
project_key: Optional project key to filter tasks by
|
|
310
|
+
data_id: Optional data identifier to filter tasks by
|
|
311
|
+
data_version: Optional data version to filter tasks by
|
|
296
312
|
|
|
297
313
|
Examples:
|
|
298
|
-
tasks =
|
|
299
|
-
tasks =
|
|
300
|
-
tasks =
|
|
314
|
+
tasks = fleet.load_tasks(env_key="fira")
|
|
315
|
+
tasks = fleet.load_tasks(keys=["task1", "task2"])
|
|
316
|
+
tasks = fleet.load_tasks(env_key="fira", version="v1.0")
|
|
317
|
+
tasks = fleet.load_tasks(data_id="my-data", data_version="v1.0")
|
|
301
318
|
"""
|
|
302
319
|
# Use the global client by default so users can pre-configure it once
|
|
303
320
|
from .global_client import get_client
|
|
@@ -309,6 +326,8 @@ def load_tasks(
|
|
|
309
326
|
version=version,
|
|
310
327
|
team_id=team_id,
|
|
311
328
|
project_key=project_key,
|
|
329
|
+
data_id=data_id,
|
|
330
|
+
data_version=data_version,
|
|
312
331
|
)
|
|
313
332
|
|
|
314
333
|
|
|
@@ -335,3 +354,44 @@ def update_task(
|
|
|
335
354
|
return client.update_task(
|
|
336
355
|
task_key=task_key, prompt=prompt, verifier_code=verifier_code
|
|
337
356
|
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def import_task(task: Task, project_key: Optional[str] = None):
|
|
360
|
+
"""Convenience function to import a single task.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
task: Task object to import
|
|
364
|
+
project_key: Optional project key to associate with the task
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
Response from the API, or None if the import failed
|
|
368
|
+
|
|
369
|
+
Examples:
|
|
370
|
+
task = fleet.Task(key="my-task", prompt="Do something", env_id="my-env")
|
|
371
|
+
response = fleet.import_task(task)
|
|
372
|
+
response = fleet.import_task(task, project_key="my-project")
|
|
373
|
+
"""
|
|
374
|
+
from .global_client import get_client
|
|
375
|
+
|
|
376
|
+
client = get_client()
|
|
377
|
+
return client.import_single_task(task, project_key=project_key)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def import_tasks(filename: str, project_key: Optional[str] = None):
|
|
381
|
+
"""Convenience function to import tasks from a JSON file.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
filename: Path to the JSON file of Task objects to import
|
|
385
|
+
project_key: Optional project key to associate with the tasks
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
List of responses from the API for successfully imported tasks
|
|
389
|
+
|
|
390
|
+
Examples:
|
|
391
|
+
responses = fleet.import_tasks("tasks.json")
|
|
392
|
+
responses = fleet.import_tasks("tasks.json", project_key="my-project")
|
|
393
|
+
"""
|
|
394
|
+
from .global_client import get_client
|
|
395
|
+
|
|
396
|
+
client = get_client()
|
|
397
|
+
return client.import_tasks(filename, project_key=project_key)
|
|
@@ -11,7 +11,9 @@ examples/example_sync.py,sha256=EkuWmUzB1ZsBJQk6ZRflB793rKsuRHeSg5HJZHVhBB0,975
|
|
|
11
11
|
examples/example_task.py,sha256=dhG6STAkNsTdHs9cO1RFH9WfuvRmq5bRC211hTeFrk8,7088
|
|
12
12
|
examples/example_tasks.py,sha256=xTL8UWVAuolSX6swskfrAcmDrLIzn45dJ7YPWCwoEBU,514
|
|
13
13
|
examples/example_verifier.py,sha256=0vwNITIG3m4CkSPwIxNXcGx9TqrxEsCGqK2A8keKZMM,2392
|
|
14
|
+
examples/export_tasks.py,sha256=BSRmsGIIk34QYanB8rPlaFbmn3MBxaf7TyFVrbsdPeU,2114
|
|
14
15
|
examples/gemini_example.py,sha256=qj9WDazQTYNiRHNeUg9Tjkp33lJMwbx8gDfpFe1sDQo,16180
|
|
16
|
+
examples/import_tasks.py,sha256=pb60dOXnOn-G1INQxV1wujeHmmY2oWz9Ddj9SbMm1pk,3139
|
|
15
17
|
examples/json_tasks_example.py,sha256=CYPESGGtOo0fmsDdLidujTfsE4QlJHw7rOhyVqPJ_Ls,5329
|
|
16
18
|
examples/nova_act_example.py,sha256=rH23Lp74Okf0rn8ynMdWjK2aviEf5NLPH4k_53Pyxho,831
|
|
17
19
|
examples/openai_example.py,sha256=dEWERrTEP5xBiGkLkQjBQGd2NqoxX6gcW6XteBPsWFQ,8231
|
|
@@ -19,22 +21,22 @@ examples/openai_simple_example.py,sha256=HmiufucrAZne7tHq9uoEsDWlEhjNC265bQAyIGB
|
|
|
19
21
|
examples/query_builder_example.py,sha256=-cOMfWGNifYfYEt_Ds73XpwATZvFDL6F4KTkVxdMjzg,3951
|
|
20
22
|
examples/quickstart.py,sha256=1VT39IRRhemsJgxi0O0gprdpcw7HB4pYO97GAYagIcg,3788
|
|
21
23
|
examples/test_cdp_logging.py,sha256=AkCwQCgOTQEI8w3v0knWK_4eXMph7L9x07wj9yIYM10,2836
|
|
22
|
-
fleet/__init__.py,sha256=
|
|
24
|
+
fleet/__init__.py,sha256=fwIcuaJPPdLMdKPsazfz4EAPeJchl9MBN4nxs0YlOjw,4117
|
|
23
25
|
fleet/base.py,sha256=bc-340sTpq_DJs7yQ9d2pDWnmJFmA1SwDB9Lagvqtb4,9182
|
|
24
|
-
fleet/client.py,sha256=
|
|
26
|
+
fleet/client.py,sha256=0RwFXBdiBSK7ziTrUe2uzhHX14MxuboIQhMCoRMC300,30315
|
|
25
27
|
fleet/config.py,sha256=uY02ZKxVoXqVDta-0IMWaYJeE1CTXF_fA9NI6QUutmU,319
|
|
26
28
|
fleet/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
27
29
|
fleet/global_client.py,sha256=frrDAFNM2ywN0JHLtlm9qbE1dQpnQJsavJpb7xSR_bU,1072
|
|
28
30
|
fleet/models.py,sha256=d9eish0KO3t4VCNu8h8Q_6K1Xj-crYo5Fejtle0Ur28,13056
|
|
29
|
-
fleet/tasks.py,sha256=
|
|
31
|
+
fleet/tasks.py,sha256=nNGyeGgWvluzSatd_AHqj9nk8jJ5dgCySPODCaySJ8M,14487
|
|
30
32
|
fleet/types.py,sha256=L4Y82xICf1tzyCLqhLYUgEoaIIS5h9T05TyFNHSWs3s,652
|
|
31
|
-
fleet/_async/__init__.py,sha256=
|
|
33
|
+
fleet/_async/__init__.py,sha256=ruGtdMl9BRQRsJygNszPT89ZbMy6AYv1mmeYoB--r60,7557
|
|
32
34
|
fleet/_async/base.py,sha256=oisVTQsx0M_yTmyQJc3oij63uKZ97MHz-xYFsWXxQE8,9202
|
|
33
|
-
fleet/_async/client.py,sha256=
|
|
35
|
+
fleet/_async/client.py,sha256=uiYufTUAG3o_nseflp_DxW8yLe8JnprIxCwjZ4CsqoI,30751
|
|
34
36
|
fleet/_async/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
35
37
|
fleet/_async/global_client.py,sha256=4WskpLHbsDEgWW7hXMD09W-brkp4euy8w2ZJ88594rQ,1103
|
|
36
38
|
fleet/_async/models.py,sha256=GX-sRciZDenW2O7Qx9w_ftOkJyE4ph1-92WMq6lynHE,12856
|
|
37
|
-
fleet/_async/tasks.py,sha256=
|
|
39
|
+
fleet/_async/tasks.py,sha256=6EzoIuU-LuL-m716_ACV4oO3fNO_3Y1mAzrQ6MZDdwc,14441
|
|
38
40
|
fleet/_async/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
41
|
fleet/_async/env/client.py,sha256=C5WG5Ir_McXaFPZNdkQjj0w4V7xMIcw3QyVP5g-3kVk,1237
|
|
40
42
|
fleet/_async/instance/__init__.py,sha256=PtmJq8J8bh0SOQ2V55QURz5GJfobozwtQoqhaOk3_tI,515
|
|
@@ -67,10 +69,10 @@ fleet/verifiers/decorator.py,sha256=nAP3O8szXu7md_kpwpz91hGSUNEVLYjwZQZTkQlV1DM,
|
|
|
67
69
|
fleet/verifiers/parse.py,sha256=qz9AfJrTbjlg-LU-lE8Ciqi7Yt2a8-cs17FdpjTLhMk,8550
|
|
68
70
|
fleet/verifiers/sql_differ.py,sha256=TqTLWyK3uOyLbitT6HYzYEzuSFC39wcyhgk3rcm__k8,6525
|
|
69
71
|
fleet/verifiers/verifier.py,sha256=_lcxXVm8e0xRrK2gNJy9up7pW1zOkPRY5n5lQ85S8jg,14197
|
|
70
|
-
fleet_python-0.2.
|
|
72
|
+
fleet_python-0.2.55.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
71
73
|
scripts/fix_sync_imports.py,sha256=X9fWLTpiPGkSHsjyQUDepOJkxOqw1DPj7nd8wFlFqLQ,8368
|
|
72
74
|
scripts/unasync.py,sha256=vWVQxRWX8SRZO5cmzEhpvnG_REhCWXpidIGIpWmEcvI,696
|
|
73
|
-
fleet_python-0.2.
|
|
74
|
-
fleet_python-0.2.
|
|
75
|
-
fleet_python-0.2.
|
|
76
|
-
fleet_python-0.2.
|
|
75
|
+
fleet_python-0.2.55.dist-info/METADATA,sha256=XOtlVrz0SxTxmvmv_EMP4UOhd-T7RJzXaHtPC9vCtzs,3304
|
|
76
|
+
fleet_python-0.2.55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
77
|
+
fleet_python-0.2.55.dist-info/top_level.txt,sha256=_3DSmTohvSDf3AIP_BYfGzhwO1ECFwuzg83X-wHCx3Y,23
|
|
78
|
+
fleet_python-0.2.55.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|