sapiopycommons 2025.9.5a726__py3-none-any.whl → 2025.9.8a728__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 sapiopycommons might be problematic. Click here for more details.

@@ -29,6 +29,7 @@ from sapiopycommons.ai.protoapi.session.sapio_conn_info_pb2 import SapioUserSecr
29
29
  from sapiopycommons.ai.protobuf_utils import ProtobufUtils
30
30
  from sapiopycommons.ai.test_client import ContainerType
31
31
  from sapiopycommons.files.file_util import FileUtil
32
+ from sapiopycommons.files.temp_files import TempFileHandler
32
33
  from sapiopycommons.general.aliases import FieldMap, FieldValue
33
34
 
34
35
 
@@ -318,8 +319,8 @@ class ToolServiceBase(ToolServiceServicer, ABC):
318
319
  # If the tool is not found, list all of the registered tools for this service so that the LLM can correct
319
320
  # the tool it is requesting.
320
321
  all_tool_names: str = "\n".join(registered_tools.keys())
321
- msg: str = (f"Tool \"{find_tool}\" not found in the registered tools for this service. The registered tools "
322
- f"for this service are: \n{all_tool_names}")
322
+ msg: str = (f"Tool \"{find_tool}\" not found in the registered tools for this service. The registered "
323
+ f"tools for this service are: \n{all_tool_names}")
323
324
  return False, msg, [], []
324
325
 
325
326
  # Instantiate the tool class.
@@ -329,7 +330,8 @@ class ToolServiceBase(ToolServiceServicer, ABC):
329
330
  tool.setup(user, request, context)
330
331
  # Validate that the provided inputs match the tool's expected inputs.
331
332
  if len(request.input) != len(tool.input_configs):
332
- msg: str = f"Expected {len(tool.input_configs)} inputs for this tool, but got {len(request.input)} instead."
333
+ msg: str = (f"Expected {len(tool.input_configs)} inputs for this tool, but got {len(request.input)} "
334
+ f"instead.")
333
335
  else:
334
336
  msg: str = tool.validate_input()
335
337
  # If there is no error message, then the inputs are valid.
@@ -347,6 +349,9 @@ class ToolServiceBase(ToolServiceServicer, ABC):
347
349
  except Exception as e:
348
350
  tool.log_exception("Exception occurred during tool execution.", e)
349
351
  return False, str(e), [], tool.logs
352
+ finally:
353
+ # Clean up any temporary files created by the tool.
354
+ tool.temp_data.cleanup()
350
355
 
351
356
 
352
357
  class ToolBase(ABC):
@@ -366,6 +371,8 @@ class ToolBase(ABC):
366
371
  logger: Logger
367
372
  verbose_logging: bool
368
373
 
374
+ temp_data: TempFileHandler
375
+
369
376
  user: SapioUser
370
377
  request: ProcessStepRequestPbo
371
378
  context: ServicerContext
@@ -403,6 +410,7 @@ class ToolBase(ABC):
403
410
  self.output_configs = []
404
411
  self._output_container_types = []
405
412
  self.config_fields = []
413
+ self.temp_data = TempFileHandler()
406
414
  self.logs = []
407
415
  self.logger = logging.getLogger(f"ToolBase.{self._name}")
408
416
  ensure_logger_initialized(self.logger)
@@ -528,8 +536,8 @@ class ToolBase(ABC):
528
536
  """
529
537
  self.config_fields.append(ProtobufUtils.field_def_to_pbo(field))
530
538
 
531
- def add_boolean_config_field(self, field_name: str, display_name: str, description: str, default_value: bool,
532
- optional: bool = False) -> None:
539
+ def add_boolean_config_field(self, field_name: str, display_name: str, description: str,
540
+ default_value: bool | None = None, optional: bool = False) -> None:
533
541
  """
534
542
  Add a boolean configuration field to the tool. This field will be used to configure the tool in the plan
535
543
  manager.
@@ -552,9 +560,9 @@ class ToolBase(ABC):
552
560
  )
553
561
  ))
554
562
 
