sapiopycommons 2025.8.22a715__py3-none-any.whl → 2025.8.22a716__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.
- sapiopycommons/ai/tool_of_tools.py +917 -0
- sapiopycommons/callbacks/callback_util.py +26 -16
- sapiopycommons/chem/IndigoMolecules.py +12 -10
- sapiopycommons/chem/ps_commons.py +773 -0
- sapiopycommons/files/assay_plate_reader.py +93 -0
- sapiopycommons/files/file_text_converter.py +207 -0
- sapiopycommons/flowcyto/flow_cyto.py +2 -24
- sapiopycommons/general/accession_service.py +2 -28
- sapiopycommons/multimodal/multimodal.py +2 -24
- sapiopycommons/webhook/webservice_handlers.py +1 -1
- {sapiopycommons-2025.8.22a715.dist-info → sapiopycommons-2025.8.22a716.dist-info}/METADATA +2 -2
- {sapiopycommons-2025.8.22a715.dist-info → sapiopycommons-2025.8.22a716.dist-info}/RECORD +14 -45
- sapiopycommons/ai/converter_service_base.py +0 -131
- sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.py +0 -43
- sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.pyi +0 -31
- sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2_grpc.py +0 -24
- sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.py +0 -123
- sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.pyi +0 -598
- sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2_grpc.py +0 -24
- sapiopycommons/ai/protoapi/plan/converter/converter_pb2.py +0 -51
- sapiopycommons/ai/protoapi/plan/converter/converter_pb2.pyi +0 -63
- sapiopycommons/ai/protoapi/plan/converter/converter_pb2_grpc.py +0 -149
- sapiopycommons/ai/protoapi/plan/item/item_container_pb2.py +0 -55
- sapiopycommons/ai/protoapi/plan/item/item_container_pb2.pyi +0 -88
- sapiopycommons/ai/protoapi/plan/item/item_container_pb2_grpc.py +0 -24
- sapiopycommons/ai/protoapi/plan/script/script_pb2.py +0 -59
- sapiopycommons/ai/protoapi/plan/script/script_pb2.pyi +0 -102
- sapiopycommons/ai/protoapi/plan/script/script_pb2_grpc.py +0 -153
- sapiopycommons/ai/protoapi/plan/step_output_pb2.py +0 -45
- sapiopycommons/ai/protoapi/plan/step_output_pb2.pyi +0 -42
- sapiopycommons/ai/protoapi/plan/step_output_pb2_grpc.py +0 -24
- sapiopycommons/ai/protoapi/plan/step_pb2.py +0 -43
- sapiopycommons/ai/protoapi/plan/step_pb2.pyi +0 -43
- sapiopycommons/ai/protoapi/plan/step_pb2_grpc.py +0 -24
- sapiopycommons/ai/protoapi/plan/tool/entry_pb2.py +0 -41
- sapiopycommons/ai/protoapi/plan/tool/entry_pb2.pyi +0 -35
- sapiopycommons/ai/protoapi/plan/tool/entry_pb2_grpc.py +0 -24
- sapiopycommons/ai/protoapi/plan/tool/tool_pb2.py +0 -75
- sapiopycommons/ai/protoapi/plan/tool/tool_pb2.pyi +0 -237
- sapiopycommons/ai/protoapi/plan/tool/tool_pb2_grpc.py +0 -154
- sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.py +0 -39
- sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.pyi +0 -32
- sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2_grpc.py +0 -24
- sapiopycommons/ai/protobuf_utils.py +0 -504
- sapiopycommons/ai/server.py +0 -104
- sapiopycommons/ai/test_client.py +0 -356
- sapiopycommons/ai/tool_service_base.py +0 -922
- {sapiopycommons-2025.8.22a715.dist-info → sapiopycommons-2025.8.22a716.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.8.22a715.dist-info → sapiopycommons-2025.8.22a716.dist-info}/licenses/LICENSE +0 -0
|
@@ -1765,8 +1765,11 @@ class CallbackUtil:
|
|
|
1765
1765
|
blank_result_handling = BlankResultHandling.REPEAT
|
|
1766
1766
|
def not_blank_func(r: list[DataRecord]) -> bool:
|
|
1767
1767
|
return bool(r)
|
|
1768
|
-
|
|
1769
|
-
|
|
1768
|
+
response: list[DataRecord] = self.__send_dialog_blank_results(request,
|
|
1769
|
+
self.callback.show_input_selection_dialog,
|
|
1770
|
+
not_blank_func, blank_result_handling,
|
|
1771
|
+
repeat_message, cancel_message)
|
|
1772
|
+
return self.rec_handler.wrap_models(response, wrapper_type)
|
|
1770
1773
|
|
|
1771
1774
|
# FR-47690: Deprecated the require_authentication parameter.
|
|
1772
1775
|
# noinspection PyUnusedLocal
|
|
@@ -1812,7 +1815,8 @@ class CallbackUtil:
|
|
|
1812
1815
|
return response
|
|
1813
1816
|
|
|
1814
1817
|
def request_file(self, title: str, exts: Iterable[str] | None = None,
|
|
1815
|
-
show_image_editor: bool = False, show_camera_button: bool = False
|
|
1818
|
+
show_image_editor: bool = False, show_camera_button: bool = False,
|
|
1819
|
+
*, enforce_file_extensions: bool = True) -> tuple[str, bytes]:
|
|
1816
1820
|
"""
|
|
1817
1821
|
Request a single file from the user.
|
|
1818
1822
|
|
|
@@ -1822,6 +1826,8 @@ class CallbackUtil:
|
|
|
1822
1826
|
:param show_image_editor: Whether the user will see an image editor when image is uploaded in this file prompt.
|
|
1823
1827
|
:param show_camera_button: Whether the user will be able to use camera to take a picture as an upload request,
|
|
1824
1828
|
rather than selecting an existing file.
|
|
1829
|
+
:param enforce_file_extensions: If true, then the file extensions provided in the exts parameter will be
|
|
1830
|
+
enforced. If false, then the user may upload any file type.
|
|
1825
1831
|
:return: The file name and bytes of the uploaded file.
|
|
1826
1832
|
"""
|
|
1827
1833
|
# If no extensions were provided, use an empty list for the extensions instead.
|
|
@@ -1841,11 +1847,12 @@ class CallbackUtil:
|
|
|
1841
1847
|
file_path: str = self.__send_dialog(request, self.callback.show_file_dialog, data_sink=do_consume)
|
|
1842
1848
|
|
|
1843
1849
|
# Verify that each of the file given matches the expected extension(s).
|
|
1844
|
-
self.__verify_file(file_path, sink.data, exts)
|
|
1850
|
+
self.__verify_file(file_path, sink.data, exts if enforce_file_extensions else None)
|
|
1845
1851
|
return file_path, sink.data
|
|
1846
1852
|
|
|
1847
1853
|
def request_files(self, title: str, exts: Iterable[str] | None = None,
|
|
1848
|
-
show_image_editor: bool = False, show_camera_button: bool = False
|
|
1854
|
+
show_image_editor: bool = False, show_camera_button: bool = False,
|
|
1855
|
+
*, enforce_file_extensions: bool = True) -> dict[str, bytes]:
|
|
1849
1856
|
"""
|
|
1850
1857
|
Request multiple files from the user.
|
|
1851
1858
|
|
|
@@ -1855,6 +1862,8 @@ class CallbackUtil:
|
|
|
1855
1862
|
:param show_image_editor: Whether the user will see an image editor when image is uploaded in this file prompt.
|
|
1856
1863
|
:param show_camera_button: Whether the user will be able to use camera to take a picture as an upload request,
|
|
1857
1864
|
rather than selecting an existing file.
|
|
1865
|
+
:param enforce_file_extensions: If true, then the file extensions provided in the exts parameter will be
|
|
1866
|
+
enforced. If false, then the user may upload any file type.
|
|
1858
1867
|
:return: A dictionary of file name to file bytes for each file the user uploaded.
|
|
1859
1868
|
"""
|
|
1860
1869
|
# If no extensions were provided, use an empty list for the extensions instead.
|
|
@@ -1870,7 +1879,7 @@ class CallbackUtil:
|
|
|
1870
1879
|
for file_path in file_paths:
|
|
1871
1880
|
sink = InMemoryRecordDataSink(self.user)
|
|
1872
1881
|
sink.consume_client_callback_file_path_data(file_path)
|
|
1873
|
-
self.__verify_file(file_path, sink.data, exts)
|
|
1882
|
+
self.__verify_file(file_path, sink.data, exts if enforce_file_extensions else None)
|
|
1874
1883
|
ret_dict.update({file_path: sink.data})
|
|
1875
1884
|
|
|
1876
1885
|
return ret_dict
|
|
@@ -1887,16 +1896,17 @@ class CallbackUtil:
|
|
|
1887
1896
|
"""
|
|
1888
1897
|
if file_path is None or len(file_path) == 0 or file_bytes is None or len(file_bytes) == 0:
|
|
1889
1898
|
raise SapioUserErrorException("Empty file provided or file unable to be read.")
|
|
1890
|
-
if allowed_extensions:
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1899
|
+
if not allowed_extensions:
|
|
1900
|
+
return
|
|
1901
|
+
matches: bool = False
|
|
1902
|
+
for ext in allowed_extensions:
|
|
1903
|
+
# FR-47690: Changed to a case-insensitive match.
|
|
1904
|
+
if file_path.casefold().endswith("." + ext.lstrip(".").casefold()):
|
|
1905
|
+
matches = True
|
|
1906
|
+
break
|
|
1907
|
+
if not matches:
|
|
1908
|
+
raise SapioUserErrorException("Unsupported file type. Expecting the following extension(s): "
|
|
1909
|
+
+ (",".join(allowed_extensions)))
|
|
1900
1910
|
|
|
1901
1911
|
def write_file(self, file_name: str, file_data: str | bytes) -> None:
|
|
1902
1912
|
"""
|
|
@@ -6,11 +6,15 @@ indigo = Indigo()
|
|
|
6
6
|
renderer = IndigoRenderer(indigo)
|
|
7
7
|
indigo.setOption("render-output-format", "svg")
|
|
8
8
|
indigo.setOption("ignore-stereochemistry-errors", True)
|
|
9
|
+
# Ignore only if loading as non-query object. That is the meaning of this flag. Does nothing if it's query molecule.
|
|
10
|
+
indigo.setOption("ignore-noncritical-query-features", True)
|
|
9
11
|
indigo.setOption("render-stereo-style", "ext")
|
|
10
12
|
indigo.setOption("aromaticity-model", "generic")
|
|
11
13
|
indigo.setOption("render-coloring", True)
|
|
12
14
|
indigo.setOption("molfile-saving-mode", "3000")
|
|
13
15
|
indigo.setOption("dearomatize-verification", False)
|
|
16
|
+
#YQ: Sapio-Only Option, this is my modification.
|
|
17
|
+
indigo.setOption("rpe-bypass-saturation-check", True)
|
|
14
18
|
indigo_inchi = IndigoInchi(indigo)
|
|
15
19
|
|
|
16
20
|
|
|
@@ -22,17 +26,15 @@ def get_aromatic_dearomatic_forms(m: IndigoObject):
|
|
|
22
26
|
:return: pair of indigo objects, first is aromatic, second is dearomatic.
|
|
23
27
|
"""
|
|
24
28
|
try:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
# Note: there are problems with aromatization being applied more than once, because it can crash.
|
|
30
|
+
# This is because an implicit assumption made on consequence of a particular perception may cause further
|
|
31
|
+
# aromatization to fail to find next steps and also fails to find termination.
|
|
32
|
+
# So, to ensure we only apply aromatization once, we dearomatize first, then aromatize.
|
|
33
|
+
dearomatic_reaction = m.clone()
|
|
28
34
|
dearomatic_reaction.dearomatize()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if match:
|
|
33
|
-
return aromatic_reaction, dearomatic_reaction
|
|
34
|
-
else:
|
|
35
|
-
return m, dearomatic_reaction
|
|
35
|
+
aromatic_reaction = dearomatic_reaction.clone()
|
|
36
|
+
aromatic_reaction.aromatize()
|
|
37
|
+
return aromatic_reaction, dearomatic_reaction
|
|
36
38
|
except (Exception):
|
|
37
39
|
# If aromatization then following deromatization fails, we just skip it.
|
|
38
40
|
dearomatic_reaction = m.clone()
|