fleet-python 0.2.9__py3-none-any.whl → 0.2.10__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/example_sync.py CHANGED
@@ -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
fleet/__init__.py CHANGED
@@ -18,6 +18,8 @@ from .exceptions import (
18
18
  FleetError,
19
19
  FleetAPIError,
20
20
  FleetTimeoutError,
21
+ FleetRateLimitError,
22
+ FleetInstanceLimitError,
21
23
  FleetConfigurationError,
22
24
  )
23
25
  from .client import Fleet, Environment
fleet/_async/base.py CHANGED
@@ -1,7 +1,15 @@
1
1
  import httpx
2
2
  from typing import Dict, Any, Optional
3
+ import json
3
4
 
4
5
  from .models import InstanceResponse
6
+ from .exceptions import (
7
+ FleetAPIError,
8
+ FleetAuthenticationError,
9
+ FleetRateLimitError,
10
+ FleetInstanceLimitError,
11
+ FleetTimeoutError,
12
+ )
5
13
 
6
14
 
7
15
  class EnvironmentBase(InstanceResponse):
@@ -16,7 +24,7 @@ class BaseWrapper:
16
24
  raise ValueError("api_key is required")
17
25
  self.api_key = api_key
18
26
  if base_url is None:
19
- base_url = "https://fleet.new"
27
+ base_url = "https://orchestrator.fleetai.com"
20
28
  self.base_url = base_url
21
29
 
22
30
  def get_headers(self) -> Dict[str, str]:
@@ -41,11 +49,84 @@ class AsyncWrapper(BaseWrapper):
41
49
  json: Optional[Any] = None,
42
50
  **kwargs,
43
51
  ) -> 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
- )
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
+ )
fleet/_async/client.py CHANGED
@@ -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=60.0)
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=60.0)
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,
@@ -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=60.0),
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]] = {
fleet/base.py CHANGED
@@ -1,7 +1,15 @@
1
1
  import httpx
2
2
  from typing import Dict, Any, Optional
3
+ import json
3
4
 
4
5
  from .models import InstanceResponse
6
+ from .exceptions import (
7
+ FleetAPIError,
8
+ FleetAuthenticationError,
9
+ FleetRateLimitError,
10
+ FleetInstanceLimitError,
11
+ FleetTimeoutError,
12
+ )
5
13
 
6
14
 
7
15
  class EnvironmentBase(InstanceResponse):
@@ -16,7 +24,7 @@ class BaseWrapper:
16
24
  raise ValueError("api_key is required")
17
25
  self.api_key = api_key
18
26
  if base_url is None:
19
- base_url = "https://fleet.new"
27
+ base_url = "https://orchestrator.fleetai.com"
20
28
  self.base_url = base_url
21
29
 
22
30
  def get_headers(self) -> Dict[str, str]:
@@ -41,11 +49,84 @@ class SyncWrapper(BaseWrapper):
41
49
  json: Optional[Any] = None,
42
50
  **kwargs,
43
51
  ) -> 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
- )
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
+ )
fleet/client.py CHANGED
@@ -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=60.0)
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=60.0)
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,
fleet/exceptions.py CHANGED
@@ -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
 
fleet/instance/client.py CHANGED
@@ -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=60.0),
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]] = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.9
3
+ Version: 0.2.10
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -1,30 +1,30 @@
1
1
  examples/dsl_example.py,sha256=3Eu5924a8x61nuSGXqGz8XjPLNKKH8Ye7lSYHSvixtk,5361
2
2
  examples/example.py,sha256=FFPfM5Oso7IP9Q8aELpof1J41zslELdHHJhAAck9vLk,1008
3
3
  examples/example_client.py,sha256=70HKEhz_Gb79YcvKQauCPdS08AAwjo9unt2dh1jN_Oo,1030
4
- examples/example_sync.py,sha256=Kj_Mj2G88ADdQ2Pw_JQqJNbnpZrfMGSNR9paRQhSHLY,967
4
+ examples/example_sync.py,sha256=_XaM3E0osxdt7AJQrJdtgE_I4-ChWuANQ5HIxCisbJk,983
5
5
  examples/gemini_example.py,sha256=8mDXGGCaodyK6uXgpWhxi-DQ5OA-GFW12Gfwh0b3EDY,16177
