sapiopycommons 2025.4.24a494__py3-none-any.whl → 2025.4.24a495__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.

@@ -183,22 +183,15 @@ class TextResult(SapioToolResult):
183
183
 
184
184
  class ToolServiceBase(ToolServiceServicer, ABC):
185
185
  """
186
- A base class for implementing a tool service.
187
-
188
- Subclasses must implement the get_details and run methods to provide specific functionality for the tool.
186
+ A base class for implementing a tool service. Subclasses should implement the register_tools method to register
187
+ their tools with the service.
189
188
  """
190
- tools: list[ToolBase]
191
-
192
189
  def GetToolDetails(self, request: ToolDetailsRequest, context: ServicerContext) -> ToolDetailsResponse:
193
190
  try:
194
- # Convert the SapioConnectionInfo proto object to a SapioUser object.
195
- user = self.create_user(request.sapio_conn_info)
196
- # Get the tool details from the subclass.
197
- # TODO: Return something other than the ToolDetails proto objects? Something that's cleaner for the
198
- # implementing class to work with?
199
- details: list[ToolDetails] = self.get_details(user, request, context)
191
+ # Get the tool details from the registered tools.
192
+ details: list[ToolDetails] = self.get_details()
200
193
  return ToolDetailsResponse(tool_framework_version=self.tool_version(), tool_details=details)
201
- except Exception as e:
194
+ except Exception:
202
195
  # TODO: This response doesn't even allow logs. What should we do if an exception occurs in this case?
203
196
  return ToolDetailsResponse()
204
197
 
@@ -206,22 +199,21 @@ class ToolServiceBase(ToolServiceServicer, ABC):
206
199
  try:
207
200
  # Convert the SapioConnectionInfo proto object to a SapioUser object.
208
201
  user = self.create_user(request.sapio_user)
209
- # Get the tool results from the subclass and convert them to proto objects.
202
+ # Get the tool results from the registered tool matching the request and convert them to proto objects.
210
203
  entry_data: list[StepEntryOutputData] = []
211
204
  new_records: list[StepRecord] = []
212
- for result in self.run(user, request, context):
205
+ # TODO: Make use of the success value after the response object has a field for it.
206
+ success, results, logs = self.run(user, request, context)
207
+ for result in results:
213
208
  data: StepEntryOutputData | list[StepRecord] = result.to_proto()
214
209
  if isinstance(data, StepEntryOutputData):
215
210
  entry_data.append(data)
216
211
  else:
217
212
  new_records.extend(data)
218
213
  # Return a ProcessStepResponse proto object containing the output data and new records to the caller.
219
- # TODO: Return an is_success = true value.
220
- # TODO: Allow logging in the tools?
221
- return ProcessStepResponse(entry_data=entry_data, new_records=new_records)
222
- except Exception as e:
223
- # TODO: Do something other than dump the full stack trace into the logs?
224
- # TODO: Eventually we'll return an is_success = false value.
214
+ return ProcessStepResponse(entry_data=entry_data, log=logs, new_records=new_records)
215
+ except Exception:
216
+ # TODO: Return a False success result after the response object has a field for it.
225
217
  return ProcessStepResponse(log=[traceback.format_exc()])
226
218
 
227
219
  @staticmethod
@@ -253,32 +245,40 @@ class ToolServiceBase(ToolServiceServicer, ABC):
253
245
 
254
246
  def _get_tools(self) -> list[ToolBase]:
255
247
  """
256
- Get the tools registered with this service.
248
+ return: Get the tools registered with this service.
249
+ """
250
+ tools: list[ToolBase] = self.register_tools()
251
+ if not tools:
252
+ raise Exception("No tools registered with this service.")
253
+ return tools
254
+
255
+ def _get_tool(self, name: str) -> ToolBase:
256
+ """
257
+ Get a specific tool by its name.
258
+
259
+ :param name: The name of the tool to retrieve.
260
+ :return: The tool object corresponding to the given name.
257
261
  """
258
- # If the tools have not been registered, call the register_tools method to do so.
259
- if not hasattr(self, "tools"):
260
- self.tools = self.register_tools()
261
- # If the tools are still not registered, raise an exception.
262
- if not self.tools:
262
+ tools: dict[str, ToolBase] = {x.name: x for x in self.register_tools()}
263
+ if not tools:
263
264
  raise Exception("No tools registered with this service.")
