fleet-python 0.2.43__py3-none-any.whl → 0.2.45__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.
- fleet/__init__.py +9 -7
- fleet/_async/__init__.py +28 -16
- fleet/_async/client.py +44 -16
- fleet/_async/instance/client.py +1 -1
- fleet/_async/resources/sqlite.py +34 -34
- fleet/_async/tasks.py +37 -2
- fleet/_async/verifiers/verifier.py +3 -3
- fleet/client.py +44 -13
- fleet/instance/client.py +2 -4
- fleet/resources/sqlite.py +37 -43
- fleet/tasks.py +46 -41
- fleet/verifiers/__init__.py +1 -1
- fleet/verifiers/db.py +41 -36
- fleet/verifiers/parse.py +4 -1
- fleet/verifiers/sql_differ.py +8 -8
- fleet/verifiers/verifier.py +19 -7
- {fleet_python-0.2.43.dist-info → fleet_python-0.2.45.dist-info}/METADATA +1 -1
- {fleet_python-0.2.43.dist-info → fleet_python-0.2.45.dist-info}/RECORD +21 -21
- {fleet_python-0.2.43.dist-info → fleet_python-0.2.45.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.43.dist-info → fleet_python-0.2.45.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.43.dist-info → fleet_python-0.2.45.dist-info}/top_level.txt +0 -0
fleet/client.py
CHANGED
|
@@ -130,7 +130,7 @@ class SyncEnv(EnvironmentBase):
|
|
|
130
130
|
return self.instance.verify(validator)
|
|
131
131
|
|
|
132
132
|
def verify_raw(
|
|
133
|
-
self, function_code: str, function_name: str
|
|
133
|
+
self, function_code: str, function_name: Optional[str] = None
|
|
134
134
|
) -> ExecuteFunctionResponse:
|
|
135
135
|
return self.instance.verify_raw(function_code, function_name)
|
|
136
136
|
|
|
@@ -302,10 +302,19 @@ class Fleet:
|
|
|
302
302
|
|
|
303
303
|
return self.load_task_array_from_string(tasks_data)
|
|
304
304
|
|
|
305
|
-
def load_task_array_from_string(self, serialized_tasks:
|
|
305
|
+
def load_task_array_from_string(self, serialized_tasks: str) -> List[Task]:
|
|
306
306
|
tasks = []
|
|
307
307
|
|
|
308
|
-
|
|
308
|
+
parsed_data = json.loads(serialized_tasks)
|
|
309
|
+
if isinstance(parsed_data, list):
|
|
310
|
+
json_tasks = parsed_data
|
|
311
|
+
elif isinstance(parsed_data, dict) and "tasks" in parsed_data:
|
|
312
|
+
json_tasks = parsed_data["tasks"]
|
|
313
|
+
else:
|
|
314
|
+
raise ValueError(
|
|
315
|
+
"Invalid JSON structure: expected array or object with 'tasks' key"
|
|
316
|
+
)
|
|
317
|
+
|
|
309
318
|
for json_task in json_tasks:
|
|
310
319
|
parsed_task = self.load_task_from_json(json_task)
|
|
311
320
|
tasks.append(parsed_task)
|
|
@@ -316,25 +325,47 @@ class Fleet:
|
|
|
316
325
|
return self.load_task_from_json(task_json)
|
|
317
326
|
|
|
318
327
|
def load_task_from_json(self, task_json: Dict) -> Task:
|
|
328
|
+
verifier = None
|
|
329
|
+
verifier_code = task_json.get("verifier_func") or task_json.get("verifier_code")
|
|
330
|
+
|
|
331
|
+
# Try to find verifier_id in multiple locations
|
|
332
|
+
verifier_id = task_json.get("verifier_id")
|
|
333
|
+
if (
|
|
334
|
+
not verifier_id
|
|
335
|
+
and "metadata" in task_json
|
|
336
|
+
and isinstance(task_json["metadata"], dict)
|
|
337
|
+
):
|
|
338
|
+
verifier_metadata = task_json["metadata"].get("verifier", {})
|
|
339
|
+
if isinstance(verifier_metadata, dict):
|
|
340
|
+
verifier_id = verifier_metadata.get("verifier_id")
|
|
341
|
+
|
|
342
|
+
# If no verifier_id found, use the task key/id as fallback
|
|
343
|
+
if not verifier_id:
|
|
344
|
+
verifier_id = task_json.get("key", task_json.get("id"))
|
|
345
|
+
|
|
319
346
|
try:
|
|
320
|
-
if
|
|
347
|
+
if verifier_id and verifier_code:
|
|
321
348
|
verifier = self._create_verifier_from_data(
|
|
322
|
-
verifier_id=
|
|
323
|
-
verifier_key=task_json
|
|
324
|
-
verifier_code=
|
|
349
|
+
verifier_id=verifier_id,
|
|
350
|
+
verifier_key=task_json.get("key", task_json.get("id")),
|
|
351
|
+
verifier_code=verifier_code,
|
|
325
352
|
verifier_sha=task_json.get("verifier_sha", ""),
|
|
326
353
|
)
|
|
327
354
|
except Exception as e:
|
|
328
|
-
logger.warning(
|
|
355
|
+
logger.warning(
|
|
356
|
+
f"Failed to create verifier {task_json.get('key', task_json.get('id'))}: {e}"
|
|
357
|
+
)
|
|
329
358
|
|
|
330
359
|
task = Task(
|
|
331
|
-
key=task_json
|
|
360
|
+
key=task_json.get("key", task_json.get("id")),
|
|
332
361
|
prompt=task_json["prompt"],
|
|
333
|
-
env_id=task_json
|
|
334
|
-
|
|
362
|
+
env_id=task_json.get(
|
|
363
|
+
"env_id", task_json.get("env_key")
|
|
364
|
+
), # Use env_id or fallback to env_key
|
|
365
|
+
created_at=task_json.get("created_at"),
|
|
335
366
|
version=task_json.get("version"),
|
|
336
367
|
env_variables=task_json.get("env_variables", {}),
|
|
337
|
-
verifier_func=
|
|
368
|
+
verifier_func=verifier_code, # Set verifier code
|
|
338
369
|
verifier=verifier, # Use created verifier or None
|
|
339
370
|
metadata=task_json.get("metadata", {}), # Default empty metadata
|
|
340
371
|
)
|
|
@@ -636,7 +667,7 @@ class Fleet:
|
|
|
636
667
|
AsyncVerifierFunction created from the verifier code
|
|
637
668
|
"""
|
|
638
669
|
# Fetch verifier from API
|
|
639
|
-
response = self.client.request("GET", f"/v1/
|
|
670
|
+
response = self.client.request("GET", f"/v1/verifiers/{verifier_id}")
|
|
640
671
|
verifier_data = response.json()
|
|
641
672
|
|
|
642
673
|
# Use the common method to create verifier
|
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
|
)
|
|
@@ -108,7 +106,7 @@ class InstanceClient:
|
|
|
108
106
|
return self.verify_raw(function_code, function_name)
|
|
109
107
|
|
|
110
108
|
def verify_raw(
|
|
111
|
-
self, function_code: str, function_name: str
|
|
109
|
+
self, function_code: str, function_name: Optional[str] = None
|
|
112
110
|
) -> ExecuteFunctionResponse:
|
|
113
111
|
try:
|
|
114
112
|
function_code = convert_verifier_string(function_code)
|
fleet/resources/sqlite.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, List, Optional
|
|
1
|
+
from typing import Any, List, Optional, Dict, Tuple
|
|
2
2
|
from ..instance.models import Resource as ResourceModel
|
|
3
3
|
from ..instance.models import DescribeResponse, QueryRequest, QueryResponse
|
|
4
4
|
from .base import Resource
|
|
@@ -25,12 +25,12 @@ from fleet.verifiers.db import (
|
|
|
25
25
|
class SyncDatabaseSnapshot:
|
|
26
26
|
"""Async database snapshot that fetches data through API and stores locally for diffing."""
|
|
27
27
|
|
|
28
|
-
def __init__(self, resource: "SQLiteResource", name: str
|
|
28
|
+
def __init__(self, resource: "SQLiteResource", name: Optional[str] = None):
|
|
29
29
|
self.resource = resource
|
|
30
30
|
self.name = name or f"snapshot_{datetime.utcnow().isoformat()}"
|
|
31
31
|
self.created_at = datetime.utcnow()
|
|
32
|
-
self._data:
|
|
33
|
-
self._schemas:
|
|
32
|
+
self._data: Dict[str, List[Dict[str, Any]]] = {}
|
|
33
|
+
self._schemas: Dict[str, List[str]] = {}
|
|
34
34
|
self._fetched = False
|
|
35
35
|
|
|
36
36
|
def _ensure_fetched(self):
|
|
@@ -69,7 +69,7 @@ class SyncDatabaseSnapshot:
|
|
|
69
69
|
|
|
70
70
|
self._fetched = True
|
|
71
71
|
|
|
72
|
-
def tables(self) ->
|
|
72
|
+
def tables(self) -> List[str]:
|
|
73
73
|
"""Get list of all tables in the snapshot."""
|
|
74
74
|
self._ensure_fetched()
|
|
75
75
|
return list(self._data.keys())
|
|
@@ -81,7 +81,7 @@ class SyncDatabaseSnapshot:
|
|
|
81
81
|
def diff(
|
|
82
82
|
self,
|
|
83
83
|
other: "SyncDatabaseSnapshot",
|
|
84
|
-
ignore_config: IgnoreConfig
|
|
84
|
+
ignore_config: Optional[IgnoreConfig] = None,
|
|
85
85
|
) -> "SyncSnapshotDiff":
|
|
86
86
|
"""Compare this snapshot with another."""
|
|
87
87
|
self._ensure_fetched()
|
|
@@ -95,13 +95,13 @@ class SyncSnapshotQueryBuilder:
|
|
|
95
95
|
def __init__(self, snapshot: SyncDatabaseSnapshot, table: str):
|
|
96
96
|
self._snapshot = snapshot
|
|
97
97
|
self._table = table
|
|
98
|
-
self._select_cols:
|
|
99
|
-
self._conditions:
|
|
100
|
-
self._limit: int
|
|
101
|
-
self._order_by: str
|
|
98
|
+
self._select_cols: List[str] = ["*"]
|
|
99
|
+
self._conditions: List[Tuple[str, str, Any]] = []
|
|
100
|
+
self._limit: Optional[int] = None
|
|
101
|
+
self._order_by: Optional[str] = None
|
|
102
102
|
self._order_desc: bool = False
|
|
103
103
|
|
|
104
|
-
def _get_data(self) ->
|
|
104
|
+
def _get_data(self) -> List[Dict[str, Any]]:
|
|
105
105
|
"""Get table data from snapshot."""
|
|
106
106
|
self._snapshot._ensure_fetched()
|
|
107
107
|
return self._snapshot._data.get(self._table, [])
|
|
@@ -122,11 +122,11 @@ class SyncSnapshotQueryBuilder:
|
|
|
122
122
|
qb._order_desc = desc
|
|
123
123
|
return qb
|
|
124
124
|
|
|
125
|
-
def first(self) ->
|
|
125
|
+
def first(self) -> Optional[Dict[str, Any]]:
|
|
126
126
|
rows = self.all()
|
|
127
127
|
return rows[0] if rows else None
|
|
128
128
|
|
|
129
|
-
def all(self) ->
|
|
129
|
+
def all(self) -> List[Dict[str, Any]]:
|
|
130
130
|
data = self._get_data()
|
|
131
131
|
|
|
132
132
|
# Apply filters
|
|
@@ -185,14 +185,14 @@ class SyncSnapshotDiff:
|
|
|
185
185
|
self,
|
|
186
186
|
before: SyncDatabaseSnapshot,
|
|
187
187
|
after: SyncDatabaseSnapshot,
|
|
188
|
-
ignore_config: IgnoreConfig
|
|
188
|
+
ignore_config: Optional[IgnoreConfig] = None,
|
|
189
189
|
):
|
|
190
190
|
self.before = before
|
|
191
191
|
self.after = after
|
|
192
192
|
self.ignore_config = ignore_config or IgnoreConfig()
|
|
193
|
-
self._cached:
|
|
193
|
+
self._cached: Optional[Dict[str, Any]] = None
|
|
194
194
|
|
|
195
|
-
def _get_primary_key_columns(self, table: str) ->
|
|
195
|
+
def _get_primary_key_columns(self, table: str) -> List[str]:
|
|
196
196
|
"""Get primary key columns for a table."""
|
|
197
197
|
# Try to get from schema
|
|
198
198
|
schema_response = self.after.resource.query(f"PRAGMA table_info({table})")
|
|
@@ -222,7 +222,7 @@ class SyncSnapshotDiff:
|
|
|
222
222
|
return self._cached
|
|
223
223
|
|
|
224
224
|
all_tables = set(self.before.tables()) | set(self.after.tables())
|
|
225
|
-
diff:
|
|
225
|
+
diff: Dict[str, Dict[str, Any]] = {}
|
|
226
226
|
|
|
227
227
|
for tbl in all_tables:
|
|
228
228
|
if self.ignore_config.should_ignore_table(tbl):
|
|
@@ -236,7 +236,7 @@ class SyncSnapshotDiff:
|
|
|
236
236
|
after_data = self.after._data.get(tbl, [])
|
|
237
237
|
|
|
238
238
|
# Create indexes by primary key
|
|
239
|
-
def make_key(row: dict, pk_cols:
|
|
239
|
+
def make_key(row: dict, pk_cols: List[str]) -> Any:
|
|
240
240
|
if len(pk_cols) == 1:
|
|
241
241
|
return row.get(pk_cols[0])
|
|
242
242
|
return tuple(row.get(col) for col in pk_cols)
|
|
@@ -304,12 +304,12 @@ class SyncSnapshotDiff:
|
|
|
304
304
|
self._cached = diff
|
|
305
305
|
return diff
|
|
306
306
|
|
|
307
|
-
def expect_only(self, allowed_changes:
|
|
307
|
+
def expect_only(self, allowed_changes: List[Dict[str, Any]]):
|
|
308
308
|
"""Ensure only specified changes occurred."""
|
|
309
309
|
diff = self._collect()
|
|
310
310
|
|
|
311
311
|
def _is_change_allowed(
|
|
312
|
-
table: str, row_id: Any, field: str
|
|
312
|
+
table: str, row_id: Any, field: Optional[str], after_value: Any
|
|
313
313
|
) -> bool:
|
|
314
314
|
"""Check if a change is in the allowed list using semantic comparison."""
|
|
315
315
|
for allowed in allowed_changes:
|
|
@@ -440,11 +440,11 @@ class SyncQueryBuilder:
|
|
|
440
440
|
def __init__(self, resource: "SQLiteResource", table: str):
|
|
441
441
|
self._resource = resource
|
|
442
442
|
self._table = table
|
|
443
|
-
self._select_cols:
|
|
444
|
-
self._conditions:
|
|
445
|
-
self._joins:
|
|
446
|
-
self._limit: int
|
|
447
|
-
self._order_by: str
|
|
443
|
+
self._select_cols: List[str] = ["*"]
|
|
444
|
+
self._conditions: List[Tuple[str, str, Any]] = []
|
|
445
|
+
self._joins: List[Tuple[str, Dict[str, str]]] = []
|
|
446
|
+
self._limit: Optional[int] = None
|
|
447
|
+
self._order_by: Optional[str] = None
|
|
448
448
|
|
|
449
449
|
# Column projection / limiting / ordering
|
|
450
450
|
def select(self, *columns: str) -> "SyncQueryBuilder":
|
|
@@ -486,12 +486,12 @@ class SyncQueryBuilder:
|
|
|
486
486
|
def lte(self, column: str, value: Any) -> "SyncQueryBuilder":
|
|
487
487
|
return self._add_condition(column, "<=", value)
|
|
488
488
|
|
|
489
|
-
def in_(self, column: str, values:
|
|
489
|
+
def in_(self, column: str, values: List[Any]) -> "SyncQueryBuilder":
|
|
490
490
|
qb = self._clone()
|
|
491
491
|
qb._conditions.append((column, "IN", tuple(values)))
|
|
492
492
|
return qb
|
|
493
493
|
|
|
494
|
-
def not_in(self, column: str, values:
|
|
494
|
+
def not_in(self, column: str, values: List[Any]) -> "SyncQueryBuilder":
|
|
495
495
|
qb = self._clone()
|
|
496
496
|
qb._conditions.append((column, "NOT IN", tuple(values)))
|
|
497
497
|
return qb
|
|
@@ -508,16 +508,16 @@ class SyncQueryBuilder:
|
|
|
508
508
|
return qb
|
|
509
509
|
|
|
510
510
|
# JOIN
|
|
511
|
-
def join(self, other_table: str, on:
|
|
511
|
+
def join(self, other_table: str, on: Dict[str, str]) -> "SyncQueryBuilder":
|
|
512
512
|
qb = self._clone()
|
|
513
513
|
qb._joins.append((other_table, on))
|
|
514
514
|
return qb
|
|
515
515
|
|
|
516
516
|
# Compile to SQL
|
|
517
|
-
def _compile(self) ->
|
|
517
|
+
def _compile(self) -> Tuple[str, List[Any]]:
|
|
518
518
|
cols = ", ".join(self._select_cols)
|
|
519
519
|
sql = [f"SELECT {cols} FROM {self._table}"]
|
|
520
|
-
params:
|
|
520
|
+
params: List[Any] = []
|
|
521
521
|
|
|
522
522
|
# Joins
|
|
523
523
|
for tbl, onmap in self._joins:
|
|
@@ -558,11 +558,11 @@ class SyncQueryBuilder:
|
|
|
558
558
|
return row_dict.get("__cnt__", 0)
|
|
559
559
|
return 0
|
|
560
560
|
|
|
561
|
-
def first(self) ->
|
|
561
|
+
def first(self) -> Optional[Dict[str, Any]]:
|
|
562
562
|
rows = self.limit(1).all()
|
|
563
563
|
return rows[0] if rows else None
|
|
564
564
|
|
|
565
|
-
def all(self) ->
|
|
565
|
+
def all(self) -> List[Dict[str, Any]]:
|
|
566
566
|
sql, params = self._compile()
|
|
567
567
|
response = self._resource.query(sql, params)
|
|
568
568
|
if not response.rows:
|
|
@@ -651,9 +651,7 @@ class SQLiteResource(Resource):
|
|
|
651
651
|
)
|
|
652
652
|
return DescribeResponse(**response.json())
|
|
653
653
|
|
|
654
|
-
def query(
|
|
655
|
-
self, query: str, args: Optional[List[Any]] = None
|
|
656
|
-
) -> QueryResponse:
|
|
654
|
+
def query(self, query: str, args: Optional[List[Any]] = None) -> QueryResponse:
|
|
657
655
|
return self._query(query, args, read_only=True)
|
|
658
656
|
|
|
659
657
|
def exec(self, query: str, args: Optional[List[Any]] = None) -> QueryResponse:
|
|
@@ -674,7 +672,7 @@ class SQLiteResource(Resource):
|
|
|
674
672
|
"""Create a query builder for the specified table."""
|
|
675
673
|
return SyncQueryBuilder(self, table_name)
|
|
676
674
|
|
|
677
|
-
def snapshot(self, name: str
|
|
675
|
+
def snapshot(self, name: Optional[str] = None) -> SyncDatabaseSnapshot:
|
|
678
676
|
"""Create a snapshot of the current database state."""
|
|
679
677
|
snapshot = SyncDatabaseSnapshot(self, name)
|
|
680
678
|
snapshot._ensure_fetched()
|
|
@@ -683,7 +681,7 @@ class SQLiteResource(Resource):
|
|
|
683
681
|
def diff(
|
|
684
682
|
self,
|
|
685
683
|
other: "SQLiteResource",
|
|
686
|
-
ignore_config: IgnoreConfig
|
|
684
|
+
ignore_config: Optional[IgnoreConfig] = None,
|
|
687
685
|
) -> SyncSnapshotDiff:
|
|
688
686
|
"""Compare this database with another AsyncSQLiteResource.
|
|
689
687
|
|
|
@@ -695,12 +693,8 @@ class SQLiteResource(Resource):
|
|
|
695
693
|
AsyncSnapshotDiff: Object containing the differences between the two databases
|
|
696
694
|
"""
|
|
697
695
|
# Create snapshots of both databases
|
|
698
|
-
before_snapshot = self.snapshot(
|
|
699
|
-
|
|
700
|
-
)
|
|
701
|
-
after_snapshot = other.snapshot(
|
|
702
|
-
name=f"after_{datetime.utcnow().isoformat()}"
|
|
703
|
-
)
|
|
696
|
+
before_snapshot = self.snapshot(name=f"before_{datetime.utcnow().isoformat()}")
|
|
697
|
+
after_snapshot = other.snapshot(name=f"after_{datetime.utcnow().isoformat()}")
|
|
704
698
|
|
|
705
699
|
# Return the diff between the snapshots
|
|
706
700
|
return before_snapshot.diff(after_snapshot, ignore_config)
|
fleet/tasks.py
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import re
|
|
6
5
|
import asyncio
|
|
7
6
|
from datetime import datetime
|
|
8
7
|
from typing import Any, Dict, Optional, List
|
|
9
|
-
from uuid import UUID
|
|
10
8
|
|
|
11
9
|
from pydantic import BaseModel, Field, validator
|
|
12
10
|
|
|
@@ -27,7 +25,9 @@ class Task(BaseModel):
|
|
|
27
25
|
version: Optional[str] = Field(None, description="Task version")
|
|
28
26
|
verifier_func: Optional[str] = Field(None, description="Verifier function code")
|
|
29
27
|
verifier: Optional[Any] = Field(
|
|
30
|
-
None,
|
|
28
|
+
None,
|
|
29
|
+
description="Verifier function with decorator (async or sync)",
|
|
30
|
+
exclude=True,
|
|
31
31
|
)
|
|
32
32
|
verifier_id: Optional[str] = Field(None, description="Verifier identifier")
|
|
33
33
|
verifier_sha: Optional[str] = Field(None, description="Verifier SHA256 hash")
|
|
@@ -47,7 +47,7 @@ class Task(BaseModel):
|
|
|
47
47
|
@property
|
|
48
48
|
def env_key(self) -> str:
|
|
49
49
|
"""Get the environment key combining env_id and version."""
|
|
50
|
-
if self.version and self.version != "None":
|
|
50
|
+
if self.version and self.version != "None" and ":" not in self.env_id:
|
|
51
51
|
return f"{self.env_id}:{self.version}"
|
|
52
52
|
return self.env_id
|
|
53
53
|
|
|
@@ -66,21 +66,21 @@ class Task(BaseModel):
|
|
|
66
66
|
For sync environments, calls the sync verifier directly.
|
|
67
67
|
For async verifiers, automatically runs them with asyncio.run().
|
|
68
68
|
"""
|
|
69
|
+
# If verifier doesn't exist but verifier_func does, rebuild it
|
|
70
|
+
if not self.verifier and self.verifier_func:
|
|
71
|
+
self._rebuild_verifier()
|
|
72
|
+
|
|
69
73
|
if self.verifier:
|
|
70
74
|
import inspect
|
|
71
75
|
|
|
72
76
|
# Check if verifier has remote method (for decorated verifiers)
|
|
73
|
-
|
|
74
|
-
result = self.verifier.remote(env, *args, **kwargs)
|
|
75
|
-
else:
|
|
76
|
-
# For verifiers created from string, call directly
|
|
77
|
-
result = self.verifier(env, *args, **kwargs)
|
|
77
|
+
result = self.verifier.remote(env, *args, **kwargs)
|
|
78
78
|
|
|
79
79
|
# If the result is a coroutine, we need to run it
|
|
80
80
|
if inspect.iscoroutine(result):
|
|
81
81
|
# Check if we're already in an event loop
|
|
82
82
|
try:
|
|
83
|
-
|
|
83
|
+
asyncio.get_running_loop()
|
|
84
84
|
# We're in an async context, can't use asyncio.run()
|
|
85
85
|
raise RuntimeError(
|
|
86
86
|
"Cannot run async verifier in sync mode while event loop is running. "
|
|
@@ -100,6 +100,10 @@ class Task(BaseModel):
|
|
|
100
100
|
For async environments, awaits the async verifier.
|
|
101
101
|
Works with both sync and async verifiers in async contexts.
|
|
102
102
|
"""
|
|
103
|
+
# If verifier doesn't exist but verifier_func does, rebuild it
|
|
104
|
+
if not self.verifier and self.verifier_func:
|
|
105
|
+
self._rebuild_verifier()
|
|
106
|
+
|
|
103
107
|
if self.verifier:
|
|
104
108
|
result = self.verifier.remote(*args, **kwargs)
|
|
105
109
|
# If it's a coroutine, await it
|
|
@@ -112,6 +116,19 @@ class Task(BaseModel):
|
|
|
112
116
|
else:
|
|
113
117
|
raise ValueError("No verifier function found for this task")
|
|
114
118
|
|
|
119
|
+
def _rebuild_verifier(self):
|
|
120
|
+
"""Rebuild the verifier from verifier_func string if it exists."""
|
|
121
|
+
if self.verifier_func:
|
|
122
|
+
# Use the same logic as in verifier_from_string
|
|
123
|
+
verifier_id = self.verifier_id or self.key
|
|
124
|
+
verifier = verifier_from_string(
|
|
125
|
+
verifier_func=self.verifier_func,
|
|
126
|
+
verifier_id=verifier_id,
|
|
127
|
+
verifier_key=self.key,
|
|
128
|
+
sha256=self.verifier_sha or "",
|
|
129
|
+
)
|
|
130
|
+
self.verifier = verifier
|
|
131
|
+
|
|
115
132
|
def make_env(self, region: Optional[str] = None):
|
|
116
133
|
"""Create an environment instance for this task's environment.
|
|
117
134
|
|
|
@@ -141,7 +158,7 @@ def verifier_from_string(
|
|
|
141
158
|
"""
|
|
142
159
|
try:
|
|
143
160
|
import inspect
|
|
144
|
-
from .verifiers import
|
|
161
|
+
from .verifiers import SyncVerifierFunction
|
|
145
162
|
from .verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
|
|
146
163
|
from .verifiers.db import IgnoreConfig
|
|
147
164
|
|
|
@@ -172,36 +189,13 @@ def verifier_from_string(
|
|
|
172
189
|
if func_obj is None:
|
|
173
190
|
raise ValueError("No function found in verifier code")
|
|
174
191
|
|
|
175
|
-
# Create
|
|
176
|
-
def wrapped_verifier(env, *args, **kwargs):
|
|
177
|
-
# Set up globals for the function execution
|
|
178
|
-
func_globals = (
|
|
179
|
-
func_obj.__globals__.copy() if hasattr(func_obj, "__globals__") else {}
|
|
180
|
-
)
|
|
181
|
-
func_globals.update(
|
|
182
|
-
{
|
|
183
|
-
"TASK_SUCCESSFUL_SCORE": TASK_SUCCESSFUL_SCORE,
|
|
184
|
-
"TASK_FAILED_SCORE": TASK_FAILED_SCORE,
|
|
185
|
-
"IgnoreConfig": IgnoreConfig,
|
|
186
|
-
}
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
# Create a new function with the updated globals
|
|
190
|
-
import types
|
|
191
|
-
|
|
192
|
-
new_func = types.FunctionType(
|
|
193
|
-
func_obj.__code__,
|
|
194
|
-
func_globals,
|
|
195
|
-
func_obj.__name__,
|
|
196
|
-
func_obj.__defaults__,
|
|
197
|
-
func_obj.__closure__,
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
return new_func(env, *args, **kwargs)
|
|
201
|
-
|
|
202
|
-
# Create an AsyncVerifierFunction instance with the wrapped function
|
|
192
|
+
# Create an SyncVerifierFunction instance with raw code
|
|
203
193
|
verifier_instance = SyncVerifierFunction(
|
|
204
|
-
|
|
194
|
+
func=func_obj,
|
|
195
|
+
key=verifier_key,
|
|
196
|
+
verifier_id=verifier_id,
|
|
197
|
+
sha256=sha256,
|
|
198
|
+
raw_code=verifier_func,
|
|
205
199
|
)
|
|
206
200
|
|
|
207
201
|
# Store additional metadata
|
|
@@ -214,6 +208,18 @@ def verifier_from_string(
|
|
|
214
208
|
raise ValueError(f"Failed to create verifier from string: {e}")
|
|
215
209
|
|
|
216
210
|
|
|
211
|
+
def load_tasks_from_file(filename: str) -> List[Task]:
|
|
212
|
+
"""Load tasks from a JSON file.
|
|
213
|
+
|
|
214
|
+
Example:
|
|
215
|
+
tasks = fleet.load_tasks_from_file("my_tasks.json")
|
|
216
|
+
"""
|
|
217
|
+
from .global_client import get_client
|
|
218
|
+
|
|
219
|
+
client = get_client()
|
|
220
|
+
return client.load_tasks_from_file(filename)
|
|
221
|
+
|
|
222
|
+
|
|
217
223
|
def load_tasks(
|
|
218
224
|
env_key: Optional[str] = None,
|
|
219
225
|
keys: Optional[List[str]] = None,
|
|
@@ -260,7 +266,6 @@ def update_task(
|
|
|
260
266
|
response = fleet.update_task("my-task", verifier_code="def verify(env): return True")
|
|
261
267
|
"""
|
|
262
268
|
from .global_client import get_client
|
|
263
|
-
from .models import TaskResponse
|
|
264
269
|
|
|
265
270
|
client = get_client()
|
|
266
271
|
return client.update_task(
|