sapiopycommons 2025.10.20a789__py3-none-any.whl → 2025.10.21a791__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.

@@ -336,9 +336,9 @@ class AgentServiceBase(ToolServiceServicer, ABC):
336
336
 
337
337
  # Instantiate the agent class.
338
338
  agent: AgentBase = registered_agents[find_agent]()
339
+ # Setup the agent with details from the request.
340
+ agent.setup(user, request, context, self.debug_mode)
339
341
  try:
340
- # Setup the agent with details from the request.
341
- agent.setup(user, request, context, self.debug_mode)
342
342
  # Validate that the provided inputs match the agent's expected inputs.
343
343
  msg: str = ""
344
344
  if len(request.input) != len(agent.input_configs):
@@ -387,14 +387,52 @@ class AgentBase(ABC):
387
387
 
388
388
  logs: list[str]
389
389
  logger: Logger
390
- verbose_logging: bool
391
-
392
- temp_data: TempFileHandler
393
-
394
- user: SapioUser
395
- request: ProcessStepRequestPbo
396
- context: ServicerContext
397
- debug_mode: bool
390
+ _verbose_logging: bool | None = None
391
+
392
+ _temp_data: TempFileHandler | None = None
393
+
394
+ _user: SapioUser | None = None
395
+ _request: ProcessStepRequestPbo | None = None
396
+ _context: ServicerContext | None = None
397
+ _debug_mode: bool | None = None
398
+
399
+ __is_setup: bool
400
+
401
+ @property
402
+ def verbose_logging(self) -> bool:
403
+ if not self.__is_setup:
404
+ raise Exception("Agent must be set up to respond to a request before accessing this property.")
405
+ return self._verbose_logging
406
+
407
+ @property
408
+ def temp_data(self) -> TempFileHandler:
409
+ if not self.__is_setup:
410
+ raise Exception("Agent must be set up to respond to a request before accessing this property.")
411
+ return self._temp_data
412
+
413
+ @property
414
+ def user(self) -> SapioUser:
415
+ if not self.__is_setup:
416
+ raise Exception("Agent must be set up to respond to a request before accessing this property.")
417
+ return self._user
418
+
419
+ @property
420
+ def request(self) -> ProcessStepRequestPbo:
421
+ if not self.__is_setup:
422
+ raise Exception("Agent must be set up to respond to a request before accessing this property.")
423
+ return self._request
424
+
425
+ @property
426
+ def context(self) -> ServicerContext:
427
+ if not self.__is_setup:
428
+ raise Exception("Agent must be set up to respond to a request before accessing this property.")
429
+ return self._context
430
+
431
+ @property
432
+ def debug_mode(self) -> bool:
433
+ if not self.__is_setup:
434
+ raise Exception("Agent must be set up to respond to a request before accessing this property.")
435
+ return self._debug_mode
398
436
 
399
437
  @classmethod
400
438
  @abstractmethod
@@ -455,12 +493,12 @@ class AgentBase(ABC):
455
493
  return None
456
494
 
457
495
  def __init__(self):
496
+ self.__is_setup = False
458
497
  self.input_configs = []
459
498
  self.input_container_types = []
460
499
  self.output_configs = []
461
500
  self.output_container_types = []
462
501
  self.config_fields = []
463
- self.temp_data = TempFileHandler()
464
502
  self.logs = []
465
503
  self.logger = logging.getLogger(f"AgentBase.{self.name()}")
466
504
  ensure_logger_initialized(self.logger)
@@ -477,11 +515,13 @@ class AgentBase(ABC):
477
515
  :param debug_mode: If true, the agent should run in debug mode, providing additional logging and not cleaning
478
516
  up temporary files.
479
517
  """
480
- self.user = user
481
- self.request = request
482
- self.context = context
483
- self.verbose_logging = request.verbose_logging
484
- self.debug_mode = debug_mode
518
+ self.__is_setup = True
519
+ self._user = user
520
+ self._request = request
521
+ self._context = context
522
+ self._verbose_logging = request.verbose_logging
523
+ self._debug_mode = debug_mode
524
+ self._temp_data = TempFileHandler()
485
525
 
486
526
  def add_input(self, container_type: ContainerType, content_type: str, display_name: str, description: str,
487
527
  structure_example: str | bytes | None = None, validation: str | None = None,
@@ -947,6 +987,8 @@ class AgentBase(ABC):
947
987
  :param host: The host for which to retrieve the credentials.
948
988
  :return: An ExternalCredentials object containing the credentials for the given category and host.
949
989
  """
990
+ if not self.__is_setup:
991
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
950
992
  # Remove leading/trailing whitespace
