smooth-py 0.2.2.post0__tar.gz → 0.2.3__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.
- {smooth_py-0.2.2.post0 → smooth_py-0.2.3}/PKG-INFO +2 -2
- {smooth_py-0.2.2.post0 → smooth_py-0.2.3}/pyproject.toml +2 -2
- {smooth_py-0.2.2.post0 → smooth_py-0.2.3}/src/smooth/__init__.py +72 -8
- {smooth_py-0.2.2.post0 → smooth_py-0.2.3}/README.md +0 -0
- {smooth_py-0.2.2.post0 → smooth_py-0.2.3}/src/smooth/mcp/__init__.py +0 -0
- {smooth_py-0.2.2.post0 → smooth_py-0.2.3}/src/smooth/mcp/server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: smooth-py
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Luca Pinchetti
|
|
6
6
|
Author-email: luca@circlemind.co
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
13
|
Provides-Extra: mcp
|
|
14
|
-
Requires-Dist: fastmcp (>=2.
|
|
14
|
+
Requires-Dist: fastmcp (>=2.12.0,<3.0.0) ; extra == "mcp"
|
|
15
15
|
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
16
16
|
Requires-Dist: pydantic (>=2.11.7,<3.0.0)
|
|
17
17
|
Requires-Dist: requests (>=2.32.5,<3.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "smooth-py"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Luca Pinchetti",email = "luca@circlemind.co"}
|
|
@@ -15,7 +15,7 @@ dependencies = [
|
|
|
15
15
|
|
|
16
16
|
[project.optional-dependencies]
|
|
17
17
|
mcp = [
|
|
18
|
-
"fastmcp (>=2.
|
|
18
|
+
"fastmcp (>=2.12.0,<3.0.0)"
|
|
19
19
|
]
|
|
20
20
|
|
|
21
21
|
[tool.poetry]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Smooth python SDK."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import io
|
|
4
5
|
import logging
|
|
5
6
|
import os
|
|
6
7
|
import time
|
|
@@ -58,9 +59,9 @@ class TaskRequest(BaseModel):
|
|
|
58
59
|
metadata: dict[str, str | int | float | bool] | None = Field(
|
|
59
60
|
default=None, description="A dictionary containing variables or parameters that will be passed to the agent."
|
|
60
61
|
)
|
|
61
|
-
files:
|
|
62
|
-
default=None, description="A dictionary of file
|
|
63
|
-
)
|
|
62
|
+
files: list[str] | None = Field(
|
|
63
|
+
default=None, description="A dictionary of file ids to pass to the agent."
|
|
64
|
+
)
|
|
64
65
|
agent: Literal["smooth"] = Field(default="smooth", description="The agent to use for the task.")
|
|
65
66
|
max_steps: int = Field(default=32, ge=2, le=128, description="Maximum number of steps the agent can take (min 2, max 128).")
|
|
66
67
|
device: Literal["desktop", "mobile"] = Field(default="mobile", description="Device type for the task. Default is mobile.")
|
|
@@ -103,6 +104,11 @@ class BrowserSessionsResponse(BaseModel):
|
|
|
103
104
|
session_ids: list[str] = Field(description="The IDs of the browser sessions.")
|
|
104
105
|
|
|
105
106
|
|
|
107
|
+
class UploadFileResponse(BaseModel):
|
|
108
|
+
"""Response model for listing browser sessions."""
|
|
109
|
+
id: str = Field(description="The ID assigned to the uploaded file.")
|
|
110
|
+
|
|
111
|
+
|
|
106
112
|
# --- Exception Handling ---
|
|
107
113
|
|
|
108
114
|
|
|
@@ -145,7 +151,6 @@ class BaseClient:
|
|
|
145
151
|
self.base_url = f"{base_url.rstrip('/')}/{api_version}"
|
|
146
152
|
self.headers = {
|
|
147
153
|
"apikey": self.api_key,
|
|
148
|
-
"Content-Type": "application/json",
|
|
149
154
|
"User-Agent": "smooth-python-sdk/0.2.0",
|
|
150
155
|
}
|
|
151
156
|
|
|
@@ -284,6 +289,19 @@ class SmoothClient(BaseClient):
|
|
|
284
289
|
logger.error(f"Request failed: {e}")
|
|
285
290
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
286
291
|
|
|
292
|
+
def _upload_file(self, file: io.IOBase, name: str) -> UploadFileResponse:
|
|
293
|
+
"""Uploads a file and returns the assigned file id."""
|
|
294
|
+
try:
|
|
295
|
+
files = {
|
|
296
|
+
"file": (name, file)
|
|
297
|
+
}
|
|
298
|
+
response = self._session.post(f"{self.base_url}/upload_file", files=files)
|
|
299
|
+
data = self._handle_response(response)
|
|
300
|
+
return UploadFileResponse(**data["r"])
|
|
301
|
+
except requests.exceptions.RequestException as e:
|
|
302
|
+
logger.error(f"Request failed: {e}")
|
|
303
|
+
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
304
|
+
|
|
287
305
|
def _get_task(self, task_id: str) -> TaskResponse:
|
|
288
306
|
"""Retrieves the status and result of a task."""
|
|
289
307
|
if not task_id:
|
|
@@ -303,7 +321,7 @@ class SmoothClient(BaseClient):
|
|
|
303
321
|
response_model: dict[str, Any] | Type[BaseModel] | None = None,
|
|
304
322
|
url: str | None = None,
|
|
305
323
|
metadata: dict[str, str | int | float | bool] | None = None,
|
|
306
|
-
files:
|
|
324
|
+
files: list[str] | None = None,
|
|
307
325
|
agent: Literal["smooth"] = "smooth",
|
|
308
326
|
max_steps: int = 32,
|
|
309
327
|
device: Literal["desktop", "mobile"] = "mobile",
|
|
@@ -324,7 +342,7 @@ class SmoothClient(BaseClient):
|
|
|
324
342
|
response_model: If provided, the schema describing the desired output structure.
|
|
325
343
|
url: The starting URL for the task. If not provided, the agent will infer it from the task.
|
|
326
344
|
metadata: A dictionary containing variables or parameters that will be passed to the agent.
|
|
327
|
-
files: A dictionary of file names to their
|
|
345
|
+
files: A dictionary of file names to their ids. These files will be passed to the agent.
|
|
328
346
|
agent: The agent to use for the task.
|
|
329
347
|
max_steps: Maximum number of steps the agent can take (max 64).
|
|
330
348
|
device: Device type for the task. Default is mobile.
|
|
@@ -407,10 +425,26 @@ class SmoothClient(BaseClient):
|
|
|
407
425
|
try:
|
|
408
426
|
response = self._session.delete(f"{self.base_url}/browser/session/{session_id}")
|
|
409
427
|
self._handle_response(response)
|
|
410
|
-
except
|
|
428
|
+
except requests.exceptions.RequestException as e:
|
|
411
429
|
logger.error(f"Request failed: {e}")
|
|
412
430
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
413
431
|
|
|
432
|
+
def upload_file(self, file: io.IOBase, name: str) -> UploadFileResponse:
|
|
433
|
+
"""Upload a file and return the file ID.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
file: File object to be uploaded.
|
|
437
|
+
name: The name to assign to the uploaded file.
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
The file ID assigned to the uploaded file.
|
|
441
|
+
|
|
442
|
+
Raises:
|
|
443
|
+
ValueError: If the file doesn't exist or can't be read.
|
|
444
|
+
ApiError: If the API request fails.
|
|
445
|
+
"""
|
|
446
|
+
return self._upload_file(file=file, name=name)
|
|
447
|
+
|
|
414
448
|
|
|
415
449
|
# --- Asynchronous Client ---
|
|
416
450
|
|
|
@@ -505,6 +539,19 @@ class SmoothAsyncClient(BaseClient):
|
|
|
505
539
|
logger.error(f"Request failed: {e}")
|
|
506
540
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
507
541
|
|
|
542
|
+
async def _upload_file(self, file: io.IOBase, name: str) -> UploadFileResponse:
|
|
543
|
+
"""Uploads a file and returns the assigned file id."""
|
|
544
|
+
try:
|
|
545
|
+
files = {
|
|
546
|
+
"file": (name, file)
|
|
547
|
+
}
|
|
548
|
+
response = await self._client.post(f"{self.base_url}/upload_file", files=files)
|
|
549
|
+
data = self._handle_response(response)
|
|
550
|
+
return UploadFileResponse(**data["r"])
|
|
551
|
+
except httpx.RequestError as e:
|
|
552
|
+
logger.error(f"Request failed: {e}")
|
|
553
|
+
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
554
|
+
|
|
508
555
|
async def _get_task(self, task_id: str) -> TaskResponse:
|
|
509
556
|
"""Retrieves the status and result of a task asynchronously."""
|
|
510
557
|
if not task_id:
|
|
@@ -524,7 +571,7 @@ class SmoothAsyncClient(BaseClient):
|
|
|
524
571
|
response_model: dict[str, Any] | Type[BaseModel] | None = None,
|
|
525
572
|
url: str | None = None,
|
|
526
573
|
metadata: dict[str, str | int | float | bool] | None = None,
|
|
527
|
-
files:
|
|
574
|
+
files: list[str] | None = None,
|
|
528
575
|
agent: Literal["smooth"] = "smooth",
|
|
529
576
|
max_steps: int = 32,
|
|
530
577
|
device: Literal["desktop", "mobile"] = "mobile",
|
|
@@ -634,6 +681,22 @@ class SmoothAsyncClient(BaseClient):
|
|
|
634
681
|
logger.error(f"Request failed: {e}")
|
|
635
682
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
636
683
|
|
|
684
|
+
async def upload_file(self, file: io.IOBase, name: str) -> UploadFileResponse:
|
|
685
|
+
"""Upload a file and return the file ID.
|
|
686
|
+
|
|
687
|
+
Args:
|
|
688
|
+
file: File object to be uploaded.
|
|
689
|
+
name: The name to assign to the uploaded file.
|
|
690
|
+
|
|
691
|
+
Returns:
|
|
692
|
+
The file ID assigned to the uploaded file.
|
|
693
|
+
|
|
694
|
+
Raises:
|
|
695
|
+
ValueError: If the file doesn't exist or can't be read.
|
|
696
|
+
ApiError: If the API request fails.
|
|
697
|
+
"""
|
|
698
|
+
return await self._upload_file(file=file, name=name)
|
|
699
|
+
|
|
637
700
|
async def close(self):
|
|
638
701
|
"""Closes the async client session."""
|
|
639
702
|
await self._client.aclose()
|
|
@@ -651,6 +714,7 @@ __all__ = [
|
|
|
651
714
|
"BrowserSessionRequest",
|
|
652
715
|
"BrowserSessionResponse",
|
|
653
716
|
"BrowserSessionsResponse",
|
|
717
|
+
"UploadFileResponse",
|
|
654
718
|
"ApiError",
|
|
655
719
|
"TimeoutError",
|
|
656
720
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|