fleet-python 0.2.13__py3-none-any.whl → 0.2.15__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 +161 -0
- examples/dsl_example.py +50 -1
- examples/example_action_log.py +28 -0
- examples/example_mcp_anthropic.py +77 -0
- examples/example_mcp_openai.py +27 -0
- examples/example_task.py +199 -0
- examples/example_verifier.py +71 -0
- examples/query_builder_example.py +117 -0
- fleet/__init__.py +51 -40
- fleet/_async/base.py +14 -1
- fleet/_async/client.py +137 -19
- fleet/_async/env/client.py +4 -4
- fleet/_async/instance/__init__.py +1 -2
- fleet/_async/instance/client.py +3 -2
- fleet/_async/playwright.py +2 -2
- fleet/_async/resources/sqlite.py +654 -0
- fleet/_async/tasks.py +44 -0
- fleet/_async/verifiers/__init__.py +17 -0
- fleet/_async/verifiers/bundler.py +699 -0
- fleet/_async/verifiers/verifier.py +301 -0
- fleet/base.py +14 -1
- fleet/client.py +645 -12
- fleet/config.py +1 -1
- fleet/instance/__init__.py +1 -2
- fleet/instance/client.py +15 -5
- fleet/models.py +171 -4
- fleet/resources/browser.py +7 -8
- fleet/resources/mcp.py +60 -0
- fleet/resources/sqlite.py +654 -0
- fleet/tasks.py +44 -0
- fleet/types.py +18 -0
- fleet/verifiers/__init__.py +11 -5
- fleet/verifiers/bundler.py +699 -0
- fleet/verifiers/decorator.py +103 -0
- fleet/verifiers/verifier.py +301 -0
- {fleet_python-0.2.13.dist-info → fleet_python-0.2.15.dist-info}/METADATA +3 -42
- fleet_python-0.2.15.dist-info/RECORD +69 -0
- fleet_python-0.2.13.dist-info/RECORD +0 -52
- {fleet_python-0.2.13.dist-info → fleet_python-0.2.15.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.13.dist-info → fleet_python-0.2.15.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.13.dist-info → fleet_python-0.2.15.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import fleet as flt
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
load_dotenv()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def main():
|
|
9
|
+
# Create a new instance
|
|
10
|
+
print("Creating new Hubspot instance...")
|
|
11
|
+
env = await flt.env.make_async("hubspot:v1.2.7")
|
|
12
|
+
print(f"New Instance: {env.instance_id}")
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
# Reset the instance
|
|
16
|
+
response = await env.reset(seed=42)
|
|
17
|
+
print(f"Reset response: {response}")
|
|
18
|
+
|
|
19
|
+
# Get the database resource
|
|
20
|
+
db = env.db()
|
|
21
|
+
|
|
22
|
+
# Example 1: Query with the builder pattern
|
|
23
|
+
print("\n=== Query Builder Examples ===")
|
|
24
|
+
|
|
25
|
+
# Find all entries of type 'deal'
|
|
26
|
+
print("\n1. Finding all deals:")
|
|
27
|
+
deals = await db.table("entries").eq("type", "deal").all()
|
|
28
|
+
print(f"Found {len(deals)} deals")
|
|
29
|
+
|
|
30
|
+
# Count entries
|
|
31
|
+
print("\n2. Counting entries:")
|
|
32
|
+
entry_count = await db.table("entries").count()
|
|
33
|
+
print(f"Total entries: {entry_count}")
|
|
34
|
+
|
|
35
|
+
# Find a specific entry
|
|
36
|
+
print("\n3. Finding specific entry:")
|
|
37
|
+
entry = await db.table("entries").eq("id", 1).first()
|
|
38
|
+
if entry:
|
|
39
|
+
print(f"Found entry: {entry['name']} (type: {entry['type']})")
|
|
40
|
+
|
|
41
|
+
# Complex query with multiple conditions
|
|
42
|
+
print("\n4. Complex query with conditions:")
|
|
43
|
+
recent_deals = await (
|
|
44
|
+
db.table("entries")
|
|
45
|
+
.eq("type", "deal")
|
|
46
|
+
.not_null("name")
|
|
47
|
+
.select("id", "name", "type", "createdDate")
|
|
48
|
+
.sort("createdDate", desc=True)
|
|
49
|
+
.limit(5)
|
|
50
|
+
.all()
|
|
51
|
+
)
|
|
52
|
+
print(f"Recent deals: {len(recent_deals)}")
|
|
53
|
+
for deal in recent_deals:
|
|
54
|
+
print(f" - {deal['name']} (id: {deal['id']})")
|
|
55
|
+
|
|
56
|
+
# Using assertions
|
|
57
|
+
print("\n5. Using assertions:")
|
|
58
|
+
try:
|
|
59
|
+
# This should succeed if there are entries
|
|
60
|
+
await db.table("entries").assert_exists()
|
|
61
|
+
print("✓ Entries table has records")
|
|
62
|
+
except AssertionError as e:
|
|
63
|
+
print(f"✗ Assertion failed: {e}")
|
|
64
|
+
|
|
65
|
+
# Check for non-existent record
|
|
66
|
+
try:
|
|
67
|
+
await db.table("entries").eq("id", 999999).assert_none()
|
|
68
|
+
print("✓ No entry with id 999999")
|
|
69
|
+
except AssertionError as e:
|
|
70
|
+
print(f"✗ Assertion failed: {e}")
|
|
71
|
+
|
|
72
|
+
# Insert a new entry and verify with query builder
|
|
73
|
+
print("\n6. Insert and verify with query builder:")
|
|
74
|
+
insert_query = """
|
|
75
|
+
INSERT INTO entries (id, name, type, owner_id, createdDate, lastModifiedDate, createdAt, updatedAt, properties)
|
|
76
|
+
VALUES (
|
|
77
|
+
99999,
|
|
78
|
+
'Test Deal via Query Builder',
|
|
79
|
+
'deal',
|
|
80
|
+
1,
|
|
81
|
+
datetime('now'),
|
|
82
|
+
datetime('now'),
|
|
83
|
+
datetime('now'),
|
|
84
|
+
datetime('now'),
|
|
85
|
+
'{}'
|
|
86
|
+
)
|
|
87
|
+
"""
|
|
88
|
+
await db.exec(insert_query)
|
|
89
|
+
|
|
90
|
+
# Verify insertion with query builder
|
|
91
|
+
new_deal = await db.table("entries").eq("id", 99999).first()
|
|
92
|
+
if new_deal:
|
|
93
|
+
print(f"✓ Successfully inserted: {new_deal['name']}")
|
|
94
|
+
|
|
95
|
+
# Assert specific field value
|
|
96
|
+
await db.table("entries").eq("id", 99999).assert_eq("name", "Test Deal via Query Builder")
|
|
97
|
+
print("✓ Name assertion passed")
|
|
98
|
+
|
|
99
|
+
# Using IN clause
|
|
100
|
+
print("\n7. Using IN clause:")
|
|
101
|
+
specific_entries = await db.table("entries").in_("id", [1, 2, 3]).all()
|
|
102
|
+
print(f"Found {len(specific_entries)} entries with ids in [1, 2, 3]")
|
|
103
|
+
|
|
104
|
+
# Pattern matching with LIKE
|
|
105
|
+
print("\n8. Pattern matching:")
|
|
106
|
+
test_entries = await db.table("entries").ilike("name", "%test%").all()
|
|
107
|
+
print(f"Found {len(test_entries)} entries with 'test' in name")
|
|
108
|
+
|
|
109
|
+
finally:
|
|
110
|
+
# Delete the instance
|
|
111
|
+
print("\n\nDeleting instance...")
|
|
112
|
+
await env.close()
|
|
113
|
+
print("Instance deleted.")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
asyncio.run(main())
|
fleet/__init__.py
CHANGED
|
@@ -23,60 +23,71 @@ from .exceptions import (
|
|
|
23
23
|
FleetConfigurationError,
|
|
24
24
|
)
|
|
25
25
|
from .client import Fleet, Environment
|
|
26
|
-
from ._async.client import AsyncFleet,
|
|
27
|
-
from .models import
|
|
28
|
-
from .instance import
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
ResetResponse,
|
|
32
|
-
CDPDescribeResponse,
|
|
33
|
-
ChromeStartRequest,
|
|
34
|
-
ChromeStartResponse,
|
|
35
|
-
ChromeStatusResponse,
|
|
36
|
-
)
|
|
37
|
-
from ._async.instance import AsyncInstanceClient
|
|
26
|
+
from ._async.client import AsyncFleet, AsyncEnv
|
|
27
|
+
from .models import InstanceRecord
|
|
28
|
+
from .instance.models import Resource, ResetResponse
|
|
29
|
+
|
|
30
|
+
# Import sync verifiers with explicit naming
|
|
38
31
|
from .verifiers import (
|
|
32
|
+
verifier as verifier_sync,
|
|
33
|
+
SyncVerifierFunction,
|
|
39
34
|
DatabaseSnapshot,
|
|
40
35
|
IgnoreConfig,
|
|
41
36
|
SnapshotDiff,
|
|
42
37
|
TASK_SUCCESSFUL_SCORE,
|
|
43
38
|
)
|
|
39
|
+
|
|
40
|
+
# Import Playwright wrapper
|
|
41
|
+
from .playwright import FleetPlaywrightWrapper
|
|
42
|
+
|
|
43
|
+
# Import async verifiers (default verifier is async for modern usage)
|
|
44
|
+
from ._async.verifiers import (
|
|
45
|
+
verifier,
|
|
46
|
+
AsyncVerifierFunction,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Import async tasks (default tasks are async for modern usage)
|
|
50
|
+
from ._async.tasks import Task
|
|
51
|
+
|
|
52
|
+
# Import shared types
|
|
53
|
+
from .types import VerifierFunction
|
|
54
|
+
|
|
55
|
+
# Create a module-level env attribute for convenient access
|
|
44
56
|
from . import env
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
try:
|
|
48
|
-
from .playwright import FleetPlaywrightWrapper
|
|
49
|
-
_PLAYWRIGHT_AVAILABLE = True
|
|
50
|
-
except ImportError:
|
|
51
|
-
FleetPlaywrightWrapper = None
|
|
52
|
-
_PLAYWRIGHT_AVAILABLE = False
|
|
58
|
+
__version__ = "0.1.0"
|
|
53
59
|
|
|
54
|
-
__version__ = "0.1.1"
|
|
55
60
|
__all__ = [
|
|
56
|
-
|
|
57
|
-
"FleetError",
|
|
58
|
-
"FleetAPIError",
|
|
59
|
-
"FleetTimeoutError",
|
|
60
|
-
"FleetConfigurationError",
|
|
61
|
+
# Core classes
|
|
61
62
|
"Fleet",
|
|
62
63
|
"Environment",
|
|
63
64
|
"AsyncFleet",
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"ResetRequest",
|
|
65
|
+
"AsyncEnv",
|
|
66
|
+
# Models
|
|
67
|
+
"InstanceRecord",
|
|
68
|
+
"Resource",
|
|
69
69
|
"ResetResponse",
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
|
|
70
|
+
# Task models
|
|
71
|
+
"Task",
|
|
72
|
+
"VerifierFunction",
|
|
73
|
+
# Exceptions
|
|
74
|
+
"FleetError",
|
|
75
|
+
"FleetAPIError",
|
|
76
|
+
"FleetTimeoutError",
|
|
77
|
+
"FleetConfigurationError",
|
|
78
|
+
# Playwright wrapper
|
|
79
|
+
"FleetPlaywrightWrapper",
|
|
80
|
+
# Verifiers (async is default)
|
|
81
|
+
"verifier",
|
|
82
|
+
"verifier_sync",
|
|
83
|
+
"AsyncVerifierFunction",
|
|
84
|
+
"SyncVerifierFunction",
|
|
74
85
|
"DatabaseSnapshot",
|
|
75
|
-
"IgnoreConfig",
|
|
86
|
+
"IgnoreConfig",
|
|
76
87
|
"SnapshotDiff",
|
|
77
88
|
"TASK_SUCCESSFUL_SCORE",
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
# Environment module
|
|
90
|
+
"env",
|
|
91
|
+
# Version
|
|
92
|
+
"__version__",
|
|
93
|
+
]
|
fleet/_async/base.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import httpx
|
|
2
2
|
from typing import Dict, Any, Optional
|
|
3
3
|
import json
|
|
4
|
+
import logging
|
|
4
5
|
|
|
5
6
|
from ..models import InstanceResponse
|
|
7
|
+
from ..config import GLOBAL_BASE_URL
|
|
6
8
|
from .exceptions import (
|
|
7
9
|
FleetAPIError,
|
|
8
10
|
FleetAuthenticationError,
|
|
@@ -18,6 +20,8 @@ from .exceptions import (
|
|
|
18
20
|
FleetPermissionError,
|
|
19
21
|
)
|
|
20
22
|
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
21
25
|
|
|
22
26
|
class EnvironmentBase(InstanceResponse):
|
|
23
27
|
@property
|
|
@@ -31,7 +35,7 @@ class BaseWrapper:
|
|
|
31
35
|
raise ValueError("api_key is required")
|
|
32
36
|
self.api_key = api_key
|
|
33
37
|
if base_url is None:
|
|
34
|
-
base_url =
|
|
38
|
+
base_url = GLOBAL_BASE_URL
|
|
35
39
|
self.base_url = base_url
|
|
36
40
|
|
|
37
41
|
def get_headers(self) -> Dict[str, str]:
|
|
@@ -40,6 +44,10 @@ class BaseWrapper:
|
|
|
40
44
|
"X-Fleet-SDK-Version": "1.0.0",
|
|
41
45
|
}
|
|
42
46
|
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
47
|
+
# Debug log
|
|
48
|
+
import logging
|
|
49
|
+
logger = logging.getLogger(__name__)
|
|
50
|
+
logger.debug(f"Headers being sent: {headers}")
|
|
43
51
|
return headers
|
|
44
52
|
|
|
45
53
|
|
|
@@ -81,6 +89,11 @@ class AsyncWrapper(BaseWrapper):
|
|
|
81
89
|
def _handle_error_response(self, response: httpx.Response) -> None:
|
|
82
90
|
"""Handle HTTP error responses and convert to appropriate Fleet exceptions."""
|
|
83
91
|
status_code = response.status_code
|
|
92
|
+
|
|
93
|
+
# Debug log 500 errors
|
|
94
|
+
if status_code == 500:
|
|
95
|
+
logger.error(f"Got 500 error from {response.url}")
|
|
96
|
+
logger.error(f"Response text: {response.text}")
|
|
84
97
|
|
|
85
98
|
# Try to parse error response as JSON
|
|
86
99
|
try:
|
fleet/_async/client.py
CHANGED
|
@@ -14,23 +14,32 @@
|
|
|
14
14
|
|
|
15
15
|
"""Fleet API Client for making HTTP requests to Fleet services."""
|
|
16
16
|
|
|
17
|
-
import
|
|
17
|
+
import base64
|
|
18
|
+
import cloudpickle
|
|
18
19
|
import httpx
|
|
19
20
|
import logging
|
|
20
|
-
|
|
21
|
+
import os
|
|
22
|
+
from typing import List, Optional
|
|
21
23
|
|
|
22
24
|
from .base import EnvironmentBase, AsyncWrapper
|
|
23
|
-
from ..models import
|
|
25
|
+
from ..models import (
|
|
26
|
+
InstanceRequest,
|
|
27
|
+
InstanceRecord,
|
|
28
|
+
Environment as EnvironmentModel,
|
|
29
|
+
VerifiersCheckResponse,
|
|
30
|
+
VerificationResponse,
|
|
31
|
+
VerifiersExecuteResponse,
|
|
32
|
+
)
|
|
24
33
|
|
|
25
34
|
from .instance import (
|
|
26
35
|
AsyncInstanceClient,
|
|
27
36
|
ResetRequest,
|
|
28
37
|
ResetResponse,
|
|
29
|
-
ValidatorType,
|
|
30
38
|
ExecuteFunctionResponse,
|
|
31
39
|
)
|
|
32
40
|
from ..config import DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT, REGION_BASE_URL
|
|
33
41
|
from .instance.base import default_httpx_client
|
|
42
|
+
from .instance.client import ValidatorType
|
|
34
43
|
from .resources.base import Resource
|
|
35
44
|
from .resources.sqlite import AsyncSQLiteResource
|
|
36
45
|
from .resources.browser import AsyncBrowserResource
|
|
@@ -38,13 +47,8 @@ from .resources.browser import AsyncBrowserResource
|
|
|
38
47
|
logger = logging.getLogger(__name__)
|
|
39
48
|
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return InstanceRecord(**response.json())
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class AsyncEnvironment(EnvironmentBase):
|
|
47
|
-
def __init__(self, client: AsyncWrapper, **kwargs):
|
|
50
|
+
class AsyncEnv(EnvironmentBase):
|
|
51
|
+
def __init__(self, client: Optional[AsyncWrapper], **kwargs):
|
|
48
52
|
super().__init__(**kwargs)
|
|
49
53
|
self._client = client
|
|
50
54
|
self._instance: Optional[AsyncInstanceClient] = None
|
|
@@ -53,10 +57,16 @@ class AsyncEnvironment(EnvironmentBase):
|
|
|
53
57
|
def instance(self) -> AsyncInstanceClient:
|
|
54
58
|
if self._instance is None:
|
|
55
59
|
self._instance = AsyncInstanceClient(
|
|
56
|
-
self.manager_url, self._client.httpx_client
|
|
60
|
+
self.manager_url, self._client.httpx_client if self._client else None
|
|
57
61
|
)
|
|
58
62
|
return self._instance
|
|
59
63
|
|
|
64
|
+
@property
|
|
65
|
+
def _load_client(self) -> AsyncWrapper:
|
|
66
|
+
if self._client is None:
|
|
67
|
+
raise ValueError("Client not initialized")
|
|
68
|
+
return self._client
|
|
69
|
+
|
|
60
70
|
async def reset(
|
|
61
71
|
self, seed: Optional[int] = None, timestamp: Optional[int] = None
|
|
62
72
|
) -> ResetResponse:
|
|
@@ -75,7 +85,7 @@ class AsyncEnvironment(EnvironmentBase):
|
|
|
75
85
|
return await self.instance.resources()
|
|
76
86
|
|
|
77
87
|
async def close(self) -> InstanceRecord:
|
|
78
|
-
return await _delete_instance(self.
|
|
88
|
+
return await _delete_instance(self._load_client, self.instance_id)
|
|
79
89
|
|
|
80
90
|
async def verify(self, validator: ValidatorType) -> ExecuteFunctionResponse:
|
|
81
91
|
return await self.instance.verify(validator)
|
|
@@ -85,6 +95,41 @@ class AsyncEnvironment(EnvironmentBase):
|
|
|
85
95
|
) -> ExecuteFunctionResponse:
|
|
86
96
|
return await self.instance.verify_raw(function_code, function_name)
|
|
87
97
|
|
|
98
|
+
async def check_bundle_exists(self, bundle_hash: str) -> VerifiersCheckResponse:
|
|
99
|
+
return await _check_bundle_exists(self._load_client, bundle_hash)
|
|
100
|
+
|
|
101
|
+
async def execute_verifier_remote(
|
|
102
|
+
self,
|
|
103
|
+
bundle_data: bytes,
|
|
104
|
+
bundle_sha: str,
|
|
105
|
+
key: str,
|
|
106
|
+
function_name: str,
|
|
107
|
+
args: tuple,
|
|
108
|
+
kwargs: dict,
|
|
109
|
+
timeout: Optional[int] = 30,
|
|
110
|
+
needs_upload: bool = True,
|
|
111
|
+
) -> VerifiersExecuteResponse:
|
|
112
|
+
return await _execute_verifier_remote(
|
|
113
|
+
self._load_client,
|
|
114
|
+
bundle_data,
|
|
115
|
+
bundle_sha,
|
|
116
|
+
key,
|
|
117
|
+
function_name,
|
|
118
|
+
args,
|
|
119
|
+
kwargs,
|
|
120
|
+
timeout,
|
|
121
|
+
needs_upload
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def __getstate__(self):
|
|
125
|
+
state = self.__dict__.copy()
|
|
126
|
+
state.pop("_client", None)
|
|
127
|
+
state.pop("_instance", None)
|
|
128
|
+
return state
|
|
129
|
+
|
|
130
|
+
def __setstate__(self, state):
|
|
131
|
+
self.__dict__.update(state)
|
|
132
|
+
|
|
88
133
|
|
|
89
134
|
class AsyncFleet:
|
|
90
135
|
def __init__(
|
|
@@ -116,7 +161,7 @@ class AsyncFleet:
|
|
|
116
161
|
|
|
117
162
|
async def make(
|
|
118
163
|
self, env_key: str, region: Optional[str] = None
|
|
119
|
-
) ->
|
|
164
|
+
) -> AsyncEnv:
|
|
120
165
|
if ":" in env_key:
|
|
121
166
|
env_key_part, version = env_key.split(":", 1)
|
|
122
167
|
if not version.startswith("v"):
|
|
@@ -133,13 +178,13 @@ class AsyncFleet:
|
|
|
133
178
|
json=request.model_dump(),
|
|
134
179
|
base_url=region_base_url,
|
|
135
180
|
)
|
|
136
|
-
instance =
|
|
181
|
+
instance = AsyncEnv(client=self.client, **response.json())
|
|
137
182
|
await instance.instance.load()
|
|
138
183
|
return instance
|
|
139
184
|
|
|
140
185
|
async def instances(
|
|
141
186
|
self, status: Optional[str] = None, region: Optional[str] = None
|
|
142
|
-
) -> List[
|
|
187
|
+
) -> List[AsyncEnv]:
|
|
143
188
|
params = {}
|
|
144
189
|
if status:
|
|
145
190
|
params["status"] = status
|
|
@@ -148,15 +193,88 @@ class AsyncFleet:
|
|
|
148
193
|
|
|
149
194
|
response = await self.client.request("GET", "/v1/env/instances", params=params)
|
|
150
195
|
return [
|
|
151
|
-
|
|
196
|
+
AsyncEnv(client=self.client, **instance_data)
|
|
152
197
|
for instance_data in response.json()
|
|
153
198
|
]
|
|
154
199
|
|
|
155
|
-
async def instance(self, instance_id: str) ->
|
|
200
|
+
async def instance(self, instance_id: str) -> AsyncEnv:
|
|
156
201
|
response = await self.client.request("GET", f"/v1/env/instances/{instance_id}")
|
|
157
|
-
instance =
|
|
202
|
+
instance = AsyncEnv(client=self.client, **response.json())
|
|
158
203
|
await instance.instance.load()
|
|
159
204
|
return instance
|
|
160
205
|
|
|
206
|
+
async def check_bundle_exists(self, bundle_hash: str) -> VerifiersCheckResponse:
|
|
207
|
+
return await _check_bundle_exists(self.client, bundle_hash)
|
|
208
|
+
|
|
209
|
+
async def execute_verifier_remote(
|
|
210
|
+
self, bundle_data: bytes, args: tuple, kwargs: dict, timeout: Optional[int] = 30
|
|
211
|
+
) -> VerifiersExecuteResponse:
|
|
212
|
+
return await _execute_verifier_remote(
|
|
213
|
+
self.client, bundle_data, args, kwargs, timeout
|
|
214
|
+
)
|
|
215
|
+
|
|
161
216
|
async def delete(self, instance_id: str) -> InstanceRecord:
|
|
162
217
|
return await _delete_instance(self.client, instance_id)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# Shared
|
|
221
|
+
async def _delete_instance(client: AsyncWrapper, instance_id: str) -> InstanceRecord:
|
|
222
|
+
response = await client.request("DELETE", f"/v1/env/instances/{instance_id}")
|
|
223
|
+
return InstanceRecord(**response.json())
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
async def _check_bundle_exists(
|
|
227
|
+
client: AsyncWrapper, bundle_hash: str
|
|
228
|
+
) -> VerifiersCheckResponse:
|
|
229
|
+
response = await client.request("GET", f"/v1/verifiers/check?sha256={bundle_hash}")
|
|
230
|
+
return VerifiersCheckResponse(**response.json())
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
async def _execute_verifier_remote(
|
|
234
|
+
client: AsyncWrapper,
|
|
235
|
+
bundle_data: bytes,
|
|
236
|
+
bundle_sha: str,
|
|
237
|
+
key: str,
|
|
238
|
+
function_name: str,
|
|
239
|
+
args: tuple,
|
|
240
|
+
kwargs: dict,
|
|
241
|
+
timeout: Optional[int] = 30,
|
|
242
|
+
needs_upload: bool = True,
|
|
243
|
+
) -> VerificationResponse:
|
|
244
|
+
# Pickle args and kwargs together
|
|
245
|
+
# The first arg should be None as a placeholder for env
|
|
246
|
+
args_with_none = (None,) + args
|
|
247
|
+
args_kwargs_pickled = cloudpickle.dumps({"args": args_with_none, "kwargs": kwargs})
|
|
248
|
+
args_kwargs_b64 = base64.b64encode(args_kwargs_pickled).decode("utf-8")
|
|
249
|
+
|
|
250
|
+
# Build request data
|
|
251
|
+
request_data = {
|
|
252
|
+
"key": key,
|
|
253
|
+
"sha256": bundle_sha,
|
|
254
|
+
"args": args_kwargs_b64,
|
|
255
|
+
"function_name": function_name,
|
|
256
|
+
"timeout": timeout,
|
|
257
|
+
"region": "us-west-1", # TODO: make configurable
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# Add bundle data only if upload is needed
|
|
261
|
+
if needs_upload:
|
|
262
|
+
bundle_b64 = base64.b64encode(bundle_data).decode("utf-8")
|
|
263
|
+
request_data["bundle"] = bundle_b64
|
|
264
|
+
|
|
265
|
+
# Debug logging
|
|
266
|
+
logger.debug(f"Sending verifier execute request: key={key}, sha256={bundle_sha[:8]}..., function_name={function_name}")
|
|
267
|
+
logger.debug(f"Request has bundle: {needs_upload}")
|
|
268
|
+
logger.debug(f"Using client with base_url: {client.base_url}")
|
|
269
|
+
logger.debug(f"Request data keys: {list(request_data.keys())}")
|
|
270
|
+
logger.debug(f"Bundle size: {len(request_data.get('bundle', ''))} chars" if 'bundle' in request_data else "No bundle")
|
|
271
|
+
|
|
272
|
+
# Note: This should be called on the instance URL, not the orchestrator
|
|
273
|
+
# The instance has manager URLs for verifier execution
|
|
274
|
+
response = await client.request("POST", "/v1/verifiers/execute", json=request_data)
|
|
275
|
+
|
|
276
|
+
# Debug the response
|
|
277
|
+
response_json = response.json()
|
|
278
|
+
logger.debug(f"Verifier execute response: {response_json}")
|
|
279
|
+
|
|
280
|
+
return VerifiersExecuteResponse(**response_json)
|
fleet/_async/env/client.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from ..client import AsyncFleet,
|
|
1
|
+
from ..client import AsyncFleet, AsyncEnv
|
|
2
2
|
from ...models import Environment as EnvironmentModel
|
|
3
3
|
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
async def make_async(env_key: str, region: Optional[str] = None) ->
|
|
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
|
|
|
@@ -17,9 +17,9 @@ async def list_regions_async() -> List[str]:
|
|
|
17
17
|
|
|
18
18
|
async def list_instances_async(
|
|
19
19
|
status: Optional[str] = None, region: Optional[str] = None
|
|
20
|
-
) -> List[
|
|
20
|
+
) -> List[AsyncEnv]:
|
|
21
21
|
return await AsyncFleet().instances(status=status, region=region)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
async def get_async(instance_id: str) ->
|
|
24
|
+
async def get_async(instance_id: str) -> AsyncEnv:
|
|
25
25
|
return await AsyncFleet().instance(instance_id)
|
|
@@ -12,7 +12,6 @@ from ...instance.models import (
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
15
|
-
"ValidatorType",
|
|
16
15
|
"AsyncInstanceClient",
|
|
17
16
|
"ResetRequest",
|
|
18
17
|
"ResetResponse",
|
|
@@ -20,5 +19,5 @@ __all__ = [
|
|
|
20
19
|
"ChromeStartRequest",
|
|
21
20
|
"ChromeStartResponse",
|
|
22
21
|
"ChromeStatusResponse",
|
|
23
|
-
"ExecuteFunctionResponse"
|
|
22
|
+
"ExecuteFunctionResponse",
|
|
24
23
|
]
|
fleet/_async/instance/client.py
CHANGED
|
@@ -51,7 +51,8 @@ class AsyncInstanceClient:
|
|
|
51
51
|
self.base_url = url
|
|
52
52
|
self.client = AsyncWrapper(
|
|
53
53
|
url=self.base_url,
|
|
54
|
-
httpx_client=httpx_client
|
|
54
|
+
httpx_client=httpx_client
|
|
55
|
+
or default_httpx_client(DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT),
|
|
55
56
|
)
|
|
56
57
|
self._resources: Optional[List[ResourceModel]] = None
|
|
57
58
|
self._resources_state: Dict[str, Dict[str, Resource]] = {
|
|
@@ -136,7 +137,7 @@ class AsyncInstanceClient:
|
|
|
136
137
|
|
|
137
138
|
self._resources = [ResourceModel(**resource) for resource in resources_list]
|
|
138
139
|
for resource in self._resources:
|
|
139
|
-
if resource.type not in self._resources_state:
|
|
140
|
+
if resource.type.value not in self._resources_state:
|
|
140
141
|
self._resources_state[resource.type.value] = {}
|
|
141
142
|
self._resources_state[resource.type.value][resource.name] = (
|
|
142
143
|
RESOURCE_TYPES[resource.type](resource, self.client)
|
fleet/_async/playwright.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
from typing import List, Dict, Any
|
|
3
3
|
from playwright.async_api import async_playwright, Browser, Page
|
|
4
|
-
from .client import
|
|
4
|
+
from .client import AsyncEnv
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# Key mapping for computer use actions
|
|
@@ -65,7 +65,7 @@ class AsyncFleetPlaywrightWrapper:
|
|
|
65
65
|
|
|
66
66
|
def __init__(
|
|
67
67
|
self,
|
|
68
|
-
env:
|
|
68
|
+
env: AsyncEnv,
|
|
69
69
|
display_width: int = 1920,
|
|
70
70
|
display_height: int = 1080,
|
|
71
71
|
):
|