fleet-python 0.2.42__py3-none-any.whl → 0.2.44__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.

@@ -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 | None = None):
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: dict[str, list[dict[str, Any]]] = {}
33
- self._schemas: dict[str, list[str]] = {}
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) -> list[str]:
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 | None = None,
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: list[str] = ["*"]
99
- self._conditions: list[tuple[str, str, Any]] = []
100
- self._limit: int | None = None
101
- self._order_by: str | None = None
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) -> list[dict[str, Any]]:
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) -> dict[str, Any] | None:
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) -> list[dict[str, Any]]:
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 | None = None,
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: dict[str, Any] | None = None
193
+ self._cached: Optional[Dict[str, Any]] = None
194
194
 
195
- async def _get_primary_key_columns(self, table: str) -> list[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: dict[str, dict[str, Any]] = {}
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: list[str]) -> Any:
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: list[dict[str, Any]]):
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 | None, after_value: Any
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: list[str] = ["*"]
444
- self._conditions: list[tuple[str, str, Any]] = []
445
- self._joins: list[tuple[str, dict[str, str]]] = []
446
- self._limit: int | None = None
447
- self._order_by: str | None = None
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: list[Any]) -> "AsyncQueryBuilder":
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: list[Any]) -> "AsyncQueryBuilder":
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: dict[str, str]) -> "AsyncQueryBuilder":
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) -> tuple[str, list[Any]]:
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: list[Any] = []
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) -> dict[str, Any] | None:
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) -> list[dict[str, Any]]:
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 | None = None) -> AsyncDatabaseSnapshot:
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 | None = None,
686
+ ignore_config: Optional[IgnoreConfig] = None,
687
687
  ) -> AsyncSnapshotDiff:
688
688
  """Compare this database with another AsyncSQLiteResource.
689
689
 
fleet/_async/tasks.py CHANGED
@@ -2,10 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import re
6
5
  from datetime import datetime
7
6
  from typing import Any, Dict, Optional, List
8
- from uuid import UUID
9
7
 
10
8
  from pydantic import BaseModel, Field, validator
11
9
 
@@ -46,7 +44,7 @@ class Task(BaseModel):
46
44
  @property
47
45
  def env_key(self) -> str:
48
46
  """Get the environment key combining env_id and version."""
49
- if self.version:
47
+ if self.version and self.version != "None" and ":" not in self.env_id:
50
48
  return f"{self.env_id}:{self.version}"
51
49
  return self.env_id
52
50
 
@@ -75,7 +73,7 @@ class Task(BaseModel):
75
73
  if inspect.iscoroutine(result):
76
74
  # Check if we're already in an event loop
77
75
  try:
78
- loop = asyncio.get_running_loop()
76
+ asyncio.get_running_loop()
79
77
  # We're in an async context, can't use asyncio.run()
80
78
  raise RuntimeError(
81
79
  "Cannot run async verifier in sync mode while event loop is running. "
@@ -121,67 +119,78 @@ class Task(BaseModel):
121
119
 
122
120
 
123
121
  def verifier_from_string(
124
- verifier_func: str,
125
- verifier_id: str,
126
- verifier_key: str,
127
- sha256: str = ""
128
- ) -> 'VerifierFunction':
122
+ verifier_func: str, verifier_id: str, verifier_key: str, sha256: str = ""
123
+ ) -> "VerifierFunction":
129
124
  """Create a verifier function from string code.
130
-
125
+
131
126
  Args:
132
127
  verifier_func: The verifier function code as a string
133
128
  verifier_id: Unique identifier for the verifier
134
129
  verifier_key: Key/name for the verifier
135
130
  sha256: SHA256 hash of the verifier code
136
-
131
+
137
132
  Returns:
138
133
  VerifierFunction instance that can be used to verify tasks