555
- def add_double_config_field(self, field_name: str, display_name: str, description: str, default_value: float,
556
- min_value: float = -10.**120, max_value: float = 10.**120, precision: int = 2,
557
- optional: bool = False) -> None:
563
+ def add_double_config_field(self, field_name: str, display_name: str, description: str,
564
+ default_value: float | None = None, min_value: float = -10.**120,
565
+ max_value: float = 10.**120, precision: int = 2, optional: bool = False) -> None:
558
566
  """
559
567
  Add a double configuration field to the tool. This field will be used to configure the tool in the plan
560
568
  manager.
@@ -584,7 +592,7 @@ class ToolBase(ABC):
584
592
  ))
585
593
 
586
594
  def add_integer_config_field(self, field_name: str, display_name: str, description: str,
587
- default_value: int, min_value: int = -2**31, max_value: int = 2**31-1,
595
+ default_value: int | None = None, min_value: int = -2**31, max_value: int = 2**31-1,
588
596
  optional: bool = False) -> None:
589
597
  """
590
598
  Add an integer configuration field to the tool. This field will be used to configure the tool in the plan
@@ -613,7 +621,8 @@ class ToolBase(ABC):
613
621
  ))
614
622
 
615
623
  def add_string_config_field(self, field_name: str, display_name: str, description: str,
616
- default_value: str, max_length: int = 1000, optional: bool = False) -> None:
624
+ default_value: str | None = None, max_length: int = 1000, optional: bool = False) \
625
+ -> None:
617
626
  """
618
627
  Add a string configuration field to the tool. This field will be used to configure the tool in the plan
619
628
  manager.
@@ -638,8 +647,9 @@ class ToolBase(ABC):
638
647
  )
639
648
  ))
640
649
 
641
- def add_list_config_field(self, field_name: str, display_name: str, description: str, default_value: str,
642
- allowed_values: list[str], direct_edit: bool = False, optional: bool = False) -> None:
650
+ def add_list_config_field(self, field_name: str, display_name: str, description: str,
651
+ default_value: str | None = None, allowed_values: list[str] | None = None,
652
+ direct_edit: bool = False, optional: bool = False) -> None:
643
653
  """
644
654
  Add a list configuration field to the tool. This field will be used to configure the tool in the plan
645
655
  manager.
@@ -668,8 +678,8 @@ class ToolBase(ABC):
668
678
  ))
669
679
 