264
- return self.tools
265
+ if name not in tools:
266
+ raise Exception(f"Tool \"{name}\" not found in registered tools.")
267
+ return tools[name]
265
268
 
266
269
  @abstractmethod
267
270
  def register_tools(self) -> list[ToolBase]:
268
271
  """
269
- Register the tools with this service.
272
+ Register the tools with this service. Create and instantiate ToolBase subclasses to register them.
273
+
274
+ :return: A list of tools to register to this service.
270
275
  """
271
276
  pass
272
277
 
273
- @abstractmethod
274
- def get_details(self, user: SapioUser, request: ToolDetailsRequest, context: ServicerContext) -> list[ToolDetails]:
278
+ def get_details(self) -> list[ToolDetails]:
275
279
  """
276
280
  Get the details of the tool.
277
281
 
278
- :param user: A user object that can be used to initialize manager classes using DataMgmtServer to query the
279
- system.
280
- :param request: The request object containing the input data.
281
- :param context: The gRPC context.
282
282
  :return: A ToolDetailsResponse object containing the tool details.
283
283
  """
284
284
  tool_details: list[ToolDetails] = []
@@ -286,8 +286,8 @@ class ToolServiceBase(ToolServiceServicer, ABC):
286
286
  tool_details.append(tool.to_proto())
287
287
  return tool_details
288
288
 
289
- @abstractmethod
290
- def run(self, user: SapioUser, request: ProcessStepRequest, context: ServicerContext) -> list[SapioToolResult]:
289
+ def run(self, user: SapioUser, request: ProcessStepRequest, context: ServicerContext) \
290
+ -> tuple[bool, list[SapioToolResult], list[str]]:
291
291
  """
292
292
  Execute a tool from this service.
293
293
 
@@ -295,32 +295,48 @@ class ToolServiceBase(ToolServiceServicer, ABC):
295
295
  system.
296
296
  :param request: The request object containing the input data.
297
297
  :param context: The gRPC context.
298
- :return: A SapioToolResults object containing the response data.
298
+ :return: Whether or not the tool succeeded, the results of the tool, and any logs generated by the tool.
299
299
  """
300
- for tool in self._get_tools():
301
- if request.tool_name == tool.name:
302
- return tool.run(user, request, context)
303
- raise Exception(f"Tool {request.tool_name} not found in registered tools.")
300
+ tool = self._get_tool(request.tool_name)
301
+ try:
302
+ results = tool.run(user, request, context)
303
+ return True, results, tool.logs
304
+ except Exception:
305
+ tool.log_message(traceback.format_exc())
306
+ return False, [], tool.logs
304
307
 
305
308
 
306
309
  class ToolBase(ABC):
310
+ """
311
+ A base class for implementing a tool.
312
+ """
307
313
  name: str
308
314
  description: str
309
315
  data_type_name: str | None
310
316
  inputs: list[ToolInputDetails]
311
317
  outputs: list[ToolOutputDetails]
312
318
  configs: list[VeloxFieldDefProto]
319
+ logs: list[str]
313
320
 
314
321
  def __init__(self, name: str, description: str, data_type_name: str | None = None):
322
+ """
323
+ :param name: The name of the tool.
324
+ :param description: A description of the tool.
325
+ :param data_type_name: The name of the output data type of this tool, if applicable. When this tool returns
326
+ FieldMapResult objects in its run method, this name will be used to set the data type of the output data.
327
+ """
315
328
  self.name = name
316
329
  self.description = description
317
330
  self.data_type_name = data_type_name
318
331
  self.inputs = []
319
332
  self.outputs = []
333
+ self.configs = []
334
+ self.logs = []
320
335
 
321
336
  def add_input(self, details: ToolInputDetails) -> None:
322
337
  """
323
- Add an input configuration to the tool.
338
+ Add an input configuration to the tool. This determines how many inputs this tool will accept in the plan
339
+ manager, as well as what those inputs are.
324
340
 
325
341
  :param details: The input configuration details.
326
342
  """
@@ -328,7 +344,8 @@ class ToolBase(ABC):
328
344
 
329
345
  def add_output(self, details: ToolOutputDetails) -> None:
