py-semtools 1.3.3__tar.gz → 1.3.4__tar.gz

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.
Files changed (179) hide show
  1. {py_semtools-1.3.3 → py_semtools-1.3.4}/PKG-INFO +4 -2
  2. {py_semtools-1.3.3 → py_semtools-1.3.4}/setup.cfg +3 -1
  3. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/cli_manager.py +66 -10
  4. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/indexers/text_indexer.py +3 -1
  5. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/lexical_engines/engine_baseclass.py +56 -21
  6. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/lexical_engines/fmEngine.py +32 -2
  7. py_semtools-1.3.4/src/py_semtools/lexical_engines/llmEngine.py +102 -0
  8. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/lexical_engines/stEngine.py +10 -6
  9. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/main_modules.py +48 -16
  10. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/ontology.py +54 -26
  11. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/ont/oboparser.py +49 -4
  12. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_basic_parser.py +6 -5
  13. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/report_ont.py +19 -2
  14. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/comorb_sugg.txt +1 -1
  15. py_semtools-1.3.4/src/py_semtools/templates/makeTermFreqDist.txt +5 -0
  16. py_semtools-1.3.4/src/py_semtools/templates/makeTermFreqTable.txt +11 -0
  17. py_semtools-1.3.4/src/py_semtools/templates/ontoICdist.txt +63 -0
  18. py_semtools-1.3.4/src/py_semtools/templates/ontodist.txt +31 -0
  19. py_semtools-1.3.4/src/py_semtools/templates/plotProfRed.txt +10 -0
  20. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/PKG-INFO +4 -2
  21. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/SOURCES.txt +7 -0
  22. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/entry_points.txt +1 -0
  23. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/requires.txt +3 -1
  24. py_semtools-1.3.4/src/py_semtools.egg-info/scm_file_list.json +169 -0
  25. py_semtools-1.3.4/src/py_semtools.egg-info/scm_version.json +8 -0
  26. py_semtools-1.3.4/tests/data/hierarchical_sample_3level_child.obo +65 -0
  27. py_semtools-1.3.4/tests/data/ref_output_scripts/set_operation_is_descendant +1 -0
  28. py_semtools-1.3.4/tests/data/ref_output_scripts/set_operation_is_parental +1 -0
  29. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_cli_manager.py +31 -15
  30. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_oboparser.py +15 -0
  31. py_semtools-1.3.3/src/py_semtools/templates/makeTermFreqTable.txt +0 -4
  32. py_semtools-1.3.3/src/py_semtools/templates/ontoICdist.txt +0 -8
  33. py_semtools-1.3.3/src/py_semtools/templates/ontodist.txt +0 -10
  34. py_semtools-1.3.3/src/py_semtools/templates/plotProfRed.txt +0 -6
  35. {py_semtools-1.3.3 → py_semtools-1.3.4}/.coveragerc +0 -0
  36. {py_semtools-1.3.3 → py_semtools-1.3.4}/.gitignore +0 -0
  37. {py_semtools-1.3.3 → py_semtools-1.3.4}/.readthedocs.yml +0 -0
  38. {py_semtools-1.3.3 → py_semtools-1.3.4}/AUTHORS.rst +0 -0
  39. {py_semtools-1.3.3 → py_semtools-1.3.4}/CHANGELOG.rst +0 -0
  40. {py_semtools-1.3.3 → py_semtools-1.3.4}/CONTRIBUTING.rst +0 -0
  41. {py_semtools-1.3.3 → py_semtools-1.3.4}/LICENSE.txt +0 -0
  42. {py_semtools-1.3.3 → py_semtools-1.3.4}/MANIFEST.in +0 -0
  43. {py_semtools-1.3.3 → py_semtools-1.3.4}/README.md +0 -0
  44. {py_semtools-1.3.3 → py_semtools-1.3.4}/README.rst +0 -0
  45. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/Makefile +0 -0
  46. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/_static/.gitignore +0 -0
  47. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/authors.rst +0 -0
  48. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/changelog.rst +0 -0
  49. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/conf.py +0 -0
  50. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/contributing.rst +0 -0
  51. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/index.rst +0 -0
  52. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/license.rst +0 -0
  53. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/readme.rst +0 -0
  54. {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/requirements.txt +0 -0
  55. {py_semtools-1.3.3 → py_semtools-1.3.4}/pyproject.toml +0 -0
  56. {py_semtools-1.3.3 → py_semtools-1.3.4}/setup.py +0 -0
  57. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/__init__.py +0 -0
  58. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/external_data/ontologies.txt +0 -0
  59. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parallelizer.py +0 -0
  60. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/ont/file_parser.py +0 -0
  61. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/ont/json_parser.py +0 -0
  62. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_pubmed_abstract_parser.py +0 -0
  63. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_pubmed_paper_parser.py +0 -0
  64. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_pubmed_parser.py +0 -0
  65. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/sim_handler.py +0 -0
  66. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/ontoplot.txt +0 -0
  67. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/plotClust.txt +0 -0
  68. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/report.txt +0 -0
  69. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/similarity_heatmap.txt +0 -0
  70. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/similarity_matrix.txt +0 -0
  71. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/stEngine.txt +0 -0
  72. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/dependency_links.txt +0 -0
  73. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/not-zip-safe +0 -0
  74. {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/top_level.txt +0 -0
  75. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/__init__.py +0 -0
  76. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/Report.html +0 -0
  77. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/example +0 -0
  78. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/launch.sh +0 -0
  79. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/styles.css +0 -0
  80. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/template.txt +0 -0
  81. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/conftest.py +0 -0
  82. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/branched.obo +0 -0
  83. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/circular_sample.obo +0 -0
  84. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/enrichment_ontology.obo +0 -0
  85. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/enrichment_ontology2.obo +0 -0
  86. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/enrichment_ontology3.obo +0 -0
  87. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_profs/report.html +0 -0
  88. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_profs/report.txt +0 -0
  89. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/filter_query_parentals.txt +0 -0
  90. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/filter_target_and_query_parentals.txt +0 -0
  91. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/filter_target_parentals.txt +0 -0
  92. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/no_filter_limit_2.txt +0 -0
  93. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/no_filter_no_limit.txt +0 -0
  94. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/input_data/enrichment_ontology3.obo +0 -0
  95. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/input_data/query_hps.txt +0 -0
  96. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/input_data/relations.txt +0 -0
  97. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/go-basic_sample.obo +0 -0
  98. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/go-onlyOne.obo +0 -0
  99. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/hierarchical_alt_chain.obo +0 -0
  100. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/hierarchical_compressed.obo.gz +0 -0
  101. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/hierarchical_sample.obo +0 -0
  102. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/profiles +0 -0
  103. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/profiles_2cols +0 -0
  104. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/profiles_with_removedTerms +0 -0
  105. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/string_values +0 -0
  106. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/terms +0 -0
  107. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/terms_for_xref +0 -0
  108. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/terms_list +0 -0
  109. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/only_header_sample.obo +0 -0
  110. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/partial_go.json +0 -0
  111. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/partial_go.obo +0 -0
  112. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/blacklisted.txt +0 -0
  113. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/cleaned_profiles +0 -0
  114. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/cleaned_profiles_2cols +0 -0
  115. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/expanded_profiles +0 -0
  116. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/expected_IC_ont +0 -0
  117. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/parental_from_terms +0 -0
  118. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/profile_stats +0 -0
  119. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/profiles_IC_onto_freq +0 -0
  120. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/strsimnet +0 -0
  121. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/strsimnet_cutoff2 +0 -0
  122. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/terms_attr +0 -0
  123. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/translated_profiles_names +0 -0
  124. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/translated_terms_codes +0 -0
  125. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/white_and_blacklisted.txt +0 -0
  126. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/whitelisted.txt +0 -0
  127. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/short_hierarchical_sample.obo +0 -0
  128. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/sparse2_sample.obo +0 -0
  129. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/sparse_sample.obo +0 -0
  130. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981088 +0 -0
  131. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981089 +0 -0
  132. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981091 +0 -0
  133. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981092 +0 -0
  134. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/None +0 -0
  135. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/single_abs_text +0 -0
  136. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/18382669 +0 -0
  137. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/31356151 +0 -0
  138. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/35042469 +0 -0
  139. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/38108203 +0 -0
  140. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/None +0 -0
  141. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/single_pap_text +0 -0
  142. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/abstracts/single_abs_index_nonsplit +0 -0
  143. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/abstracts/single_abs_index_split +0 -0
  144. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/papers/single_pap_index_nonsplit +0 -0
  145. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/papers/single_pap_index_split +0 -0
  146. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/abs_chunk1.xml.gz +0 -0
  147. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/abs_chunk2.xml.gz +0 -0
  148. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/single_abstract.xml +0 -0
  149. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/single_abstract.xml.gz +0 -0
  150. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/blacklisted_words1.txt +0 -0
  151. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/blacklisted_words2.txt +0 -0
  152. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/blacklisted_words3.txt +0 -0
  153. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/example.xml +0 -0
  154. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/example_zipped.xml.gz +0 -0
  155. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/pap_chunk1.tar.gz +0 -0
  156. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/pap_chunk2.tar.gz +0 -0
  157. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/single_paper.tar.gz +0 -0
  158. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/single_paper.xml +0 -0
  159. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/prepared_indexes/abstracts/example1.txt.gz +0 -0
  160. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/prepared_indexes/queries/hpo_list +0 -0
  161. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/prepared_indexes/queries/mondo_list +0 -0
  162. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/st_engine_report/abs_profiles.txt +0 -0
  163. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/st_engine_report/pmids_and_titles.txt +0 -0
  164. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/data/profiles.txt +0 -0
  165. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/data/ref_profile.txt +0 -0
  166. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/launch.sh +0 -0
  167. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/launch_plots.py +0 -0
  168. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/profiles_freqs.html +0 -0
  169. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/template.txt +0 -0
  170. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_go.py +0 -0
  171. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_jsonparser.py +0 -0
  172. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_ontology.py +0 -0
  173. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_pubmed_abs_parser.py +0 -0
  174. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_pubmed_pap_parser.py +0 -0
  175. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_pubmed_text_parser.py +0 -0
  176. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_similitudes.py +0 -0
  177. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_stengine.py +0 -0
  178. {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_text_indexer.py +0 -0
  179. {py_semtools-1.3.3 → py_semtools-1.3.4}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py_semtools
3
- Version: 1.3.3
3
+ Version: 1.3.4
4
4
  Summary: Library to handle ontologies that allows queries and calculations such as information coefficients, semantic similarity, ontology representations, etc in a easy way. It can load any ontology that complies the obo format supported by OBO Foundry.
5
5
  Home-page: https://github.com/seoanezonjic/py_semtools
6
6
  Author: seoanezonjic
@@ -26,10 +26,12 @@ Requires-Dist: py_report_html
26
26
  Requires-Dist: loguru
27
27
  Requires-Dist: loky
28
28
  Requires-Dist: bm25s
29
+ Requires-Dist: langchain-text-splitters
29
30
  Provides-Extra: models
30
31
  Requires-Dist: sentence_transformers; extra == "models"
31
32
  Requires-Dist: langchain; extra == "models"
32
- Requires-Dist: langchain-text-splitters; extra == "models"
33
+ Requires-Dist: ontogpt; extra == "models"
34
+ Requires-Dist: faiss-cpu; extra == "models"
33
35
  Provides-Extra: testing
34
36
  Requires-Dist: setuptools; extra == "testing"
35
37
  Requires-Dist: pytest; extra == "testing"
@@ -36,6 +36,7 @@ install_requires =
36
36
  loguru
37
37
  loky
38
38
  bm25s
39
+ langchain-text-splitters
39
40
 
40
41
  [options.packages.find]
41
42
  where = src
@@ -49,7 +50,7 @@ py_semtools.templates =
49
50
  *.txt
50
51
 
51
52
  [options.extras_require]
52
- MODELS = sentence_transformers; langchain; langchain-text-splitters
53
+ MODELS = sentence_transformers; langchain; ontogpt; faiss-cpu
53
54
  testing =
54
55
  setuptools
55
56
  pytest
@@ -62,6 +63,7 @@ console_scripts =
62
63
  get_sorted_suggestions = py_semtools.cli_manager:get_sorted_suggestions
63
64
  get_sorted_profs = py_semtools.cli_manager:get_sorted_profs
64
65
  remote_retriever = py_semtools.cli_manager:remote_retriever
66
+ llmEngine = py_semtools.cli_manager:llmEngine
65
67
  fmEngine = py_semtools.cli_manager:fmEngine
66
68
  stEngine = py_semtools.cli_manager:stEngine
67
69
  stEngine_report = py_semtools.cli_manager:stEngine_report
@@ -1,4 +1,4 @@
1
- import argparse, re, sys, inspect
1
+ import argparse, re, sys, inspect, codecs
2
2
  from py_semtools.main_modules import *
3
3
  from collections import defaultdict
4
4
 
@@ -42,6 +42,9 @@ def split_keyword_and_fields(string):
42
42
  def split_column_numbers(string):
43
43
  return [int(number) for number in string.split(",")]
44
44
 
45
+ def unescaped_str(arg_str):
46
+ return codecs.decode(str(arg_str), 'unicode_escape')
47
+
45
48
  #########################################################################
46
49
  #CLI PARSERS
47
50
  #########################################################################
@@ -187,7 +190,10 @@ def semtools(args = None):
187
190
  help="With flags 'get_LCA_from_profile' and 'get_MICA_from_profile' constraint the ancestors to the given list of terms (one per line).")
188
191
  parser.add_argument("--get_representative_profile", dest="get_representative_profile", default=None, type=float,
189
192
  help="For a list of profiles, give one single profile with terms presented with al least FLOAT frecuency in the profile list. Parents are infered and computed too. The most spefic terms are retained.")
190
-
193
+ parser.add_argument("--set_a", dest="set_a", default=None, type=text_list, help="Set of terms A to be used in set operations. It must be a file with one term per line.")
194
+ parser.add_argument("--set_b", dest="set_b", default=None, type=text_list, help="Set of terms B to be used in set operations. It must be a file with one term per line.")
195
+ parser.add_argument("--set_operation", dest="set_operation", default=None, type=str, help="Set operation to perform between set A and B. Options: 'is_parental', 'is_descendant'")
196
+ parser.add_argument("--set_operation_output", dest="set_operation_output", default=None, type=str, help="Path to save the output of the set operation between set A and B.")
191
197
  opts = parser.parse_args(args)
192
198
 
193
199
  main_semtools(opts)
@@ -229,11 +235,47 @@ def get_sorted_suggestions(args = None):
229
235
  help="Path to the output report file. If not used, no report will be generated")
230
236
  parser.add_argument("--heatmap_color_preset", dest="heatmap_color_preset", default= "1",
231
237
  help="Use to set the color preset for the heatmap. Options: '1' (default) or '2'. Each preset has a different color scheme.")
238
+ parser.add_argument("--color_heatmap_by", dest="color_heatmap_by", default="IC",
239
+ help="Use to set the values to color the heatmap. Options: 'IC' (information content of the target term) or "
240
+ "'rel_value' (the value of the relation between query and target term)")
232
241
 
233
242
  opts = parser.parse_args(args)
234
243
  main_get_sorted_suggestions(opts)
235
244
 
236
245
 
246
+ def llmEngine(args = None):
247
+ parser = argparse.ArgumentParser(description='Perform Ontology driven analysis with LLMs')
248
+ parser.add_argument('-P', "--model_provider", dest="model_provider", default= None,
249
+ help="Provider of the LLM model to be used")
250
+ parser.add_argument('-m', "--model_name", dest="model_name", default= None,
251
+ help="Model to be used")
252
+ parser.add_argument('-A', "--listening_addresses", dest="listening_addresses", default= None,
253
+ help="Addresses to listen for incoming requests in the format 'host:port'. For example: 'http://localhost:11434'")
254
+ parser.add_argument("-t", "--template", dest="template", default= None,
255
+ help="For ontoGPT, it is the template to be used for the prompt.")
256
+ parser.add_argument("-T", "--regex_tag", dest="regex_tag", default= None,
257
+ help="Regex tag for filtering grounded terms (like HP:), etc")
258
+ parser.add_argument("--logging_level", dest="logging_level", default=None,
259
+ help="Logging level for the process (if the model support it). Options: 'DEBUG', 'INFO', 'WARNING' or 'ERROR'. By default, logging is not activated.")
260
+ parser.add_argument('-q', "--query", dest="query", default= None,
261
+ help="Path to the query file. Wildcards are accepted to process multiple files")
262
+ parser.add_argument('-c', "--corpus", dest="corpus", default= None,
263
+ help="Path to the corpus file. Wildcards are accepted to process multiple files")
264
+ parser.add_argument('-o', "--output_file", dest="output_file", default= None,
265
+ help="Path to save the output file with semantic scores")
266
+ parser.add_argument('-s', "--split_level", dest="split_level", default= "sentence",
267
+ help="Use it if your corpus comes splitted in smaller parts and want to unify it at document level. Options: 'doc', 'paragraph', or 'sentence'")
268
+ parser.add_argument('-v', "--verbose", dest="verbose", default= False, action='store_true',
269
+ help="Toogle on to get verbose output")
270
+ parser.add_argument("--print_relevant_pairs", dest="print_relevant_pairs", default= False, action='store_true',
271
+ help="Use it to print the relevant pairs of query-corpus with their scores")
272
+ parser.add_argument("--get_total_time_file", dest="get_total_time_file", default= None,
273
+ help="Use it to save the calculation time of the process to a file")
274
+ parser.add_argument("--stream_write_results", dest="stream_write_results", default= False, action='store_true',
275
+ help="Use it to write results in streaming mode, which means that results will be written as they are calculated instead of waiting for the whole process to end.")
276
+ opts = parser.parse_args(args)
277
+ main_llmEngine(opts)
278
+
237
279
  def fmEngine(args = None):
238
280
  if args is None:
239
281
  args = sys.argv[1:]
@@ -241,7 +283,7 @@ def fmEngine(args = None):
241
283
  parser = argparse.ArgumentParser(description='Perform Ontology driven analysis with Sentence Transformer')
242
284
 
243
285
  parser.add_argument('-m', "--model_name", dest="model_name", default= None,
244
- help="Model to be used. Current options include 'bm25' and 'tfidf'")
286
+ help="Model to be used. Current options include 'bm25', 'tfidf', 'tfidf_faiss'")
245
287
  parser.add_argument('-q', "--query", dest="query", default= None,
246
288
  help="Path to the query file. Wildcards are accepted to process multiple files")