951
993
  category = category.strip() if category else None
952
994
  host = host.strip() if host else None
@@ -978,6 +1020,8 @@ class AgentBase(ABC):
978
1020
  :param value: The value of the credentials config field.
979
1021
  :return: An ExternalCredentials object containing the credentials.
980
1022
  """
1023
+ if not self.__is_setup:
1024
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
981
1025
  # Values should be of the format "Name (Identifier)"
982
1026
  match = re.match(r"^(.*) \((.*)\)$", value)
983
1027
  if not match:
@@ -1004,23 +1048,34 @@ class AgentBase(ABC):
1004
1048
  """
1005
1049
  try:
1006
1050
  self.log_info(f"Running subprocess with command: {' '.join(args)}")
1007
- return subprocess.run(args, check=True, capture_output=True, text=True, cwd=cwd, **kwargs)
1051
+ p: CompletedProcess[str] = subprocess.run(args, check=True, capture_output=True, text=True, cwd=cwd,
1052
+ **kwargs)
1053
+ if p.stdout:
1054
+ self.log_info(f"STDOUT: {p.stdout}")
1055
+ if p.stderr:
1056
+ self.log_info(f"STDERR: {p.stderr}")
1057
+ return p
1008
1058
  except subprocess.CalledProcessError as e:
1009
1059
  self.log_error(f"Error running subprocess. Return code: {e.returncode}")
1010
- self.log_error(f"STDOUT: {e.stdout}")
1011
- self.log_error(f"STDERR: {e.stderr}")
1060
+ if e.stdout:
1061
+ self.log_error(f"STDOUT: {e.stdout}")
1062
+ if e.stderr:
1063
+ self.log_error(f"STDERR: {e.stderr}")
1012
1064
  raise
1013
1065
 
1014
1066
  def log_info(self, message: str) -> None:
1015
1067
  """
1016
1068
  Log an info message for this agent. If verbose logging is enabled, this message will be included in the logs
1017
1069
  returned to the caller. Empty/None inputs will not be logged.
1070
+
1071
+ Logging info can be done during initialization, but those logs will not be returned to the caller. Other
1072
+ log calls will be returned to the caller, even if done during initialization.
1018
1073
 
1019
1074
  :param message: The message to log.
1020
1075
  """
1021
1076
  if not message:
1022
1077
  return
1023
- if self.verbose_logging:
1078
+ if self.__is_setup and self.verbose_logging:
1024
1079
  self.logs.append(f"INFO: {self.name()}: {message}")
1025
1080
  self.logger.info(message)
1026
1081
 
@@ -1068,6 +1123,8 @@ class AgentBase(ABC):
1068
1123
  :param index: The index of the input to check. Defaults to 0. Used for agents that accept multiple inputs.
1069
1124
  :return: True if the input is marked as partial, False otherwise.
1070
1125
  """
1126
+ if not self.__is_setup:
1127
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1071
1128
  return self.request.input[index].is_partial
1072
1129
 
1073
1130
  def get_input_name(self, index: int = 0) -> str | None:
@@ -1077,6 +1134,8 @@ class AgentBase(ABC):
1077
1134
  :param index: The index of the input to parse. Defaults to 0. Used for agents that accept multiple inputs.
1078
1135
  :return: The name of the input from the request object, or None if no name is set.
1079
1136
  """
1137
+ if not self.__is_setup:
1138
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1080
1139
  return self.request.input[index].item_container.container_name
1081
1140
 
1082
1141
  def get_input_content_type(self, index: int = 0) -> ContentTypePbo:
@@ -1086,6 +1145,8 @@ class AgentBase(ABC):
1086
1145
  :param index: The index of the input to parse. Defaults to 0. Used for agents that accept multiple inputs.
1087
1146
  :return: The content type of the input from the request object.
1088
1147
  """
1148
+ if not self.__is_setup:
1149
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1089
1150
  return self.request.input[index].item_container.content_type
1090
1151
 
1091
1152
  def get_input_binary(self, index: int = 0) -> list[bytes]:
@@ -1095,6 +1156,8 @@ class AgentBase(ABC):
1095
1156
  :param index: The index of the input to parse. Defaults to 0. Used for agents that accept multiple inputs.
1096
1157
  :return: The binary data from the request object.
1097
1158
  """
1159
+ if not self.__is_setup:
1160
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1098
1161
  container: StepItemContainerPbo = self.request.input[index].item_container
1099
1162
  if not container.HasField("binary_container"):
1100
1163
  raise Exception(f"Input {index} does not contain a binary container.")