330
346
  """
331
- Add an output configuration to the tool.
347
+ Add an output configuration to the tool. This determines how many outputs this tool will return in the plan
348
+ manager, as well as what those outputs are.
332
349
 
333
350
  :param details: The output configuration details.
334
351
  """
@@ -336,7 +353,7 @@ class ToolBase(ABC):
336
353
 
337
354
  def add_config_field(self, field: VeloxFieldDefProto) -> None:
338
355
  """
339
- Add a configuration field to the tool.
356
+ Add a configuration field to the tool. This field will be used to configure the tool in the plan manager.
340
357
 
341
358
  :param field: The configuration field details.
342
359
  """
@@ -344,9 +361,7 @@ class ToolBase(ABC):
344
361
 
345
362
  def to_proto(self) -> ToolDetails:
346
363
  """
347
- Convert this tool to a ToolDetails proto object.
348
-
349
- :return: The ToolDetails proto object.
364
+ :return: The ToolDetails proto object representing this tool.
350
365
  """
351
366
  return ToolDetails(
352
367
  name=self.name,
@@ -357,6 +372,14 @@ class ToolBase(ABC):
357
372
  config_fields=self.configs
358
373
  )
359
374
 
375
+ def log_message(self, message: str) -> None:
376
+ """
377
+ Log a message for this tool. This message will be included in the logs returned to the caller.
378
+
379
+ :param message: The message to log.
380
+ """
381
+ self.logs.append(message)
382
+
360
383
  @abstractmethod
361
384
  def run(self, user: SapioUser, request: ProcessStepRequest, context: ServicerContext) -> list[SapioToolResult]:
362
385
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2025.4.24a494
3
+ Version: 2025.4.24a495
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,7 +1,7 @@
1
1
  sapiopycommons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  sapiopycommons/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  sapiopycommons/ai/tool_of_tools.py,sha256=zYmQ4rNX-qYQnc-vNDnYZjtv9JgmQAmVVuHfVOdBF3w,46984
4
- sapiopycommons/ai/tool_service_base.py,sha256=tftW0TFatccbx47QtfiKcsZaPpOHoOlEfDlrU_IQVUU,14376
4
+ sapiopycommons/ai/tool_service_base.py,sha256=NFW5ulS__vDyASZ4VwsUDYRZNpN7QlKULGflvqcq6i4,15279
5
5
  sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2.py,sha256=qPkyQsREtTLMliV9JB6tC5-NhmdWVWHJr70XNfcAfDI,20605
6
6
  sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2.pyi,sha256=gVXRsuscx9XavKsTcepzXWf0LDAAyQ_J5ZjFK6kPYuo,34028
7
7
  sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2_grpc.py,sha256=4vD4jWanaJ4uclSkFmS7JIz_lwYXDWBE3DomuPjUyII,941
@@ -82,7 +82,7 @@ sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
82
82
  sapiopycommons/webhook/webhook_context.py,sha256=D793uLsb1691SalaPnBUk3rOSxn_hYLhdvkaIxjNXss,1909
83
83
  sapiopycommons/webhook/webhook_handlers.py,sha256=L0HetSm43NvA5KyW3xbLpGFh2DbAaeZJVtXIEl2fvV8,39689
84
84
  sapiopycommons/webhook/webservice_handlers.py,sha256=Y5dHx_UFWFuSqaoPL6Re-fsKYRuxvCWZ8bj6KSZ3jfM,14285
85
- sapiopycommons-2025.4.24a494.dist-info/METADATA,sha256=sLGtVufAGHGHXHpjTPI4TYwjcG1ya14ACJRNVWkLPHw,3143
86
- sapiopycommons-2025.4.24a494.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
87
- sapiopycommons-2025.4.24a494.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
88
- sapiopycommons-2025.4.24a494.dist-info/RECORD,,
85
+ sapiopycommons-2025.4.24a495.dist-info/METADATA,sha256=Q0s3dSO7fgqX3UkSA5n0GxaaNjIADxUHhvToU8XBkmM,3143
86
+ sapiopycommons-2025.4.24a495.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
87
+ sapiopycommons-2025.4.24a495.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
88
+ sapiopycommons-2025.4.24a495.dist-info/RECORD,,