247
289
  parser.add_argument('-c', "--corpus", dest="corpus", default= None,
@@ -256,10 +298,15 @@ def fmEngine(args = None):
256
298
  help="Toogle on to get verbose output")
257
299
  parser.add_argument("--order", dest="order", default= "corpus-query",
258
300
  help="Order of the semantic search. Options: 'corpus-query' or 'query-corpus'")
259
- parser.add_argument('-s', "--split", dest="split", default= False, action='store_true',
260
- help="Use it if your corpus comes splitted in smaller parts as list of lists (embedded in a json)")
301
+ parser.add_argument('-s', "--split_level", dest="split_level", default= "sentence",
302
+ help="Use it if your corpus comes splitted in smaller parts and want to unify it at document level. Options: 'doc', 'paragraph', or 'sentence'")
261
303
  parser.add_argument("--print_relevant_pairs", dest="print_relevant_pairs", default= False, action='store_true',
262
- help="Use it to print the relevant pairs of query-corpus with their scores")
304
+ help="Use it to print the relevant pairs of query-corpus with their scores")
305
+ parser.add_argument("--get_total_time_file", dest="get_total_time_file", default= None,
306
+ help="Use it to save the calculation time of the process to a file")
307
+ parser.add_argument("--write_sims_mode", dest="write_sims_mode", default= "w",
308
+ help="Use it to set the mode for writing similarities to the output file. Options: 'w' for write (overwrite) and 'a' for append.")
309
+
263
310
  opts = parser.parse_args(args)
