smooth-py 0.1.3.post0__tar.gz → 0.1.4__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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: smooth-py
3
- Version: 0.1.3.post0
3
+ Version: 0.1.4
4
4
  Summary:
5
5
  Author: Luca Pinchetti
6
6
  Author-email: luca@circlemind.co
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "smooth-py"
3
- version = "0.1.3.post0"
3
+ version = "0.1.4"
4
4
  description = ""
5
5
  authors = [
6
6
  {name = "Luca Pinchetti",email = "luca@circlemind.co"}
@@ -4,11 +4,11 @@ import asyncio
4
4
  import logging
5
5
  import os
6
6
  import time
7
+ import urllib.parse
7
8
  from typing import (
8
9
  Any,
9
10
  Literal,
10
11
  )
11
- from urllib.parse import urlencode
12
12
 
13
13
  import httpx
14
14
  import requests
@@ -20,6 +20,20 @@ logger = logging.getLogger("smooth")
20
20
 
21
21
  BASE_URL = "https://api2.circlemind.co/api/"
22
22
 
23
+
24
+ # --- Utils ---
25
+
26
+
27
+ def _encode_url(url: str, interactive: bool = True, embed: bool = False) -> str:
28
+ parsed_url = urllib.parse.urlparse(url)
29
+ params = urllib.parse.parse_qs(parsed_url.query)
30
+ params.update({
31
+ "interactive": "true" if interactive else "false",
32
+ "embed": "true" if embed else "false"
33
+ })
34
+ return urllib.parse.urlunparse(parsed_url._replace(query=urllib.parse.urlencode(params)))
35
+
36
+
23
37
  # --- Models ---
24
38
  # These models define the data structures for API requests and responses.
25
39
 
@@ -151,6 +165,20 @@ class BaseClient:
151
165
  # --- Synchronous Client ---
152
166
 
153
167
 
168
+ class BrowserSessionHandle(BaseModel):
169
+ """Browser session handle model."""
170
+
171
+ browser_session: BrowserSessionResponse = Field(description="The browser session associated with this handle.")
172
+
173
+ def session_id(self):
174
+ """Returns the session ID for the browser session."""
175
+ return self.browser_session.session_id
176
+
177
+ def live_url(self, interactive: bool = True, embed: bool = False):
178
+ """Returns the live URL for the browser session."""
179
+ return _encode_url(self.browser_session.live_url, interactive=interactive, embed=embed)
180
+
181
+
154
182
  class TaskHandle:
155
183
  """A handle to a running task."""
156
184
 
@@ -182,20 +210,15 @@ class TaskHandle:
182
210
 
183
211
  def live_url(self, interactive: bool = True, embed: bool = False, timeout: int | None = None):
184
212
  """Returns the live URL for the task."""
185
- params = {
186
- "interactive": interactive,
187
- "embed": embed
188
- }
189
-
190
213
  if self._task_response and self._task_response.live_url:
191
- return f"{self._task_response.live_url}?{urlencode(params)}"
214
+ return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
192
215
 
193
216
  start_time = time.time()
194
217
  while timeout is None or (time.time() - start_time) < timeout:
195
218
  task_response = self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
196
219
  self._task_response = task_response
197
220
  if self._task_response.live_url:
198
- return f"{self._task_response.live_url}?{urlencode(params)}"
221
+ return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
199
222
  time.sleep(1)
200
223
 
201
224
  raise TimeoutError(f"Live URL not available for task {self.id}.")
@@ -312,7 +335,7 @@ class SmoothClient(BaseClient):
312
335
 
313
336
  return TaskHandle(initial_response.id, self)
314
337
 
315
- def open_session(self, session_id: str | None = None) -> BrowserSessionResponse:
338
+ def open_session(self, session_id: str | None = None) -> BrowserSessionHandle:
316
339
  """Gets an interactive browser instance.
317
340
 
318
341
  Args:
@@ -330,7 +353,7 @@ class SmoothClient(BaseClient):
330
353
  json=BrowserSessionRequest(session_id=session_id).model_dump(exclude_none=True),
331
354
  )
332
355
  data = self._handle_response(response)
333
- return BrowserSessionResponse(**data["r"])
356
+ return BrowserSessionHandle(browser_session=BrowserSessionResponse(**data["r"]))
334
357
  except requests.exceptions.RequestException as e:
335
358
  logger.error(f"Request failed: {e}")
336
359
  raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
@@ -396,19 +419,15 @@ class AsyncTaskHandle:
396
419
 
397
420
  async def live_url(self, interactive: bool = True, embed: bool = False, timeout: int | None = None):
398
421
  """Returns the live URL for the task."""
399
- params = {
400
- "interactive": interactive,
401
- "embed": embed
402
- }
403
422
  if self._task_response and self._task_response.live_url:
404
- return f"{self._task_response.live_url}?{urlencode(params)}"
423
+ return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
405
424
 
406
425
  start_time = time.time()
407
426
  while timeout is None or (time.time() - start_time) < timeout:
408
427
  task_response = await self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
409
428
  self._task_response = task_response
410
429
  if task_response.live_url is not None:
411
- return f"{task_response.live_url}?{urlencode(params)}"
430
+ return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
412
431
  await asyncio.sleep(1)
413
432
 
414
433
  raise TimeoutError(f"Live URL not available for task {self.id}.")
@@ -521,7 +540,7 @@ class SmoothAsyncClient(BaseClient):
521
540
  initial_response = await self._submit_task(payload)
522
541
  return AsyncTaskHandle(initial_response.id, self)
523
542
 
524
- async def open_session(self, session_id: str | None = None) -> BrowserSessionResponse:
543
+ async def open_session(self, session_id: str | None = None) -> BrowserSessionHandle:
525
544
  """Opens an interactive browser instance asynchronously.
526
545
 
527
546
  Args:
@@ -539,7 +558,7 @@ class SmoothAsyncClient(BaseClient):
539
558
  json=BrowserSessionRequest(session_id=session_id).model_dump(exclude_none=True),
540
559
  )
541
560
  data = self._handle_response(response)
542
- return BrowserSessionResponse(**data["r"])
561
+ return BrowserSessionHandle(browser_session=BrowserSessionResponse(**data["r"]))
543
562
  except httpx.RequestError as e:
544
563
  logger.error(f"Request failed: {e}")
545
564
  raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
File without changes