smooth-py 0.1.2.post0__py3-none-any.whl → 0.1.3.post0__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 smooth-py might be problematic. Click here for more details.
smooth/__init__.py
CHANGED
|
@@ -8,6 +8,7 @@ from typing import (
|
|
|
8
8
|
Any,
|
|
9
9
|
Literal,
|
|
10
10
|
)
|
|
11
|
+
from urllib.parse import urlencode
|
|
11
12
|
|
|
12
13
|
import httpx
|
|
13
14
|
import requests
|
|
@@ -42,7 +43,7 @@ class TaskRequest(BaseModel):
|
|
|
42
43
|
agent: Literal["smooth"] = Field(default="smooth", description="The agent to use for the task.")
|
|
43
44
|
max_steps: int = Field(default=32, ge=2, le=128, description="Maximum number of steps the agent can take (min 2, max 128).")
|
|
44
45
|
device: Literal["desktop", "mobile"] = Field(default="mobile", description="Device type for the task. Default is mobile.")
|
|
45
|
-
enable_recording: bool = Field(default=
|
|
46
|
+
enable_recording: bool = Field(default=True, description="Enable video recording of the task execution. Default is True")
|
|
46
47
|
session_id: str | None = Field(
|
|
47
48
|
default=None,
|
|
48
49
|
description="Browser session ID to use. Each session maintains its own state, such as login credentials.",
|
|
@@ -51,7 +52,7 @@ class TaskRequest(BaseModel):
|
|
|
51
52
|
proxy_server: str | None = Field(
|
|
52
53
|
default=None,
|
|
53
54
|
description=(
|
|
54
|
-
"Proxy server url to route browser traffic through.
|
|
55
|
+
"Proxy server url to route browser traffic through. Must include the protocol to use (e.g. http:// or https://)"
|
|
55
56
|
),
|
|
56
57
|
)
|
|
57
58
|
proxy_username: str | None = Field(default=None, description="Proxy server username.")
|
|
@@ -153,29 +154,65 @@ class BaseClient:
|
|
|
153
154
|
class TaskHandle:
|
|
154
155
|
"""A handle to a running task."""
|
|
155
156
|
|
|
156
|
-
def __init__(self, task_id: str, client: "SmoothClient"
|
|
157
|
+
def __init__(self, task_id: str, client: "SmoothClient"):
|
|
157
158
|
"""Initializes the task handle."""
|
|
158
159
|
self._client = client
|
|
159
|
-
self._poll_interval = poll_interval
|
|
160
|
-
self._timeout = timeout
|
|
161
160
|
self._task_response: TaskResponse | None = None
|
|
162
161
|
|
|
163
162
|
self.id = task_id
|
|
164
|
-
self.live_url = live_url
|
|
165
163
|
|
|
166
|
-
def result(self) -> TaskResponse:
|
|
164
|
+
def result(self, timeout: int | None = None, poll_interval: float = 1) -> TaskResponse:
|
|
167
165
|
"""Waits for the task to complete and returns the result."""
|
|
168
166
|
if self._task_response and self._task_response.status not in ["running", "waiting"]:
|
|
169
167
|
return self._task_response
|
|
170
168
|
|
|
169
|
+
if timeout is not None and timeout < 1:
|
|
170
|
+
raise ValueError("Timeout must be at least 1 second.")
|
|
171
|
+
if poll_interval < 0.1:
|
|
172
|
+
raise ValueError("Poll interval must be at least 100 milliseconds.")
|
|
173
|
+
|
|
171
174
|
start_time = time.time()
|
|
172
|
-
while
|
|
175
|
+
while timeout is None or (time.time() - start_time) < timeout:
|
|
173
176
|
task_response = self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
177
|
+
self._task_response = task_response
|
|
174
178
|
if task_response.status not in ["running", "waiting"]:
|
|
175
|
-
self._task_response = task_response
|
|
176
179
|
return task_response
|
|
177
|
-
time.sleep(
|
|
178
|
-
raise TimeoutError(f"Task {self.id} did not complete within {
|
|
180
|
+
time.sleep(poll_interval)
|
|
181
|
+
raise TimeoutError(f"Task {self.id} did not complete within {timeout} seconds.")
|
|
182
|
+
|
|
183
|
+
def live_url(self, interactive: bool = True, embed: bool = False, timeout: int | None = None):
|
|
184
|
+
"""Returns the live URL for the task."""
|
|
185
|
+
params = {
|
|
186
|
+
"interactive": interactive,
|
|
187
|
+
"embed": embed
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if self._task_response and self._task_response.live_url:
|
|
191
|
+
return f"{self._task_response.live_url}?{urlencode(params)}"
|
|
192
|
+
|
|
193
|
+
start_time = time.time()
|
|
194
|
+
while timeout is None or (time.time() - start_time) < timeout:
|
|
195
|
+
task_response = self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
196
|
+
self._task_response = task_response
|
|
197
|
+
if self._task_response.live_url:
|
|
198
|
+
return f"{self._task_response.live_url}?{urlencode(params)}"
|
|
199
|
+
time.sleep(1)
|
|
200
|
+
|
|
201
|
+
raise TimeoutError(f"Live URL not available for task {self.id}.")
|
|
202
|
+
|
|
203
|
+
def recording_url(self, timeout: int | None = None) -> str:
|
|
204
|
+
"""Returns the recording URL for the task."""
|
|
205
|
+
if self._task_response and self._task_response.recording_url is not None:
|
|
206
|
+
return self._task_response.recording_url
|
|
207
|
+
|
|
208
|
+
start_time = time.time()
|
|
209
|
+
while timeout is None or (time.time() - start_time) < timeout:
|
|
210
|
+
task_response = self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
211
|
+
self._task_response = task_response
|
|
212
|
+
if task_response.recording_url is not None:
|
|
213
|
+
return task_response.recording_url
|
|
214
|
+
time.sleep(1)
|
|
215
|
+
raise TimeoutError(f"Recording URL not available for task {self.id}.")
|
|
179
216
|
|
|
180
217
|
|
|
181
218
|
class SmoothClient(BaseClient):
|
|
@@ -226,8 +263,6 @@ class SmoothClient(BaseClient):
|
|
|
226
263
|
def run(
|
|
227
264
|
self,
|
|
228
265
|
task: str,
|
|
229
|
-
poll_interval: int = 1,
|
|
230
|
-
timeout: int = 60 * 15,
|
|
231
266
|
agent: Literal["smooth"] = "smooth",
|
|
232
267
|
max_steps: int = 32,
|
|
233
268
|
device: Literal["desktop", "mobile"] = "mobile",
|
|
@@ -245,8 +280,6 @@ class SmoothClient(BaseClient):
|
|
|
245
280
|
|
|
246
281
|
Args:
|
|
247
282
|
task: The task to run.
|
|
248
|
-
poll_interval: The time in seconds to wait between polling for status.
|
|
249
|
-
timeout: The maximum time in seconds to wait for the task to complete.
|
|
250
283
|
agent: The agent to use for the task.
|
|
251
284
|
max_steps: Maximum number of steps the agent can take (max 64).
|
|
252
285
|
device: Device type for the task. Default is mobile.
|
|
@@ -263,11 +296,6 @@ class SmoothClient(BaseClient):
|
|
|
263
296
|
Raises:
|
|
264
297
|
ApiException: If the API request fails.
|
|
265
298
|
"""
|
|
266
|
-
if poll_interval < 0.1:
|
|
267
|
-
raise ValueError("Poll interval must be at least 100 milliseconds.")
|
|
268
|
-
if timeout < 1:
|
|
269
|
-
raise ValueError("Timeout must be at least 1 second.")
|
|
270
|
-
|
|
271
299
|
payload = TaskRequest(
|
|
272
300
|
task=task,
|
|
273
301
|
agent=agent,
|
|
@@ -281,12 +309,8 @@ class SmoothClient(BaseClient):
|
|
|
281
309
|
proxy_password=proxy_password,
|
|
282
310
|
)
|
|
283
311
|
initial_response = self._submit_task(payload)
|
|
284
|
-
start_time = time.time()
|
|
285
|
-
while time.time() - start_time < 16 and initial_response.live_url is None:
|
|
286
|
-
initial_response = self._get_task(initial_response.id)
|
|
287
|
-
time.sleep(poll_interval)
|
|
288
312
|
|
|
289
|
-
return TaskHandle(initial_response.id, self
|
|
313
|
+
return TaskHandle(initial_response.id, self)
|
|
290
314
|
|
|
291
315
|
def open_session(self, session_id: str | None = None) -> BrowserSessionResponse:
|
|
292
316
|
"""Gets an interactive browser instance.
|
|
@@ -344,30 +368,65 @@ class SmoothClient(BaseClient):
|
|
|
344
368
|
class AsyncTaskHandle:
|
|
345
369
|
"""An asynchronous handle to a running task."""
|
|
346
370
|
|
|
347
|
-
def __init__(self, task_id: str, client: "SmoothAsyncClient"
|
|
371
|
+
def __init__(self, task_id: str, client: "SmoothAsyncClient"):
|
|
348
372
|
"""Initializes the asynchronous task handle."""
|
|
349
373
|
self._client = client
|
|
350
|
-
self._poll_interval = poll_interval
|
|
351
|
-
self._timeout = timeout
|
|
352
374
|
self._task_response: TaskResponse | None = None
|
|
353
375
|
|
|
354
376
|
self.id = task_id
|
|
355
|
-
self.live_url = live_url
|
|
356
377
|
|
|
357
|
-
async def result(self) -> TaskResponse:
|
|
378
|
+
async def result(self, timeout: int | None = None, poll_interval: float = 1) -> TaskResponse:
|
|
358
379
|
"""Waits for the task to complete and returns the result."""
|
|
359
380
|
if self._task_response and self._task_response.status not in ["running", "waiting"]:
|
|
360
381
|
return self._task_response
|
|
361
382
|
|
|
383
|
+
if timeout is not None and timeout < 1:
|
|
384
|
+
raise ValueError("Timeout must be at least 1 second.")
|
|
385
|
+
if poll_interval < 0.1:
|
|
386
|
+
raise ValueError("Poll interval must be at least 100 milliseconds.")
|
|
387
|
+
|
|
362
388
|
start_time = time.time()
|
|
363
|
-
while
|
|
389
|
+
while timeout is None or (time.time() - start_time) < timeout:
|
|
364
390
|
task_response = await self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
391
|
+
self._task_response = task_response
|
|
365
392
|
if task_response.status not in ["running", "waiting"]:
|
|
366
|
-
self._task_response = task_response
|
|
367
393
|
return task_response
|
|
368
|
-
await asyncio.sleep(
|
|
369
|
-
raise TimeoutError(f"Task {self.id} did not complete within {
|
|
394
|
+
await asyncio.sleep(poll_interval)
|
|
395
|
+
raise TimeoutError(f"Task {self.id} did not complete within {timeout} seconds.")
|
|
396
|
+
|
|
397
|
+
async def live_url(self, interactive: bool = True, embed: bool = False, timeout: int | None = None):
|
|
398
|
+
"""Returns the live URL for the task."""
|
|
399
|
+
params = {
|
|
400
|
+
"interactive": interactive,
|
|
401
|
+
"embed": embed
|
|
402
|
+
}
|
|
403
|
+
if self._task_response and self._task_response.live_url:
|
|
404
|
+
return f"{self._task_response.live_url}?{urlencode(params)}"
|
|
405
|
+
|
|
406
|
+
start_time = time.time()
|
|
407
|
+
while timeout is None or (time.time() - start_time) < timeout:
|
|
408
|
+
task_response = await self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
409
|
+
self._task_response = task_response
|
|
410
|
+
if task_response.live_url is not None:
|
|
411
|
+
return f"{task_response.live_url}?{urlencode(params)}"
|
|
412
|
+
await asyncio.sleep(1)
|
|
413
|
+
|
|
414
|
+
raise TimeoutError(f"Live URL not available for task {self.id}.")
|
|
415
|
+
|
|
416
|
+
async def recording_url(self, timeout: int | None = None):
|
|
417
|
+
"""Returns the recording URL for the task."""
|
|
418
|
+
if self._task_response and self._task_response.recording_url is not None:
|
|
419
|
+
return self._task_response.recording_url
|
|
420
|
+
|
|
421
|
+
start_time = time.time()
|
|
422
|
+
while timeout is None or (time.time() - start_time) < timeout:
|
|
423
|
+
task_response = await self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
424
|
+
self._task_response = task_response
|
|
425
|
+
if task_response.recording_url is not None:
|
|
426
|
+
return task_response.recording_url
|
|
427
|
+
await asyncio.sleep(1)
|
|
370
428
|
|
|
429
|
+
raise TimeoutError(f"Recording URL not available for task {self.id}.")
|
|
371
430
|
|
|
372
431
|
class SmoothAsyncClient(BaseClient):
|
|
373
432
|
"""An asynchronous client for the API."""
|
|
@@ -411,8 +470,6 @@ class SmoothAsyncClient(BaseClient):
|
|
|
411
470
|
async def run(
|
|
412
471
|
self,
|
|
413
472
|
task: str,
|
|
414
|
-
poll_interval: int = 1,
|
|
415
|
-
timeout: int = 60 * 15,
|
|
416
473
|
agent: Literal["smooth"] = "smooth",
|
|
417
474
|
max_steps: int = 32,
|
|
418
475
|
device: Literal["desktop", "mobile"] = "mobile",
|
|
@@ -430,8 +487,6 @@ class SmoothAsyncClient(BaseClient):
|
|
|
430
487
|
|
|
431
488
|
Args:
|
|
432
489
|
task: The task to run.
|
|
433
|
-
poll_interval: The time in seconds to wait between polling for status.
|
|
434
|
-
timeout: The maximum time in seconds to wait for the task to complete.
|
|
435
490
|
agent: The agent to use for the task.
|
|
436
491
|
max_steps: Maximum number of steps the agent can take (max 64).
|
|
437
492
|
device: Device type for the task. Default is mobile.
|
|
@@ -441,6 +496,8 @@ class SmoothAsyncClient(BaseClient):
|
|
|
441
496
|
proxy_server: Proxy server url to route browser traffic through.
|
|
442
497
|
proxy_username: Proxy server username.
|
|
443
498
|
proxy_password: Proxy server password.
|
|
499
|
+
poll_interval: The time in seconds to wait between polling for status.
|
|
500
|
+
timeout: The maximum time in seconds to wait for the task to complete.
|
|
444
501
|
|
|
445
502
|
Returns:
|
|
446
503
|
A handle to the running task.
|
|
@@ -448,11 +505,6 @@ class SmoothAsyncClient(BaseClient):
|
|
|
448
505
|
Raises:
|
|
449
506
|
ApiException: If the API request fails.
|
|
450
507
|
"""
|
|
451
|
-
if poll_interval < 0.1:
|
|
452
|
-
raise ValueError("Poll interval must be at least 100 milliseconds.")
|
|
453
|
-
if timeout < 1:
|
|
454
|
-
raise ValueError("Timeout must be at least 1 second.")
|
|
455
|
-
|
|
456
508
|
payload = TaskRequest(
|
|
457
509
|
task=task,
|
|
458
510
|
agent=agent,
|
|
@@ -467,11 +519,7 @@ class SmoothAsyncClient(BaseClient):
|
|
|
467
519
|
)
|
|
468
520
|
|
|
469
521
|
initial_response = await self._submit_task(payload)
|
|
470
|
-
|
|
471
|
-
while time.time() - start_time < 16 and initial_response.live_url is None:
|
|
472
|
-
initial_response = await self._get_task(initial_response.id)
|
|
473
|
-
await asyncio.sleep(poll_interval)
|
|
474
|
-
return AsyncTaskHandle(initial_response.id, self, initial_response.live_url, poll_interval, timeout)
|
|
522
|
+
return AsyncTaskHandle(initial_response.id, self)
|
|
475
523
|
|
|
476
524
|
async def open_session(self, session_id: str | None = None) -> BrowserSessionResponse:
|
|
477
525
|
"""Opens an interactive browser instance asynchronously.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
smooth/__init__.py,sha256=jIEM1usuwrZWq-m1YgsUsLCkvUkhS9BNqgFc3HUPEvw,21263
|
|
2
|
+
smooth_py-0.1.3.post0.dist-info/METADATA,sha256=x4u365t5CvrRvMPsrCrLy2sVNT4ojf8nK8pdntodEdw,5394
|
|
3
|
+
smooth_py-0.1.3.post0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
4
|
+
smooth_py-0.1.3.post0.dist-info/RECORD,,
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
smooth/__init__.py,sha256=65IQjCGmh5xRFfOk-48V_NNgItnh7uUCSva_PYGHd5I,19326
|
|
2
|
-
smooth_py-0.1.2.post0.dist-info/METADATA,sha256=WveUBUPZIOGiNvYT1fEB47GOk4SuGRkxIhv_4oqYQpA,5394
|
|
3
|
-
smooth_py-0.1.2.post0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
4
|
-
smooth_py-0.1.2.post0.dist-info/RECORD,,
|
|
File without changes
|