fleet-python 0.2.127__tar.gz → 0.2.129__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.
Files changed (126) hide show
  1. {fleet_python-0.2.127/fleet_python.egg-info → fleet_python-0.2.129}/PKG-INFO +1 -1
  2. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/__init__.py +1 -1
  3. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/__init__.py +1 -1
  4. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/base.py +1 -1
  5. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/client.py +29 -1
  6. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/models.py +16 -0
  7. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/tasks.py +52 -8
  8. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/verifiers/verifier.py +38 -5
  9. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/base.py +1 -1
  10. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/client.py +28 -1
  11. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/models.py +16 -0
  12. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/tasks.py +54 -8
  13. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/verifier.py +38 -5
  14. {fleet_python-0.2.127 → fleet_python-0.2.129/fleet_python.egg-info}/PKG-INFO +1 -1
  15. {fleet_python-0.2.127 → fleet_python-0.2.129}/pyproject.toml +1 -1
  16. {fleet_python-0.2.127 → fleet_python-0.2.129}/LICENSE +0 -0
  17. {fleet_python-0.2.127 → fleet_python-0.2.129}/README.md +0 -0
  18. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/diff_example.py +0 -0
  19. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/dsl_example.py +0 -0
  20. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example.py +0 -0
  21. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/exampleResume.py +0 -0
  22. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_account.py +0 -0
  23. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_action_log.py +0 -0
  24. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_client.py +0 -0
  25. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_mcp_anthropic.py +0 -0
  26. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_mcp_openai.py +0 -0
  27. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_sync.py +0 -0
  28. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_task.py +0 -0
  29. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_tasks.py +0 -0
  30. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/example_verifier.py +0 -0
  31. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/export_tasks.py +0 -0
  32. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/export_tasks_filtered.py +0 -0
  33. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/fetch_tasks.py +0 -0
  34. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/gemini_example.py +0 -0
  35. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/import_tasks.py +0 -0
  36. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/iterate_verifiers.py +0 -0
  37. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/json_tasks_example.py +0 -0
  38. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/nova_act_example.py +0 -0
  39. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/openai_example.py +0 -0
  40. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/openai_simple_example.py +0 -0
  41. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/query_builder_example.py +0 -0
  42. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/quickstart.py +0 -0
  43. {fleet_python-0.2.127 → fleet_python-0.2.129}/examples/test_cdp_logging.py +0 -0
  44. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/browser.py +0 -0
  45. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/env/__init__.py +0 -0
  46. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/env/client.py +0 -0
  47. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/exceptions.py +0 -0
  48. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/global_client.py +0 -0
  49. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/instance/__init__.py +0 -0
  50. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/instance/base.py +0 -0
  51. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/instance/client.py +0 -0
  52. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/judge.py +0 -0
  53. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/resources/__init__.py +0 -0
  54. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/resources/api.py +0 -0
  55. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/resources/base.py +0 -0
  56. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/resources/browser.py +0 -0
  57. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/resources/filesystem.py +0 -0
  58. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/resources/mcp.py +0 -0
  59. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/resources/sqlite.py +0 -0
  60. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/verifiers/__init__.py +0 -0
  61. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/_async/verifiers/bundler.py +0 -0
  62. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/__init__.py +0 -0
  63. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/Dockerfile +0 -0
  64. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/__init__.py +0 -0
  65. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/agent.py +0 -0
  66. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/mcp/main.py +0 -0
  67. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/mcp_server/__init__.py +0 -0
  68. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/mcp_server/main.py +0 -0
  69. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/mcp_server/tools.py +0 -0
  70. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/requirements.txt +0 -0
  71. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/gemini_cua/start.sh +0 -0
  72. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/orchestrator.py +0 -0
  73. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/types.py +0 -0
  74. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/agent/utils.py +0 -0
  75. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/browser.py +0 -0
  76. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/cli.py +0 -0
  77. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/config.py +0 -0
  78. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/env/__init__.py +0 -0
  79. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/env/client.py +0 -0
  80. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/eval/__init__.py +0 -0
  81. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/eval/uploader.py +0 -0
  82. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/exceptions.py +0 -0
  83. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/global_client.py +0 -0
  84. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/instance/__init__.py +0 -0
  85. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/instance/base.py +0 -0
  86. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/instance/client.py +0 -0
  87. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/instance/models.py +0 -0
  88. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/judge.py +0 -0
  89. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/proxy/__init__.py +0 -0
  90. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/proxy/proxy.py +0 -0
  91. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/proxy/whitelist.py +0 -0
  92. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/resources/__init__.py +0 -0
  93. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/resources/api.py +0 -0
  94. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/resources/base.py +0 -0
  95. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/resources/browser.py +0 -0
  96. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/resources/filesystem.py +0 -0
  97. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/resources/mcp.py +0 -0
  98. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/resources/sqlite.py +0 -0
  99. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/types.py +0 -0
  100. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/utils/__init__.py +0 -0
  101. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/utils/http_logging.py +0 -0
  102. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/utils/logging.py +0 -0
  103. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/utils/playwright.py +0 -0
  104. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/__init__.py +0 -0
  105. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/bundler.py +0 -0
  106. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/code.py +0 -0
  107. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/db.py +0 -0
  108. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/decorator.py +0 -0
  109. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/parse.py +0 -0
  110. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet/verifiers/sql_differ.py +0 -0
  111. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet_python.egg-info/SOURCES.txt +0 -0
  112. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet_python.egg-info/dependency_links.txt +0 -0
  113. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet_python.egg-info/entry_points.txt +0 -0
  114. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet_python.egg-info/requires.txt +0 -0
  115. {fleet_python-0.2.127 → fleet_python-0.2.129}/fleet_python.egg-info/top_level.txt +0 -0
  116. {fleet_python-0.2.127 → fleet_python-0.2.129}/scripts/fix_sync_imports.py +0 -0
  117. {fleet_python-0.2.127 → fleet_python-0.2.129}/scripts/unasync.py +0 -0
  118. {fleet_python-0.2.127 → fleet_python-0.2.129}/setup.cfg +0 -0
  119. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/__init__.py +0 -0
  120. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/test_app_method.py +0 -0
  121. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/test_expect_exactly.py +0 -0
  122. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/test_expect_only.py +0 -0
  123. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/test_instance_dispatch.py +0 -0
  124. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/test_sqlite_resource_dual_mode.py +0 -0
  125. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/test_sqlite_shared_memory_behavior.py +0 -0
  126. {fleet_python-0.2.127 → fleet_python-0.2.129}/tests/test_verifier_from_string.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.127
