smooth-py 0.3.0.dev20251013__py3-none-any.whl → 0.3.0.post2__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
@@ -82,7 +82,11 @@ class TaskRequest(BaseModel):
82
82
  description=("Browser profile ID to use. Each profile maintains its own state, such as cookies and login credentials."),
83
83
  )
84
84
  profile_read_only: bool = Field(
85
- default=False, description="If true, the profile specified by `profile_id` will be loaded in read-only mode."
85
+ default=False,
86
+ description=(
87
+ "If true, the profile specified by `profile_id` will be loaded in read-only mode. "
88
+ "Changes made during the task will not be saved back to the profile."
89
+ ),
86
90
  )
87
91
  stealth_mode: bool = Field(default=False, description="Run the browser in stealth mode.")
88
92
  proxy_server: str | None = Field(
@@ -330,6 +334,10 @@ class BrowserSessionHandle(BaseModel):
330
334
  return _encode_url(self.browser_session.live_url, interactive=interactive, embed=embed)
331
335
  return None
332
336
 
337
+ def live_id(self):
338
+ """Returns the live ID for the browser session."""
339
+ return self.browser_session.live_id
340
+
333
341
 
334
342
  class TaskHandle:
335
343
  """A handle to a running task."""
@@ -393,6 +401,14 @@ class TaskHandle:
393
401
  task_response = self._client._get_task(self.id())
394
402
  self._task_response = task_response
395
403
  if task_response.recording_url is not None:
404
+ if not task_response.recording_url:
405
+ raise ApiError(
406
+ status_code=404,
407
+ detail=(
408
+ f"Recording URL not available for task {self.id()}."
409
+ " Set `enable_recording=True` when creating the task to enable it."
410
+ )
411
+ )
396
412
  return task_response.recording_url
397
413
  time.sleep(1)
398
414
  raise TimeoutError(f"Recording URL not available for task {self.id()}.")
@@ -466,7 +482,7 @@ class SmoothClient(BaseClient):
466
482
  max_steps: int = 32,
467
483
  device: Literal["desktop", "mobile"] = "mobile",
468
484
  allowed_urls: list[str] | None = None,
469
- enable_recording: bool = False,
485
+ enable_recording: bool = True,
470
486
  session_id: str | None = None,
471
487
  profile_id: str | None = None,
472
488
  profile_read_only: bool = False,
@@ -486,7 +502,7 @@ class SmoothClient(BaseClient):
486
502
  response_model: If provided, the schema describing the desired output structure.
487
503
  url: The starting URL for the task. If not provided, the agent will infer it from the task.
488
504
  metadata: A dictionary containing variables or parameters that will be passed to the agent.
489
- files: A dictionary of file names to their ids. These files will be passed to the agent.
505
+ files: A list of file ids to pass to the agent.
490
506
  agent: The agent to use for the task.
491
507
  max_steps: Maximum number of steps the agent can take (max 64).
492
508
  device: Device type for the task. Default is mobile.
@@ -577,7 +593,7 @@ class SmoothClient(BaseClient):
577
593
  ApiException: If the API request fails.
578
594
  """
579
595
  try:
580
- response = self._session.get(f"{self.base_url}/browser/session")
596
+ response = self._session.get(f"{self.base_url}/browser/profile")
581
597
  data = self._handle_response(response)
582
598
  return BrowserProfilesResponse(**data["r"])
583
599
  except requests.exceptions.RequestException as e:
@@ -592,7 +608,7 @@ class SmoothClient(BaseClient):
592
608
  def delete_profile(self, profile_id: str):
593
609
  """Delete a browser profile."""
594
610
  try:
595
- response = self._session.delete(f"{self.base_url}/browser/session/{profile_id}")
611
+ response = self._session.delete(f"{self.base_url}/browser/profile/{profile_id}")
596
612
  self._handle_response(response)
597
613
  except requests.exceptions.RequestException as e:
598
614
  logger.error(f"Request failed: {e}")
@@ -686,7 +702,7 @@ class AsyncTaskHandle:
686
702
  await asyncio.sleep(poll_interval)
687
703
  raise TimeoutError(f"Task {self.id()} did not complete within {timeout} seconds.")
688
704
 
689
- async def live_url(self, interactive: bool = True, embed: bool = False, timeout: int | None = None):
705
+ async def live_url(self, interactive: bool = False, embed: bool = False, timeout: int | None = None):
690
706
  """Returns the live URL for the task."""
691
707
  if self._task_response and self._task_response.live_url:
692
708
  return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
@@ -695,13 +711,13 @@ class AsyncTaskHandle:
695
711
  while timeout is None or (time.time() - start_time) < timeout:
696
712
  task_response = await self._client._get_task(self.id())
697
713
  self._task_response = task_response
698
- if task_response.live_url is not None:
699
- return _encode_url(task_response.live_url, interactive=interactive, embed=embed)
714
+ if self._task_response.live_url:
715
+ return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
700
716
  await asyncio.sleep(1)
701
717
 
702
718
  raise TimeoutError(f"Live URL not available for task {self.id()}.")
703
719
 
704
- async def recording_url(self, timeout: int | None = None):
720
+ async def recording_url(self, timeout: int | None = None) -> str:
705
721
  """Returns the recording URL for the task."""
706
722
  if self._task_response and self._task_response.recording_url is not None:
707
723
  return self._task_response.recording_url
@@ -711,6 +727,14 @@ class AsyncTaskHandle:
711
727
  task_response = await self._client._get_task(self.id())
712
728
  self._task_response = task_response
713
729
  if task_response.recording_url is not None:
730
+ if not task_response.recording_url:
731
+ raise ApiError(
732
+ status_code=404,
733
+ detail=(
734
+ f"Recording URL not available for task {self.id()}."
735
+ " Set `enable_recording=True` when creating the task to enable it."
736
+ )
737
+ )
714
738
  return task_response.recording_url
715
739
  await asyncio.sleep(1)
716
740
 
@@ -779,7 +803,7 @@ class SmoothAsyncClient(BaseClient):
779
803
  max_steps: int = 32,
780
804
  device: Literal["desktop", "mobile"] = "mobile",
781
805
  allowed_urls: list[str] | None = None,
782
- enable_recording: bool = False,
806
+ enable_recording: bool = True,
783
807
  session_id: str | None = None,
784
808
  profile_id: str | None = None,
785
809
  profile_read_only: bool = False,
@@ -799,7 +823,7 @@ class SmoothAsyncClient(BaseClient):
799
823
  response_model: If provided, the schema describing the desired output structure.
800
824
  url: The starting URL for the task. If not provided, the agent will infer it from the task.
801
825
  metadata: A dictionary containing variables or parameters that will be passed to the agent.
802
- files: A dictionary of file names to their url or base64-encoded content to be used by the agent.
826
+ files: A list of file ids to pass to the agent.
803
827
  agent: The agent to use for the task.
804
828
  max_steps: Maximum number of steps the agent can take (max 64).
805
829
  device: Device type for the task. Default is mobile.
@@ -850,8 +874,8 @@ class SmoothAsyncClient(BaseClient):
850
874
  """Opens an interactive browser instance asynchronously.
851
875
 
852
876
  Args:
877
+ profile_id: The profile ID to use for the session. If None, a new profile will be created.
853
878
  session_id: (Deprecated, now `profile_id`) The session ID to associate with the browser.
854
- profile_id: The profile ID to associate with the browser.
855
879
  live_view: Whether to enable live view for the session.
856
880
 
857
881
  Returns:
@@ -890,7 +914,7 @@ class SmoothAsyncClient(BaseClient):
890
914
  ApiException: If the API request fails.
891
915
  """
892
916
  try:
893
- response = await self._client.get(f"{self.base_url}/browser/session")
917
+ response = await self._client.get(f"{self.base_url}/browser/profile")
894
918
  data = self._handle_response(response)
895
919
  return BrowserProfilesResponse(**data["r"])
896
920
  except httpx.RequestError as e:
@@ -905,7 +929,7 @@ class SmoothAsyncClient(BaseClient):
905
929
  async def delete_profile(self, profile_id: str):
906
930
  """Delete a browser profile."""
907
931
  try:
908
- response = await self._client.delete(f"{self.base_url}/browser/session/{profile_id}")
932
+ response = await self._client.delete(f"{self.base_url}/browser/profile/{profile_id}")
909
933
  self._handle_response(response)
910
934
  except httpx.RequestError as e:
911
935
  logger.error(f"Request failed: {e}")
@@ -936,11 +960,12 @@ class SmoothAsyncClient(BaseClient):
936
960
  if name is None:
937
961
  raise ValueError("File name must be provided or the file object must have a 'name' attribute.")
938
962
 
939
- files = {"file": (Path(name).name, file)}
940
963
  if purpose:
941
964
  data = {"file_purpose": purpose}
942
965
  else:
943
966
  data = None
967
+
968
+ files = {"file": (Path(name).name, file)}
944
969
  response = await self._client.post(f"{self.base_url}/file", files=files, data=data)
945
970
  data = self._handle_response(response)
946
971
  return UploadFileResponse(**data["r"])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: smooth-py
3
- Version: 0.3.0.dev20251013
3
+ Version: 0.3.0.post2
4
4
  Summary:
5
5
  Author: Luca Pinchetti
6
6
  Author-email: luca@circlemind.co
@@ -0,0 +1,6 @@
1
+ smooth/__init__.py,sha256=RlpjOBgw2CiLM3kgtOUBdDqslme2Cqge8rXQTV6lUh0,39212
2
+ smooth/mcp/__init__.py,sha256=0aJVFi2a8Ah3-5xtgyZ5UMbaaJsBWu2T8QLWoFQITk8,219
3
+ smooth/mcp/server.py,sha256=9SymTD4NOGTMN8P-LNGlvYNvv81yCIZfZeeuhEcAc6s,20068
4
+ smooth_py-0.3.0.post2.dist-info/METADATA,sha256=Phb1MamNNmlyJ9g1MWGG0FLTG5JKNSIvIURVuI8EqwM,7523
5
+ smooth_py-0.3.0.post2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
6
+ smooth_py-0.3.0.post2.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- smooth/__init__.py,sha256=8cuQGi1QbHBbW0dcM9ZyyZL-PrMmAoR3LmLZcqksdJA,38460
2
- smooth/mcp/__init__.py,sha256=0aJVFi2a8Ah3-5xtgyZ5UMbaaJsBWu2T8QLWoFQITk8,219
3
- smooth/mcp/server.py,sha256=9SymTD4NOGTMN8P-LNGlvYNvv81yCIZfZeeuhEcAc6s,20068
4
- smooth_py-0.3.0.dev20251013.dist-info/METADATA,sha256=CJIChVHrQ4YqTk7TioEH-u_4ecw9OurNAw1KAOf3Q1w,7529
5
- smooth_py-0.3.0.dev20251013.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
6
- smooth_py-0.3.0.dev20251013.dist-info/RECORD,,