sapiopycommons 2025.10.13a781__py3-none-any.whl → 2025.10.16a785__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.

@@ -22,7 +22,7 @@ from sapiopycommons.ai.protoapi.externalcredentials.external_credentials_pb2 imp
22
22
  from sapiopycommons.ai.protoapi.fielddefinitions.fields_pb2 import FieldValueMapPbo, FieldValuePbo
23
23
  from sapiopycommons.ai.protoapi.fielddefinitions.velox_field_def_pb2 import VeloxFieldDefPbo, FieldTypePbo, \
24
24
  SelectionPropertiesPbo, IntegerPropertiesPbo, DoublePropertiesPbo, BooleanPropertiesPbo, StringPropertiesPbo, \
25
- FieldValidatorPbo
25
+ FieldValidatorPbo, DatePropertiesPbo
26
26
  from sapiopycommons.ai.protoapi.plan.item.item_container_pb2 import ContentTypePbo
27
27
  from sapiopycommons.ai.protoapi.plan.tool.entry_pb2 import StepOutputBatchPbo, StepItemContainerPbo, \
28
28
  StepBinaryContainerPbo, StepCsvContainerPbo, StepCsvHeaderRowPbo, StepCsvRowPbo, StepJsonContainerPbo, \
@@ -782,6 +782,67 @@ class AgentBase(ABC):
782
782
  )
783
783
  ))
784
784
 
785
+ def add_date_config_field(self, field_name: str, display_name: str, description: str, optional: bool = False,
786
+ date_time_format: str = "MMM dd, yyyy", default_to_today: bool = False,
787
+ is_static_date: bool = False) -> None:
788
+ """
789
+ Add a date configuration field to the agent. This field will be used to configure the agent in the plan
790
+ manager.
791
+
792
+ :param field_name: The name of the field.
793
+ :param display_name: The display name of the field.
794
+ :param description: The description of the field.
795
+ :param date_time_format: The format that this date field should appear in. The date format is Java-style.
796
+ See https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/text/SimpleDateFormat.html for more
797
+ details.
798
+ :param default_to_today: If true, the default value of the field will be set to today's date. If false, the
799
+ default value will be None.
800
+ :param is_static_date: If true, the user will input the date as UTC. If false, the user will input the date
801
+ as local time.
802
+ :param optional: If true, this field is optional. If false, this field is required.
803
+ """
804
+ self.config_fields.append(VeloxFieldDefPbo(
805
+ data_field_type=FieldTypePbo.DATE,
806
+ data_field_name=field_name,
807
+ display_name=display_name,
808
+ description=description,
809
+ required=not optional,
810
+ editable=True,
811
+ date_properties=DatePropertiesPbo(
812
+ default_value="@Today" if default_to_today else None,
813
+ static_date=is_static_date,
814
+ date_time_format=date_time_format
815
+ )
816
+ ))
817
+
818
+ def add_credentials_config_field(self, field_name: str, display_name: str, description: str, optional: bool = False,
819
+ category: str | None = None) -> None:
820
+ """
821
+ Add a list field that asks the user to choose which credentials to use. This field will be used to
822
+ configure the agent in the plan manager.
823
+
824
+ :param field_name: The name of the field.
825
+ :param display_name: The display name of the field.
826
+ :param description: The description of the field.
827
+ :param optional: If true, this field is optional. If false, this field is required.
828
+ :param category: If provided, only credentials in this category will be shown to the user.
829
+ """
830
+ self.config_fields.append(VeloxFieldDefPbo(
831
+ data_field_type=FieldTypePbo.SELECTION,
832
+ data_field_name=field_name,
833
+ display_name=display_name,
834
+ description=description,
835
+ required=not optional,
836
+ editable=True,
837
+ selection_properties=SelectionPropertiesPbo(
838
+ # A credentials field is just a selection field with its list mode set to [ExternalCredentials].
839
+ list_mode=f"[ExternalCredentials]{category.strip() if category else ''}",
840
+ multi_select=False,
841
+ default_value=None,
842
+ direct_edit=False
843
+ )
844
+ ))
845
+
785
846
  def to_pbo(self) -> ToolDetailsPbo:
786
847
  """
787
848
  :return: The ToolDetailsPbo proto object representing this agent.
@@ -908,7 +969,24 @@ class AgentBase(ABC):
908
969
  if len(matching_creds) > 1:
909
970
  raise ValueError(f"Multiple credentials found for category '{category}' and host '{host}'.")
910
971
 
911
- return ExternalCredentials(matching_creds[0])
972
+ return ExternalCredentials.from_pbo(matching_creds[0])
973
+
974
+ def get_credentials_from_config(self, value: str) -> ExternalCredentials:
975
+ """
976
+ Get credentials given the value of a credentials config field.
977
+
978
+ :param value: The value of the credentials config field.
979
+ :return: An ExternalCredentials object containing the credentials.
980
+ """
981
+ # Values should be of the format "Name (Identifier)"
982
+ match = re.match(r"^(.*) \((.*)\)$", value)
983
+ if not match:
984
+ raise ValueError(f"Invalid credentials value '{value}'. Expected format 'Name (Identifier)'.")
985
+ identifier: str = match.group(2)
986
+ for cred in self.request.external_credential:
987
+ if cred.id == identifier:
988
+ return ExternalCredentials.from_pbo(cred)
989
+ raise ValueError(f"No credentials found with identifier '{identifier}'.")
912
990
 
913
991
  def call_subprocess(self,
914
992
  args: str | bytes | PathLike[str] | PathLike[bytes] | Sequence[str | bytes | PathLike[str] | PathLike[bytes]],
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from sapiopycommons.ai.protoapi.externalcredentials.external_credentials_pb2 import ExternalCredentialsPbo
2
4
 
3
5
 
@@ -5,26 +7,97 @@ class ExternalCredentials:
5
7
  """
6
8
  A class representing external credentials.
7
9
  """
8
- id: str
9
- display_name: str
10
- description: str
11
- category: str
12
- url: str
13
- username: str
14
- password: str
15
- token: str
16
- custom_fields: dict[str, str]
17
-
18
- def __init__(self, pbo: ExternalCredentialsPbo):
19
- self.id = pbo.id
20
- self.display_name = pbo.display_name
21
- self.description = pbo.description
22
- self.category = pbo.category
23
- self.url = pbo.url
24
- self.username = pbo.username
25
- self.password = pbo.password
26
- self.token = pbo.token
27
- self.custom_fields = dict(pbo.custom_field)
10
+ _identifier: str
11
+ _display_name: str
12
+ _description: str
13
+ _category: str
14
+ _url: str
15
+ _username: str
16
+ _password: str
17
+ _token: str
18
+ _custom_fields: dict[str, str]
19
+
20
+ def __init__(self, url: str, category: str, identifier: str = "", display_name: str = "", description: str = "",
21
+ username: str = "", password: str = "", token: str = "", custom_fields: dict[str, str] | None = None):
22
+ """
23
+ :param url: The URL that the credentials are for.
24
+ :param category: The category of the credentials. This can be used to search for the credentials using the
25
+ AgentBase.get_credentials function.
26
+ :param identifier: The unique identifier for the credentials.
27
+ :param display_name: The display name for the credentials.
28
+ :param description: A description of the credentials.
29
+ :param username: The username for the credentials.
30
+ :param password: The password for the credentials.
31
+ :param token: The token for the credentials.
32
+ :param custom_fields: A dictionary of custom fields associated with the credentials.
33
+ """
34
+ self._identifier = identifier
35
+ self._display_name = display_name
36
+ self._description = description
37
+ self._category = category
38
+ self._url = url
39
+ self._username = username
40
+ self._password = password
41
+ self._token = token
42
+ self._custom_fields = custom_fields or {}
43
+
44
+ @staticmethod
45
+ def from_pbo(pbo: ExternalCredentialsPbo) -> ExternalCredentials:
46
+ """
47
+ Create an ExternalCredentials instance from a protobuf object.
48
+
49
+ :param pbo: An ExternalCredentialsPbo object.
50
+ :return: An ExternalCredentials instance.
51
+ """
52
+ creds = ExternalCredentials(pbo.url, pbo.category)
53
+ creds._identifier = pbo.id
54
+ creds._display_name = pbo.display_name
55
+ creds._description = pbo.description
56
+ creds._username = pbo.username
57
+ creds._password = pbo.password
58
+ creds._token = pbo.token
59
+ creds._custom_fields = dict(pbo.custom_field)
60
+ return creds
61
+
62
+ @property
63
+ def identifier(self) -> str:
64
+ """The unique identifier for the credentials."""
65
+ return self._identifier
66
+
67
+ @property
68
+ def display_name(self) -> str:
69
+ """The display name for the credentials."""
70
+ return self._display_name
71
+
72
+ @property
73
+ def description(self) -> str:
74
+ """A description of the credentials."""
75
+ return self._description
76
+
77
+ @property
78
+ def category(self) -> str:
79
+ """The category of the credentials."""
80
+ return self._category
81
+
82
+ @property
83
+ def url(self) -> str:
84
+ """The URL that the credentials are for."""
85
+ return self._url
86
+
87
+ @property
88
+ def username(self) -> str:
89
+ """The username for the credentials."""
90
+ return self._username
91
+
92
+ @property
93
+ def password(self) -> str:
94
+ """The password for the credentials."""
95
+ return self._password
96
+
97
+ @property
98
+ def token(self) -> str:
99
+ """The token for the credentials."""
100
+ return self._token
28
101
 
29
102
  def get_custom_field(self, key: str, default: str = None) -> str | None:
30
103
  """