3
+ Version: 0.2.129
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -78,7 +78,7 @@ from . import env
78
78
  from . import global_client as _global_client
79
79
  from ._async import global_client as _async_global_client
80
80
 
81
- __version__ = "0.2.127"
81
+ __version__ = "0.2.129"
82
82
 
83
83
  __all__ = [
84
84
  # Core classes
@@ -44,7 +44,7 @@ from ..types import VerifierFunction
44
44
  from .. import env
45
45
  from . import global_client as _async_global_client
46
46
 
47
- __version__ = "0.2.127"
47
+ __version__ = "0.2.129"
48
48
 
49
49
  __all__ = [
50
50
  # Core classes
@@ -26,7 +26,7 @@ from .exceptions import (
26
26
  try:
27
27
  from .. import __version__
28
28
  except ImportError:
29
- __version__ = "0.2.127"
29
+ __version__ = "0.2.129"
30
30
 
31
31
  logger = logging.getLogger(__name__)
32
32
 
@@ -530,6 +530,8 @@ class AsyncEnv(EnvironmentBase):
530
530
  timeout: Optional[int] = 30,
531
531
  needs_upload: bool = True,
532
532
  verifier_runtime_version: Optional[str] = None,
533
+ async_: bool = False,
534
+ poll_interval: float = 5.0,
533
535
  ) -> VerifiersExecuteResponse:
534
536
  return await _execute_verifier_remote(
535
537
  self._load_client,
@@ -543,6 +545,8 @@ class AsyncEnv(EnvironmentBase):
543
545
  timeout,
544
546
  needs_upload,
545
547
  verifier_runtime_version,
548
+ async_=async_,
549
+ poll_interval=poll_interval,
546
550
  )
547
551
 
548
552
  def __getstate__(self):
@@ -1814,6 +1818,8 @@ async def _execute_verifier_remote(
1814
1818
  timeout: Optional[int] = 30,
1815
1819
  needs_upload: bool = True,
1816
1820
  verifier_runtime_version: Optional[str] = None,
1821
+ async_: bool = False,
1822
+ poll_interval: float = 5.0,
1817
1823
  ) -> VerifiersExecuteResponse:
1818
1824
  # Pickle args and kwargs together
1819
1825
  # The first arg should be None as a placeholder for env
@@ -1841,6 +1847,11 @@ async def _execute_verifier_remote(
1841
1847
  if verifier_runtime_version:
1842
1848
  request_data["verifier_runtime_version"] = verifier_runtime_version
1843
1849
 
1850
+ # Async submit-and-poll path. When async_ is False the behavior below is
1851
+ # identical to the original synchronous request.
1852
+ if async_:
1853
+ request_data["async"] = True
1854
+
1844
1855
  # Debug logging
1845
1856
  # logger.debug(
1846
1857
  # f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}"
@@ -1862,4 +1873,21 @@ async def _execute_verifier_remote(
1862
1873
  response_json = response.json()
1863
1874
  # logger.debug(f"Verifier execute response: {response_json}")
1864
1875
 
1865
- return VerifiersExecuteResponse(**response_json)
1876
+ if not async_:
1877
+ return VerifiersExecuteResponse(**response_json)
1878
+
1879
+ # Async: the submit returns a job handle; poll until the job reaches a
1880
+ # terminal state (completed/failed). Branch on `status`, never `success`.
1881
+ job_id = response_json.get("job_id")
1882
+ if not job_id:
1883
+ # No job handle returned (e.g. server ran it inline) - surface as-is.
1884
+ return VerifiersExecuteResponse(**response_json)
1885
+
1886
+ while True:
1887
+ poll_response = await client.request(
1888
+ "GET", f"/v1/verifiers/jobs/{job_id}"
1889
+ )
1890
+ poll_json = poll_response.json()
1891
+ if poll_json.get("status") in ("completed", "failed"):
1892
+ return VerifiersExecuteResponse(**poll_json)
1893
+ await asyncio.sleep(poll_interval)
@@ -259,6 +259,12 @@ class VerifiersExecuteRequest(BaseModel):
259
259
  display_src: Optional[str] = Field(
260
260
  None, description="Display source code", title="Display Src"
261
261
  )
262
+ async_: Optional[bool] = Field(
263
+ None,
264
+ alias="async",
265
+ description="Submit asynchronously and return a job handle instead of waiting",
266
+ title="Async",
267
+ )
262
268
 
263
269
 
264
270
  class VerifiersExecuteResponse(BaseModel):
@@ -303,6 +309,16 @@ class VerifiersExecuteResponse(BaseModel):
303
309
  stdout: Optional[str] = Field(
304
310
  None, description="Captured stdout from execution", title="Stdout"
305
311
  )
312
+ status: Optional[str] = Field(
313
+ None,
314
+ description="Job status for async execution (pending/running/completed/failed)",
315
+ title="Status",
316
+ )
317
+ job_id: Optional[str] = Field(
318
+ None,
319
+ description="Job handle for async execution; poll GET /v1/verifiers/jobs/{job_id}",
320
+ title="Job Id",
321
+ )
306
322
 
307
323
 
308
324
  class DescribeResponse(BaseModel):
@@ -81,11 +81,23 @@ class Task(BaseModel):
81
81
  # Allow arbitrary types for the verifier field
82
82
  arbitrary_types_allowed = True
83
83
 
84
- def verify(self, env, *args, **kwargs) -> float:
84
+ def verify(
85
+ self,
86
+ env,
87
+ *args,
88
+ async_: bool = False,
89
+ poll_interval: float = 5.0,
90
+ **kwargs,
91
+ ) -> float:
85
92
  """Verify the task using the verifier function (sync version).
86
93
 
87
94
  For sync environments, calls the sync verifier directly.
88
95
  For async verifiers, automatically runs them with asyncio.run().
96
+
97
+ When ``async_`` is True the verifier is submitted to run in the
98
+ background and polled (every ``poll_interval`` seconds) until it
99
+ completes, avoiding HTTP/edge idle timeouts for long-running
100
+ verifiers. When False the behavior is unchanged.
89
101
  """
90
102
  # If verifier doesn't exist but verifier_func does, rebuild it
91
103
  if not self.verifier and self.verifier_func:
@@ -95,7 +107,9 @@ class Task(BaseModel):
95
107
  import asyncio
96
108
  import inspect
97
109
 
98
- result = self.verifier.remote(env, *args, **kwargs)
110
+ result = self.verifier.remote(
111
+ env, *args, async_=async_, poll_interval=poll_interval, **kwargs
112
+ )
99
113
 
100
114
  # If the result is a coroutine, we need to run it
101
115
  if inspect.iscoroutine(result):
@@ -115,18 +129,27 @@ class Task(BaseModel):
115
129
  else:
116
130
  raise ValueError("No verifier function found for this task")
117
131
 
118
- async def verify_async(self, *args, **kwargs) -> float:
132
+ async def verify_async(
133
+ self, *args, async_: bool = False, poll_interval: float = 5.0, **kwargs
134
+ ) -> float:
119
135
  """Verify the task using the verifier function (async version).
120
136
 
121
137
  For async environments, awaits the async verifier.
122
138
  Works with both sync and async verifiers in async contexts.
139
+
140
+ When ``async_`` is True the verifier is submitted to run in the
141
+ background and polled (every ``poll_interval`` seconds) until it
142
+ completes, avoiding HTTP/edge idle timeouts for long-running
143
+ verifiers. When False the behavior is unchanged.
123
144
  """
124
145
  # If verifier doesn't exist but verifier_func does, rebuild it
125
146
  if not self.verifier and self.verifier_func:
126
147
  self._rebuild_verifier()
127
148
 
128
149
  if self.verifier:
129
- result = self.verifier.remote(*args, **kwargs)
150
+ result = self.verifier.remote(
151
+ *args, async_=async_, poll_interval=poll_interval, **kwargs
152
+ )
130
153
  # If it's a coroutine, await it
131
154
  import inspect
132
155
 
@@ -138,19 +161,26 @@ class Task(BaseModel):
138
161
  raise ValueError("No verifier function found for this task")
139
162
 
140
163
  async def verify_detailed_async(
141
- self, *args, **kwargs
164
+ self, *args, async_: bool = False, poll_interval: float = 5.0, **kwargs
142
165
  ) -> "VerifiersExecuteResponse":
143
166
  """Verify the task and return the full execute response model.
144
167
 
145
168
  For async environments, awaits the async verifier.
146
169
  Works with both sync and async verifiers in async contexts.
170
+
171
+ When ``async_`` is True the verifier is submitted to run in the
172
+ background and polled (every ``poll_interval`` seconds) until it
173
+ completes, avoiding HTTP/edge idle timeouts for long-running
174
+ verifiers. When False the behavior is unchanged.
147
175
  """
148
176
  # If verifier doesn't exist but verifier_func does, rebuild it
149
177
  if not self.verifier and self.verifier_func:
150
178
  self._rebuild_verifier()
151
179
 
152
180
  if self.verifier:
153
- result = self.verifier.remote_with_response(*args, **kwargs)
181
+ result = self.verifier.remote_with_response(
182
+ *args, async_=async_, poll_interval=poll_interval, **kwargs
183
+ )
154
184
  # If it's a coroutine, await it
155
185
  import inspect
156
186
 
@@ -161,11 +191,23 @@ class Task(BaseModel):
161
191
  else:
162
192
  raise ValueError("No verifier function found for this task")
163
193
 
164
- def verify_detailed(self, env, *args, **kwargs) -> "VerifiersExecuteResponse":
194
+ def verify_detailed(
195
+ self,
196
+ env,
197
+ *args,
198
+ async_: bool = False,
199
+ poll_interval: float = 5.0,
200
+ **kwargs,
201
+ ) -> "VerifiersExecuteResponse":
165
202
  """Verify the task and return the full execute response model (sync version).
166
203
 
167
204
  For sync environments, calls the sync verifier directly.
168
205
  For async verifiers, automatically runs them with asyncio.run().
206
+
207
+ When ``async_`` is True the verifier is submitted to run in the
208
+ background and polled (every ``poll_interval`` seconds) until it
209
+ completes, avoiding HTTP/edge idle timeouts for long-running
210
+ verifiers. When False the behavior is unchanged.
169
211
  """
170
212
  # If verifier doesn't exist but verifier_func does, rebuild it
171
213
  if not self.verifier and self.verifier_func:
@@ -176,7 +218,9 @@ class Task(BaseModel):
176
218
  import inspect
177
219
 
178
220
  # Check if verifier has remote_with_response method (for decorated verifiers)
179
- result = self.verifier.remote_with_response(env, *args, **kwargs)
221
+ result = self.verifier.remote_with_response(
222
+ env, *args, async_=async_, poll_interval=poll_interval, **kwargs
223
+ )
180
224
 
181
225
  # If the result is a coroutine, we need to run it
182
226
  if inspect.iscoroutine(result):
@@ -154,9 +154,25 @@ class AsyncVerifierFunction:
154
154
  # Return error score 0
155
155
  return 0.0
156
156
 
157
- async def remote(self, env: AsyncEnv, *args, **kwargs) -> float:
158
- """Remote execution of the verifier function with SHA-based bundle caching."""
159
- response = await self.remote_with_response(env, *args, **kwargs)
157
+ async def remote(
158
+ self,
159
+ env: AsyncEnv,
160
+ *args,
161
+ async_: bool = False,
162
+ poll_interval: float = 5.0,
163
+ **kwargs,
164
+ ) -> float:
165
+ """Remote execution of the verifier function with SHA-based bundle caching.
166
+
167
+ When ``async_`` is True the verifier is submitted to run in the
168
+ background and the result is polled (every ``poll_interval`` seconds)
169
+ until it completes — this avoids HTTP/edge idle timeouts for
170
+ long-running verifiers. When False the behavior is unchanged (the
171
+ request blocks until the verifier finishes).
172
+ """
173
+ response = await self.remote_with_response(
174
+ env, *args, async_=async_, poll_interval=poll_interval, **kwargs
175
+ )
160
176
 
161
177
  # Handle response
162
178
  if response.stdout:
@@ -228,9 +244,20 @@ Remote traceback:
228
244
  )
229
245
 
230
246
  async def remote_with_response(
231
- self, env: "AsyncEnv", *args, **kwargs
247
+ self,
248
+ env: "AsyncEnv",
249
+ *args,
250
+ async_: bool = False,
251
+ poll_interval: float = 5.0,
252
+ **kwargs,
232
253
  ) -> "VerifiersExecuteResponse":
233
- """Remote execution of the verifier function that returns the full response model."""
254
+ """Remote execution of the verifier function that returns the full response model.
255
+
256
+ When ``async_`` is True the verifier is submitted asynchronously and
257
+ polled (every ``poll_interval`` seconds) until it reaches a terminal
258
+ state; the returned response is the completed/failed job result. When
259
+ False the request blocks until the verifier finishes (unchanged).
260
+ """
234
261
  args_array = list(args)
235
262
  args_array.append({"env": env.instance_id})
236
263
  args = tuple(args_array)
@@ -254,6 +281,8 @@ Remote traceback:
254
281
  kwargs=kwargs,
255
282
  needs_upload=True,
256
283
  verifier_runtime_version=self.verifier_runtime_version,
284
+ async_=async_,
285
+ poll_interval=poll_interval,
257
286
  )
258
287
 
259
288
  # logger.debug(f"Bundle {bundle_sha[:8]}... uploaded successfully")
@@ -271,6 +300,8 @@ Remote traceback:
271
300
  kwargs=kwargs,
272
301
  needs_upload=False,
273
302
  verifier_runtime_version=self.verifier_runtime_version,
303
+ async_=async_,
304
+ poll_interval=poll_interval,
274
305
  )
275
306
 
276
307
  return response
@@ -292,6 +323,8 @@ Remote traceback:
292
323
  kwargs=kwargs,
293
324
  needs_upload=True,
294
325
  verifier_runtime_version=self.verifier_runtime_version,
326
+ async_=async_,
327
+ poll_interval=poll_interval,
295
328
  )
