smooth-py 0.1.0__tar.gz → 0.1.1.post0__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 smooth-py might be problematic. Click here for more details.

@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: smooth-py
3
- Version: 0.1.0
3
+ Version: 0.1.1.post0
4
4
  Summary:
5
5
  Author: Luca Pinchetti
6
- Author-email: pincoluca1@gmail.com
6
+ Author-email: luca@circlemind.co
7
7
  Requires-Python: >=3.10
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.10
@@ -46,10 +46,10 @@ The SDK requires an API key for authentication. You can provide the API key in t
46
46
 
47
47
  2. **As an environment variable**:
48
48
 
49
- Set the `SMOOTH_API_KEY` environment variable, and the client will automatically use it.
49
+ Set the `CIRCLEMIND_API_KEY` environment variable, and the client will automatically use it.
50
50
 
51
51
  ```bash
52
- export SMOOTH_API_KEY="YOUR_API_KEY"
52
+ export CIRCLEMIND_API_KEY="YOUR_API_KEY"
53
53
  ```
54
54
 
55
55
  ```python
@@ -30,10 +30,10 @@ The SDK requires an API key for authentication. You can provide the API key in t
30
30
 
31
31
  2. **As an environment variable**:
32
32
 
33
- Set the `SMOOTH_API_KEY` environment variable, and the client will automatically use it.
33
+ Set the `CIRCLEMIND_API_KEY` environment variable, and the client will automatically use it.
34
34
 
35
35
  ```bash
36
- export SMOOTH_API_KEY="YOUR_API_KEY"
36
+ export CIRCLEMIND_API_KEY="YOUR_API_KEY"
37
37
  ```
38
38
 
39
39
  ```python
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "smooth-py"
3
- version = "0.1.0"
3
+ version = "0.1.1.post0"
4
4
  description = ""
5
5
  authors = [
6
- {name = "Luca Pinchetti",email = "pincoluca1@gmail.com"}
6
+ {name = "Luca Pinchetti",email = "luca@circlemind.co"}
7
7
  ]
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10"
@@ -20,23 +20,17 @@ BASE_URL = "https://api2.circlemind.co/api/"
20
20
  # These models define the data structures for API requests and responses.
21
21
 
22
22
 
23
- class TaskData(BaseModel):
24
- """Task data model."""
25
-
26
- result: Any | None = Field(default=None, description="The result of the task if successful.")
27
- error: str | None = Field(default=None, description="Error message if the task failed.")
28
- credits_used: int | None = Field(default=None, description="The amount of credits used to perform the task.")
29
- src: str | None = Field(default=None, description="")
30
-
31
-
32
23
  class TaskResponse(BaseModel):
33
24
  """Task response model."""
34
25
 
35
26
  model_config = ConfigDict(extra="forbid")
36
27
 
37
28
  id: str = Field(description="The ID of the task.")
38
- status: str = Field(default="RUNNING", description="The status of the task.")
39
- data: TaskData = Field(default_factory=lambda: TaskData(), description="The data associated with the task.")
29
+ status: Literal["waiting", "running", "done", "failed"] = Field(description="The status of the task.")
30
+ result: Any | None = Field(default=None, description="The result of the task if successful.")
31
+ error: str | None = Field(default=None, description="Error message if the task failed.")
32
+ credits_used: int | None = Field(default=None, description="The amount of credits used to perform the task.")
33
+ src: str | None = Field(default=None, description="")
40
34
 
41
35
 
42
36
  class TaskRequest(BaseModel):
@@ -53,7 +47,13 @@ class TaskRequest(BaseModel):
53
47
  description="(optional) Browser session ID to use. Each session maintains its own state, such as login credentials.",
54
48
  )
55
49
  stealth_mode: bool = Field(default=False, description="(optional) Run the browser in stealth mode.")
56
- proxy_server: Optional[str] = Field(default=None, description="(optional) Proxy server URL.")
50
+ proxy_server: Optional[str] = Field(
51
+ default=None,
52
+ description=(
53
+ "(optional) Proxy server url to route browser traffic through."
54
+ " Must include the protocol to use (e.g. http:// or https://)"
55
+ ),
56
+ )
57
57
  proxy_username: Optional[str] = Field(default=None, description="(optional) Proxy server username.")
58
58
  proxy_password: Optional[str] = Field(default=None, description="(optional) Proxy server password.")
59
59
 
@@ -109,10 +109,10 @@ class BaseClient:
109
109
  """Initializes the base client."""