264
311
  main_fmEngine(opts)
265
312
 
@@ -292,8 +339,8 @@ def stEngine(args = None):
292
339
  help="Similarity threshold to filter results to write")
293
340
  parser.add_argument('-v', "--verbose", dest="verbose", default= False, action='store_true',
294
341
  help="Toogle on to get verbose output")
295
- parser.add_argument('-s', "--split", dest="split", default= False, action='store_true',
296
- help="Use it if your corpus comes splitted in smaller parts as list of lists (embedded in a json)")
342
+ parser.add_argument('-s', "--split_level", dest="split_level", default= "sentence",
343
+ help="Use it if your corpus comes splitted in smaller parts and want to unify it at document level. Options: 'doc', 'paragraph', or 'sentence'")
297
344
  parser.add_argument("--order", dest="order", default= "corpus-query",
298
345
  help="Order of the semantic search. Options: 'corpus-query' or 'query-corpus'")
299
346
  parser.add_argument("--print_relevant_pairs", dest="print_relevant_pairs", default= False, action='store_true',
@@ -309,7 +356,12 @@ def stEngine(args = None):
309
356
  parser.add_argument("--single_worker_chunk_size", dest="single_worker_chunk_size", default=10000, type=int,
310
357
  help="(Only for multi GPU processing) Chunk size for each worker when multi-GPU processing is used.")
311
358
  parser.add_argument("-b", "--batch_size", dest="batch_size", default= 32, type=int,
312
- help="Use to specify batch size for multi-GPU embedding step")
359
+ help="Use to specify batch size for multi-GPU embedding step")
360
+ parser.add_argument("--get_total_time_file", dest="get_total_time_file", default= None,
361
+ help="Use it to save the calculation time of the process to a file")
362
+ parser.add_argument("--write_sims_mode", dest="write_sims_mode", default= "w",
363
+ help="Use it to set the mode for writing similarities to the output file. Options: 'w' for write (overwrite) and 'a' for append.")
364
+
313
365
  opts = parser.parse_args(args)