670
680
  def add_multi_list_config_field(self, field_name: str, display_name: str, description: str,
671
- default_value: list[str], allowed_values: list[str], direct_edit: bool = False,
672
- optional: bool = False) -> None:
681
+ default_value: list[str] | None = None, allowed_values: list[str] | None = None,
682
+ direct_edit: bool = False, optional: bool = False) -> None:
673
683
  """
674
684
  Add a multi-select list configuration field to the tool. This field will be used to configure the tool in the
675
685
  plan manager.
@@ -691,7 +701,7 @@ class ToolBase(ABC):
691
701
  required=not optional,
692
702
  editable=True,
693
703
  selection_properties=SelectionPropertiesPbo(
694
- default_value=",".join(default_value),
704
+ default_value=",".join(default_value) if default_value else None,
695
705
  static_list_values=allowed_values,
696
706
  multi_select=True,
697
707
  direct_edit=direct_edit,
@@ -0,0 +1,49 @@
1
+ import os
2
+ import shutil
3
+ import tempfile
4
+
5
+
6
+ # FR-47422: Created class.
7
+ class TempFileHandler:
8
+ """
9
+ A utility class to manage temporary files and directories.
10
+ """
11
+ directories: list[str]
12
+ files: list[str]
13
+
14
+ def __init__(self) -> None:
15
+ self.directories = []
16
+ self.files = []
17
+
18
+ def create_temp_directory(self) -> str:
19
+ """
20
+ :return: The path to a newly created temporary directory.
21
+ """
22
+ directory: str = tempfile.mkdtemp()
23
+ self.directories.append(directory)
24
+ return directory
25
+
26
+ def create_temp_file(self, data: str | bytes, suffix: str = "") -> str:
27
+ """
28
+ :param data: The data to write to the temporary file.
29
+ :param suffix: An optional suffix for the temporary file.
30
+ :return: The path to a newly created temporary file containing the provided data.
31
+ """
32
+ mode: str = 'w' if isinstance(data, str) else 'wb'
33
+ with tempfile.NamedTemporaryFile(mode=mode, suffix=suffix, delete=False) as tmp_file:
34
+ tmp_file.write(data)
35
+ file_path: str = tmp_file.name
36
+ self.files.append(file_path)
37
+ return file_path
38
+
39
+ def cleanup(self) -> None:
40
+ """
41
+ Delete all temporary files and directories created by this handler.
42
+ """
43
+ for directory in self.directories:
44
+ if os.path.exists(directory):
45
+ shutil.rmtree(directory)
46
+
47
+ for file_path in self.files:
48
+ if os.path.exists(file_path):
49
+ os.remove(file_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2025.9.5a726
3
+ Version: 2025.9.8a728
4
4
  Summary: Official Sapio Python API Utilities Package
5
5
  Project-URL: Homepage, https://github.com/sapiosciences
6
6
  Author-email: Jonathan Steck <jsteck@sapiosciences.com>, Yechen Qiao <yqiao@sapiosciences.com>
@@ -4,7 +4,7 @@ sapiopycommons/ai/converter_service_base.py,sha256=TMSyEekbbqMk9dRuAtLlSJ1sA1H8K
4
4
  sapiopycommons/ai/protobuf_utils.py,sha256=cBjbxoFAwU02kNUxEce95WnMU2CMuDD-qFaeWgvQJMQ,24599
5
5
  sapiopycommons/ai/server.py,sha256=jvmAcs4y8qp0d483wCAUgPlSBSoUUQvCjv_OvQXEGUs,4274
6
6
  sapiopycommons/ai/test_client.py,sha256=iPhn7cvKNLmDAXrjpmIkZpW2pDWlUhJZHDLHJbEoWsg,15673
7
- sapiopycommons/ai/tool_service_base.py,sha256=Cj4ewRfBSiW1Tdzn4hkm9P-gDa6NS6fh4jc4TUmvNMs,45336
7
+ sapiopycommons/ai/tool_service_base.py,sha256=AVW6Yf0l_l4x__dosIZyAJun4UmZAZaBJRYzNru_fD4,45806
8
8
  sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.py,sha256=8tKXwLXcqFGdQHHSEBSi6Fd7dcaCFoOqmhjzqhenb_M,2372
9
9
  sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.pyi,sha256=FwtXmNAf7iYGEFm4kbqb04v77jNHbZg18ZmEDhle_bU,1444
10
10
  sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2_grpc.py,sha256=uO25bcnfGqXpP4ggUur54Nr73Wj-DGWftExzLNcxdHI,931
@@ -66,6 +66,7 @@ sapiopycommons/files/file_data_handler.py,sha256=f96MlkMuQhUCi4oLnzJK5AiuElCp5jL
66
66
  sapiopycommons/files/file_util.py,sha256=djouyGjsYgWzjz2OBRnSeMDgj6NrsJUm1a2J93J8Wco,31915
67
67
  sapiopycommons/files/file_validator.py,sha256=ryg22-93csmRO_Pv0ZpWphNkB74xWZnHyJ23K56qLj0,28761
68
68
  sapiopycommons/files/file_writer.py,sha256=hACVl0duCjP28gJ1NPljkjagNCLod0ygUlPbvUmRDNM,17605
69
+ sapiopycommons/files/temp_files.py,sha256=Puv59qtGwiXVJnTm4YuyeZPKw_leXDW906Uz_xbIt6A,1542
69
70
  sapiopycommons/flowcyto/flow_cyto.py,sha256=vs9WhXXKz3urpjL8QKSk56B-NSmQR3O3x_WFBKoeO10,3227
70
71
  sapiopycommons/flowcyto/flowcyto_data.py,sha256=mYKFuLbtpJ-EsQxLGtu4tNHVlygTxKixgJxJqD68F58,2596
71
72
  sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -98,7 +99,7 @@ sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
98
99
  sapiopycommons/webhook/webhook_context.py,sha256=D793uLsb1691SalaPnBUk3rOSxn_hYLhdvkaIxjNXss,1909
99
100
  sapiopycommons/webhook/webhook_handlers.py,sha256=7o_wXOruhT9auNh8OfhJAh4WhhiPKij67FMBSpGPICc,39939
100
101
  sapiopycommons/webhook/webservice_handlers.py,sha256=cvW6Mk_110BzYqkbk63Kg7jWrltBCDALOlkJRu8h4VQ,14300
101
- sapiopycommons-2025.9.5a726.dist-info/METADATA,sha256=VLxZnp7_F2kc3BrihRHSG3dN_oeBhoDWNazF-dUWsDo,3142
102
- sapiopycommons-2025.9.5a726.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
103
- sapiopycommons-2025.9.5a726.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
104
- sapiopycommons-2025.9.5a726.dist-info/RECORD,,
102
+ sapiopycommons-2025.9.8a728.dist-info/METADATA,sha256=6IMqQ6KDSTEcXMpbfbwpBSC_eGnhn-336JCdZkvNmR0,3142
103
+ sapiopycommons-2025.9.8a728.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
104
+ sapiopycommons-2025.9.8a728.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
105
+ sapiopycommons-2025.9.8a728.dist-info/RECORD,,