110
110
  # Try to get API key from environment if not provided
111
111
  if not api_key:
112
- api_key = os.getenv("SMOOTH_API_KEY")
112
+ api_key = os.getenv("CIRCLEMIND_API_KEY")
113
113
 
114
114
  if not api_key:
115
- raise ValueError("API key is required. Provide it directly or set SMOOTH_API_KEY environment variable.")
115
+ raise ValueError("API key is required. Provide it directly or set CIRCLEMIND_API_KEY environment variable.")
116
116
 
117
117
  if not base_url:
118
118
  raise ValueError("Base URL cannot be empty.")
@@ -120,12 +120,12 @@ class BaseClient:
120
120
  self.api_key = api_key
121
121
  self.base_url = f"{base_url.rstrip('/')}/{api_version}"
122
122
  self.headers = {
123
- "Authorization": f"Bearer {self.api_key}",
123
+ "apikey": self.api_key,
124
124
  "Content-Type": "application/json",
125
125
  "User-Agent": "smooth-python-sdk/0.1.0",
126
126
  }
127
127
 
128
- def _handle_response(self, response: Union[requests.Response, httpx.Response]) -> dict:
128
+ def _handle_response(self, response: Union[requests.Response, httpx.Response]) -> dict[str, Any]:
129
129
  """Handles HTTP responses and raises exceptions for errors."""
130
130
  if 200 <= response.status_code < 300:
131
131
  try:
@@ -135,6 +135,7 @@ class BaseClient:
135
135
  raise ApiError(status_code=response.status_code, detail="Invalid JSON response from server") from None
136
136
 
137
137
  # Handle error responses
138
+ error_data = None
138
139
  try:
139
140
  error_data = response.json()
140
141
  detail = error_data.get("detail", response.text)
@@ -143,7 +144,7 @@ class BaseClient:
143
144
 
144
145
  logger.error(f"API error: {response.status_code} - {detail}")
145
146
  raise ApiError(
146
- status_code=response.status_code, detail=detail, response_data=error_data if "error_data" in locals() else None
147
+ status_code=response.status_code, detail=detail, response_data=error_data
147
148
  )
148
149
 
149
150
 
@@ -163,7 +164,7 @@ class SyncClient(BaseClient):
163
164
  """Enters the synchronous context manager."""
164
165
  return self
165
166
 
166
- def __exit__(self, exc_type, exc_val, exc_tb):
167
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any):
167
168
  """Exits the synchronous context manager."""
168
169
  self.close()
169
170
 
@@ -267,7 +268,7 @@ class SyncClient(BaseClient):
267
268
  Raises:
268
269
  ApiException: If the API request fails.
269
270
  """
270
- params = {}
271
+ params: dict[str, Any] = {}
271
272
  if session_id:
272
273
  params["session_id"] = session_id
273
274
  if session_name:
@@ -314,7 +315,7 @@ class AsyncClient(BaseClient):
314
315
  """Enters the asynchronous context manager."""
315
316
  return self
316
317
 
317
- async def __aexit__(self, exc_type, exc_val, exc_tb):
318
+ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any):
318
319
  """Exits the asynchronous context manager."""
319
320
  await self.close()
320
321
 
@@ -416,7 +417,7 @@ class AsyncClient(BaseClient):
416
417
  Raises:
417
418
  ApiException: If the API request fails.
418
419
  """
419
- params = {}
420
+ params: dict[str, Any] = {}
420
421
  if session_id:
421
422
  params["session_id"] = session_id
422
423
  if session_name:
@@ -430,6 +431,23 @@ class AsyncClient(BaseClient):
430
431
  logger.error(f"Request failed: {e}")
431
432
  raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
432
433
 
434
+ async def list_sessions(self) -> BrowserSessionsResponse:
435
+ """Lists all browser sessions for the user.
436
+
437
+ Returns:
438
+ A list of existing browser sessions.
439
+
440
+ Raises:
441
+ ApiException: If the API request fails.
442
+ """
443
+ try:
444
+ response = await self._client.get(f"{self.base_url}/browser/session")
445
+ data = self._handle_response(response)
446
+ return BrowserSessionsResponse(**data["r"])
447
+ except httpx.RequestError as e:
448
+ logger.error(f"Request failed: {e}")
449
+ raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
450
+
433
451
  async def close(self):
434
452
  """Closes the async client session."""
435
453
  await self._client.aclose()