314
366
  main_stEngine(opts)
315
367
 
@@ -386,7 +438,7 @@ def get_corpus_index(args = None):
386
438
  help="When using 'filter_by_blacklist' use this option to choose between 'exact' or 'partial' match. Default is 'partial'")
387
439
  parser.add_argument("--clean_type", dest="clean_type", default= 'hard',
388
440
  help="Use to set the type of cleaning to be performed on the text. Options: basic (do not clean at all), soft or hard (default).")
389
- parser.add_argument("--split_type", dest="split_type", default= 'classic',
441
+ parser.add_argument("--split_type", dest="split_type", default= 'classic', type=unescaped_str,
390
442
  help="Use to set the type of splitting to be performed on the text. Options: classic (default, splitting by '\n\n', \n', '.', ';', ',') or space_overlap ('\n\n', '\n', ' ', '' with overlap to avoid losing information).")
391
443
  parser.add_argument("--extra_fields", dest="extra_fields", default= defaultdict(lambda: False), type=text_to_default_dict,
392
444
  help="Comma-separated list of extra fields to include in the processed corpus. Available: title and/or keywords. Default: none")
@@ -431,6 +483,10 @@ def get_sorted_profs(args=None):
431
483
  help="When reference profile is not given, a general ine is computed with all profiles. If a freq is defined (0-1), all terms with freq minor than limit are removed")
432
484
  parser.add_argument("-L", "--matrix_limits", dest="matrix_limits", default= [20, 40], type= lambda data: [int(i) for i in data.split(",")],
433
485
  help="Number of rows and columns to show in heatmap defined as 'Nrows,Ncols'. Default 20,40")
486
+ parser.add_argument("--sim_bidirectional", dest="sim_bidirectional", default= False, action="store_true",
487
+ help="Use to activate similarity average calculation onto term profiles when both directios are computed (A to B, and B to A profiles)")
488
+ parser.add_argument("--sim_direction", dest="sim_direction", default= None,
489
+ help="If not defined or set to 'ext-int' are external profiles against reference profile. If set 'int-ext' is the ref profile against the external profiles")
434
490
  parser.add_argument("--header_id", dest="header_id", default="HP", help="Header ID to use in plotted heatmaps")
435
491
  opts = parser.parse_args(args)
436
492
  main_get_sorted_profs(opts)
@@ -105,7 +105,7 @@ class TextIndexer:
105
105
  for parsed_text in parsed_texts:
106
106
  doc_id, file, text = parsed_text
107
107
  if doc_id == None or text == "": continue