296
329
  return response
297
330
  else:
@@ -27,7 +27,7 @@ from .exceptions import (
27
27
  try:
28
28
  from . import __version__
29
29
  except ImportError:
30
- __version__ = "0.2.127"
30
+ __version__ = "0.2.129"
31
31
 
32
32
  logger = logging.getLogger(__name__)
33
33
 
@@ -22,6 +22,7 @@ import httpx
22
22
  import json
23
23
  import logging
24
24
  import os
25
+ import time
25
26
  from datetime import date, datetime
26
27
  from decimal import Decimal
27
28
  from enum import Enum
@@ -542,6 +543,8 @@ class SyncEnv(EnvironmentBase):
542
543
  timeout: Optional[int] = 30,
543
544
  needs_upload: bool = True,
544
545
  verifier_runtime_version: Optional[str] = None,
546
+ async_: bool = False,
547
+ poll_interval: float = 5.0,
545
548
  ) -> VerifiersExecuteResponse:
546
549
  return _execute_verifier_remote(
547
550
  self._load_client,
@@ -555,6 +558,8 @@ class SyncEnv(EnvironmentBase):
555
558
  timeout,
556
559
  needs_upload,
557
560
  verifier_runtime_version,
561
+ async_=async_,
562
+ poll_interval=poll_interval,
558
563
  )
