fleet-python 0.2.29__py3-none-any.whl → 0.2.32__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/diff_example.py +30 -20
- examples/dsl_example.py +12 -7
- examples/example.py +4 -4
- examples/example_account.py +8 -0
- examples/example_action_log.py +2 -2
- examples/example_client.py +2 -2
- examples/example_mcp_anthropic.py +8 -5
- examples/example_mcp_openai.py +2 -2
- examples/example_sync.py +4 -4
- examples/example_task.py +16 -6
- examples/example_tasks.py +3 -6
- examples/example_verifier.py +16 -3
- examples/gemini_example.py +6 -6
- examples/json_tasks_example.py +2 -2
- examples/nova_act_example.py +2 -2
- examples/openai_example.py +3 -3
- examples/openai_simple_example.py +3 -3
- examples/query_builder_example.py +11 -7
- fleet/__init__.py +60 -5
- fleet/_async/__init__.py +258 -1
- fleet/_async/base.py +2 -1
- fleet/_async/client.py +164 -144
- fleet/_async/env/client.py +2 -0
- fleet/_async/global_client.py +43 -0
- fleet/_async/instance/client.py +1 -1
- fleet/_async/models.py +172 -171
- fleet/_async/resources/base.py +1 -1
- fleet/_async/resources/mcp.py +55 -0
- fleet/_async/resources/sqlite.py +141 -130
- fleet/_async/tasks.py +69 -16
- fleet/_async/verifiers/__init__.py +2 -2
- fleet/_async/verifiers/bundler.py +18 -14
- fleet/_async/verifiers/verifier.py +77 -71
- fleet/base.py +2 -1
- fleet/client.py +145 -158
- fleet/config.py +3 -2
- fleet/env/__init__.py +9 -1
- fleet/env/client.py +3 -0
- fleet/global_client.py +43 -0
- fleet/instance/__init__.py +1 -1
- fleet/instance/client.py +2 -4
- fleet/models.py +172 -171
- fleet/resources/base.py +1 -1
- fleet/resources/mcp.py +27 -33
- fleet/resources/sqlite.py +136 -131
- fleet/tasks.py +195 -16
- fleet/types.py +1 -1
- fleet/verifiers/__init__.py +2 -2
- fleet/verifiers/bundler.py +18 -14
- fleet/verifiers/code.py +1 -1
- fleet/verifiers/decorator.py +25 -34
- fleet/verifiers/parse.py +98 -68
- fleet/verifiers/verifier.py +77 -78
- {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/METADATA +9 -9
- fleet_python-0.2.32.dist-info/RECORD +74 -0
- scripts/fix_sync_imports.py +87 -59
- scripts/unasync.py +10 -9
- fleet_python-0.2.29.dist-info/RECORD +0 -70
- {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.29.dist-info → fleet_python-0.2.32.dist-info}/top_level.txt +0 -0
fleet/_async/client.py
CHANGED
|
@@ -34,7 +34,6 @@ from ..models import (
|
|
|
34
34
|
TaskRequest,
|
|
35
35
|
)
|
|
36
36
|
from .tasks import Task
|
|
37
|
-
from ..verifiers.parse import extract_function_name, convert_new_to_old_verifier
|
|
38
37
|
|
|
39
38
|
if TYPE_CHECKING:
|
|
40
39
|
from .verifiers import AsyncVerifierFunction
|
|
@@ -51,6 +50,7 @@ from .instance.client import ValidatorType
|
|
|
51
50
|
from .resources.base import Resource
|
|
52
51
|
from .resources.sqlite import AsyncSQLiteResource
|
|
53
52
|
from .resources.browser import AsyncBrowserResource
|
|
53
|
+
from ..resources.mcp import MCPResource
|
|
54
54
|
|
|
55
55
|
logger = logging.getLogger(__name__)
|
|
56
56
|
|
|
@@ -69,18 +69,18 @@ class AsyncEnv(EnvironmentBase):
|
|
|
69
69
|
self.manager_url, self._client.httpx_client if self._client else None
|
|
70
70
|
)
|
|
71
71
|
return self._instance
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
def app(self, name: str) -> AsyncInstanceClient:
|
|
74
74
|
if name not in self._apps:
|
|
75
75
|
# Extract base URL by removing the current app path (e.g., /sentry/api/v1/env)
|
|
76
76
|
# manager_url looks like: https://xxx.fleetai.com/sentry/api/v1/env
|
|
77
|
-
base_url = self.manager_url.split(
|
|
77
|
+
base_url = self.manager_url.split("/api/v1/env")[0]
|
|
78
78
|
# Remove the current app name (e.g., /sentry) to get the root
|
|
79
|
-
if
|
|
80
|
-
parts = base_url.rsplit(
|
|
79
|
+
if "/" in base_url:
|
|
80
|
+
parts = base_url.rsplit("/", 1)
|
|
81
81
|
if len(parts) == 2 and parts[0] != "https:/":
|
|
82
82
|
base_url = parts[0]
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
self._apps[name] = AsyncInstanceClient(
|
|
85
85
|
f"{base_url}/{name}/api/v1/env",
|
|
86
86
|
self._client.httpx_client if self._client else None,
|
|
@@ -104,6 +104,11 @@ class AsyncEnv(EnvironmentBase):
|
|
|
104
104
|
def browser(self, name: str = "cdp") -> AsyncBrowserResource:
|
|
105
105
|
return self.instance.browser(name)
|
|
106
106
|
|
|
107
|
+
@property
|
|
108
|
+
def mcp(self) -> MCPResource:
|
|
109
|
+
mcp_url = f"{self.urls.root}mcp"
|
|
110
|
+
return MCPResource(url=mcp_url, env_key=self.env_key)
|
|
111
|
+
|
|
107
112
|
def state(self, uri: str) -> Resource:
|
|
108
113
|
return self.instance.state(uri)
|
|
109
114
|
|
|
@@ -125,28 +130,28 @@ class AsyncEnv(EnvironmentBase):
|
|
|
125
130
|
return await _check_bundle_exists(self._load_client, bundle_hash)
|
|
126
131
|
|
|
127
132
|
async def execute_verifier_remote(
|
|
128
|
-
self,
|
|
129
|
-
bundle_data: bytes,
|
|
133
|
+
self,
|
|
134
|
+
bundle_data: bytes,
|
|
130
135
|
bundle_sha: str,
|
|
131
136
|
key: str,
|
|
132
137
|
function_name: str,
|
|
133
|
-
args: tuple,
|
|
138
|
+
args: tuple,
|
|
134
139
|
args_array: list,
|
|
135
|
-
kwargs: dict,
|
|
140
|
+
kwargs: dict,
|
|
136
141
|
timeout: Optional[int] = 30,
|
|
137
142
|
needs_upload: bool = True,
|
|
138
143
|
) -> VerifiersExecuteResponse:
|
|
139
144
|
return await _execute_verifier_remote(
|
|
140
|
-
self._load_client,
|
|
141
|
-
bundle_data,
|
|
145
|
+
self._load_client,
|
|
146
|
+
bundle_data,
|
|
142
147
|
bundle_sha,
|
|
143
148
|
key,
|
|
144
149
|
function_name,
|
|
145
|
-
args,
|
|
150
|
+
args,
|
|
146
151
|
args_array,
|
|
147
|
-
kwargs,
|
|
152
|
+
kwargs,
|
|
148
153
|
timeout,
|
|
149
|
-
needs_upload
|
|
154
|
+
needs_upload,
|
|
150
155
|
)
|
|
151
156
|
|
|
152
157
|
def __getstate__(self):
|
|
@@ -170,6 +175,8 @@ class AsyncFleet:
|
|
|
170
175
|
):
|
|
171
176
|
if api_key is None:
|
|
172
177
|
api_key = os.getenv("FLEET_API_KEY")
|
|
178
|
+
if base_url is None:
|
|
179
|
+
base_url = os.getenv("FLEET_BASE_URL")
|
|
173
180
|
self._httpx_client = httpx_client or default_httpx_client(max_retries, timeout)
|
|
174
181
|
self.client = AsyncWrapper(
|
|
175
182
|
api_key=api_key,
|
|
@@ -189,23 +196,27 @@ class AsyncFleet:
|
|
|
189
196
|
response = await self.client.request("GET", f"/v1/env/{env_key}")
|
|
190
197
|
return EnvironmentModel(**response.json())
|
|
191
198
|
|
|
192
|
-
async def make(
|
|
193
|
-
self, env_key: str, region: Optional[str] = None
|
|
194
|
-
) -> AsyncEnv:
|
|
199
|
+
async def make(self, env_key: str, region: Optional[str] = None) -> AsyncEnv:
|
|
195
200
|
if ":" in env_key:
|
|
196
201
|
env_key_part, version = env_key.split(":", 1)
|
|
197
|
-
if
|
|
202
|
+
if (
|
|
203
|
+
not version.startswith("v")
|
|
204
|
+
and len(version) != 0
|
|
205
|
+
and version[0].isdigit()
|
|
206
|
+
):
|
|
198
207
|
version = f"v{version}"
|
|
199
208
|
else:
|
|
200
209
|
env_key_part = env_key
|
|
201
210
|
version = None
|
|
202
211
|
|
|
203
|
-
request = InstanceRequest(
|
|
212
|
+
request = InstanceRequest(
|
|
213
|
+
env_key=env_key_part, version=version, region=region, created_from="sdk"
|
|
214
|
+
)
|
|
204
215
|
region_base_url = REGION_BASE_URL.get(region)
|
|
205
216
|
response = await self.client.request(
|
|
206
217
|
"POST",
|
|
207
218
|
"/v1/env/instances",
|
|
208
|
-
json=request.model_dump(),
|
|
219
|
+
json=request.model_dump(exclude_none=True),
|
|
209
220
|
base_url=region_base_url,
|
|
210
221
|
)
|
|
211
222
|
|
|
@@ -214,7 +225,7 @@ class AsyncFleet:
|
|
|
214
225
|
return instance
|
|
215
226
|
|
|
216
227
|
async def make_for_task(self, task: Task) -> AsyncEnv:
|
|
217
|
-
return self.make(env_key=f"{task.env_id}:{task.version}")
|
|
228
|
+
return await self.make(env_key=f"{task.env_id}:{task.version}")
|
|
218
229
|
|
|
219
230
|
async def instances(
|
|
220
231
|
self, status: Optional[str] = None, region: Optional[str] = None
|
|
@@ -251,12 +262,14 @@ class AsyncFleet:
|
|
|
251
262
|
return await _delete_instance(self.client, instance_id)
|
|
252
263
|
|
|
253
264
|
async def load_tasks_from_file(self, filename: str) -> List[Task]:
|
|
254
|
-
with open(filename,
|
|
265
|
+
with open(filename, "r", encoding="utf-8") as f:
|
|
255
266
|
tasks_data = f.read()
|
|
256
267
|
|
|
257
268
|
return self.load_task_array_from_string(tasks_data)
|
|
258
269
|
|
|
259
|
-
async def load_task_array_from_string(
|
|
270
|
+
async def load_task_array_from_string(
|
|
271
|
+
self, serialized_tasks: List[Dict]
|
|
272
|
+
) -> List[Task]:
|
|
260
273
|
tasks = []
|
|
261
274
|
|
|
262
275
|
json_tasks = json.loads(serialized_tasks)
|
|
@@ -271,67 +284,101 @@ class AsyncFleet:
|
|
|
271
284
|
|
|
272
285
|
async def load_task_from_json(self, task_json: Dict) -> Task:
|
|
273
286
|
try:
|
|
274
|
-
if
|
|
287
|
+
if "verifier_id" in task_json and task_json["verifier_id"]:
|
|
275
288
|
verifier = self._create_verifier_from_data(
|
|
276
|
-
verifier_id=task_json[
|
|
277
|
-
verifier_key=task_json[
|
|
278
|
-
verifier_code=task_json[
|
|
279
|
-
verifier_sha=task_json.get(
|
|
289
|
+
verifier_id=task_json["verifier_id"],
|
|
290
|
+
verifier_key=task_json["key"],
|
|
291
|
+
verifier_code=task_json["verifier_func"],
|
|
292
|
+
verifier_sha=task_json.get("verifier_sha", ""),
|
|
280
293
|
)
|
|
281
294
|
except Exception as e:
|
|
282
295
|
logger.warning(f"Failed to create verifier {task_json['key']}: {e}")
|
|
283
|
-
|
|
296
|
+
|
|
284
297
|
task = Task(
|
|
285
|
-
key=task_json[
|
|
286
|
-
prompt=task_json[
|
|
287
|
-
env_id=task_json[
|
|
288
|
-
created_at=task_json[
|
|
289
|
-
version=task_json.get(
|
|
290
|
-
env_variables=task_json.get(
|
|
291
|
-
verifier_func=task_json.get(
|
|
298
|
+
key=task_json["key"],
|
|
299
|
+
prompt=task_json["prompt"],
|
|
300
|
+
env_id=task_json["env_id"], # Use env_id from the data
|
|
301
|
+
created_at=task_json["created_at"],
|
|
302
|
+
version=task_json.get("version"),
|
|
303
|
+
env_variables=task_json.get("env_variables", {}),
|
|
304
|
+
verifier_func=task_json.get("verifier_func"), # Set verifier code
|
|
292
305
|
verifier=verifier, # Use created verifier or None
|
|
293
|
-
metadata=task_json.get(
|
|
306
|
+
metadata=task_json.get("metadata", {}), # Default empty metadata
|
|
294
307
|
)
|
|
295
308
|
return task
|
|
296
309
|
|
|
297
|
-
async def load_tasks(
|
|
298
|
-
|
|
299
|
-
|
|
310
|
+
async def load_tasks(
|
|
311
|
+
self,
|
|
312
|
+
env_key: Optional[str] = None,
|
|
313
|
+
keys: Optional[List[str]] = None,
|
|
314
|
+
version: Optional[str] = None,
|
|
315
|
+
team_id: Optional[str] = None
|
|
316
|
+
) -> List[Task]:
|
|
317
|
+
"""Load tasks for the authenticated team, with optional filtering.
|
|
318
|
+
|
|
300
319
|
Args:
|
|
301
320
|
env_key: Optional environment key to filter tasks by
|
|
302
|
-
|
|
321
|
+
keys: Optional list of task keys to filter by
|
|
322
|
+
version: Optional version to filter tasks by (client-side filter)
|
|
323
|
+
team_id: Optional team_id to filter by (admin only)
|
|
324
|
+
|
|
303
325
|
Returns:
|
|
304
326
|
List[Task] containing Task objects
|
|
305
327
|
"""
|
|
306
328
|
params = {}
|
|
307
329
|
if env_key is not None:
|
|
308
330
|
params["env_key"] = env_key
|
|
309
|
-
|
|
331
|
+
if keys is not None:
|
|
332
|
+
params["task_keys"] = keys
|
|
333
|
+
if team_id is not None:
|
|
334
|
+
params["team_id"] = team_id
|
|
335
|
+
|
|
310
336
|
response = await self.client.request("GET", "/v1/tasks", params=params)
|
|
311
337
|
task_list_response = TaskListResponse(**response.json())
|
|
312
|
-
|
|
338
|
+
|
|
313
339
|
# Transform TaskResponse objects to Task objects
|
|
314
340
|
tasks = []
|
|
315
341
|
for task_response in task_list_response.tasks:
|
|
316
342
|
# Create verifier function if verifier data is present
|
|
317
343
|
verifier = None
|
|
318
344
|
verifier_func = task_response.verifier_func
|
|
319
|
-
|
|
345
|
+
|
|
320
346
|
if task_response.verifier:
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
347
|
+
embedded_code = task_response.verifier.code or ""
|
|
348
|
+
is_embedded_error = embedded_code.strip().startswith(
|
|
349
|
+
"<error loading code:"
|
|
350
|
+
)
|
|
351
|
+
if not is_embedded_error:
|
|
352
|
+
# Only override if the embedded code looks valid
|
|
353
|
+
verifier_func = embedded_code
|
|
354
|
+
# Create VerifierFunction from the embedded data
|
|
355
|
+
try:
|
|
356
|
+
verifier = await self._create_verifier_from_data(
|
|
357
|
+
verifier_id=task_response.verifier.verifier_id,
|
|
358
|
+
verifier_key=task_response.verifier.key,
|
|
359
|
+
verifier_code=embedded_code,
|
|
360
|
+
verifier_sha=task_response.verifier.sha256,
|
|
361
|
+
)
|
|
362
|
+
except Exception as e:
|
|
363
|
+
logger.warning(
|
|
364
|
+
f"Failed to create verifier {task_response.verifier.key}: {e}"
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
# Fallback: try fetching by ID if embedded code failed to load
|
|
368
|
+
try:
|
|
369
|
+
logger.warning(
|
|
370
|
+
f"Embedded verifier code missing for {task_response.verifier.key} (NoSuchKey). "
|
|
371
|
+
f"Attempting to refetch by id {task_response.verifier.verifier_id}"
|
|
372
|
+
)
|
|
373
|
+
verifier = await self._load_verifier(
|
|
374
|
+
task_response.verifier.verifier_id
|
|
375
|
+
)
|
|
376
|
+
except Exception as e:
|
|
377
|
+
logger.warning(
|
|
378
|
+
f"Refetch by verifier id failed for {task_response.verifier.key}: {e}. "
|
|
379
|
+
"Leaving verifier unset."
|
|
380
|
+
)
|
|
381
|
+
|
|
335
382
|
task = Task(
|
|
336
383
|
key=task_response.key,
|
|
337
384
|
prompt=task_response.prompt,
|
|
@@ -341,19 +388,25 @@ class AsyncFleet:
|
|
|
341
388
|
env_variables=task_response.env_variables or {},
|
|
342
389
|
verifier_func=verifier_func, # Set verifier code
|
|
343
390
|
verifier=verifier, # Use created verifier or None
|
|
344
|
-
metadata={} # Default empty metadata
|
|
391
|
+
metadata={}, # Default empty metadata
|
|
345
392
|
)
|
|
346
393
|
tasks.append(task)
|
|
347
|
-
|
|
394
|
+
|
|
395
|
+
# Apply client-side filtering for version if specified
|
|
396
|
+
if version is not None:
|
|
397
|
+
tasks = [task for task in tasks if task.version == version]
|
|
398
|
+
|
|
348
399
|
return tasks
|
|
349
400
|
|
|
350
|
-
async def export_tasks(
|
|
401
|
+
async def export_tasks(
|
|
402
|
+
self, env_key: Optional[str] = None, filename: Optional[str] = None
|
|
403
|
+
):
|
|
351
404
|
"""Export tasks for the authenticated team, optionally filtered by environment.
|
|
352
|
-
|
|
405
|
+
|
|
353
406
|
Args:
|
|
354
407
|
env_key: Optional environment key to filter tasks by
|
|
355
408
|
filename: Optional filename to write tasks to. If not provided, defaults to 'tasks.json' or 'tasks_{env_key}.json'
|
|
356
|
-
|
|
409
|
+
|
|
357
410
|
Returns:
|
|
358
411
|
str: Path to the exported file if tasks were written, None if no tasks found
|
|
359
412
|
"""
|
|
@@ -365,38 +418,38 @@ class AsyncFleet:
|
|
|
365
418
|
filename = f"tasks_{env_key}.json"
|
|
366
419
|
else:
|
|
367
420
|
filename = "tasks.json"
|
|
368
|
-
|
|
421
|
+
|
|
369
422
|
# Convert tasks to serializable format
|
|
370
423
|
tasks_data = []
|
|
371
424
|
for task in tasks:
|
|
372
425
|
task_dict = task.model_dump()
|
|
373
426
|
# Remove non-serializable verifier object, keep verifier_func (code string)
|
|
374
|
-
if
|
|
375
|
-
task_dict.pop(
|
|
427
|
+
if "verifier" in task_dict:
|
|
428
|
+
task_dict.pop("verifier")
|
|
376
429
|
tasks_data.append(task_dict)
|
|
377
|
-
|
|
430
|
+
|
|
378
431
|
# Write to JSON file
|
|
379
|
-
with open(filename,
|
|
432
|
+
with open(filename, "w", encoding="utf-8") as f:
|
|
380
433
|
json.dump(tasks_data, f, indent=2, default=str)
|
|
381
|
-
|
|
434
|
+
|
|
382
435
|
logger.info(f"Exported {len(tasks)} tasks to {filename}")
|
|
383
436
|
return filename
|
|
384
437
|
else:
|
|
385
438
|
logger.info("No tasks found to export")
|
|
386
439
|
return None
|
|
387
|
-
|
|
440
|
+
|
|
388
441
|
async def import_tasks(self, filename: str):
|
|
389
442
|
"""Import tasks from a JSON file.
|
|
390
|
-
|
|
443
|
+
|
|
391
444
|
Args:
|
|
392
445
|
filename: Path to the JSON file of Task objects to import
|
|
393
|
-
|
|
446
|
+
|
|
394
447
|
Returns:
|
|
395
448
|
List[Task] containing imported Task objects
|
|
396
449
|
"""
|
|
397
|
-
with open(filename,
|
|
450
|
+
with open(filename, "r", encoding="utf-8") as f:
|
|
398
451
|
tasks_data = json.load(f)
|
|
399
|
-
|
|
452
|
+
|
|
400
453
|
# Create tasks from the loaded data
|
|
401
454
|
tasks = []
|
|
402
455
|
for task_data in tasks_data:
|
|
@@ -413,12 +466,13 @@ class AsyncFleet:
|
|
|
413
466
|
env_variables=task.env_variables or {},
|
|
414
467
|
)
|
|
415
468
|
try:
|
|
416
|
-
response = await self.client.request(
|
|
469
|
+
response = await self.client.request(
|
|
470
|
+
"POST", "/v1/tasks", json=payload.model_dump()
|
|
471
|
+
)
|
|
417
472
|
except Exception as e:
|
|
418
473
|
logger.error(f"Failed to import task {task.key}: {e}")
|
|
419
474
|
continue
|
|
420
475
|
|
|
421
|
-
|
|
422
476
|
async def account(self) -> AccountResponse:
|
|
423
477
|
"""Get account information including instance limits and usage.
|
|
424
478
|
|
|
@@ -427,102 +481,62 @@ class AsyncFleet:
|
|
|
427
481
|
"""
|
|
428
482
|
response = await self.client.request("GET", "/v1/account")
|
|
429
483
|
return AccountResponse(**response.json())
|
|
430
|
-
|
|
484
|
+
|
|
431
485
|
async def _create_verifier_from_data(
|
|
432
|
-
self,
|
|
433
|
-
verifier_id: str,
|
|
434
|
-
verifier_key: str,
|
|
435
|
-
verifier_code: str,
|
|
436
|
-
verifier_sha: str
|
|
486
|
+
self, verifier_id: str, verifier_key: str, verifier_code: str, verifier_sha: str
|
|
437
487
|
) -> "AsyncVerifierFunction":
|
|
438
488
|
"""Create an AsyncVerifierFunction from verifier data.
|
|
439
|
-
|
|
489
|
+
|
|
440
490
|
Args:
|
|
441
491
|
verifier_id: The verifier ID
|
|
442
492
|
verifier_key: The verifier key
|
|
443
493
|
verifier_code: The verifier code
|
|
444
494
|
verifier_sha: The verifier SHA256
|
|
445
|
-
|
|
495
|
+
|
|
446
496
|
Returns:
|
|
447
497
|
AsyncVerifierFunction created from the verifier code
|
|
448
498
|
"""
|
|
499
|
+
from ..tasks import verifier_from_string
|
|
449
500
|
from .verifiers.verifier import AsyncVerifierFunction
|
|
450
501
|
|
|
451
|
-
#
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
verifier_code = convert_new_to_old_verifier(verifier_code)
|
|
455
|
-
# Update function name since wrapper adds _wrapper suffix
|
|
456
|
-
original_name = extract_function_name(verifier_code.split('\n')[0])
|
|
457
|
-
if original_name and original_name.endswith('_wrapper'):
|
|
458
|
-
function_name = original_name
|
|
459
|
-
else:
|
|
460
|
-
function_name = extract_function_name(verifier_code)
|
|
461
|
-
else:
|
|
462
|
-
# Extract function name from code
|
|
463
|
-
function_name = extract_function_name(verifier_code)
|
|
464
|
-
|
|
465
|
-
if not function_name:
|
|
466
|
-
raise ValueError(f"Could not extract function name from verifier {verifier_id}")
|
|
467
|
-
|
|
468
|
-
# Create a function object from the code
|
|
469
|
-
# Import necessary classes for the namespace
|
|
470
|
-
from ..verifiers.db import IgnoreConfig, DatabaseSnapshot
|
|
471
|
-
|
|
472
|
-
# Create a namespace for the function
|
|
473
|
-
namespace = {
|
|
474
|
-
'__builtins__': __builtins__,
|
|
475
|
-
'Environment': object, # Placeholder, will be provided at runtime
|
|
476
|
-
'IgnoreConfig': IgnoreConfig,
|
|
477
|
-
'DatabaseSnapshot': DatabaseSnapshot,
|
|
478
|
-
'TASK_FAILED_SCORE': 0,
|
|
479
|
-
'TASK_SUCCESSFUL_SCORE': 1,
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
# Execute the code to define the function
|
|
483
|
-
exec(verifier_code, namespace)
|
|
484
|
-
|
|
485
|
-
# Get the function object
|
|
486
|
-
if function_name not in namespace:
|
|
487
|
-
raise ValueError(f"Function {function_name} not found in verifier code")
|
|
488
|
-
|
|
489
|
-
func = namespace[function_name]
|
|
490
|
-
|
|
491
|
-
# Create and return AsyncVerifierFunction with SHA if available
|
|
492
|
-
# Since the function was created via exec, we need to provide the raw code
|
|
493
|
-
verifier_func = AsyncVerifierFunction(
|
|
494
|
-
func=func,
|
|
495
|
-
key=verifier_key,
|
|
496
|
-
extra_requirements=[],
|
|
502
|
+
# Use verifier_from_string to create the verifier
|
|
503
|
+
verifier_func = verifier_from_string(
|
|
504
|
+
verifier_func=verifier_code,
|
|
497
505
|
verifier_id=verifier_id,
|
|
498
|
-
|
|
499
|
-
|
|
506
|
+
verifier_key=verifier_key,
|
|
507
|
+
sha256=verifier_sha,
|
|
500
508
|
)
|
|
501
509
|
|
|
510
|
+
# Ensure we return an AsyncVerifierFunction
|
|
511
|
+
if not isinstance(verifier_func, AsyncVerifierFunction):
|
|
512
|
+
raise TypeError(
|
|
513
|
+
f"Expected AsyncVerifierFunction but got {type(verifier_func).__name__}"
|
|
514
|
+
)
|
|
515
|
+
|
|
502
516
|
# Store the original verifier code for reference
|
|
503
517
|
verifier_func._verifier_code = verifier_code
|
|
504
|
-
|
|
518
|
+
|
|
505
519
|
return verifier_func
|
|
506
|
-
|
|
520
|
+
|
|
507
521
|
async def _load_verifier(self, verifier_id: str) -> "AsyncVerifierFunction":
|
|
508
522
|
"""Load a verifier by ID and create an AsyncVerifierFunction.
|
|
509
|
-
|
|
523
|
+
|
|
510
524
|
Args:
|
|
511
525
|
verifier_id: The verifier ID to fetch
|
|
512
|
-
|
|
526
|
+
|
|
513
527
|
Returns:
|
|
514
528
|
AsyncVerifierFunction created from the verifier code
|
|
515
529
|
"""
|
|
516
530
|
# Fetch verifier from API
|
|
517
531
|
response = await self.client.request("GET", f"/v1/verifier/{verifier_id}")
|
|
518
532
|
verifier_data = response.json()
|
|
519
|
-
|
|
533
|
+
|
|
520
534
|
# Use the common method to create verifier
|
|
521
535
|
return await self._create_verifier_from_data(
|
|
522
536
|
verifier_id=verifier_id,
|
|
523
537
|
verifier_key=verifier_data["key"],
|
|
524
538
|
verifier_code=verifier_data["code"],
|
|
525
|
-
verifier_sha=verifier_data.get("sha256", "")
|
|
539
|
+
verifier_sha=verifier_data.get("sha256", ""),
|
|
526
540
|
)
|
|
527
541
|
|
|
528
542
|
|
|
@@ -567,23 +581,29 @@ async def _execute_verifier_remote(
|
|
|
567
581
|
"timeout": timeout,
|
|
568
582
|
"region": "us-west-1", # TODO: make configurable
|
|
569
583
|
}
|
|
570
|
-
|
|
584
|
+
|
|
571
585
|
# Add bundle data only if upload is needed
|
|
572
586
|
if needs_upload:
|
|
573
587
|
bundle_b64 = base64.b64encode(bundle_data).decode("utf-8")
|
|
574
588
|
request_data["bundle"] = bundle_b64
|
|
575
|
-
|
|
589
|
+
|
|
576
590
|
# Debug logging
|
|
577
|
-
logger.debug(
|
|
591
|
+
logger.debug(
|
|
592
|
+
f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}"
|
|
593
|
+
)
|
|
578
594
|
logger.debug(f"Request has bundle: {needs_upload}")
|
|
579
595
|
logger.debug(f"Using client with base_url: {client.base_url}")
|
|
580
596
|
logger.debug(f"Request data keys: {list(request_data.keys())}")
|
|
581
|
-
logger.debug(
|
|
597
|
+
logger.debug(
|
|
598
|
+
f"Bundle size: {len(request_data.get('bundle', ''))} chars"
|
|
599
|
+
if "bundle" in request_data
|
|
600
|
+
else "No bundle"
|
|
601
|
+
)
|
|
582
602
|
|
|
583
603
|
# Note: This should be called on the instance URL, not the orchestrator
|
|
584
604
|
# The instance has manager URLs for verifier execution
|
|
585
605
|
response = await client.request("POST", "/v1/verifiers/execute", json=request_data)
|
|
586
|
-
|
|
606
|
+
|
|
587
607
|
# Debug the response
|
|
588
608
|
response_json = response.json()
|
|
589
609
|
logger.debug(f"Verifier execute response: {response_json}")
|
fleet/_async/env/client.py
CHANGED
|
@@ -6,9 +6,11 @@ from typing import List, Optional
|
|
|
6
6
|
async def make_async(env_key: str, region: Optional[str] = None) -> AsyncEnv:
|
|
7
7
|
return await AsyncFleet().make(env_key, region=region)
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
async def make_for_task_async(task: Task) -> AsyncEnv:
|
|
10
11
|
return await AsyncFleet().make_for_task(task)
|
|
11
12
|
|
|
13
|
+
|
|
12
14
|
async def list_envs_async() -> List[EnvironmentModel]:
|
|
13
15
|
return await AsyncFleet().list_envs()
|
|
14
16
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from .client import AsyncFleet
|
|
6
|
+
from ..config import DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_default_client: Optional[AsyncFleet] = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_client() -> AsyncFleet:
|
|
13
|
+
"""Get the global default AsyncFleet client, creating it if needed."""
|
|
14
|
+
global _default_client
|
|
15
|
+
if _default_client is None:
|
|
16
|
+
_default_client = AsyncFleet()
|
|
17
|
+
return _default_client
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def configure(
|
|
21
|
+
api_key: Optional[str] = None,
|
|
22
|
+
base_url: Optional[str] = None,
|
|
23
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
24
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
25
|
+
) -> AsyncFleet:
|
|
26
|
+
"""Configure the global default AsyncFleet client.
|
|
27
|
+
|
|
28
|
+
Returns the configured client instance.
|
|
29
|
+
"""
|
|
30
|
+
global _default_client
|
|
31
|
+
_default_client = AsyncFleet(
|
|
32
|
+
api_key=api_key,
|
|
33
|
+
base_url=base_url,
|
|
34
|
+
max_retries=max_retries,
|
|
35
|
+
timeout=timeout,
|
|
36
|
+
)
|
|
37
|
+
return _default_client
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def reset_client() -> None:
|
|
41
|
+
"""Reset the global default client. A new one will be created on next access."""
|
|
42
|
+
global _default_client
|
|
43
|
+
_default_client = None
|
fleet/_async/instance/client.py
CHANGED
|
@@ -130,7 +130,7 @@ class AsyncInstanceClient:
|
|
|
130
130
|
|
|
131
131
|
async def _load_resources(self) -> None:
|
|
132
132
|
if self._resources is None:
|
|
133
|
-
response = await self.client.request("GET", "/resources")
|
|
133
|
+
response = await self.client.request("GET", "/resources", timeout=1.0)
|
|
134
134
|
if response.status_code != 200:
|
|
135
135
|
self._resources = []
|
|
136
136
|
return
|