fleet-python 0.2.9__tar.gz → 0.2.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fleet-python might be problematic. Click here for more details.
- {fleet_python-0.2.9 → fleet_python-0.2.11}/PKG-INFO +1 -1
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/example.py +3 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/example_sync.py +1 -1
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/__init__.py +2 -0
- fleet_python-0.2.11/fleet/_async/base.py +132 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/client.py +6 -2
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/env/client.py +4 -0
- {fleet_python-0.2.9/fleet → fleet_python-0.2.11/fleet/_async}/exceptions.py +9 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/instance/client.py +1 -1
- fleet_python-0.2.11/fleet/base.py +132 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/client.py +2 -2
- fleet_python-0.2.11/fleet/env/__init__.py +25 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/env/client.py +4 -0
- {fleet_python-0.2.9/fleet/_async → fleet_python-0.2.11/fleet}/exceptions.py +9 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/instance/client.py +1 -1
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet_python.egg-info/PKG-INFO +1 -1
- {fleet_python-0.2.9 → fleet_python-0.2.11}/pyproject.toml +2 -1
- fleet_python-0.2.9/fleet/_async/base.py +0 -51
- fleet_python-0.2.9/fleet/base.py +0 -51
- fleet_python-0.2.9/fleet/env/__init__.py +0 -17
- {fleet_python-0.2.9 → fleet_python-0.2.11}/LICENSE +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/README.md +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/dsl_example.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/example_client.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/gemini_example.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/json_tasks_example.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/nova_act_example.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/openai_example.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/openai_simple_example.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/examples/quickstart.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/__init__.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/env/__init__.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/instance/__init__.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/instance/base.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/instance/models.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/models.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/playwright.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/resources/__init__.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/resources/base.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/resources/browser.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/_async/resources/sqlite.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/instance/__init__.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/instance/base.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/instance/models.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/models.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/playwright.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/resources/__init__.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/resources/base.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/resources/browser.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/resources/sqlite.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/verifiers/__init__.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/verifiers/code.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/verifiers/db.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet/verifiers/sql_differ.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet_python.egg-info/SOURCES.txt +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet_python.egg-info/dependency_links.txt +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet_python.egg-info/requires.txt +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/fleet_python.egg-info/top_level.txt +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/scripts/fix_sync_imports.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/scripts/unasync.py +0 -0
- {fleet_python-0.2.9 → fleet_python-0.2.11}/setup.cfg +0 -0
|
@@ -11,7 +11,7 @@ def main():
|
|
|
11
11
|
environments = flt.env.list_envs()
|
|
12
12
|
print("Environments:", len(environments))
|
|
13
13
|
|
|
14
|
-
instances = flt.env.list_instances()
|
|
14
|
+
instances = flt.env.list_instances(status="running")
|
|
15
15
|
print("Instances:", len(instances))
|
|
16
16
|
|
|
17
17
|
# Create a new instance
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from typing import Dict, Any, Optional
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from .models import InstanceResponse
|
|
6
|
+
from .exceptions import (
|
|
7
|
+
FleetAPIError,
|
|
8
|
+
FleetAuthenticationError,
|
|
9
|
+
FleetRateLimitError,
|
|
10
|
+
FleetInstanceLimitError,
|
|
11
|
+
FleetTimeoutError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EnvironmentBase(InstanceResponse):
|
|
16
|
+
@property
|
|
17
|
+
def manager_url(self) -> str:
|
|
18
|
+
return f"{self.urls.manager.api}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseWrapper:
|
|
22
|
+
def __init__(self, *, api_key: Optional[str], base_url: Optional[str]):
|
|
23
|
+
if api_key is None:
|
|
24
|
+
raise ValueError("api_key is required")
|
|
25
|
+
self.api_key = api_key
|
|
26
|
+
if base_url is None:
|
|
27
|
+
base_url = "https://orchestrator.fleetai.com"
|
|
28
|
+
self.base_url = base_url
|
|
29
|
+
|
|
30
|
+
def get_headers(self) -> Dict[str, str]:
|
|
31
|
+
headers: Dict[str, str] = {
|
|
32
|
+
"X-Fleet-SDK-Language": "Python",
|
|
33
|
+
"X-Fleet-SDK-Version": "1.0.0",
|
|
34
|
+
}
|
|
35
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
36
|
+
return headers
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AsyncWrapper(BaseWrapper):
|
|
40
|
+
def __init__(self, *, httpx_client: httpx.AsyncClient, **kwargs):
|
|
41
|
+
super().__init__(**kwargs)
|
|
42
|
+
self.httpx_client = httpx_client
|
|
43
|
+
|
|
44
|
+
async def request(
|
|
45
|
+
self,
|
|
46
|
+
method: str,
|
|
47
|
+
url: str,
|
|
48
|
+
params: Optional[Dict[str, Any]] = None,
|
|
49
|
+
json: Optional[Any] = None,
|
|
50
|
+
**kwargs,
|
|
51
|
+
) -> httpx.Response:
|
|
52
|
+
try:
|
|
53
|
+
response = await self.httpx_client.request(
|
|
54
|
+
method,
|
|
55
|
+
f"{self.base_url}{url}",
|
|
56
|
+
headers=self.get_headers(),
|
|
57
|
+
params=params,
|
|
58
|
+
json=json,
|
|
59
|
+
**kwargs,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Check for HTTP errors
|
|
63
|
+
if response.status_code >= 400:
|
|
64
|
+
self._handle_error_response(response)
|
|
65
|
+
|
|
66
|
+
return response
|
|
67
|
+
except httpx.TimeoutException as e:
|
|
68
|
+
raise FleetTimeoutError(f"Request timed out: {str(e)}")
|
|
69
|
+
except httpx.RequestError as e:
|
|
70
|
+
raise FleetAPIError(f"Request failed: {str(e)}")
|
|
71
|
+
|
|
72
|
+
def _handle_error_response(self, response: httpx.Response) -> None:
|
|
73
|
+
"""Handle HTTP error responses and convert to appropriate Fleet exceptions."""
|
|
74
|
+
status_code = response.status_code
|
|
75
|
+
|
|
76
|
+
# Try to parse error response as JSON
|
|
77
|
+
try:
|
|
78
|
+
error_data = response.json()
|
|
79
|
+
detail = error_data.get("detail", response.text)
|
|
80
|
+
|
|
81
|
+
# Handle structured error responses
|
|
82
|
+
if isinstance(detail, dict):
|
|
83
|
+
error_type = detail.get("error_type", "")
|
|
84
|
+
error_message = detail.get("message", str(detail))
|
|
85
|
+
|
|
86
|
+
if error_type == "instance_limit_exceeded":
|
|
87
|
+
raise FleetInstanceLimitError(
|
|
88
|
+
error_message,
|
|
89
|
+
running_instances=detail.get("running_instances"),
|
|
90
|
+
instance_limit=detail.get("instance_limit")
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
error_message = detail.get("message", str(detail))
|
|
94
|
+
else:
|
|
95
|
+
error_message = detail
|
|
96
|
+
|
|
97
|
+
except (json.JSONDecodeError, ValueError):
|
|
98
|
+
error_message = response.text
|
|
99
|
+
error_data = None
|
|
100
|
+
|
|
101
|
+
# Handle specific error types
|
|
102
|
+
if status_code == 401:
|
|
103
|
+
raise FleetAuthenticationError(error_message)
|
|
104
|
+
elif status_code == 429:
|
|
105
|
+
# Check if it's an instance limit error vs rate limit error (fallback for unstructured errors)
|
|
106
|
+
if "instance limit" in error_message.lower():
|
|
107
|
+
# Try to extract instance counts from the error message
|
|
108
|
+
running_instances = None
|
|
109
|
+
instance_limit = None
|
|
110
|
+
if "You have" in error_message and "running instances out of a maximum of" in error_message:
|
|
111
|
+
try:
|
|
112
|
+
# Extract numbers from message like "You have 5 running instances out of a maximum of 10"
|
|
113
|
+
parts = error_message.split("You have ")[1].split(" running instances out of a maximum of ")
|
|
114
|
+
if len(parts) == 2:
|
|
115
|
+
running_instances = int(parts[0])
|
|
116
|
+
instance_limit = int(parts[1].split(".")[0])
|
|
117
|
+
except (IndexError, ValueError):
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
raise FleetInstanceLimitError(
|
|
121
|
+
error_message,
|
|
122
|
+
running_instances=running_instances,
|
|
123
|
+
instance_limit=instance_limit
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
raise FleetRateLimitError(error_message)
|
|
127
|
+
else:
|
|
128
|
+
raise FleetAPIError(
|
|
129
|
+
error_message,
|
|
130
|
+
status_code=status_code,
|
|
131
|
+
response_data=error_data if 'error_data' in locals() else None
|
|
132
|
+
)
|
|
@@ -39,7 +39,7 @@ logger = logging.getLogger(__name__)
|
|
|
39
39
|
class AsyncEnvironment(EnvironmentBase):
|
|
40
40
|
def __init__(self, httpx_client: Optional[httpx.AsyncClient] = None, **kwargs):
|
|
41
41
|
super().__init__(**kwargs)
|
|
42
|
-
self._httpx_client = httpx_client or httpx.AsyncClient(timeout=
|
|
42
|
+
self._httpx_client = httpx_client or httpx.AsyncClient(timeout=180.0)
|
|
43
43
|
self._instance: Optional[AsyncInstanceClient] = None
|
|
44
44
|
|
|
45
45
|
@property
|
|
@@ -84,7 +84,7 @@ class AsyncFleet:
|
|
|
84
84
|
base_url: Optional[str] = None,
|
|
85
85
|
httpx_client: Optional[httpx.AsyncClient] = None,
|
|
86
86
|
):
|
|
87
|
-
self._httpx_client = httpx_client or httpx.AsyncClient(timeout=
|
|
87
|
+
self._httpx_client = httpx_client or httpx.AsyncClient(timeout=180.0)
|
|
88
88
|
self.client = AsyncWrapper(
|
|
89
89
|
api_key=api_key,
|
|
90
90
|
base_url=base_url,
|
|
@@ -95,6 +95,10 @@ class AsyncFleet:
|
|
|
95
95
|
response = await self.client.request("GET", "/v1/env/")
|
|
96
96
|
return [EnvironmentModel(**env_data) for env_data in response.json()]
|
|
97
97
|
|
|
98
|
+
async def list_regions(self) -> List[str]:
|
|
99
|
+
response = await self.client.request("GET", "/v1/regions")
|
|
100
|
+
return response.json()
|
|
101
|
+
|
|
98
102
|
async def environment(self, env_key: str) -> EnvironmentModel:
|
|
99
103
|
response = await self.client.request("GET", f"/v1/env/{env_key}")
|
|
100
104
|
return EnvironmentModel(**response.json())
|
|
@@ -11,6 +11,10 @@ async def list_envs_async() -> List[EnvironmentModel]:
|
|
|
11
11
|
return await AsyncFleet().list_envs()
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
async def list_regions_async() -> List[str]:
|
|
15
|
+
return await AsyncFleet().list_regions()
|
|
16
|
+
|
|
17
|
+
|
|
14
18
|
async def list_instances_async(
|
|
15
19
|
status: Optional[str] = None, region: Optional[str] = None
|
|
16
20
|
) -> List[AsyncEnvironment]:
|
|
@@ -49,6 +49,15 @@ class FleetRateLimitError(FleetAPIError):
|
|
|
49
49
|
super().__init__(message, status_code=429)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
class FleetInstanceLimitError(FleetAPIError):
|
|
53
|
+
"""Exception raised when team instance limit is exceeded."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, message: str = "Instance limit exceeded", running_instances: Optional[int] = None, instance_limit: Optional[int] = None):
|
|
56
|
+
super().__init__(message, status_code=429)
|
|
57
|
+
self.running_instances = running_instances
|
|
58
|
+
self.instance_limit = instance_limit
|
|
59
|
+
|
|
60
|
+
|
|
52
61
|
class FleetEnvironmentError(FleetError):
|
|
53
62
|
"""Exception raised when environment operations fail."""
|
|
54
63
|
|
|
@@ -51,7 +51,7 @@ class AsyncInstanceClient:
|
|
|
51
51
|
self.base_url = url
|
|
52
52
|
self.client = AsyncWrapper(
|
|
53
53
|
url=self.base_url,
|
|
54
|
-
httpx_client=httpx_client or httpx.AsyncClient(timeout=
|
|
54
|
+
httpx_client=httpx_client or httpx.AsyncClient(timeout=180.0),
|
|
55
55
|
)
|
|
56
56
|
self._resources: Optional[List[ResourceModel]] = None
|
|
57
57
|
self._resources_state: Dict[str, Dict[str, Resource]] = {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from typing import Dict, Any, Optional
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from .models import InstanceResponse
|
|
6
|
+
from .exceptions import (
|
|
7
|
+
FleetAPIError,
|
|
8
|
+
FleetAuthenticationError,
|
|
9
|
+
FleetRateLimitError,
|
|
10
|
+
FleetInstanceLimitError,
|
|
11
|
+
FleetTimeoutError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EnvironmentBase(InstanceResponse):
|
|
16
|
+
@property
|
|
17
|
+
def manager_url(self) -> str:
|
|
18
|
+
return f"{self.urls.manager.api}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseWrapper:
|
|
22
|
+
def __init__(self, *, api_key: Optional[str], base_url: Optional[str]):
|
|
23
|
+
if api_key is None:
|
|
24
|
+
raise ValueError("api_key is required")
|
|
25
|
+
self.api_key = api_key
|
|
26
|
+
if base_url is None:
|
|
27
|
+
base_url = "https://orchestrator.fleetai.com"
|
|
28
|
+
self.base_url = base_url
|
|
29
|
+
|
|
30
|
+
def get_headers(self) -> Dict[str, str]:
|
|
31
|
+
headers: Dict[str, str] = {
|
|
32
|
+
"X-Fleet-SDK-Language": "Python",
|
|
33
|
+
"X-Fleet-SDK-Version": "1.0.0",
|
|
34
|
+
}
|
|
35
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
36
|
+
return headers
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SyncWrapper(BaseWrapper):
|
|
40
|
+
def __init__(self, *, httpx_client: httpx.Client, **kwargs):
|
|
41
|
+
super().__init__(**kwargs)
|
|
42
|
+
self.httpx_client = httpx_client
|
|
43
|
+
|
|
44
|
+
def request(
|
|
45
|
+
self,
|
|
46
|
+
method: str,
|
|
47
|
+
url: str,
|
|
48
|
+
params: Optional[Dict[str, Any]] = None,
|
|
49
|
+
json: Optional[Any] = None,
|
|
50
|
+
**kwargs,
|
|
51
|
+
) -> httpx.Response:
|
|
52
|
+
try:
|
|
53
|
+
response = self.httpx_client.request(
|
|
54
|
+
method,
|
|
55
|
+
f"{self.base_url}{url}",
|
|
56
|
+
headers=self.get_headers(),
|
|
57
|
+
params=params,
|
|
58
|
+
json=json,
|
|
59
|
+
**kwargs,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Check for HTTP errors
|
|
63
|
+
if response.status_code >= 400:
|
|
64
|
+
self._handle_error_response(response)
|
|
65
|
+
|
|
66
|
+
return response
|
|
67
|
+
except httpx.TimeoutException as e:
|
|
68
|
+
raise FleetTimeoutError(f"Request timed out: {str(e)}")
|
|
69
|
+
except httpx.RequestError as e:
|
|
70
|
+
raise FleetAPIError(f"Request failed: {str(e)}")
|
|
71
|
+
|
|
72
|
+
def _handle_error_response(self, response: httpx.Response) -> None:
|
|
73
|
+
"""Handle HTTP error responses and convert to appropriate Fleet exceptions."""
|
|
74
|
+
status_code = response.status_code
|
|
75
|
+
|
|
76
|
+
# Try to parse error response as JSON
|
|
77
|
+
try:
|
|
78
|
+
error_data = response.json()
|
|
79
|
+
detail = error_data.get("detail", response.text)
|
|
80
|
+
|
|
81
|
+
# Handle structured error responses
|
|
82
|
+
if isinstance(detail, dict):
|
|
83
|
+
error_type = detail.get("error_type", "")
|
|
84
|
+
error_message = detail.get("message", str(detail))
|
|
85
|
+
|
|
86
|
+
if error_type == "instance_limit_exceeded":
|
|
87
|
+
raise FleetInstanceLimitError(
|
|
88
|
+
error_message,
|
|
89
|
+
running_instances=detail.get("running_instances"),
|
|
90
|
+
instance_limit=detail.get("instance_limit")
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
error_message = detail.get("message", str(detail))
|
|
94
|
+
else:
|
|
95
|
+
error_message = detail
|
|
96
|
+
|
|
97
|
+
except (json.JSONDecodeError, ValueError):
|
|
98
|
+
error_message = response.text
|
|
99
|
+
error_data = None
|
|
100
|
+
|
|
101
|
+
# Handle specific error types
|
|
102
|
+
if status_code == 401:
|
|
103
|
+
raise FleetAuthenticationError(error_message)
|
|
104
|
+
elif status_code == 429:
|
|
105
|
+
# Check if it's an instance limit error vs rate limit error (fallback for unstructured errors)
|
|
106
|
+
if "instance limit" in error_message.lower():
|
|
107
|
+
# Try to extract instance counts from the error message
|
|
108
|
+
running_instances = None
|
|
109
|
+
instance_limit = None
|
|
110
|
+
if "You have" in error_message and "running instances out of a maximum of" in error_message:
|
|
111
|
+
try:
|
|
112
|
+
# Extract numbers from message like "You have 5 running instances out of a maximum of 10"
|
|
113
|
+
parts = error_message.split("You have ")[1].split(" running instances out of a maximum of ")
|
|
114
|
+
if len(parts) == 2:
|
|
115
|
+
running_instances = int(parts[0])
|
|
116
|
+
instance_limit = int(parts[1].split(".")[0])
|
|
117
|
+
except (IndexError, ValueError):
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
raise FleetInstanceLimitError(
|
|
121
|
+
error_message,
|
|
122
|
+
running_instances=running_instances,
|
|
123
|
+
instance_limit=instance_limit
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
raise FleetRateLimitError(error_message)
|
|
127
|
+
else:
|
|
128
|
+
raise FleetAPIError(
|
|
129
|
+
error_message,
|
|
130
|
+
status_code=status_code,
|
|
131
|
+
response_data=error_data if 'error_data' in locals() else None
|
|
132
|
+
)
|
|
@@ -39,7 +39,7 @@ logger = logging.getLogger(__name__)
|
|
|
39
39
|
class Environment(EnvironmentBase):
|
|
40
40
|
def __init__(self, httpx_client: Optional[httpx.Client] = None, **kwargs):
|
|
41
41
|
super().__init__(**kwargs)
|
|
42
|
-
self._httpx_client = httpx_client or httpx.Client(timeout=
|
|
42
|
+
self._httpx_client = httpx_client or httpx.Client(timeout=180.0)
|
|
43
43
|
self._instance: Optional[InstanceClient] = None
|
|
44
44
|
|
|
45
45
|
@property
|
|
@@ -84,7 +84,7 @@ class Fleet:
|
|
|
84
84
|
base_url: Optional[str] = None,
|
|
85
85
|
httpx_client: Optional[httpx.Client] = None,
|
|
86
86
|
):
|
|
87
|
-
self._httpx_client = httpx_client or httpx.Client(timeout=
|
|
87
|
+
self._httpx_client = httpx_client or httpx.Client(timeout=180.0)
|
|
88
88
|
self.client = SyncWrapper(
|
|
89
89
|
api_key=api_key,
|
|
90
90
|
base_url=base_url,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Fleet env module - convenience functions for environment management."""
|
|
2
|
+
|
|
3
|
+
from .client import make, list_envs, list_regions, get, list_instances
|
|
4
|
+
|
|
5
|
+
# Import async versions from _async
|
|
6
|
+
from .._async.env.client import (
|
|
7
|
+
make_async,
|
|
8
|
+
list_envs_async,
|
|
9
|
+
list_regions_async,
|
|
10
|
+
get_async,
|
|
11
|
+
list_instances_async,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"make",
|
|
16
|
+
"list_envs",
|
|
17
|
+
"list_regions",
|
|
18
|
+
"list_instances",
|
|
19
|
+
"get",
|
|
20
|
+
"make_async",
|
|
21
|
+
"list_envs_async",
|
|
22
|
+
"list_regions_async",
|
|
23
|
+
"list_instances_async",
|
|
24
|
+
"get_async",
|
|
25
|
+
]
|
|
@@ -11,6 +11,10 @@ def list_envs() -> List[EnvironmentModel]:
|
|
|
11
11
|
return Fleet().list_envs()
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def list_regions() -> List[str]:
|
|
15
|
+
return Fleet().list_regions()
|
|
16
|
+
|
|
17
|
+
|
|
14
18
|
def list_instances(
|
|
15
19
|
status: Optional[str] = None, region: Optional[str] = None
|
|
16
20
|
) -> List[Environment]:
|
|
@@ -49,6 +49,15 @@ class FleetRateLimitError(FleetAPIError):
|
|
|
49
49
|
super().__init__(message, status_code=429)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
class FleetInstanceLimitError(FleetAPIError):
|
|
53
|
+
"""Exception raised when team instance limit is exceeded."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, message: str = "Instance limit exceeded", running_instances: Optional[int] = None, instance_limit: Optional[int] = None):
|
|
56
|
+
super().__init__(message, status_code=429)
|
|
57
|
+
self.running_instances = running_instances
|
|
58
|
+
self.instance_limit = instance_limit
|
|
59
|
+
|
|
60
|
+
|
|
52
61
|
class FleetEnvironmentError(FleetError):
|
|
53
62
|
"""Exception raised when environment operations fail."""
|
|
54
63
|
|
|
@@ -50,7 +50,7 @@ class InstanceClient:
|
|
|
50
50
|
self.base_url = url
|
|
51
51
|
self.client = SyncWrapper(
|
|
52
52
|
url=self.base_url,
|
|
53
|
-
httpx_client=httpx_client or httpx.Client(timeout=
|
|
53
|
+
httpx_client=httpx_client or httpx.Client(timeout=180.0),
|
|
54
54
|
)
|
|
55
55
|
self._resources: Optional[List[ResourceModel]] = None
|
|
56
56
|
self._resources_state: Dict[str, Dict[str, Resource]] = {
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fleet-python"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.11"
|
|
8
8
|
description = "Python SDK for Fleet environments"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Fleet AI", email = "nic@fleet.so"},
|
|
@@ -92,6 +92,7 @@ testpaths = ["tests"]
|
|
|
92
92
|
"AsyncFleetPlaywrightWrapper" = "FleetPlaywrightWrapper"
|
|
93
93
|
"make_async" = "make"
|
|
94
94
|
"list_envs_async" = "list_envs"
|
|
95
|
+
"list_regions_async" = "list_regions"
|
|
95
96
|
"list_instances_async" = "list_instances"
|
|
96
97
|
"get_async" = "get"
|
|
97
98
|
"async def" = "def"
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import httpx
|
|
2
|
-
from typing import Dict, Any, Optional
|
|
3
|
-
|
|
4
|
-
from .models import InstanceResponse
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class EnvironmentBase(InstanceResponse):
|
|
8
|
-
@property
|
|
9
|
-
def manager_url(self) -> str:
|
|
10
|
-
return f"{self.urls.manager.api}"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class BaseWrapper:
|
|
14
|
-
def __init__(self, *, api_key: Optional[str], base_url: Optional[str]):
|
|
15
|
-
if api_key is None:
|
|
16
|
-
raise ValueError("api_key is required")
|
|
17
|
-
self.api_key = api_key
|
|
18
|
-
if base_url is None:
|
|
19
|
-
base_url = "https://fleet.new"
|
|
20
|
-
self.base_url = base_url
|
|
21
|
-
|
|
22
|
-
def get_headers(self) -> Dict[str, str]:
|
|
23
|
-
headers: Dict[str, str] = {
|
|
24
|
-
"X-Fleet-SDK-Language": "Python",
|
|
25
|
-
"X-Fleet-SDK-Version": "1.0.0",
|
|
26
|
-
}
|
|
27
|
-
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
28
|
-
return headers
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class AsyncWrapper(BaseWrapper):
|
|
32
|
-
def __init__(self, *, httpx_client: httpx.AsyncClient, **kwargs):
|
|
33
|
-
super().__init__(**kwargs)
|
|
34
|
-
self.httpx_client = httpx_client
|
|
35
|
-
|
|
36
|
-
async def request(
|
|
37
|
-
self,
|
|
38
|
-
method: str,
|
|
39
|
-
url: str,
|
|
40
|
-
params: Optional[Dict[str, Any]] = None,
|
|
41
|
-
json: Optional[Any] = None,
|
|
42
|
-
**kwargs,
|
|
43
|
-
) -> httpx.Response:
|
|
44
|
-
return await self.httpx_client.request(
|
|
45
|
-
method,
|
|
46
|
-
f"{self.base_url}{url}",
|
|
47
|
-
headers=self.get_headers(),
|
|
48
|
-
params=params,
|
|
49
|
-
json=json,
|
|
50
|
-
**kwargs,
|
|
51
|
-
)
|
fleet_python-0.2.9/fleet/base.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import httpx
|
|
2
|
-
from typing import Dict, Any, Optional
|
|
3
|
-
|
|
4
|
-
from .models import InstanceResponse
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class EnvironmentBase(InstanceResponse):
|
|
8
|
-
@property
|
|
9
|
-
def manager_url(self) -> str:
|
|
10
|
-
return f"{self.urls.manager.api}"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class BaseWrapper:
|
|
14
|
-
def __init__(self, *, api_key: Optional[str], base_url: Optional[str]):
|
|
15
|
-
if api_key is None:
|
|
16
|
-
raise ValueError("api_key is required")
|
|
17
|
-
self.api_key = api_key
|
|
18
|
-
if base_url is None:
|
|
19
|
-
base_url = "https://fleet.new"
|
|
20
|
-
self.base_url = base_url
|
|
21
|
-
|
|
22
|
-
def get_headers(self) -> Dict[str, str]:
|
|
23
|
-
headers: Dict[str, str] = {
|
|
24
|
-
"X-Fleet-SDK-Language": "Python",
|
|
25
|
-
"X-Fleet-SDK-Version": "1.0.0",
|
|
26
|
-
}
|
|
27
|
-
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
28
|
-
return headers
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class SyncWrapper(BaseWrapper):
|
|
32
|
-
def __init__(self, *, httpx_client: httpx.Client, **kwargs):
|
|
33
|
-
super().__init__(**kwargs)
|
|
34
|
-
self.httpx_client = httpx_client
|
|
35
|
-
|
|
36
|
-
def request(
|
|
37
|
-
self,
|
|
38
|
-
method: str,
|
|
39
|
-
url: str,
|
|
40
|
-
params: Optional[Dict[str, Any]] = None,
|
|
41
|
-
json: Optional[Any] = None,
|
|
42
|
-
**kwargs,
|
|
43
|
-
) -> httpx.Response:
|
|
44
|
-
return self.httpx_client.request(
|
|
45
|
-
method,
|
|
46
|
-
f"{self.base_url}{url}",
|
|
47
|
-
headers=self.get_headers(),
|
|
48
|
-
params=params,
|
|
49
|
-
json=json,
|
|
50
|
-
**kwargs,
|
|
51
|
-
)
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""Fleet env module - convenience functions for environment management."""
|
|
2
|
-
|
|
3
|
-
from .client import make, list_envs, get, list_instances
|
|
4
|
-
|
|
5
|
-
# Import async versions from _async
|
|
6
|
-
from .._async.env.client import make_async, list_envs_async, get_async, list_instances_async
|
|
7
|
-
|
|
8
|
-
__all__ = [
|
|
9
|
-
"make",
|
|
10
|
-
"list_envs",
|
|
11
|
-
"list_instances",
|
|
12
|
-
"get",
|
|
13
|
-
"make_async",
|
|
14
|
-
"list_envs_async",
|
|
15
|
-
"list_instances_async",
|
|
16
|
-
"get_async",
|
|
17
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|