fleet-python 0.2.43__tar.gz → 0.2.44__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.43 → fleet_python-0.2.44}/PKG-INFO +1 -1
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/__init__.py +9 -7
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/__init__.py +28 -16
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/client.py +44 -16
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/instance/client.py +1 -1
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/resources/sqlite.py +34 -34
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/tasks.py +13 -1
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/verifiers/verifier.py +3 -3
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/client.py +44 -13
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/instance/client.py +2 -4
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/resources/sqlite.py +37 -43
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/tasks.py +22 -37
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/__init__.py +1 -1
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/db.py +41 -36
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/parse.py +4 -1
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/sql_differ.py +8 -8
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/verifier.py +19 -7
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet_python.egg-info/PKG-INFO +1 -1
- {fleet_python-0.2.43 → fleet_python-0.2.44}/pyproject.toml +1 -1
- {fleet_python-0.2.43 → fleet_python-0.2.44}/LICENSE +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/README.md +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/diff_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/dsl_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/exampleResume.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_account.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_action_log.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_client.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_mcp_anthropic.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_mcp_openai.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_sync.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_task.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_tasks.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/example_verifier.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/gemini_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/json_tasks_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/nova_act_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/openai_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/openai_simple_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/query_builder_example.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/quickstart.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/examples/test_cdp_logging.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/base.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/env/__init__.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/env/client.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/exceptions.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/global_client.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/instance/__init__.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/instance/base.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/models.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/resources/__init__.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/resources/base.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/resources/browser.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/resources/mcp.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/verifiers/__init__.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/_async/verifiers/bundler.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/base.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/config.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/env/__init__.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/env/client.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/exceptions.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/global_client.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/instance/__init__.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/instance/base.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/instance/models.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/models.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/resources/__init__.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/resources/base.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/resources/browser.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/resources/mcp.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/types.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/bundler.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/code.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet/verifiers/decorator.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet_python.egg-info/SOURCES.txt +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet_python.egg-info/dependency_links.txt +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet_python.egg-info/requires.txt +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/fleet_python.egg-info/top_level.txt +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/scripts/fix_sync_imports.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/scripts/unasync.py +0 -0
- {fleet_python-0.2.43 → fleet_python-0.2.44}/setup.cfg +0 -0
|
@@ -46,11 +46,15 @@ from ._async.verifiers import (
|
|
|
46
46
|
AsyncVerifierFunction,
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
# Import async tasks (default tasks are async for modern usage)
|
|
50
|
-
from ._async.tasks import
|
|
49
|
+
# Import async tasks (default tasks are async for modern usage)
|
|
50
|
+
from ._async.tasks import (
|
|
51
|
+
Task,
|
|
52
|
+
load_tasks as load_tasks_async,
|
|
53
|
+
load_tasks_from_file as load_tasks_from_file_async,
|
|
54
|
+
)
|
|
51
55
|
|
|
52
56
|
# Import sync load_tasks function
|
|
53
|
-
from .tasks import load_tasks
|
|
57
|
+
from .tasks import load_tasks, load_tasks_from_file
|
|
54
58
|
|
|
55
59
|
# Import shared types
|
|
56
60
|
from .types import VerifierFunction
|
|
@@ -105,13 +109,11 @@ __all__ = [
|
|
|
105
109
|
]
|
|
106
110
|
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
|
|
110
112
|
def configure(
|
|
111
113
|
api_key: Optional[str] = None,
|
|
112
114
|
base_url: Optional[str] = None,
|
|
113
|
-
max_retries: int
|
|
114
|
-
timeout: float
|
|
115
|
+
max_retries: Optional[int] = None,
|
|
116
|
+
timeout: Optional[float] = None,
|
|
115
117
|
):
|
|
116
118
|
"""Configure global clients (sync and async) once per process.
|
|
117
119
|
|
|
@@ -73,17 +73,17 @@ __all__ = [
|
|
|
73
73
|
"reset_client",
|
|
74
74
|
# Module-level functions
|
|
75
75
|
"load_tasks",
|
|
76
|
-
"list_envs",
|
|
76
|
+
"list_envs",
|
|
77
77
|
"list_regions",
|
|
78
78
|
"environment",
|
|
79
79
|
"make",
|
|
80
80
|
"make_for_task",
|
|
81
81
|
"instances",
|
|
82
|
-
"instance",
|
|
82
|
+
"instance",
|
|
83
83
|
"delete",
|
|
84
84
|
"load_tasks_from_file",
|
|
85
85
|
"load_task_array_from_string",
|
|
86
|
-
"load_task_from_string",
|
|
86
|
+
"load_task_from_string",
|
|
87
87
|
"load_task_from_json",
|
|
88
88
|
"export_tasks",
|
|
89
89
|
"import_tasks",
|
|
@@ -108,9 +108,13 @@ async def environment(env_key: str) -> Environment:
|
|
|
108
108
|
return await _async_global_client.get_client().environment(env_key)
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
async def make(
|
|
111
|
+
async def make(
|
|
112
|
+
env_key: str,
|
|
113
|
+
region: Optional[str] = None,
|
|
114
|
+
env_variables: Optional[Dict[str, Any]] = None,
|
|
115
|
+
) -> AsyncEnv:
|
|
112
116
|
"""Create a new environment instance.
|
|
113
|
-
|
|
117
|
+
|
|
114
118
|
Example:
|
|
115
119
|
env = await fleet.make("fira")
|
|
116
120
|
env_with_vars = await fleet.make("fira", env_variables={"LOGGED_IN_NAME": "Alice"})
|
|
@@ -123,7 +127,9 @@ async def make_for_task(task: Task) -> AsyncEnv:
|
|
|
123
127
|
return await _async_global_client.get_client().make_for_task(task)
|
|
124
128
|
|
|
125
129
|
|
|
126
|
-
async def instances(
|
|
130
|
+
async def instances(
|
|
131
|
+
status: Optional[str] = None, region: Optional[str] = None
|
|
132
|
+
) -> List[AsyncEnv]:
|
|
127
133
|
"""List existing environment instances."""
|
|
128
134
|
return await _async_global_client.get_client().instances(status, region)
|
|
129
135
|
|
|
@@ -140,7 +146,7 @@ async def delete(instance_id: str) -> InstanceResponse:
|
|
|
140
146
|
|
|
141
147
|
async def load_tasks_from_file(filename: str) -> List[Task]:
|
|
142
148
|
"""Load tasks from a JSON file.
|
|
143
|
-
|
|
149
|
+
|
|
144
150
|
Example:
|
|
145
151
|
tasks = await fleet.load_tasks_from_file("my_tasks.json")
|
|
146
152
|
"""
|
|
@@ -149,16 +155,18 @@ async def load_tasks_from_file(filename: str) -> List[Task]:
|
|
|
149
155
|
|
|
150
156
|
async def load_task_array_from_string(serialized_tasks: str) -> List[Task]:
|
|
151
157
|
"""Load tasks from a JSON string containing an array of tasks.
|
|
152
|
-
|
|
158
|
+
|
|
153
159
|
Example:
|
|
154
160
|
tasks = await fleet.load_task_array_from_string(json_string)
|
|
155
161
|
"""
|
|
156
|
-
return await _async_global_client.get_client().load_task_array_from_string(
|
|
162
|
+
return await _async_global_client.get_client().load_task_array_from_string(
|
|
163
|
+
serialized_tasks
|
|
164
|
+
)
|
|
157
165
|
|
|
158
166
|
|
|
159
167
|
async def load_task_from_string(task_string: str) -> Task:
|
|
160
168
|
"""Load a single task from a JSON string.
|
|
161
|
-
|
|
169
|
+
|
|
162
170
|
Example:
|
|
163
171
|
task = await fleet.load_task_from_string(task_json_string)
|
|
164
172
|
"""
|
|
@@ -167,16 +175,18 @@ async def load_task_from_string(task_string: str) -> Task:
|
|
|
167
175
|
|
|
168
176
|
async def load_task_from_json(task_json: dict) -> Task:
|
|
169
177
|
"""Load a single task from a dictionary.
|
|
170
|
-
|
|
178
|
+
|
|
171
179
|
Example:
|
|
172
180
|
task = await fleet.load_task_from_json(task_dict)
|
|
173
181
|
"""
|
|
174
182
|
return await _async_global_client.get_client().load_task_from_json(task_json)
|
|
175
183
|
|
|
176
184
|
|
|
177
|
-
async def export_tasks(
|
|
185
|
+
async def export_tasks(
|
|
186
|
+
env_key: Optional[str] = None, filename: Optional[str] = None
|
|
187
|
+
) -> Optional[str]:
|
|
178
188
|
"""Export tasks to a JSON file.
|
|
179
|
-
|
|
189
|
+
|
|
180
190
|
Example:
|
|
181
191
|
await fleet.export_tasks("fira", "fira_tasks.json")
|
|
182
192
|
"""
|
|
@@ -185,7 +195,7 @@ async def export_tasks(env_key: Optional[str] = None, filename: Optional[str] =
|
|
|
185
195
|
|
|
186
196
|
async def import_tasks(filename: str):
|
|
187
197
|
"""Import tasks from a JSON file.
|
|
188
|
-
|
|
198
|
+
|
|
189
199
|
Example:
|
|
190
200
|
await fleet.import_tasks("tasks.json")
|
|
191
201
|
"""
|
|
@@ -200,8 +210,8 @@ async def account() -> AccountResponse:
|
|
|
200
210
|
def configure(
|
|
201
211
|
api_key: Optional[str] = None,
|
|
202
212
|
base_url: Optional[str] = None,
|
|
203
|
-
max_retries: int
|
|
204
|
-
timeout: float
|
|
213
|
+
max_retries: Optional[int] = None,
|
|
214
|
+
timeout: Optional[float] = None,
|
|
205
215
|
):
|
|
206
216
|
"""Configure global async client once per process.
|
|
207
217
|
|
|
@@ -213,9 +223,11 @@ def configure(
|
|
|
213
223
|
"""
|
|
214
224
|
if max_retries is None:
|
|
215
225
|
from ..config import DEFAULT_MAX_RETRIES as _MR
|
|
226
|
+
|
|
216
227
|
max_retries = _MR
|
|
217
228
|
if timeout is None:
|
|
218
229
|
from ..config import DEFAULT_TIMEOUT as _TO
|
|
230
|
+
|
|
219
231
|
timeout = _TO
|
|
220
232
|
_async_global_client.configure(
|
|
221
233
|
api_key=api_key, base_url=base_url, max_retries=max_retries, timeout=timeout
|
|
@@ -130,7 +130,7 @@ class AsyncEnv(EnvironmentBase):
|
|
|
130
130
|
return await self.instance.verify(validator)
|
|
131
131
|
|
|
132
132
|
async 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 await self.instance.verify_raw(function_code, function_name)
|
|
136
136
|
|
|
@@ -304,12 +304,19 @@ class AsyncFleet:
|
|
|
304
304
|
|
|
305
305
|
return await self.load_task_array_from_string(tasks_data)
|
|
306
306
|
|
|
307
|
-
async def load_task_array_from_string(
|
|
308
|
-
self, serialized_tasks: List[Dict]
|
|
309
|
-
) -> List[Task]:
|
|
307
|
+
async def load_task_array_from_string(self, serialized_tasks: str) -> List[Task]:
|
|
310
308
|
tasks = []
|
|
311
309
|
|
|
312
|
-
|
|
310
|
+
parsed_data = json.loads(serialized_tasks)
|
|
311
|
+
if isinstance(parsed_data, list):
|
|
312
|
+
json_tasks = parsed_data
|
|
313
|
+
elif isinstance(parsed_data, dict) and "tasks" in parsed_data:
|
|
314
|
+
json_tasks = parsed_data["tasks"]
|
|
315
|
+
else:
|
|
316
|
+
raise ValueError(
|
|
317
|
+
"Invalid JSON structure: expected array or object with 'tasks' key"
|
|
318
|
+
)
|
|
319
|
+
|
|
313
320
|
for json_task in json_tasks:
|
|
314
321
|
parsed_task = await self.load_task_from_json(json_task)
|
|
315
322
|
tasks.append(parsed_task)
|
|
@@ -321,25 +328,46 @@ class AsyncFleet:
|
|
|
321
328
|
|
|
322
329
|
async def load_task_from_json(self, task_json: Dict) -> Task:
|
|
323
330
|
verifier = None
|
|
331
|
+
verifier_code = task_json.get("verifier_func") or task_json.get("verifier_code")
|
|
332
|
+
|
|
333
|
+
# Try to find verifier_id in multiple locations
|
|
334
|
+
verifier_id = task_json.get("verifier_id")
|
|
335
|
+
if (
|
|
336
|
+
not verifier_id
|
|
337
|
+
and "metadata" in task_json
|
|
338
|
+
and isinstance(task_json["metadata"], dict)
|
|
339
|
+
):
|
|
340
|
+
verifier_metadata = task_json["metadata"].get("verifier", {})
|
|
341
|
+
if isinstance(verifier_metadata, dict):
|
|
342
|
+
verifier_id = verifier_metadata.get("verifier_id")
|
|
343
|
+
|
|
344
|
+
# If no verifier_id found, use the task key/id as fallback
|
|
345
|
+
if not verifier_id:
|
|
346
|
+
verifier_id = task_json.get("key", task_json.get("id"))
|
|
347
|
+
|
|
324
348
|
try:
|
|
325
|
-
if
|
|
349
|
+
if verifier_id and verifier_code:
|
|
326
350
|
verifier = await self._create_verifier_from_data(
|
|
327
|
-
verifier_id=
|
|
328
|
-
verifier_key=task_json
|
|
329
|
-
verifier_code=
|
|
351
|
+
verifier_id=verifier_id,
|
|
352
|
+
verifier_key=task_json.get("key", task_json.get("id")),
|
|
353
|
+
verifier_code=verifier_code,
|
|
330
354
|
verifier_sha=task_json.get("verifier_sha", ""),
|
|
331
355
|
)
|
|
332
356
|
except Exception as e:
|
|
333
|
-
logger.warning(
|
|
357
|
+
logger.warning(
|
|
358
|
+
f"Failed to create verifier {task_json.get('key', task_json.get('id'))}: {e}"
|
|
359
|
+
)
|
|
334
360
|
|
|
335
361
|
task = Task(
|
|
336
|
-
key=task_json
|
|
362
|
+
key=task_json.get("key", task_json.get("id")),
|
|
337
363
|
prompt=task_json["prompt"],
|
|
338
|
-
env_id=task_json
|
|
339
|
-
|
|
364
|
+
env_id=task_json.get(
|
|
365
|
+
"env_id", task_json.get("env_key")
|
|
366
|
+
), # Use env_id or fallback to env_key
|
|
367
|
+
created_at=task_json.get("created_at"),
|
|
340
368
|
version=task_json.get("version"),
|
|
341
369
|
env_variables=task_json.get("env_variables", {}),
|
|
342
|
-
verifier_func=
|
|
370
|
+
verifier_func=verifier_code, # Set verifier code
|
|
343
371
|
verifier=verifier, # Use created verifier or None
|
|
344
372
|
metadata=task_json.get("metadata", {}), # Default empty metadata
|
|
345
373
|
)
|
|
@@ -377,7 +405,7 @@ class AsyncFleet:
|
|
|
377
405
|
# Prepare verifier loading coroutines with concurrency limit
|
|
378
406
|
verifier_coroutines = []
|
|
379
407
|
task_responses_with_indices = []
|
|
380
|
-
semaphore = asyncio.Semaphore(
|
|
408
|
+
semaphore = asyncio.Semaphore(100) # Limit to 10 concurrent operations
|
|
381
409
|
|
|
382
410
|
for idx, task_response in enumerate(task_list_response.tasks):
|
|
383
411
|
if task_response.verifier:
|
|
@@ -632,7 +660,7 @@ class AsyncFleet:
|
|
|
632
660
|
AsyncVerifierFunction created from the verifier code
|
|
633
661
|
"""
|
|
634
662
|
# Fetch verifier from API
|
|
635
|
-
response = await self.client.request("GET", f"/v1/
|
|
663
|
+
response = await self.client.request("GET", f"/v1/verifiers/{verifier_id}")
|
|
636
664
|
verifier_data = response.json()
|
|
637
665
|
|
|
638
666
|
# Use the common method to create verifier
|
|
@@ -108,7 +108,7 @@ class AsyncInstanceClient:
|
|
|
108
108
|
return await self.verify_raw(function_code, function_name)
|
|
109
109
|
|
|
110
110
|
async def verify_raw(
|
|
111
|
-
self, function_code: str, function_name: str
|
|
111
|
+
self, function_code: str, function_name: Optional[str] = None
|
|
112
112
|
) -> ExecuteFunctionResponse:
|
|
113
113
|
try:
|
|
114
114
|
function_code = convert_verifier_string(function_code)
|
|
@@ -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 AsyncDatabaseSnapshot:
|
|
26
26
|
"""Async database snapshot that fetches data through API and stores locally for diffing."""
|
|
27
27
|
|
|
28
|
-
def __init__(self, resource: "AsyncSQLiteResource", name: str
|
|
28
|
+
def __init__(self, resource: "AsyncSQLiteResource", 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
|
async def _ensure_fetched(self):
|
|
@@ -69,7 +69,7 @@ class AsyncDatabaseSnapshot:
|
|
|
69
69
|
|
|
70
70
|
self._fetched = True
|
|
71
71
|
|
|
72
|
-
async def tables(self) ->
|
|
72
|
+
async def tables(self) -> List[str]:
|
|
73
73
|
"""Get list of all tables in the snapshot."""
|
|
74
74
|
await self._ensure_fetched()
|
|
75
75
|
return list(self._data.keys())
|
|
@@ -81,7 +81,7 @@ class AsyncDatabaseSnapshot:
|
|
|
81
81
|
async def diff(
|
|
82
82
|
self,
|
|
83
83
|
other: "AsyncDatabaseSnapshot",
|
|
84
|
-
ignore_config: IgnoreConfig
|
|
84
|
+
ignore_config: Optional[IgnoreConfig] = None,
|
|
85
85
|
) -> "AsyncSnapshotDiff":
|
|
86
86
|
"""Compare this snapshot with another."""
|
|
87
87
|
await self._ensure_fetched()
|
|
@@ -95,13 +95,13 @@ class AsyncSnapshotQueryBuilder:
|
|
|
95
95
|
def __init__(self, snapshot: AsyncDatabaseSnapshot, 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
|
-
async def _get_data(self) ->
|
|
104
|
+
async def _get_data(self) -> List[Dict[str, Any]]:
|
|
105
105
|
"""Get table data from snapshot."""
|
|
106
106
|
await self._snapshot._ensure_fetched()
|
|
107
107
|
return self._snapshot._data.get(self._table, [])
|
|
@@ -122,11 +122,11 @@ class AsyncSnapshotQueryBuilder:
|
|
|
122
122
|
qb._order_desc = desc
|
|
123
123
|
return qb
|
|
124
124
|
|
|
125
|
-
async def first(self) ->
|
|
125
|
+
async def first(self) -> Optional[Dict[str, Any]]:
|
|
126
126
|
rows = await self.all()
|
|
127
127
|
return rows[0] if rows else None
|
|
128
128
|
|
|
129
|
-
async def all(self) ->
|
|
129
|
+
async def all(self) -> List[Dict[str, Any]]:
|
|
130
130
|
data = await self._get_data()
|
|
131
131
|
|
|
132
132
|
# Apply filters
|
|
@@ -185,14 +185,14 @@ class AsyncSnapshotDiff:
|
|
|
185
185
|
self,
|
|
186
186
|
before: AsyncDatabaseSnapshot,
|
|
187
187
|
after: AsyncDatabaseSnapshot,
|
|
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
|
-
async def _get_primary_key_columns(self, table: str) ->
|
|
195
|
+
async 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 = await self.after.resource.query(f"PRAGMA table_info({table})")
|
|
@@ -222,7 +222,7 @@ class AsyncSnapshotDiff:
|
|
|
222
222
|
return self._cached
|
|
223
223
|
|
|
224
224
|
all_tables = set(await self.before.tables()) | set(await 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 AsyncSnapshotDiff:
|
|
|
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 AsyncSnapshotDiff:
|
|
|
304
304
|
self._cached = diff
|
|
305
305
|
return diff
|
|
306
306
|
|
|
307
|
-
async def expect_only(self, allowed_changes:
|
|
307
|
+
async def expect_only(self, allowed_changes: List[Dict[str, Any]]):
|
|
308
308
|
"""Ensure only specified changes occurred."""
|
|
309
309
|
diff = await 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 AsyncQueryBuilder:
|
|
|
440
440
|
def __init__(self, resource: "AsyncSQLiteResource", 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) -> "AsyncQueryBuilder":
|
|
@@ -486,12 +486,12 @@ class AsyncQueryBuilder:
|
|
|
486
486
|
def lte(self, column: str, value: Any) -> "AsyncQueryBuilder":
|
|
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]) -> "AsyncQueryBuilder":
|
|
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]) -> "AsyncQueryBuilder":
|
|
495
495
|
qb = self._clone()
|
|
496
496
|
qb._conditions.append((column, "NOT IN", tuple(values)))
|
|
497
497
|
return qb
|
|
@@ -508,16 +508,16 @@ class AsyncQueryBuilder:
|
|
|
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]) -> "AsyncQueryBuilder":
|
|
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 AsyncQueryBuilder:
|
|
|
558
558
|
return row_dict.get("__cnt__", 0)
|
|
559
559
|
return 0
|
|
560
560
|
|
|
561
|
-
async def first(self) ->
|
|
561
|
+
async def first(self) -> Optional[Dict[str, Any]]:
|
|
562
562
|
rows = await self.limit(1).all()
|
|
563
563
|
return rows[0] if rows else None
|
|
564
564
|
|
|
565
|
-
async def all(self) ->
|
|
565
|
+
async def all(self) -> List[Dict[str, Any]]:
|
|
566
566
|
sql, params = self._compile()
|
|
567
567
|
response = await self._resource.query(sql, params)
|
|
568
568
|
if not response.rows:
|
|
@@ -674,7 +674,7 @@ class AsyncSQLiteResource(Resource):
|
|
|
674
674
|
"""Create a query builder for the specified table."""
|
|
675
675
|
return AsyncQueryBuilder(self, table_name)
|
|
676
676
|
|
|
677
|
-
async def snapshot(self, name: str
|
|
677
|
+
async def snapshot(self, name: Optional[str] = None) -> AsyncDatabaseSnapshot:
|
|
678
678
|
"""Create a snapshot of the current database state."""
|
|
679
679
|
snapshot = AsyncDatabaseSnapshot(self, name)
|
|
680
680
|
await snapshot._ensure_fetched()
|
|
@@ -683,7 +683,7 @@ class AsyncSQLiteResource(Resource):
|
|
|
683
683
|
async def diff(
|
|
684
684
|
self,
|
|
685
685
|
other: "AsyncSQLiteResource",
|
|
686
|
-
ignore_config: IgnoreConfig
|
|
686
|
+
ignore_config: Optional[IgnoreConfig] = None,
|
|
687
687
|
) -> AsyncSnapshotDiff:
|
|
688
688
|
"""Compare this database with another AsyncSQLiteResource.
|
|
689
689
|
|
|
@@ -44,7 +44,7 @@ class Task(BaseModel):
|
|
|
44
44
|
@property
|
|
45
45
|
def env_key(self) -> str:
|
|
46
46
|
"""Get the environment key combining env_id and version."""
|
|
47
|
-
if self.version and self.version != "None":
|
|
47
|
+
if self.version and self.version != "None" and ":" not in self.env_id:
|
|
48
48
|
return f"{self.env_id}:{self.version}"
|
|
49
49
|
return self.env_id
|
|
50
50
|
|
|
@@ -174,6 +174,18 @@ def verifier_from_string(
|
|
|
174
174
|
raise ValueError(f"Failed to create verifier from string: {e}")
|
|
175
175
|
|
|
176
176
|
|
|
177
|
+
async def load_tasks_from_file(filename: str) -> List[Task]:
|
|
178
|
+
"""Load tasks from a JSON file.
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
tasks = await fleet.load_tasks_from_file("my_tasks.json")
|
|
182
|
+
"""
|
|
183
|
+
from .global_client import get_client
|
|
184
|
+
|
|
185
|
+
client = get_client()
|
|
186
|
+
return await client.load_tasks_from_file(filename)
|
|
187
|
+
|
|
188
|
+
|
|
177
189
|
async def load_tasks(
|
|
178
190
|
env_key: Optional[str] = None,
|
|
179
191
|
keys: Optional[List[str]] = None,
|
|
@@ -12,7 +12,7 @@ import uuid
|
|
|
12
12
|
import logging
|
|
13
13
|
import hashlib
|
|
14
14
|
import asyncio
|
|
15
|
-
from typing import Any, Callable, Dict, Optional, List, TypeVar, Set
|
|
15
|
+
from typing import Any, Callable, Dict, Optional, List, TypeVar, Set, Tuple
|
|
16
16
|
|
|
17
17
|
from .bundler import FunctionBundler
|
|
18
18
|
from ..client import AsyncEnv
|
|
@@ -56,7 +56,7 @@ class AsyncVerifierFunction:
|
|
|
56
56
|
# Copy function metadata
|
|
57
57
|
functools.update_wrapper(self, func)
|
|
58
58
|
|
|
59
|
-
def _get_or_create_bundle(self) ->
|
|
59
|
+
def _get_or_create_bundle(self) -> Tuple[bytes, str]:
|
|
60
60
|
"""Get or create bundle data and return (bundle_data, sha)."""
|
|
61
61
|
if self._bundle_data is None or self._bundle_sha is None:
|
|
62
62
|
# If we have raw code, create a bundle from it
|
|
@@ -98,7 +98,7 @@ class AsyncVerifierFunction:
|
|
|
98
98
|
|
|
99
99
|
return self._bundle_data, self._bundle_sha
|
|
100
100
|
|
|
101
|
-
async def _check_bundle_status(self, env: AsyncEnv) ->
|
|
101
|
+
async def _check_bundle_status(self, env: AsyncEnv) -> Tuple[str, bool]:
|
|
102
102
|
"""Check if bundle needs to be uploaded and return (sha, needs_upload)."""
|
|
103
103
|
bundle_data, bundle_sha = self._get_or_create_bundle()
|
|
104
104
|
|
|
@@ -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
|
|
@@ -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)
|