6
6
  examples/json_tasks_example.py,sha256=3ub2LLiC6hXpVEH1175QxCmfCD3Blfo3yoG85uV5CS8,5334
7
7
  examples/nova_act_example.py,sha256=hZLpObVsiXKQzqGwMZVMf4A2j_z4TYE-YO9pgNmaKPk,836
8
8
  examples/openai_example.py,sha256=I2vk_SJN9BkSRQCYRJfbtGJ-HJ2xzQj-lOjwqmLos5M,8234
9
9
  examples/openai_simple_example.py,sha256=I42ytIwv0INgDO39pp1MOQSqsJz2YYH8GeNNBaUtq3A,1748
10
10
  examples/quickstart.py,sha256=1VT39IRRhemsJgxi0O0gprdpcw7HB4pYO97GAYagIcg,3788
11
- fleet/__init__.py,sha256=p-0zZMxKoD6dlQ9IGQ753QQWuwcw-RPl4E6HezhKfV4,2111
12
- fleet/base.py,sha256=t4xkgazl8kEP05JFjNByyf39RvvASRP0GsvxuoqKPY0,1395
13
- fleet/client.py,sha256=Qn2DEXqO21POxhT1Gg5hZhTEeR3EVe6huocdw_o9MZo,4919
14
- fleet/exceptions.py,sha256=yG3QWprCw1OnF-vdFBFJWE4m3ftBLBng31Dr__VbjI4,2249
11
+ fleet/__init__.py,sha256=-EFlLzHmyJIUGSZ4_XIts6OhXIXGPyu5PgI9JxgDzTg,2165
12
+ fleet/base.py,sha256=0ZxYHuIW3e4BKA2jRK41IP2N6SiOcoQwmTqBM5Z236I,4876
13
+ fleet/client.py,sha256=RDU7uJesgOERTnIIz4wSq6BP_uQuY_M9TSXrlr1-jbU,4921
14
+ fleet/exceptions.py,sha256=UHKUYK5OSeyLj9bmIPXwdtBkDMpAzfD3uxnH4yuE6k8,2659
15
15
  fleet/models.py,sha256=Jf6Zmk689TPXhTSnVENK_VCw0VsujWzEWsN3T29MQ0k,3713
16
16
  fleet/playwright.py,sha256=BmRvez5DUa0ttAQB084hPAyt9_8WxdzCGBGF-GZbTuQ,8593
17
17
  fleet/_async/__init__.py,sha256=AJWCnuo7XKja4yBb8fK2wX7ntciLXQrpzdRHwjTRP6M,62
18
- fleet/_async/base.py,sha256=hUch1I5oUPgaCXR3IpJ8f_PjigifAZg2-LR7BJdZSo8,1413
19
- fleet/_async/client.py,sha256=Do5HPlhGw7iYpqlJ1eVmdwF_RATv9yAFAvENO_XwUq0,5170
20
- fleet/_async/exceptions.py,sha256=yG3QWprCw1OnF-vdFBFJWE4m3ftBLBng31Dr__VbjI4,2249
18
+ fleet/_async/base.py,sha256=j6Pqo0disJU3Nl2dLOacL9_7pYBslm5puIzFgDU54zg,4894
19
+ fleet/_async/client.py,sha256=lubzRY8rzqDkpj_cCev5sN4OmTN6szittA3BQkC4ATY,5172
20
+ fleet/_async/exceptions.py,sha256=UHKUYK5OSeyLj9bmIPXwdtBkDMpAzfD3uxnH4yuE6k8,2659
21
21
  fleet/_async/models.py,sha256=Jf6Zmk689TPXhTSnVENK_VCw0VsujWzEWsN3T29MQ0k,3713
22
22
  fleet/_async/playwright.py,sha256=2r4ywuv2ZqT0Qu3-k8A7V4YijeAOHnN8HiqJreLEYGI,8924
23
23
  fleet/_async/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  fleet/_async/env/client.py,sha256=JPFTWRjyFkUVYFodQ4bRlafH5GRu34LckeFSmb8YdZo,692
25
25
  fleet/_async/instance/__init__.py,sha256=jIt-7EEJ0WM_ipheT_s0lniCbLei6yUdN0qQv1bMJ3E,524
