sapiopycommons 2025.10.10a778__py3-none-any.whl → 2025.10.13a781__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.

@@ -1,5 +1,6 @@
1
1
  import base64
2
2
  import json
3
+ import os
3
4
  from enum import Enum
4
5
  from typing import Any
5
6
 
@@ -20,6 +21,7 @@ from sapiopycommons.ai.protoapi.plan.tool.tool_pb2_grpc import ToolServiceStub
20
21
  from sapiopycommons.ai.protoapi.session.sapio_conn_info_pb2 import SapioConnectionInfoPbo, SapioUserSecretTypePbo
21
22
  from sapiopycommons.ai.protobuf_utils import ProtobufUtils
22
23
  from sapiopycommons.general.aliases import FieldValue
24
+ from sapiopycommons.general.time_util import TimeUtil
23
25
 
24
26
 
25
27
  class ContainerType(Enum):
@@ -43,6 +45,8 @@ class AgentOutput:
43
45
  status: str
44
46
  message: str
45
47
 
48
+ # Outputs are lists of lists, where the outer lists are the different outputs of the tool, and the inner lists
49
+ # are the entries for that output.
46
50
  binary_output: list[list[bytes]]
47
51
  csv_output: list[list[dict[str, Any]]]
48
52
  json_output: list[list[dict[str, Any]]]
@@ -61,10 +65,54 @@ class AgentOutput:
61
65
  self.new_records = []
62
66
  self.logs = []
63
67
 
68
+ def save_outputs(self, path: str = "test_outputs", subfolder: str | None = None, file_extensions: list[str] | None = None) -> None:
69
+ """
70
+ Save all outputs to files in the specified output directory.
71
+
72
+ :param path: The directory to save the output files to.
73
+ :param subfolder: An optional subfolder within the path to save the output files to. Useful for when you are
74
+ calling the same agent multiple times for separate test cases.
75
+ :param file_extensions: A list of file extensions to use for binary output files. The length of this list
76
+ should match the number of binary outputs.
77
+ """
78
+ if not self:
79
+ return
80
+ output_path: str = os.path.join(path, self.agent_name)
81
+ if subfolder:
82
+ output_path = os.path.join(output_path, subfolder)
83
+ os.makedirs(output_path, exist_ok=True)
84
+ if self.binary_output and (file_extensions is None or len(file_extensions) != len(self.binary_output)):
85
+ raise ValueError("File extensions must be provided for each binary output.")
86
+ for i, output in enumerate(self.binary_output):
87
+ ext: str = "." + file_extensions[i].lstrip(".")
88
+ for j, entry in enumerate(output):
89
+ with open(os.path.join(output_path, f"binary_output_{i}_{j}{ext}"), "wb") as f:
90
+ f.write(entry)
91
+ for i, output in enumerate(self.csv_output):
92
+ with open(os.path.join(output_path, f"csv_output_{i}.csv"), "w", encoding="utf-8") as f:
93
+ headers = output[0].keys()
94
+ f.write(",".join(headers) + "\n")
95
+ for row in output:
96
+ f.write(",".join(f'"{str(row[h])}"' for h in headers) + "\n")
97
+ for i, output in enumerate(self.json_output):
98
+ for j, entry in enumerate(output):
99
+ with open(os.path.join(output_path, f"json_output_{i}_{j}.json"), "w", encoding="utf-8") as f:
100
+ json.dump(entry, f, indent=2)
101
+ for i, output in enumerate(self.text_output):
102
+ for j, entry in enumerate(output):
103
+ with open(os.path.join(output_path, f"text_output_{i}_{j}.txt"), "w", encoding="utf-8") as f:
104
+ f.write(entry)
105
+
64
106
  def __bool__(self):
107
+ """
108
+ Return True if the agent call was successful, False otherwise.
109
+ """
65
110
  return self.status == "Success"
66
111
 
67
112
  def __str__(self):
