sapiopycommons 2025.8.20a714__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/__init__.py +0 -0
- sapiopycommons/ai/tool_of_tools.py +917 -0
- sapiopycommons/callbacks/callback_util.py +21 -14
- 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-2025.8.20a714.dist-info → sapiopycommons-2025.8.22a716.dist-info}/METADATA +2 -2
- {sapiopycommons-2025.8.20a714.dist-info → sapiopycommons-2025.8.22a716.dist-info}/RECORD +14 -9
- {sapiopycommons-2025.8.20a714.dist-info → sapiopycommons-2025.8.22a716.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.8.20a714.dist-info → sapiopycommons-2025.8.22a716.dist-info}/licenses/LICENSE +0 -0
|
@@ -1815,7 +1815,8 @@ class CallbackUtil:
|
|
|
1815
1815
|
return response
|
|
1816
1816
|
|
|
1817
1817
|
def request_file(self, title: str, exts: Iterable[str] | None = None,
|
|
1818
|
-
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]:
|
|
1819
1820
|
"""
|
|
1820
1821
|
Request a single file from the user.
|
|
1821
1822
|
|
|
@@ -1825,6 +1826,8 @@ class CallbackUtil:
|
|
|
1825
1826
|
:param show_image_editor: Whether the user will see an image editor when image is uploaded in this file prompt.
|
|
1826
1827
|
:param show_camera_button: Whether the user will be able to use camera to take a picture as an upload request,
|
|
1827
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.
|
|
1828
1831
|
:return: The file name and bytes of the uploaded file.
|
|
1829
1832
|
"""
|
|
1830
1833
|
# If no extensions were provided, use an empty list for the extensions instead.
|
|
@@ -1844,11 +1847,12 @@ class CallbackUtil:
|
|
|
1844
1847
|
file_path: str = self.__send_dialog(request, self.callback.show_file_dialog, data_sink=do_consume)
|
|
1845
1848
|
|
|
1846
1849
|
# Verify that each of the file given matches the expected extension(s).
|
|
1847
|
-
self.__verify_file(file_path, sink.data, exts)
|
|
1850
|
+
self.__verify_file(file_path, sink.data, exts if enforce_file_extensions else None)
|
|
1848
1851
|
return file_path, sink.data
|
|
1849
1852
|
|
|
1850
1853
|
def request_files(self, title: str, exts: Iterable[str] | None = None,
|
|
1851
|
-
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]:
|
|
1852
1856
|
"""
|
|
1853
1857
|
Request multiple files from the user.
|
|
1854
1858
|
|
|
@@ -1858,6 +1862,8 @@ class CallbackUtil:
|
|
|
1858
1862
|
:param show_image_editor: Whether the user will see an image editor when image is uploaded in this file prompt.
|
|
1859
1863
|
:param show_camera_button: Whether the user will be able to use camera to take a picture as an upload request,
|
|
1860
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.
|
|
1861
1867
|
:return: A dictionary of file name to file bytes for each file the user uploaded.
|
|
1862
1868
|
"""
|
|
1863
1869
|
# If no extensions were provided, use an empty list for the extensions instead.
|
|
@@ -1873,7 +1879,7 @@ class CallbackUtil:
|
|
|
1873
1879
|
for file_path in file_paths:
|
|
1874
1880
|
sink = InMemoryRecordDataSink(self.user)
|
|
1875
1881
|
sink.consume_client_callback_file_path_data(file_path)
|
|
1876
|
-
self.__verify_file(file_path, sink.data, exts)
|
|
1882
|
+
self.__verify_file(file_path, sink.data, exts if enforce_file_extensions else None)
|
|
1877
1883
|
ret_dict.update({file_path: sink.data})
|
|
1878
1884
|
|
|
1879
1885
|
return ret_dict
|
|
@@ -1890,16 +1896,17 @@ class CallbackUtil:
|
|
|
1890
1896
|
"""
|
|
1891
1897
|
if file_path is None or len(file_path) == 0 or file_bytes is None or len(file_bytes) == 0:
|
|
1892
1898
|
raise SapioUserErrorException("Empty file provided or file unable to be read.")
|
|
1893
|
-
if allowed_extensions:
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
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)))
|
|
1903
1910
|
|
|
1904
1911
|
def write_file(self, file_name: str, file_data: str | bytes) -> None:
|
|
1905
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()
|