139
134
  """
140
135
  try:
141
136
  import inspect
142
- from .verifiers import verifier, AsyncVerifierFunction
137
+ from .verifiers.verifier import AsyncVerifierFunction
143
138
  from fleet.verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
144
139
  from fleet.verifiers.db import IgnoreConfig
145
-
140
+
146
141
  # Create a local namespace for executing the code
147
142
  local_namespace = {
148
- 'TASK_SUCCESSFUL_SCORE': TASK_SUCCESSFUL_SCORE,
149
- 'TASK_FAILED_SCORE': TASK_FAILED_SCORE,
150
- 'IgnoreConfig': IgnoreConfig,
151
- 'Environment': object # Add Environment type if needed
143
+ "TASK_SUCCESSFUL_SCORE": TASK_SUCCESSFUL_SCORE,
144
+ "TASK_FAILED_SCORE": TASK_FAILED_SCORE,
145
+ "IgnoreConfig": IgnoreConfig,
146
+ "Environment": object, # Add Environment type if needed
152
147
  }
153
-
148
+
154
149
  # Execute the verifier code in the namespace
155
150
  exec(verifier_func, globals(), local_namespace)
156
-
151
+
157
152
  # Find the function that was defined
158
153
  func_obj = None
159
154
  for name, obj in local_namespace.items():
160
155
  if inspect.isfunction(obj):
161
156
  func_obj = obj
162
157
  break
163
-
158
+
164
159
  if func_obj is None:
165
160
  raise ValueError("No function found in verifier code")
166
-
167
- # Create an AsyncVerifierFunction instance
168
- verifier_instance = AsyncVerifierFunction(func_obj, verifier_key, verifier_id)
169
-
170
- # Store additional metadata
171
- verifier_instance._verifier_code = verifier_func
172
- verifier_instance._sha256 = sha256
173
-
161
+
162
+ # Create an AsyncVerifierFunction instance with raw code
163
+ verifier_instance = AsyncVerifierFunction(
164
+ func_obj,
165
+ verifier_key,
166
+ verifier_id=verifier_id,
167
+ sha256=sha256,
168
+ raw_code=verifier_func,
169
+ )
170
+
174
171
  return verifier_instance
175
-
172
+
176
173
  except Exception as e:
177
174
  raise ValueError(f"Failed to create verifier from string: {e}")
178
175
 
179
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
+
180
189
  async def load_tasks(
181
190
  env_key: Optional[str] = None,
182
191
  keys: Optional[List[str]] = None,
183
192
  version: Optional[str] = None,
184
- team_id: Optional[str] = None
193
+ team_id: Optional[str] = None,
185
194
  ) -> List[Task]:
186
195
  """Convenience function to load tasks with optional filtering.
187
196
 
@@ -201,17 +210,12 @@ async def load_tasks(
201
210
 
202
211
  client = get_client()
203
212
  return await client.load_tasks(
204
- env_key=env_key,
205
- keys=keys,
206
- version=version,
207
- team_id=team_id
213
+ env_key=env_key, keys=keys, version=version, team_id=team_id
208
214
  )
209
215
 
210
216
 
211
217
  async def update_task(
212
- task_key: str,
213
- prompt: Optional[str] = None,
214
- verifier_code: Optional[str] = None
218
+ task_key: str, prompt: Optional[str] = None, verifier_code: Optional[str] = None
215
219
  ):
216
220
  """Convenience function to update an existing task.
217
221
 
@@ -228,11 +232,8 @@ async def update_task(
228
232
  response = await fleet.update_task("my-task", verifier_code="def verify(env): return True")
229
233
  """
230
234
  from .global_client import get_client
231
- from ..models import TaskResponse
232
235
 
233
236
  client = get_client()
234
237
  return await client.update_task(
235
- task_key=task_key,
236
- prompt=prompt,
237
- verifier_code=verifier_code
238
+ task_key=task_key, prompt=prompt, verifier_code=verifier_code
238
239
  )
@@ -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) -> tuple[bytes, str]:
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) -> tuple[str, bool]:
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