sutro 0.1.12__tar.gz → 0.1.14__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 sutro might be problematic. Click here for more details.

sutro-0.1.14/PKG-INFO ADDED
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: sutro
3
+ Version: 0.1.14
4
+ Summary: Sutro Python SDK
5
+ Project-URL: Homepage, https://sutro.sh
6
+ Project-URL: Documentation, https://docs.sutro.sh
7
+ License-Expression: Apache-2.0
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: click==8.1.7
11
+ Requires-Dist: colorama==0.4.4
12
+ Requires-Dist: numpy==2.1.1
13
+ Requires-Dist: pandas==2.2.3
14
+ Requires-Dist: polars==1.8.2
15
+ Requires-Dist: pydantic==2.11.4
16
+ Requires-Dist: requests==2.32.3
17
+ Requires-Dist: tqdm==4.67.1
18
+ Requires-Dist: yaspin==3.1.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ # sutro-client
22
+
23
+ The official Python client for Sutro. See [docs.sutro.sh](https://docs.sutro.sh/) for more information.
sutro-0.1.14/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # sutro-client
2
+
3
+ The official Python client for Sutro. See [docs.sutro.sh](https://docs.sutro.sh/) for more information.
@@ -9,7 +9,7 @@ installer = "uv"
9
9
 
10
10
  [project]
11
11
  name = "sutro"
12
- version = "0.1.12"
12
+ version = "0.1.14"
13
13
  description = "Sutro Python SDK"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.10"
@@ -275,34 +275,34 @@ def cancel(job_id):
275
275
 
276
276
 
277
277
  @cli.group()
278
- def stages():
279
- """Manage stages."""
278
+ def datasets():
279
+ """Manage datasets."""
280
280
  pass
281
281
 
282
282
 
283
- @stages.command()
283
+ @datasets.command()
284
284
  def create():
285
- """Create a new stage."""
285
+ """Create a new dataset."""
286
286
  sdk = get_sdk()
287
- stage_id = sdk.create_stage()
288
- if not stage_id:
287
+ dataset_id = sdk.create_dataset()
288
+ if not dataset_id:
289
289
  return
290
290
  click.echo(
291
291
  Fore.GREEN
292
- + f"Stage created successfully. Stage ID: {stage_id}"
292
+ + f"Dataset created successfully. Dataset ID: {dataset_id}"
293
293
  + Style.RESET_ALL
294
294
  )
295
295
 
296
296
 
297
- @stages.command()
297
+ @datasets.command()
298
298
  def list():
299
- """List all stages."""
299
+ """List all datasets."""
300
300
  sdk = get_sdk()
301
- stages = sdk.list_stages()
302
- if stages is None or len(stages) == 0:
303
- click.echo(Fore.YELLOW + "No stages found." + Style.RESET_ALL)
301
+ datasets = sdk.list_datasets()
302
+ if datasets is None or len(datasets) == 0:
303
+ click.echo(Fore.YELLOW + "No datasets found." + Style.RESET_ALL)
304
304
  return
305
- df = pl.DataFrame(stages)
305
+ df = pl.DataFrame(datasets)
306
306
 
307
307
  df = df.with_columns(
308
308
  pl.col("schema")
@@ -319,37 +319,37 @@ def list():
319
319
  print(df.select(pl.all()))
320
320
 
321
321
 
322
- @stages.command()
323
- @click.argument("stage_id")
324
- def files(stage_id):
325
- """List all files in a stage."""
322
+ @datasets.command()
323
+ @click.argument("dataset_id")
324
+ def files(dataset_id):
325
+ """List all files in a dataset."""
326
326
  sdk = get_sdk()
327
- files = sdk.list_stage_files(stage_id)
327
+ files = sdk.list_dataset_files(dataset_id)
328
328
  if not files:
329
329
  return
330
330
 
331
- print(Fore.YELLOW + "Files in stage " + stage_id + ":" + Style.RESET_ALL)
331
+ print(Fore.YELLOW + "Files in dataset " + dataset_id + ":" + Style.RESET_ALL)
332
332
  for file in files:
333
333
  print(f"\t{file}")
334
334
 
335
335
 
336
- @stages.command()
337
- @click.argument("stage_id", required=False)
336
+ @datasets.command()
337
+ @click.argument("dataset_id", required=False)
338
338
  @click.argument("file_path")
339
- def upload(file_path, stage_id):
340
- """Upload files to a stage. You can provide a single file path or a directory path to upload all files in the directory."""
339
+ def upload(file_path, dataset_id):
340
+ """Upload files to a dataset. You can provide a single file path or a directory path to upload all files in the directory."""
341
341
  sdk = get_sdk()
342
- sdk.upload_to_stage(file_path, stage_id)
342
+ sdk.upload_to_dataset(file_path, dataset_id)
343
343
 
344
344
 
345
- @stages.command()
346
- @click.argument("stage_id")
345
+ @datasets.command()
346
+ @click.argument("dataset_id")
347
347
  @click.argument("file_name", required=False)
348
348
  @click.argument("output_path", required=False)
349
- def download(stage_id, file_name=None, output_path=None):
350
- """Download a file/files from a stage. If no files are provided, all files in the stage will be downloaded. If no output path is provided, the file will be saved to the current working directory."""
349
+ def download(dataset_id, file_name=None, output_path=None):
350
+ """Download a file/files from a dataset. If no files are provided, all files in the dataset will be downloaded. If no output path is provided, the file will be saved to the current working directory."""
351
351
  sdk = get_sdk()
352
- files = sdk.download_from_stage(stage_id, [file_name], output_path)
352
+ files = sdk.download_from_dataset(dataset_id, [file_name], output_path)
353
353
  if not files:
354
354
  return
355
355
  for file in files:
@@ -114,7 +114,7 @@ class Sutro:
114
114
  raise ValueError("Column name must be specified for DataFrame input")
115
115
  input_data = data[column].to_list()
116
116
  elif isinstance(data, str):
117
- if data.startswith("stage-"):
117
+ if data.startswith("dataset-"):
118
118
  input_data = data + ":" + column
119
119
  else:
120
120
  file_ext = os.path.splitext(data)[1].lower()
@@ -172,12 +172,12 @@ class Sutro:
172
172
  Run inference on the provided data.
173
173
 
174
174
  This method allows you to run inference on the provided data using the Sutro API.
175
- It supports various data types such as lists, pandas DataFrames, polars DataFrames, file paths and stages.
175
+ It supports various data types such as lists, pandas DataFrames, polars DataFrames, file paths and datasets.
176
176
 
177
177
  Args:
178
178
  data (Union[List, pd.DataFrame, pl.DataFrame, str]): The data to run inference on.
179
179
  model (str, optional): The model to use for inference. Defaults to "llama-3.1-8b".
180
- column (str, optional): The column name to use for inference. Required if data is a DataFrame, file path, or stage.
180
+ column (str, optional): The column name to use for inference. Required if data is a DataFrame, file path, or dataset.
181
181
  output_column (str, optional): The column name to store the inference results in if the input is a DataFrame. Defaults to "inference_result".
182
182
  job_priority (int, optional): The priority of the job. Defaults to 0.
183
183
  output_schema (Union[Dict[str, Any], BaseModel], optional): A structured schema for the output.
@@ -437,8 +437,10 @@ class Sutro:
437
437
  if not stop_event.is_set(): # Only log if we weren't stopping anyway
438
438
  print(f"Heartbeat failed for job {job_id}: {e}")
439
439
 
440
- # Use time.sleep instead of asyncio.sleep since this is synchronous
441
- time.sleep(self.HEARTBEAT_INTERVAL_SECONDS)
440
+ for _ in range(self.HEARTBEAT_INTERVAL_SECONDS):
441
+ if stop_event.is_set():
442
+ break
443
+ time.sleep(1)
442
444
 
443
445
  @contextmanager
444
446
  def stream_heartbeat_session(self, job_id: str, session_token: str) -> Generator[requests.Session, None, None]:
@@ -449,7 +451,7 @@ class Sutro:
449
451
  # Run this concurrently in a thread so we can not block main SDK path/behavior
450
452
  # but still run heartbeat requests
451
453
  with ThreadPoolExecutor(max_workers=1) as executor:
452
- future = executor.submit(
454
+ executor.submit(
453
455
  self.start_heartbeat,
454
456
  job_id,
455
457
  session_token,
@@ -462,7 +464,6 @@ class Sutro:
462
464
  finally:
463
465
  # Signal stop and cleanup
464
466
  stop_heartbeat.set()
465
- future.result() # Wait for heartbeat to finish
466
467
  self.unregister_stream_listener(job_id, session_token)
467
468
  session.close()
468
469
 
@@ -792,22 +793,22 @@ class Sutro:
792
793
  return
793
794
  return response.json()
794
795
 
795
- def create_stage(self):
796
+ def create_dataset(self):
796
797
  """
797
- Create a new stage.
798
+ Create a new dataset.
798
799
 
799
- This method creates a new stage and returns its ID.
800
+ This method creates a new empty dataset and returns its ID.
800
801
 
801
802
  Returns:
802
- str: The ID of the new stage.
803
+ str: The ID of the new dataset.
803
804
  """
804
- endpoint = f"{self.base_url}/create-stage"
805
+ endpoint = f"{self.base_url}/create-dataset"
805
806
  headers = {
806
807
  "Authorization": f"Key {self.api_key}",
807
808
  "Content-Type": "application/json",
808
809
  }
809
810
  with yaspin(
810
- SPINNER, text=to_colored_text("Creating stage"), color=YASPIN_COLOR
811
+ SPINNER, text=to_colored_text("Creating dataset"), color=YASPIN_COLOR
811
812
  ) as spinner:
812
813
  response = requests.get(endpoint, headers=headers)
813
814
  if response.status_code != 200:
@@ -819,25 +820,25 @@ class Sutro:
819
820
  spinner.stop()
820
821
  print(to_colored_text(response.json(), state="fail"))
821
822
  return
822
- stage_id = response.json()["stage_id"]
823
+ dataset_id = response.json()["dataset_id"]
823
824
  spinner.write(
824
- to_colored_text(f"✔ Stage created with ID: {stage_id}", state="success")
825
+ to_colored_text(f"✔ Dataset created with ID: {dataset_id}", state="success")
825
826
  )
826
- return stage_id
827
+ return dataset_id
827
828
 
828
- def upload_to_stage(
829
+ def upload_to_dataset(
829
830
  self,
830
- stage_id: Union[List[str], str] = None,
831
+ dataset_id: Union[List[str], str] = None,
831
832
  file_paths: Union[List[str], str] = None,
832
833
  verify_ssl: bool = True,
833
834
  ):
834
835
  """
835
- Upload data to a stage.
836
+ Upload data to a dataset.
836
837
 
837
- This method uploads files to a stage. Accepts a stage ID and file paths. If only a single parameter is provided, it will be interpreted as the file paths.
838
+ This method uploads files to a dataset. Accepts a dataset ID and file paths. If only a single parameter is provided, it will be interpreted as the file paths.
838
839
 
839
840
  Args:
840
- stage_id (str): The ID of the stage to upload to. If not provided, a new stage will be created.
841
+ dataset_id (str): The ID of the dataset to upload to. If not provided, a new dataset will be created.
841
842
  file_paths (Union[List[str], str]): A list of paths to the files to upload, or a single path to a collection of files.
842
843
  verify_ssl (bool): Whether to verify SSL certificates. Set to False to bypass SSL verification for troubleshooting.
843
844
 
@@ -845,17 +846,17 @@ class Sutro:
845
846
  dict: The response from the API.
846
847
  """
847
848
  # when only a single parameter is provided, it is interpreted as the file paths
848
- if file_paths is None and stage_id is not None:
849
- file_paths = stage_id
850
- stage_id = None
849
+ if file_paths is None and dataset_id is not None:
850
+ file_paths = dataset_id
851
+ dataset_id = None
851
852
 
852
853
  if file_paths is None:
853
854
  raise ValueError("File paths must be provided")
854
855
 
855
- if stage_id is None:
856
- stage_id = self.create_stage()
856
+ if dataset_id is None:
857
+ dataset_id = self.create_dataset()
857
858
 
858
- endpoint = f"{self.base_url}/upload-to-stage"
859
+ endpoint = f"{self.base_url}/upload-to-dataset"
859
860
 
860
861
  if isinstance(file_paths, str):
861
862
  # check if the file path is a directory
@@ -870,7 +871,7 @@ class Sutro:
870
871
 
871
872
  with yaspin(
872
873
  SPINNER,
873
- text=to_colored_text(f"Uploading files to stage: {stage_id}"),
874
+ text=to_colored_text(f"Uploading files to dataset: {dataset_id}"),
874
875
  color=YASPIN_COLOR,
875
876
  ) as spinner:
876
877
  count = 0
@@ -886,7 +887,7 @@ class Sutro:
886
887
  }
887
888
 
888
889
  payload = {
889
- "stage_id": stage_id,
890
+ "dataset_id": dataset_id,
890
891
  }
891
892
 
892
893
  headers = {
@@ -895,7 +896,7 @@ class Sutro:
895
896
  count += 1
896
897
  spinner.write(
897
898
  to_colored_text(
898
- f"Uploading file {count}/{len(file_paths)} to stage: {stage_id}"
899
+ f"Uploading file {count}/{len(file_paths)} to dataset: {dataset_id}"
899
900
  )
900
901
  )
901
902
 
@@ -922,19 +923,19 @@ class Sutro:
922
923
 
923
924
  spinner.write(
924
925
  to_colored_text(
925
- f"✔ {count} files successfully uploaded to stage", state="success"
926
+ f"✔ {count} files successfully uploaded to dataset", state="success"
926
927
  )
927
928
  )
928
- return stage_id
929
+ return dataset_id
929
930
 
930
- def list_stages(self):
931
- endpoint = f"{self.base_url}/list-stages"
931
+ def list_datasets(self):
932
+ endpoint = f"{self.base_url}/list-datasets"
932
933
  headers = {
933
934
  "Authorization": f"Key {self.api_key}",
934
935
  "Content-Type": "application/json",
935
936
  }
936
937
  with yaspin(
937
- SPINNER, text=to_colored_text("Retrieving stages"), color=YASPIN_COLOR
938
+ SPINNER, text=to_colored_text("Retrieving datasets"), color=YASPIN_COLOR
938
939
  ) as spinner:
939
940
  response = requests.post(endpoint, headers=headers)
940
941
  if response.status_code != 200:
@@ -945,21 +946,21 @@ class Sutro:
945
946
  )
946
947
  print(to_colored_text(f"Error: {response.json()}", state="fail"))
947
948
  return
948
- spinner.write(to_colored_text("✔ Stages retrieved", state="success"))
949
- return response.json()["stages"]
949
+ spinner.write(to_colored_text("✔ Datasets retrieved", state="success"))
950
+ return response.json()["datasets"]
950
951
 
951
- def list_stage_files(self, stage_id: str):
952
- endpoint = f"{self.base_url}/list-stage-files"
952
+ def list_dataset_files(self, dataset_id: str):
953
+ endpoint = f"{self.base_url}/list-dataset-files"
953
954
  headers = {
954
955
  "Authorization": f"Key {self.api_key}",
955
956
  "Content-Type": "application/json",
956
957
  }
957
958
  payload = {
958
- "stage_id": stage_id,
959
+ "dataset_id": dataset_id,
959
960
  }
960
961
  with yaspin(
961
962
  SPINNER,
962
- text=to_colored_text(f"Listing files in stage: {stage_id}"),
963
+ text=to_colored_text(f"Listing files in dataset: {dataset_id}"),
963
964
  color=YASPIN_COLOR,
964
965
  ) as spinner:
965
966
  response = requests.post(
@@ -974,27 +975,27 @@ class Sutro:
974
975
  print(to_colored_text(f"Error: {response.json()}", state="fail"))
975
976
  return
976
977
  spinner.write(
977
- to_colored_text(f"✔ Files listed in stage: {stage_id}", state="success")
978
+ to_colored_text(f"✔ Files listed in dataset: {dataset_id}", state="success")
978
979
  )
979
980
  return response.json()["files"]
980
981
 
981
- def download_from_stage(
982
+ def download_from_dataset(
982
983
  self,
983
- stage_id: str,
984
+ dataset_id: str,
984
985
  files: Union[List[str], str] = None,
985
986
  output_path: str = None,
986
987
  ):
987
- endpoint = f"{self.base_url}/download-from-stage"
988
+ endpoint = f"{self.base_url}/download-from-dataset"
988
989
 
989
990
  if files is None:
990
- files = self.list_stage_files(stage_id)
991
+ files = self.list_dataset_files(dataset_id)
991
992
  elif isinstance(files, str):
992
993
  files = [files]
993
994
 
994
995
  if not files:
995
996
  print(
996
997
  to_colored_text(
997
- f"Couldn't find files for stage ID: {stage_id}", state="fail"
998
+ f"Couldn't find files for dataset ID: {dataset_id}", state="fail"
998
999
  )
999
1000
  )
1000
1001
  return
@@ -1005,7 +1006,7 @@ class Sutro:
1005
1006
 
1006
1007
  with yaspin(
1007
1008
  SPINNER,
1008
- text=to_colored_text(f"Downloading files from stage: {stage_id}"),
1009
+ text=to_colored_text(f"Downloading files from dataset: {dataset_id}"),
1009
1010
  color=YASPIN_COLOR,
1010
1011
  ) as spinner:
1011
1012
  count = 0
@@ -1015,11 +1016,11 @@ class Sutro:
1015
1016
  "Content-Type": "application/json",
1016
1017
  }
1017
1018
  payload = {
1018
- "stage_id": stage_id,
1019
+ "dataset_id": dataset_id,
1019
1020
  "file_name": file,
1020
1021
  }
1021
1022
  spinner.text = to_colored_text(
1022
- f"Downloading file {count + 1}/{len(files)} from stage: {stage_id}"
1023
+ f"Downloading file {count + 1}/{len(files)} from dataset: {dataset_id}"
1023
1024
  )
1024
1025
  response = requests.post(
1025
1026
  endpoint, headers=headers, data=json.dumps(payload)
@@ -1038,7 +1039,7 @@ class Sutro:
1038
1039
  count += 1
1039
1040
  spinner.write(
1040
1041
  to_colored_text(
1041
- f"✔ {count} files successfully downloaded from stage: {stage_id}",
1042
+ f"✔ {count} files successfully downloaded from dataset: {dataset_id}",
1042
1043
  state="success",
1043
1044
  )
1044
1045
  )
sutro-0.1.12/PKG-INFO DELETED
@@ -1,41 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: sutro
3
- Version: 0.1.12
4
- Summary: Sutro Python SDK
5
- Project-URL: Homepage, https://sutro.sh
6
- Project-URL: Documentation, https://docs.sutro.sh
7
- License-Expression: Apache-2.0
8
- License-File: LICENSE
9
- Requires-Python: >=3.10
10
- Requires-Dist: click==8.1.7
11
- Requires-Dist: colorama==0.4.4
12
- Requires-Dist: numpy==2.1.1
13
- Requires-Dist: pandas==2.2.3
14
- Requires-Dist: polars==1.8.2
15
- Requires-Dist: pydantic==2.11.4
16
- Requires-Dist: requests==2.32.3
17
- Requires-Dist: tqdm==4.67.1
18
- Requires-Dist: yaspin==3.1.0
19
- Description-Content-Type: text/markdown
20
-
21
- # sutro-client
22
-
23
- The official Python client for Sutro. See [docs.sutro.sh](https://docs.sutro.sh/) for more information.
24
-
25
- ## Installing Locally (to test changes during development)
26
-
27
- Run `make install` from the root directory. This should remove the old builds and reinstall the package in your environment with the latest. You can run `uv pip list` to ensure the package is pointing at the local files instead of the PyPI package.
28
-
29
- ## Creating releases
30
-
31
- Make sure you increment the version appropriately in `pyproject.toml`. Generally speaking we'll do patch versions for small tweaks, minor versions for large additions or changes to behavior, and probably do major releases once it makes sense. Since we're still in beta and `0.x.x` releases, its probably okay to add backwards-incompatible changes to minor releases, but we want to avoid this if possible.
32
-
33
- To create a release, run:
34
-
35
- `make release <version>` with `<version>` formatted like `0.1.1`
36
-
37
- It'll prompt you for an API key to PyPI, which you must have for it to work.
38
-
39
- We also have a test PyPI account which you can use to test creating releases before pushing to the actual PyPI hub. I believe you can only create **one** release per version number, so it may be worth testing if you're paranoid about getting it right.
40
-
41
- Also make sure to update the docs and increment the docs version number to match the new release. Keeping these consistent will provide a better user experience.
sutro-0.1.12/README.md DELETED
@@ -1,21 +0,0 @@
1
- # sutro-client
2
-
3
- The official Python client for Sutro. See [docs.sutro.sh](https://docs.sutro.sh/) for more information.
4
-
5
- ## Installing Locally (to test changes during development)
6
-
7
- Run `make install` from the root directory. This should remove the old builds and reinstall the package in your environment with the latest. You can run `uv pip list` to ensure the package is pointing at the local files instead of the PyPI package.
8
-
9
- ## Creating releases
10
-
11
- Make sure you increment the version appropriately in `pyproject.toml`. Generally speaking we'll do patch versions for small tweaks, minor versions for large additions or changes to behavior, and probably do major releases once it makes sense. Since we're still in beta and `0.x.x` releases, its probably okay to add backwards-incompatible changes to minor releases, but we want to avoid this if possible.
12
-
13
- To create a release, run:
14
-
15
- `make release <version>` with `<version>` formatted like `0.1.1`
16
-
17
- It'll prompt you for an API key to PyPI, which you must have for it to work.
18
-
19
- We also have a test PyPI account which you can use to test creating releases before pushing to the actual PyPI hub. I believe you can only create **one** release per version number, so it may be worth testing if you're paranoid about getting it right.
20
-
21
- Also make sure to update the docs and increment the docs version number to match the new release. Keeping these consistent will provide a better user experience.
File without changes
File without changes
File without changes