@@ -1109,6 +1172,8 @@ class AgentBase(ABC):
1109
1172
  the column names, and the data rows are a list of dictionaries where each dictionary represents a row in the
1110
1173
  CSV with the column names as keys and the corresponding values as strings.
1111
1174
  """
1175
+ if not self.__is_setup:
1176
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1112
1177
  container: StepItemContainerPbo = self.request.input[index].item_container
1113
1178
  if not container.HasField("csv_container"):
1114
1179
  raise Exception(f"Input {index} does not contain a CSV container.")
@@ -1128,6 +1193,8 @@ class AgentBase(ABC):
1128
1193
  :param index: The index of the input to parse. Defaults to 0. Used for agents that accept multiple inputs.
1129
1194
  :return: A list of parsed JSON objects, which are represented as dictionaries.
1130
1195
  """
1196
+ if not self.__is_setup:
1197
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1131
1198
  container: StepItemContainerPbo = self.request.input[index].item_container
1132
1199
  if not container.HasField("json_container"):
1133
1200
  raise Exception(f"Input {index} does not contain a JSON container.")
@@ -1148,6 +1215,8 @@ class AgentBase(ABC):
1148
1215
  :param index: The index of the input to parse. Defaults to 0. Used for agents that accept multiple inputs.
1149
1216
  :return: A list of text data as strings.
1150
1217
  """
1218
+ if not self.__is_setup:
1219
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1151
1220
  container: StepItemContainerPbo = self.request.input[index].item_container
1152
1221
  if not container.HasField("text_container"):
1153
1222
  raise Exception(f"Input {index} does not contain a text container.")
@@ -1175,6 +1244,8 @@ class AgentBase(ABC):
1175
1244
  (bool for boolean fields, float for double fields, int for short, integer, long, and enum fields, and
1176
1245
  string for everything else).
1177
1246
  """
1247
+ if not self.__is_setup:
1248
+ raise Exception("Cannot call this function before the agent has been set up to respond to a request.")
1178
1249
  config_fields: dict[str, Any] = {}
1179
1250
  raw_configs: Mapping[str, FieldValuePbo] = self.request.config_field_values
1180
1251
  for field_name, field_def in self.get_config_defs().items():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2025.10.20a789
3
+ Version: 2025.10.21a791
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>
@@ -1,6 +1,6 @@
1
1
  sapiopycommons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  sapiopycommons/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- sapiopycommons/ai/agent_service_base.py,sha256=eTJunFQoxLc0risWiQIkQK946XpHzSFO38NzzRvOn9Q,60325
3
+ sapiopycommons/ai/agent_service_base.py,sha256=XrfIa4g6qaZcVw4aIp1d7yM3zUrSd4rSXBivT0WYWA4,63863
4
4
  sapiopycommons/ai/converter_service_base.py,sha256=HiUXmwqv1STgyQeF9_eTFXzjIFXp5-NJ7sEhMpV3aAU,6351
5
5
  sapiopycommons/ai/external_credentials.py,sha256=ki_xIH4J843b_sSwEa8YHr8vW9erVv-jowZJXSgPQs8,4347
6
6
  sapiopycommons/ai/protobuf_utils.py,sha256=cBjbxoFAwU02kNUxEce95WnMU2CMuDD-qFaeWgvQJMQ,24599
@@ -106,7 +106,7 @@ sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
106
106
  sapiopycommons/webhook/webhook_context.py,sha256=D793uLsb1691SalaPnBUk3rOSxn_hYLhdvkaIxjNXss,1909
107
107
  sapiopycommons/webhook/webhook_handlers.py,sha256=7o_wXOruhT9auNh8OfhJAh4WhhiPKij67FMBSpGPICc,39939
108
108
  sapiopycommons/webhook/webservice_handlers.py,sha256=cvW6Mk_110BzYqkbk63Kg7jWrltBCDALOlkJRu8h4VQ,14300
109
- sapiopycommons-2025.10.20a789.dist-info/METADATA,sha256=LW1ECHFl-cG6BPslNdeFfIGKFHCxPgtivYUg9mqsnSQ,3143
110
- sapiopycommons-2025.10.20a789.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
111
- sapiopycommons-2025.10.20a789.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
112
- sapiopycommons-2025.10.20a789.dist-info/RECORD,,
109
+ sapiopycommons-2025.10.21a791.dist-info/METADATA,sha256=bQa43tNthQ6Nj8OUw7tVaFMwSEQUY-o8RB_hsupFXw4,3143
110
+ sapiopycommons-2025.10.21a791.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
111
+ sapiopycommons-2025.10.21a791.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
112
+ sapiopycommons-2025.10.21a791.dist-info/RECORD,,