108
- pmid_content_and_stats = cls.prepare_indexes(text, doc_id, file, "None", "None", "None", "None", options)
108
+ pmid_content_and_stats = cls.prepare_indexes(text, doc_id, file, "None", "None", "None", "None", "None", options)
109
109
  texts.append(pmid_content_and_stats)
110
110
  if logger != None: logger.warning(f"stats:file={file_path},total={stats['total']}")
111
111
  if logger != None: logger.warning(f"logs_errors:file={file_path},errors_number={stats['errors']}")
@@ -209,6 +209,8 @@ class TextIndexer:
209
209
  sentences_splitter = RecursiveCharacterTextSplitter(chunk_size = 100, chunk_overlap = 20, length_function = len, separators=["\n", " ", ""], keep_separator=False, is_separator_regex=False)
210
210
  #sentences_splitter = RecursiveCharacterTextSplitter(chunk_size = 20, chunk_overlap = 2, length_function = cls._len_by_words_func, separators=["\n", " ", ""], keep_separator=False, is_separator_regex=False)
211
211
  #sentences_splitter = RecursiveCharacterTextSplitter(chunk_size = 120, chunk_overlap = 20, length_function = len, separators=["\n", " ", ""], keep_separator=False, is_separator_regex=False)
212
+ else:
213
+ sentences_splitter = RecursiveCharacterTextSplitter(chunk_size = 10, chunk_overlap = 0, length_function = len, separators=split_type.split("%-%"), keep_separator=False, is_separator_regex=False)
212
214
  paragraphs = [paragraph.strip() for paragraph in paragraph_splitter.split_text(text)]
213
215
  paragraph_sentences = [ sentences_splitter.split_text(paragraph) for paragraph in paragraphs ]
214
216
 
@@ -1,12 +1,14 @@
1
1
  #JPG: THIS IS AN ABSTRACT BASE CLASS FOR LEXICAL ENGINES AND SHOULD NOT BE INSTANTIATED DIRECTLY
2
2
 
3
- import os, warnings, json, gzip
3
+ import os, warnings, json, gzip, time, sys
4
4
  from abc import ABC, abstractmethod
5
5
 
6
6
  class LexicalEngineBaseClass(ABC):
7
7
 
8
8
  def __init__(self):
9
+ self.model_name = None
9
10
  self.embedder = None
11
+ self.reranker = None
10
12
  self.queries_content = {}
11
13
 
12
14
  @abstractmethod
@@ -68,7 +70,7 @@ class LexicalEngineBaseClass(ABC):
68
70
  return queries, query_ids
69
71
 
70
72
 
71
- def get_splitted_document(self, id, text):
73
+ def get_splitted_document(self, id, text, split_level = "sentence"):
72
74
  """
73
75
  Process a document that has been split by get_corpus_index. It is a json formmated list of lists, where each sublist is a paragraph and each element inside the sublist is a sentence.
74
76
  Each sentence is stored in a dictionary with the key being the ID of the document in the format "id_paragraphNumber_sentenceNumber" and the value being the sentence text.
@@ -78,29 +80,42 @@ class LexicalEngineBaseClass(ABC):
78
80
  Returns:
79
81
  dict: A dictionary where keys are IDs in the format "id_paragraph_sentence" and values are the corresponding sentences.
80
82
  """
83
+ from py_exp_calc.exp_calc import flatten
81
84
  pubmed_index = {}
82
85
  abstract_parts = json.loads(text)
83
86
  paragraph_number = 0
84
- for paragraph in abstract_parts:
85
- if paragraph[0] == "TITLE": paragraph_number = -2
86
- if paragraph[0] == "KEYWORDS": paragraph_number = -1
87
- sentence_number = 0
88
- for sentence in paragraph:
89
- if sentence in ["TITLE", "KEYWORDS", "None"]:
90
- sentence_number += 1
91
- continue
92
- id_tag = f"{id}_{paragraph_number}_{sentence_number}"
93
- pubmed_index[id_tag] = sentence
94
- sentence_number += 1
95
- paragraph_number += 1
87
+ sentence_number = 0
88
+
89
+ if split_level == "doc":
90
+ joined_text = self._join_text_back(abstract_parts, join_back_to = "doc")
91
+ pubmed_index[f"{id}_{paragraph_number}_{sentence_number}"] = joined_text
92
+ else:
93
+ for paragraph in abstract_parts:
94
+ if paragraph[0] == "TITLE": paragraph_number = -2
95
+ if paragraph[0] == "KEYWORDS": paragraph_number = -1
96
+ sentence_number = 0
97
+
98
+ if split_level == "paragraph":
99
+ joined_paragraph = self._join_text_back(paragraph, join_back_to = "paragraph")
100
+ pubmed_index[f"{id}_{paragraph_number}_{sentence_number}"] = joined_paragraph
101
+ paragraph_number += 1
102
+ else: #case for split_level == "sentence"
103
+ for sentence in paragraph:
104
+ if sentence in ["TITLE", "KEYWORDS", "None"]:
105
+ sentence_number += 1
106
+ continue
107
+ id_tag = f"{id}_{paragraph_number}_{sentence_number}"
108
+ pubmed_index[id_tag] = sentence
109
+ sentence_number += 1
110
+ paragraph_number += 1
96
111
  return pubmed_index
97
112
 
98
- def load_pubmed_index(self, file, is_splitted):
113
+ def load_pubmed_index(self, file, split_level = "sentence"):
99
114
  """
100
115
  Load a PubMed processed index file get by 'get_corpus_index' binary. The file is expected to be in a specific format, where each line contains an ID and the corresponding text (and other columns with possible metadata not loaded).
101
116
  Args:
102
117
  file (str): Path to the file containing the PubMed index.
103
- is_splitted (bool): If True, the text is expected to be in a JSON format with sentences split into paragraphs.
118
+ split_level (str): The level of splitting to use. Can be "doc", "paragraph", or "sentence".
104
119
  Returns:
105
120
  tuple: A tuple containing a dictionary where keys are IDs in the format "id_paragraphNumber_sentenceNumber" and values are the corresponding sentences, and the total number of papers processed.
106
121
  """
@@ -110,7 +125,7 @@ class LexicalEngineBaseClass(ABC):
110
125
  for line in f:
111
126
  try:
112
127
  id, text, *_rest = line.rstrip().split("\t")
113
- pubmed_index_iter = self.get_splitted_document(id, text)
128
+ pubmed_index_iter = self.get_splitted_document(id, text, split_level = split_level)
114
129
  pubmed_index.update(pubmed_index_iter)
115
130
  n_papers += 1
116
131
  except Exception as e:
@@ -120,17 +135,22 @@ class LexicalEngineBaseClass(ABC):
120
135
  def calculate_similarities(self, options, corpus_info):
121
136
  if options.get("output_file"):
122
137
  for query_basename, query_info in self.queries_content.items():
138
+ self.current_query_basename = query_basename
139
+ self.start_time = time.time()
123
140
  best_matches = self.calculate_similarity(query_info, corpus_info, options)
141
+ end = time.time()
142
+ if options['get_total_time_file']:
143
+ with open(options['get_total_time_file'], 'a') as f: f.write(f"TIMELOG:{self.model_name}:{self.reranker}:Total time calculating similarities for {query_basename} query ({len(query_info.get('query_ids', []))} names and synonims) and {len(corpus_info['all_corpus'])} sentences: {end - self.start_time}\n")
124
144
  if options['print_relevant_pairs']: self.print_similarities(query_info, corpus_info, best_matches, options)
125
145
  output_filename = os.path.join(options["output_file"],query_basename)
126
146
  self.save_similarities(output_filename, best_matches, options)
127
147
 
128
148
  def save_similarities(self, filepath, best_matches, options):
129
149
  #with gzip.open(filepath, "a") as f: #TODO: add it later
130
- with open(filepath, 'a') as f:
150
+ with open(filepath, options["write_sims_mode"]) as f:
131
151
  for kwdID, matches in best_matches.items():
132
152
  for textID, score in matches.items():
133
- if score >= options["threshold"]:
153
+ if score == "-" or score >= options["threshold"]: #"-" means full match for ontogpt
134
154
  if options["order"] == "corpus-query":
135
155
  f.write(f"{textID}\t{kwdID}\t{score}\n")
136
156
  else:
@@ -142,7 +162,7 @@ class LexicalEngineBaseClass(ABC):
142
162
  textIDX = corpus_info["textIDs"].index(textID)
143
163
  text = corpus_info["all_corpus"][textIDX]
144
164
  for kwdID, score in matches.items():
145
- if score >= options["threshold"]:
165
+ if score == "-" or score >= options["threshold"]:
146
166
  kwIDXs = (idx for idx, char in enumerate(query_info['query_ids']) if char == kwdID)
147
167
  term = " -- ".join([query_info["queries"][kwIDX] for kwIDX in kwIDXs])
148
168
  if term not in term_related_sentences: term_related_sentences[term] = {"term_id": kwdID, "sentences": []}
@@ -153,4 +173,19 @@ class LexicalEngineBaseClass(ABC):
153
173
  for textID, text, score in data["sentences"]:
154
174
  print(f" - Text ID: {textID}, Score: {score}")
155
175
  print(f" Text: {text}")
156
- print("-"*30)
176
+ print("-"*30)
177
+
178
+ def _join_text_back(self, text, join_back_to = "doc"):
179
+ final_text = ""
180
+
181
+ if join_back_to == "doc":
182
+ for paragraph in text:
183
+ for sentence in paragraph:
184
+ final_text += sentence + ", "
185
+ final_text += ".\n\n"
186
+
187
+ elif join_back_to == "paragraph":
188
+ for sentence in text:
189
+ final_text += sentence + ", "
190
+
191
+ return final_text
@@ -3,10 +3,12 @@ from py_semtools.lexical_engines.engine_baseclass import LexicalEngineBaseClass
3
3
  class FMengine(LexicalEngineBaseClass):
4
4
  def __init__(self, gpu_devices = []):
5
5
  self.model_name = None
6
+ self.embedder = None
7
+ self.reranker = None
6
8
  self.queries_content = {}
7
9
 
8
10
  def init_model(self, model_name: str, verbose: bool = False) -> None:
9
- if model_name not in ["bm25", "tfidf"]:
11
+ if model_name not in ["bm25", "tfidf", "tfidf_faiss"]:
10
12
  raise Exception(f"Model {model_name} not supported in FMengine")
11
13
  if verbose: print(f"\n-Loading {model_name} model")
12
14
  self.model_name = model_name
@@ -21,7 +23,7 @@ class FMengine(LexicalEngineBaseClass):
21
23
  all_textIDs = []; all_corpus = []; total_papers = 0
22
24
  for corpus_filename in corpus_filenames:
23
25
  if verbose: print(f"---Loading corpus of {corpus_filename}")
24
- pubmed_index, n_papers = self.load_pubmed_index(corpus_filename, options["split"]) # abstracts
26
+ pubmed_index, n_papers = self.load_pubmed_index(corpus_filename, split_level=options["split_level"]) # abstracts
25
27
  if verbose: print(f"------Loaded {n_papers} documents with {len(pubmed_index)} sentences from {corpus_filename}")
26
28
  total_papers += n_papers
27
29
  all_textIDs.extend(pubmed_index.keys())
@@ -45,6 +47,9 @@ class FMengine(LexicalEngineBaseClass):
45
47
  results_indexes, scores = self.calculate_bm25_similarity(query_text, corpus_text, options)
46
48
  elif self.model_name == "tfidf":
47
49
  results_indexes, scores = self.calculate_tfidf_similarity(query_text, corpus_text, options)
50
+ elif self.model_name == "tfidf_faiss":
51
+ results_indexes, scores = self.calculate_tfidf_similarity_faiss(query_text, corpus_text, options)
52
+
48
53
  matches = self.find_best_matches(results_indexes, scores, query_ids, corpus_ids)
49
54
  return matches
50
55
 
@@ -88,6 +93,31 @@ class FMengine(LexicalEngineBaseClass):
88
93
 
89
94
  return batch_indices, batch_scores
90
95
 