113
+ """
114
+ Return a string representing a summary of the agent output.
115
+ """
68
116
  ret_val: str = f"{self.agent_name} Output:\n"
69
117
  ret_val += f"\tStatus: {self.status}\n"
70
118
  ret_val += f"\tMessage: {self.message}\n"
@@ -271,9 +319,11 @@ class TestClient:
271
319
  :param is_dry_run: If True, the agent will not be executed, but the request will be validated.
272
320
  :return: An AgentOutput object containing the results of the agent service call.
273
321
  """
322
+ print(f"Calling agent \"{agent_name}\"...")
274
323
  with grpc.insecure_channel(self.grpc_server_url, options=self.options) as channel:
275
324
  stub = ToolServiceStub(channel)
276
325
 
326
+ start = TimeUtil.now_in_millis()
277
327
  response: ProcessStepResponsePbo = stub.ProcessData(
278
328
  ProcessStepRequestPbo(
279
329
  sapio_user=self.connection,
@@ -287,6 +337,8 @@ class TestClient:
287
337
  ]
288
338
  )
289
339
  )
340
+ end = TimeUtil.now_in_millis()
341
+ print(f"Agent call completed in {(end - start) / 1000.:.3f} seconds")
290
342
 
291
343
  results = AgentOutput(agent_name)
292
344
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2025.10.10a778
3
+ Version: 2025.10.13a781
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>
@@ -6,7 +6,7 @@ sapiopycommons/ai/external_credentials.py,sha256=40AI7VtHf6PzvwJLR_mZemUCrfAUvC-
6
6
  sapiopycommons/ai/protobuf_utils.py,sha256=cBjbxoFAwU02kNUxEce95WnMU2CMuDD-qFaeWgvQJMQ,24599
7
7
  sapiopycommons/ai/request_validation.py,sha256=TD2EUi_G5cy1OOVK1AmY2SQc3PEoAKGWs2pT8Qnp2Oo,25092
8
8
  sapiopycommons/ai/server.py,sha256=gutSskn_Fenq1uz0DDMvjx4QVFiKt2WVEP3-01a69eU,6384
9
- sapiopycommons/ai/test_client.py,sha256=C35Z9m4wCQhWPsX_p59VD8eAHP1OkZoJjnUVIdxSmZA,16546
9
+ sapiopycommons/ai/test_client.py,sha256=aiS58O_A3KSgRCzDT61iKyVXy9F4UY7xUyGQu5E7bTw,19432
10
10
  sapiopycommons/ai/protoapi/externalcredentials/external_credentials_pb2.py,sha256=mEonoj6Iq-AyvO4m3YsPYu85aZfD1E0a0cL8B9yPfEo,2481
11
11
  sapiopycommons/ai/protoapi/externalcredentials/external_credentials_pb2.pyi,sha256=sfExq8fFwIwFxCpV50ytdxW5ePNBjJBr_80_trq_JQw,1658
12
12
  sapiopycommons/ai/protoapi/externalcredentials/external_credentials_pb2_grpc.py,sha256=TNS1CB_QGBSa1YU9sYR_hF-pmBwv2GpxjaNQoM_r9iU,948
@@ -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.10a778.dist-info/METADATA,sha256=9wZ4a_ATdKHyPMio03ozGETcptRc1PGztlbDFXd1CVo,3143
110
- sapiopycommons-2025.10.10a778.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
111
- sapiopycommons-2025.10.10a778.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
112
- sapiopycommons-2025.10.10a778.dist-info/RECORD,,
109
+ sapiopycommons-2025.10.13a781.dist-info/METADATA,sha256=Ex7Fc6UXveEGSs8qgYNAUSzjwKhsBOZU3jyOzIWKlyY,3143
110
+ sapiopycommons-2025.10.13a781.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
111
+ sapiopycommons-2025.10.13a781.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
112
+ sapiopycommons-2025.10.13a781.dist-info/RECORD,,