@@ -34,4 +107,22 @@ class ExternalCredentials:
34
107
  :param default: The value to return if the key does not exist.
35
108
  :return: The value of the custom field, or None if the key does not exist.
36
109
  """
37
- return self.custom_fields.get(key, default)
110
+ return self._custom_fields.get(key, default)
111
+
112
+ def to_pbo(self) -> ExternalCredentialsPbo:
113
+ """
114
+ Convert the ExternalCredentials instance to a protobuf object.
115
+
116
+ :return: An ExternalCredentialsPbo object.
117
+ """
118
+ return ExternalCredentialsPbo(
119
+ id=self._identifier,
120
+ display_name=self._display_name,
121
+ description=self._description,
122
+ category=self._category,
123
+ url=self._url,
124
+ username=self._username,
125
+ password=self._password,
126
+ token=self._token,
127
+ custom_field=self._custom_fields
128
+ )
@@ -7,6 +7,8 @@ from typing import Any
7
7
  import grpc
8
8
  from sapiopylib.rest.User import SapioUser
9
9
 
10
+ from sapiopycommons.ai.external_credentials import ExternalCredentials
11
+ from sapiopycommons.ai.protoapi.externalcredentials.external_credentials_pb2 import ExternalCredentialsPbo
10
12
  from sapiopycommons.ai.protoapi.fielddefinitions.fields_pb2 import FieldValuePbo
11
13
  from sapiopycommons.ai.protoapi.plan.converter.converter_pb2 import ConverterDetailsRequestPbo, \
12
14
  ConverterDetailsResponsePbo, ConvertResponsePbo, ConvertRequestPbo
@@ -65,7 +67,8 @@ class AgentOutput:
65
67
  self.new_records = []
66
68
  self.logs = []
67
69
 
68
- def save_outputs(self, path: str = "test_outputs", subfolder: str | None = None, file_extensions: list[str] | None = None) -> None:
70
+ def save_outputs(self, path: str = "test_outputs", subfolder: str | None = None,
71
+ file_extensions: list[str] | None = None) -> None:
69
72
  """
70
73
  Save all outputs to files in the specified output directory.
71
74
 