96
+ def calculate_tfidf_similarity_faiss(self, query_text, corpus_text, options):
97
+ from sklearn.feature_extraction.text import TfidfVectorizer
98
+ import faiss
99
+ # 1. Vectorization (remains the same)
100
+ vectorizer = TfidfVectorizer().fit(corpus_text)
101
+ tfidf_matrix = vectorizer.transform(corpus_text).toarray().astype('float32')
102
+ query_vecs = vectorizer.transform(query_text).toarray().astype('float32')
103
+
104
+ # 2. Normalize vectors for Cosine Similarity
105
+ faiss.normalize_L2(tfidf_matrix)
106
+ faiss.normalize_L2(query_vecs)
107
+
108
+ # 3. Initialize FAISS Index (Inner Product)
109
+ d = tfidf_matrix.shape[1] # dimensionality
110
+ index = faiss.IndexFlatIP(d)
111
+
112
+ # 4. Add corpus to index (Fast process)
113
+ index.add(tfidf_matrix)
114
+
115
+ # 5. Search (This handles batching and parallelization internally)
116
+ k = min(options['top_k'], tfidf_matrix.shape[0])
117
+ batch_scores, batch_indices = index.search(query_vecs, k)
118
+
119
+ return batch_indices, batch_scores
120
+
91
121
  def find_best_matches(self, results_indexes, scores, query_ids, corpus_ids):
92
122
  matches = {}
93
123
  for row_idx, res_idxs in enumerate(results_indexes):
@@ -0,0 +1,102 @@
1
+ import os, re, logging, requests, warnings
2
+ from py_semtools.lexical_engines.engine_baseclass import LexicalEngineBaseClass
3
+ from ontogpt.io.template_loader import get_template_details, get_template_path
4
+ from ontogpt.engines.spires_engine import SPIRESEngine
5
+
6
+ class LLMengine(LexicalEngineBaseClass):
7
+ def __init__(self, gpu_devices = []):
8
+ self.model_name = None
9
+ self.embedder = None
10
+ self.reranker = None
11
+ self.queries_content = {}
12
+
13
+ def init_model(self, model_name: str, model_provider: str = None, verbose: bool = False) -> None:
14
+ if verbose: print(f"\n-Using {model_name} model with {model_provider} provider")
15
+ self.model_name = model_name
16
+ self.model_provider = model_provider
17
+
18
+ def load_several_queries(self, options, query_filenames, verbose=False):
19
+ if verbose: print("\n-Loading and embedding queries:")
20
+ for query_filename in query_filenames:
21
+ query_basename = os.path.splitext(os.path.basename(query_filename))[0]
22
+ self.queries_content[query_basename] = {'template': options["template"]}
23
+ if options["verbose"]: print(f"---Using query template {options['template']} for {query_basename}")
24
+
25
+ def load_several_corpora(self, options, corpus_filenames, verbose=False):
26
+ all_textIDs = []; all_corpus = []; total_papers = 0
27
+ for corpus_filename in corpus_filenames:
28
+ if verbose: print(f"---Loading corpus of {corpus_filename}")
29
+ pubmed_index, n_papers = self.load_pubmed_index(corpus_filename, split_level=options["split_level"]) # abstracts
30
+ if verbose: print(f"------Loaded {n_papers} documents with {len(pubmed_index)} sentences from {corpus_filename}")
31
+ total_papers += n_papers
32
+ all_textIDs.extend(pubmed_index.keys())
33
+ all_corpus.extend(pubmed_index.values())
34
+ corpus_info = {'textIDs': all_textIDs, "all_corpus": all_corpus}
35
+ if verbose: print(f"---Loaded a total of {total_papers} documents with {len(all_corpus)} sentences from {len(corpus_filenames)} corpora")
36
+ return corpus_info
37
+
38
+ def calculate_similarity(self, query_info, corpus_info, options):
39
+ kwargs = {'recurse': True, 'auto_prefix': 'AUTO'}
40
+ corpus_texts = corpus_info["all_corpus"]
41
+ corpus_ids = corpus_info["textIDs"]
42
+ template = options["template"]
43
+ model = options["model_name"]
44
+ address = options["listening_addresses"]
45
+
46
+ if options["logging_level"]:
47
+ loggin_levels = {"DEBUG": logging.DEBUG, "INFO":logging.INFO, "WARNING":logging.WARNING, "ERROR":logging.ERROR}
48
+ logger = logging.getLogger()
49
+ logger.setLevel(level=loggin_levels[options["logging_level"]])
50
+
51
+ if options['verbose']: print(f"\n-Calculating similarities between queries and corpus using {model} model with {template} template:")
52
+
53
+ if template:
54
+ template_details = get_template_details(template=template)
55
+ else:
56
+ raise ValueError("No template specified. Use -t/--template option.")
57
+
58
+ ke = SPIRESEngine(template_details=template_details, model=model, temperature=1,
59
+ api_base=address, api_version=None, model_provider=None, system_message=None, max_text_length=None,
60
+ **kwargs)
61
+
62
+ best_matches = {}
63
+ for idx, text in enumerate(corpus_texts):
64
+ if options['verbose']: print(f"Extracting for text ID {corpus_ids[idx]}") # Print the beginning of the text for context
65
+
66
+ #results = ke.extract_from_text( text=text, cls=None, show_prompt=None)
67
+ results = self._execute_with_retry( func=ke.extract_from_text,
68
+ exception_type=requests.exceptions.ReadTimeout, max_retries=3, text_id=corpus_ids[idx],
69
+ text=text, cls=None, show_prompt=None)
70
+
71
+ if results != None:
72
+ hpos = self._get_genuine_terms(results.named_entities, options["regex_tag"])
73
+ best_matches[corpus_ids[idx]] = hpos
74
+
75
+ if options['stream_write_results']:
76
+ backup_file = os.path.join(options["output_file"], self.current_query_basename + "_backup")
77
+ warnings.warn(f"Streaming mode enabled: writing {len(hpos)}terms for text ID {corpus_ids[idx]} immediately after processing (inside file {backup_file} ).")
78
+ self.save_similarities(backup_file, {corpus_ids[idx]: hpos}, options) # Write results in streaming mode if enabled
79
+
80
+ return best_matches
81
+
82
+ def find_best_matches(self, results_indexes, scores, query_ids, corpus_ids):
83
+ pass
84
+
85
+
86
+ def _get_genuine_terms(self, named_entities, regex_tag):
87
+ if regex_tag:
88
+ return {entity.id : "-" for entity in named_entities if re.match(regex_tag, entity.id) and len(entity.original_spans) > 0}
89
+ else:
90
+ return {entity.id : "-" for entity in named_entities if len(entity.original_spans) > 0}
91
+
92
+ def _execute_with_retry(self, func, exception_type, max_retries, text_id=None, *args, **kwargs):
93
+ tries = 0
94
+ while True:
95
+ try:
96
+ return func(*args, **kwargs)
97
+ except exception_type as e:
98
+ tries += 1
99
+ warnings.warn(f"{exception_type.__name__} occurred for text ID {text_id}. Retrying... Error: {e}")
100
+ if max_retries > 0 and tries >= max_retries:
101
+ warnings.warn(f"Failed to process text ID {text_id} after {max_retries} attempts. Skipping this text.")
102
+ return None # Salimos devolviendo None tras agotar intentos
@@ -62,7 +62,8 @@ class STengine(LexicalEngineBaseClass):
62
62
  full_model_path = os.path.join(cache_folder, model_name)