559
564
 
560
565
  def __getstate__(self):
@@ -1933,6 +1938,8 @@ def _execute_verifier_remote(
1933
1938
  timeout: Optional[int] = 30,
1934
1939
  needs_upload: bool = True,
1935
1940
  verifier_runtime_version: Optional[str] = None,
1941
+ async_: bool = False,
1942
+ poll_interval: float = 5.0,
1936
1943
  ) -> VerifiersExecuteResponse:
1937
1944
  # Pickle args and kwargs together
1938
1945
  # The first arg should be None as a placeholder for env
@@ -1960,6 +1967,11 @@ def _execute_verifier_remote(
1960
1967
  if verifier_runtime_version:
1961
1968
  request_data["verifier_runtime_version"] = verifier_runtime_version
1962
1969
 
1970
+ # Async submit-and-poll path. When async_ is False the behavior below is
1971
+ # identical to the original synchronous request.
1972
+ if async_:
1973
+ request_data["async"] = True
1974
+
1963
1975
  # Debug logging
1964
1976
  # logger.debug(
1965
1977
  # f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}"
@@ -1981,4 +1993,19 @@ def _execute_verifier_remote(
1981
1993
  response_json = response.json()
1982
1994
  # logger.debug(f"Verifier execute response: {response_json}")
1983
1995
 
1984
- return VerifiersExecuteResponse(**response_json)
1996
+ if not async_:
1997
+ return VerifiersExecuteResponse(**response_json)
1998
+
1999
+ # Async: the submit returns a job handle; poll until the job reaches a
2000
+ # terminal state (completed/failed). Branch on `status`, never `success`.
2001
+ job_id = response_json.get("job_id")
2002
+ if not job_id:
2003
+ # No job handle returned (e.g. server ran it inline) - surface as-is.
2004
+ return VerifiersExecuteResponse(**response_json)
2005
+
2006
+ while True:
2007
+ poll_response = client.request("GET", f"/v1/verifiers/jobs/{job_id}")
2008
+ poll_json = poll_response.json()
2009
+ if poll_json.get("status") in ("completed", "failed"):
2010
+ return VerifiersExecuteResponse(**poll_json)
2011
+ time.sleep(poll_interval)
@@ -267,6 +267,12 @@ class VerifiersExecuteRequest(BaseModel):
267
267
  display_src: Optional[str] = Field(
268
268
  None, description="Display source code", title="Display Src"
269
269
  )
270
+ async_: Optional[bool] = Field(
271
+ None,
272
+ alias="async",
273
+ description="Submit asynchronously and return a job handle instead of waiting",
274
+ title="Async",
275
+ )
270
276
 
271
277
 
272
278
  class VerifiersExecuteResponse(BaseModel):
@@ -311,6 +317,16 @@ class VerifiersExecuteResponse(BaseModel):
311
317
  stdout: Optional[str] = Field(
312
318
  None, description="Captured stdout from execution", title="Stdout"
313
319
  )
320
+ status: Optional[str] = Field(
321
+ None,
322
+ description="Job status for async execution (pending/running/completed/failed)",
323
+ title="Status",
324
+ )
325
+ job_id: Optional[str] = Field(
326
+ None,
327
+ description="Job handle for async execution; poll GET /v1/verifiers/jobs/{job_id}",
328
+ title="Job Id",
329
+ )
314
330
 
315
331
 
316
332
  class DescribeResponse(BaseModel):
@@ -83,11 +83,23 @@ class Task(BaseModel):
83
83
  # Allow arbitrary types for the verifier field
84
84
  arbitrary_types_allowed = True
85
85
 
86
- def verify(self, env, *args, **kwargs) -> float:
86
+ def verify(
87
+ self,
88
+ env,
89
+ *args,
90
+ async_: bool = False,
91
+ poll_interval: float = 5.0,
92
+ **kwargs,
93
+ ) -> float:
87
94
  """Verify the task using the verifier function (sync version).
88
95
 
89
96
  For sync environments, calls the sync verifier directly.
90
97
  For async verifiers, automatically runs them with asyncio.run().
98
+
99
+ When ``async_`` is True the verifier is submitted to run in the
100
+ background and polled (every ``poll_interval`` seconds) until it
101
+ completes, avoiding HTTP/edge idle timeouts for long-running
102
+ verifiers. When False the behavior is unchanged.
91
103
  """
92
104
  # If verifier doesn't exist but verifier_func does, rebuild it
93
105
  if not self.verifier and self.verifier_func:
@@ -97,7 +109,9 @@ class Task(BaseModel):
97
109
  import inspect
98
110
 
99
111
  # Check if verifier has remote method (for decorated verifiers)
100
- result = self.verifier.remote(env, *args, **kwargs)
112
+ result = self.verifier.remote(
113
+ env, *args, async_=async_, poll_interval=poll_interval, **kwargs
114
+ )
101
115
 
102
116
  # If the result is a coroutine, we need to run it
103
117
  if inspect.iscoroutine(result):
@@ -117,18 +131,27 @@ class Task(BaseModel):
117
131
  else:
118
132
  raise ValueError("No verifier function found for this task")
119
133
 
120
- def verify_async(self, *args, **kwargs) -> float:
134
+ def verify_async(
135
+ self, *args, async_: bool = False, poll_interval: float = 5.0, **kwargs
136
+ ) -> float:
121
137
  """Verify the task using the verifier function (async version).
122
138
 
123
139
  For async environments, awaits the async verifier.
124
140
  Works with both sync and async verifiers in async contexts.
141
+
142
+ When ``async_`` is True the verifier is submitted to run in the
143
+ background and polled (every ``poll_interval`` seconds) until it
144
+ completes, avoiding HTTP/edge idle timeouts for long-running
145
+ verifiers. When False the behavior is unchanged.
125
146
  """
126
147
  # If verifier doesn't exist but verifier_func does, rebuild it
127
148
  if not self.verifier and self.verifier_func:
128
149
  self._rebuild_verifier()
129
150
 
130
151
  if self.verifier:
131
- result = self.verifier.remote(*args, **kwargs)
152
+ result = self.verifier.remote(
153
+ *args, async_=async_, poll_interval=poll_interval, **kwargs
154
+ )
132
155
  # If it's a coroutine, await it
133
156
  import inspect
134
157
 
@@ -139,11 +162,23 @@ class Task(BaseModel):
139
162
  else:
140
163
  raise ValueError("No verifier function found for this task")
141
164
 
142
- def verify_detailed(self, env, *args, **kwargs) -> "VerifiersExecuteResponse":
165
+ def verify_detailed(
166
+ self,
167
+ env,
168
+ *args,
169
+ async_: bool = False,
170
+ poll_interval: float = 5.0,
171
+ **kwargs,
172
+ ) -> "VerifiersExecuteResponse":
143
173
  """Verify the task and return the full execute response model.
144
174
 
145
175
  For sync environments, calls the sync verifier directly.
146
176
  For async verifiers, automatically runs them with asyncio.run().
177
+
178
+ When ``async_`` is True the verifier is submitted to run in the
179
+ background and polled (every ``poll_interval`` seconds) until it
180
+ completes, avoiding HTTP/edge idle timeouts for long-running
181
+ verifiers. When False the behavior is unchanged.
147
182
  """
148
183
  # If verifier doesn't exist but verifier_func does, rebuild it
149
184
  if not self.verifier and self.verifier_func:
@@ -153,7 +188,9 @@ class Task(BaseModel):
153
188
  import inspect
154
189
 
155
190
  # Check if verifier has remote_with_response method (for decorated verifiers)
156
- result = self.verifier.remote_with_response(env, *args, **kwargs)
191
+ result = self.verifier.remote_with_response(
192
+ env, *args, async_=async_, poll_interval=poll_interval, **kwargs
193
+ )
157
194
 
158
195
  # If the result is a coroutine, we need to run it
159
196
  if inspect.iscoroutine(result):
@@ -173,18 +210,27 @@ class Task(BaseModel):
173
210
  else:
174
211
  raise ValueError("No verifier function found for this task")
175
212
 
176
- def verify_detailed_async(self, *args, **kwargs) -> "VerifiersExecuteResponse":
213
+ def verify_detailed_async(
214
+ self, *args, async_: bool = False, poll_interval: float = 5.0, **kwargs
215
+ ) -> "VerifiersExecuteResponse":
177
216
  """Verify the task and return the full execute response model (async version).
178
217
 
179
218
  For async environments, returns a coroutine that when awaited returns the response.
180
219
  Works with both sync and async verifiers in async contexts.
220
+
221
+ When ``async_`` is True the verifier is submitted to run in the
222
+ background and polled (every ``poll_interval`` seconds) until it
223
+ completes, avoiding HTTP/edge idle timeouts for long-running
224
+ verifiers. When False the behavior is unchanged.
181
225
  """
182
226
  # If verifier doesn't exist but verifier_func does, rebuild it
183
227
  if not self.verifier and self.verifier_func:
184
228
  self._rebuild_verifier()
185
229
 
186
230
  if self.verifier:
187
- result = self.verifier.remote_with_response(*args, **kwargs)
231
+ result = self.verifier.remote_with_response(
232
+ *args, async_=async_, poll_interval=poll_interval, **kwargs
233
+ )
188
234
  # Return the result (could be a coroutine or a value)
189
235
  return result
190
236
  else:
@@ -165,9 +165,25 @@ class SyncVerifierFunction:
165
165
  # Return error score 0
166
166
  return 0.0
167
167
 
168
- def remote(self, env: "SyncEnv", *args, **kwargs) -> float:
169
- """Remote execution of the verifier function with SHA-based bundle caching."""
170
- response = self.remote_with_response(env, *args, **kwargs)
168
+ def remote(
169
+ self,
170
+ env: "SyncEnv",
171
+ *args,
172
+ async_: bool = False,
173
+ poll_interval: float = 5.0,
174
+ **kwargs,
175
+ ) -> float:
176
+ """Remote execution of the verifier function with SHA-based bundle caching.
177
+
178
+ When ``async_`` is True the verifier is submitted to run in the
179
+ background and the result is polled (every ``poll_interval`` seconds)
180
+ until it completes — this avoids HTTP/edge idle timeouts for
181
+ long-running verifiers. When False the behavior is unchanged (the
182
+ request blocks until the verifier finishes).
183
+ """
184
+ response = self.remote_with_response(
185
+ env, *args, async_=async_, poll_interval=poll_interval, **kwargs
186
+ )
171
187
 
172
188
  # Handle response
173
189
  if response.stdout:
@@ -239,9 +255,20 @@ Remote traceback:
239
255
  )
240
256
 
241
257
  def remote_with_response(
242
- self, env: "SyncEnv", *args, **kwargs
258
+ self,
259
+ env: "SyncEnv",
260
+ *args,
261
+ async_: bool = False,
262
+ poll_interval: float = 5.0,
263
+ **kwargs,
243
264
  ) -> "VerifiersExecuteResponse":
244
- """Remote execution of the verifier function that returns the full response model."""
265
+ """Remote execution of the verifier function that returns the full response model.
266
+
267
+ When ``async_`` is True the verifier is submitted asynchronously and
268
+ polled (every ``poll_interval`` seconds) until it reaches a terminal
269
+ state; the returned response is the completed/failed job result. When
270
+ False the request blocks until the verifier finishes (unchanged).
271
+ """
245
272
  args_array = list(args)
246
273
  args_array.append({"env": env.instance_id})
247
274
  args = tuple(args_array)
@@ -265,6 +292,8 @@ Remote traceback:
265
292
  kwargs=kwargs,
266
293
  needs_upload=True,
267
294
  verifier_runtime_version=self.verifier_runtime_version,
295
+ async_=async_,
296
+ poll_interval=poll_interval,
268
297
  )
269
298
 
270
299
  # logger.debug(f"Bundle {bundle_sha[:8]}... uploaded successfully")
@@ -283,6 +312,8 @@ Remote traceback:
283
312
  kwargs=kwargs,
284
313
  needs_upload=False,
285
314
  verifier_runtime_version=self.verifier_runtime_version,
315
+ async_=async_,
316
+ poll_interval=poll_interval,
286
317
  )
287
318
  return response
288
319
 
@@ -303,6 +334,8 @@ Remote traceback:
303
334
  kwargs=kwargs,
304
335
  needs_upload=True,
305
336
  verifier_runtime_version=self.verifier_runtime_version,
337
+ async_=async_,
338
+ poll_interval=poll_interval,
306
339
  )
307
340
  return response
308
341
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.127
3
+ Version: 0.2.129
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "fleet-python"
7
7
 
8
- version = "0.2.127"
8
+ version = "0.2.129"
9
9
  description = "Python SDK for Fleet environments"
10
10
  authors = [
11
11
  {name = "Fleet AI", email = "nic@fleet.so"},
File without changes
File without changes
File without changes