@@ -168,6 +171,7 @@ class TestClient:
168
171
  connection: SapioConnectionInfoPbo
169
172
  _request_inputs: list[StepItemContainerPbo]
170
173
  _config_fields: dict[str, FieldValuePbo]
174
+ _credentials: list[ExternalCredentialsPbo]
171
175
 
172
176
  def __init__(self, grpc_server_url: str, message_mb_size: int = 1024, user: SapioUser | None = None,
173
177
  options: list[tuple[str, Any]] | None = None):
@@ -188,6 +192,7 @@ class TestClient:
188
192
  self._create_connection(user)
189
193
  self._request_inputs = []
190
194
  self._config_fields = {}
195
+ self._credentials = []
191
196
 
192
197
  def _create_connection(self, user: SapioUser | None = None):
193
198
  """
@@ -272,10 +277,28 @@ class TestClient:
272
277
  """
273
278
  self._config_fields.clear()
274
279
 
280
+ def add_credentials(self, credentials: list[ExternalCredentials]) -> None:
281
+ """
282
+ Add external credentials to the connection info for the next request.
283
+
284
+ :param credentials: A list of ExternalCredentialsPbo objects to add to the connection info.
285
+ """
286
+ for cred in credentials:
287
+ self._credentials.append(cred.to_pbo())
288
+
289
+ def clear_credentials(self) -> None:
290
+ """
291
+ Clear all external credentials that have been added to the next request.
292
+ This is useful if you want to start a new request without the previous credentials.
293
+ """
294
+ self._credentials.clear()
295
+
275
296
  def clear_request(self) -> None:
276
297
  """
277
298
  Clear all inputs and configuration fields that have been added to the next request.
278
299
  This is useful if you want to start a new request without the previous inputs and configurations.
300
+
301
+ Credentials are not cleared, as they may be reused across multiple requests.
279
302
  """
280
303
  self.clear_inputs()
281
304
  self.clear_configs()
@@ -331,6 +354,7 @@ class TestClient:
331
354
  config_field_values=self._config_fields,
332
355
  dry_run=is_dry_run,
333
356
  verbose_logging=is_verbose,
357
+ external_credential=self._credentials,
334
358
  input=[
335
359
  StepInputBatchPbo(is_partial=False, item_container=item)
336
360
  for item in self._request_inputs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2025.10.13a781
3
+ Version: 2025.10.16a785
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,12 +1,12 @@
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=UF5mSCXMxX0qUtEUWRtQ6_690aOo6pCRB89dyj8Amug,56292
3
+ sapiopycommons/ai/agent_service_base.py,sha256=eTJunFQoxLc0risWiQIkQK946XpHzSFO38NzzRvOn9Q,60325
4
4
  sapiopycommons/ai/converter_service_base.py,sha256=HiUXmwqv1STgyQeF9_eTFXzjIFXp5-NJ7sEhMpV3aAU,6351
5
- sapiopycommons/ai/external_credentials.py,sha256=40AI7VtHf6PzvwJLR_mZemUCrfAUvC--tGH-npaDIgo,1163
5
+ sapiopycommons/ai/external_credentials.py,sha256=ki_xIH4J843b_sSwEa8YHr8vW9erVv-jowZJXSgPQs8,4347
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=aiS58O_A3KSgRCzDT61iKyVXy9F4UY7xUyGQu5E7bTw,19432
9
+ sapiopycommons/ai/test_client.py,sha256=IRZ-8prhg7XMDmN9aC1MQr5mSkMgfT37aLsOy-VB-MU,20495
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.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,,
109
+ sapiopycommons-2025.10.16a785.dist-info/METADATA,sha256=2yQ4MjvJOysrdpojvELXBXHk9Pk_hDU7D1cl3k6zHKQ,3143
110
+ sapiopycommons-2025.10.16a785.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
111
+ sapiopycommons-2025.10.16a785.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
112
+ sapiopycommons-2025.10.16a785.dist-info/RECORD,,