63
63
  if verbose: print(f"\n-Downloading or loading bi encoder model {model_name} inside path {full_model_path}")
64
64
  self.model_name = model_name
65
- has_cached_model = os.path.exists(os.path.join(full_model_path, f'models--sentence-transformers--{model_name}')) #SentenceTransfomers now uses local_files_only to control internet access, even if the model is cached, and trying to download the model with this variable false gives error, so we have to control it
65
+ has_cached_model = os.path.exists(os.path.join(full_model_path, f'models--{model_name.replace("/", "--")}')) #SentenceTransfomers now uses local_files_only to control internet access, even if the model is cached, and trying to download the model with this variable false gives error, so we have to control it
66
+ if verbose: print(f"Seeking cached model (found:{has_cached_model}) in", os.path.join(full_model_path, f'models--{model_name.replace("/", "--")}'))
66
67
  self.embedder = SentenceTransformer(model_name, cache_folder = full_model_path, local_files_only=has_cached_model)
67
68
 
68
69
  def init_rerank_model(self, model_name, cache_folder = None, verbose = False):
@@ -70,8 +71,9 @@ class STengine(LexicalEngineBaseClass):
70
71
  full_model_path = os.path.join(cache_folder, model_name)
71
72
  if verbose: print(f"\n-Downloading or loading cross-encoder model {model_name} inside path {full_model_path}")
72
73
  self.reranker_model_name = model_name
73
- has_cached_model = os.path.exists(os.path.join(full_model_path, f'models--cross-encoder--{model_name}')) #SentenceTransfomers now uses local_files_only to control internet access, even if the model is cached, and trying to download the model with this variable false gives error, so we have to control it
74
- self.reranker = CrossEncoder(model_name, cache_folder = full_model_path, local_files_only=has_cached_model)
74
+ has_cached_model = os.path.exists(os.path.join(full_model_path, f'models--{model_name.replace("/", "--")}')) #SentenceTransfomers now uses local_files_only to control internet access, even if the model is cached, and trying to download the model with this variable false gives error, so we have to control it
75
+ if verbose: print(f"Seeking cached model (found:{has_cached_model}) in ", os.path.join(full_model_path, f'models--{model_name.replace("/", "--")}'))
76
+ self.reranker = CrossEncoder(model_name_or_path = model_name, cache_folder = full_model_path, local_files_only=has_cached_model)
75
77
 
76
78
  def embed_save_corpus(self, options, corpus_basename, all_textIDs, all_corpus, total_papers):
77
79
  if options["verbose"]: print(f"---Embedding corpus of {corpus_basename} comprised by {total_papers} initial papers with {len(all_textIDs)} sentences, with {'GPU' if options.get('gpu_device') else 'CPU'}")
@@ -198,7 +200,6 @@ class STengine(LexicalEngineBaseClass):
198
200
  results[query_idx].append({'score': score, 'corpus_id': corpus_idx})
199
201
  return results
200
202
 
201
-
202
203
  def make_rerank(self, sentence_pairs, options):
203
204
  import torch
204
205
  if options['verbose']: print(f"---Reranking {len(sentence_pairs)} sentence pairs with cross-encoder model")
@@ -208,7 +209,10 @@ class STengine(LexicalEngineBaseClass):
208
209
  scores = self.reranker.predict(sentence_pairs, pool=pool, batch_size=options["batch_size"], chunk_size=options['single_worker_chunk_size'], activation_fn=torch.nn.Sigmoid())
209
210
  self.reranker.stop_multi_process_pool(pool)
210
211
  elif len(options["gpu_device"]) == 1:
211
- scores = self.reranker.encode(sentence_pairs, device= options["gpu_device"][0], batch_size=options["batch_size"], activation_fn=torch.nn.Sigmoid()) #convert_to_tensor=True
212
+ scores = self.reranker.predict(sentence_pairs, device= options["gpu_device"][0], batch_size=options["batch_size"], activation_fn=torch.nn.Sigmoid()) #convert_to_tensor=True
213
+ else:
214
+ scores = self.reranker.predict(sentence_pairs, batch_size=options["batch_size"], activation_fn=torch.nn.Sigmoid()) #convert_to_tensor=True
215
+ #raise Exception("You need to provide GPU to use reranker. It should be a list of GPU devices like: ['cuda:0'] or ['cuda:0', 'cuda:1']")
212
216
  if options["verbose"]: print(f"---Reranking time with {0 if options.get('gpu_device') == None else len(options['gpu_device'])} GPUs: {time.time() - start} seconds")
213
217
  return scores
214
218
 
@@ -271,7 +275,7 @@ class STengine(LexicalEngineBaseClass):
271
275
 
272
276
  if options.get("corpus") != None: #LOAD RAW CORPUS AND EMBEDD (AND MAYBE SAVE)
273
277
  if verbose: print(f"---Loading corpus of {corpus_filename}")
274
- pubmed_index, n_papers = self.load_pubmed_index(corpus_filename, options["split"]) # abstracts
278
+ pubmed_index, n_papers = self.load_pubmed_index(corpus_filename, split_level=options["split_level"]) # abstracts
275
279
  total_papers += n_papers
276
280
  all_textIDs.extend(pubmed_index.keys())
277
281
  all_corpus.extend(pubmed_index.values())