26
26
  fleet/_async/instance/base.py,sha256=QgcCTHdcqhi5VQi6_a1uuR-uO2_2Z19-RwVPp1k266A,947
27
- fleet/_async/instance/client.py,sha256=qmX5g6lPrq0b3BQ6LvTApeyquTtCse98Cu_Kwc72y6A,5653
27
+ fleet/_async/instance/client.py,sha256=XTQWCQ6QfTpmw4x8ctlWvvnkcO8fCYnYkzbPAgs2_U4,5654
28
28
  fleet/_async/instance/models.py,sha256=ZTiue0YOuhuwX8jYfJAoCzGfqjLqqXRLqK1LVFhq6rQ,4183
29
29
  fleet/_async/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  fleet/_async/resources/base.py,sha256=203gD54NP1IvjuSqFo-f7FvrkhtjChggtzrxJK7xf2E,667
@@ -34,7 +34,7 @@ fleet/env/__init__.py,sha256=dlh41oWCKwg1-wUsy1c4lCA34hRKlBjfGpgEXO96TyY,426
34
34
  fleet/env/client.py,sha256=kS6Qx466i37CA6gU-LR18p7c16OzL7rb80Gsa7ggcrM,575
35
35
  fleet/instance/__init__.py,sha256=Hr8xPPoqzKOViXZXWmaL6dQ7NOBn-GooTGzoIvGmiE4,514
36
36
  fleet/instance/base.py,sha256=U-qW1EQVBo6yvMpP1JeKiPRhCjZ3y3aTsYFhLPNOTtQ,929
37
- fleet/instance/client.py,sha256=2EcuBpq21kUHCFommYdS9Ya-unLn-e8mrdAZBIZea3Q,5467
37
+ fleet/instance/client.py,sha256=6Qm81vjVD-7p6ZMx9gbJwLP2R1vkqRe6Drs89w4Tf7I,5468
38
38
  fleet/instance/models.py,sha256=ZTiue0YOuhuwX8jYfJAoCzGfqjLqqXRLqK1LVFhq6rQ,4183
39
39
  fleet/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  fleet/resources/base.py,sha256=203gD54NP1IvjuSqFo-f7FvrkhtjChggtzrxJK7xf2E,667
@@ -44,10 +44,10 @@ fleet/verifiers/__init__.py,sha256=mRMN8x0gDWFJ1MRLqdBtQw0gn_q8kDV3lMLyoiEf1yY,2
44
44
  fleet/verifiers/code.py,sha256=NJ4OLZnpqLkI1lXY7-5m2GuZklLxMzHUCnRMVyN2_OI,25
45
45
  fleet/verifiers/db.py,sha256=tssmvJjDHuBIy8qlL_P5-UdmEFUw2DZcqLsWZ8ot3Xw,27766
46
46
  fleet/verifiers/sql_differ.py,sha256=dmiGCFXVMEMbAX519OjhVqgA8ZvhnvdmC1BVpL7QCF0,6490
47
- fleet_python-0.2.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
47
+ fleet_python-0.2.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
48
48
  scripts/fix_sync_imports.py,sha256=b7tRvShgOFqyildqs1qI-Io0gaHappykBI-PSWWqUwE,2941
49
49
  scripts/unasync.py,sha256=--Fmaae47o-dZ1HYgX1c3Nvi-rMjcFymTRlJcWWnmpw,725
50
- fleet_python-0.2.9.dist-info/METADATA,sha256=UEzKyrhsKU7cZtJFwwvBt2za7-inTrtEB2M6utTmq3Q,4321
51
- fleet_python-0.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
- fleet_python-0.2.9.dist-info/top_level.txt,sha256=_3DSmTohvSDf3AIP_BYfGzhwO1ECFwuzg83X-wHCx3Y,23
53
- fleet_python-0.2.9.dist-info/RECORD,,
50
+ fleet_python-0.2.10.dist-info/METADATA,sha256=aJB94O9zI2KjgEwcXhRYF0CTnhPSMAfUsMHIvOB4Mlc,4322
51
+ fleet_python-0.2.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
+ fleet_python-0.2.10.dist-info/top_level.txt,sha256=_3DSmTohvSDf3AIP_BYfGzhwO1ECFwuzg83X-wHCx3Y,23
53
+ fleet_python-0.2.10.dist-info/RECORD,,