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.
- {py_semtools-1.3.3 → py_semtools-1.3.4}/PKG-INFO +4 -2
- {py_semtools-1.3.3 → py_semtools-1.3.4}/setup.cfg +3 -1
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/cli_manager.py +66 -10
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/indexers/text_indexer.py +3 -1
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/lexical_engines/engine_baseclass.py +56 -21
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/lexical_engines/fmEngine.py +32 -2
- py_semtools-1.3.4/src/py_semtools/lexical_engines/llmEngine.py +102 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/lexical_engines/stEngine.py +10 -6
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/main_modules.py +48 -16
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/ontology.py +54 -26
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/ont/oboparser.py +49 -4
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_basic_parser.py +6 -5
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/report_ont.py +19 -2
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/comorb_sugg.txt +1 -1
- py_semtools-1.3.4/src/py_semtools/templates/makeTermFreqDist.txt +5 -0
- py_semtools-1.3.4/src/py_semtools/templates/makeTermFreqTable.txt +11 -0
- py_semtools-1.3.4/src/py_semtools/templates/ontoICdist.txt +63 -0
- py_semtools-1.3.4/src/py_semtools/templates/ontodist.txt +31 -0
- py_semtools-1.3.4/src/py_semtools/templates/plotProfRed.txt +10 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/PKG-INFO +4 -2
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/SOURCES.txt +7 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/entry_points.txt +1 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/requires.txt +3 -1
- py_semtools-1.3.4/src/py_semtools.egg-info/scm_file_list.json +169 -0
- py_semtools-1.3.4/src/py_semtools.egg-info/scm_version.json +8 -0
- py_semtools-1.3.4/tests/data/hierarchical_sample_3level_child.obo +65 -0
- py_semtools-1.3.4/tests/data/ref_output_scripts/set_operation_is_descendant +1 -0
- py_semtools-1.3.4/tests/data/ref_output_scripts/set_operation_is_parental +1 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_cli_manager.py +31 -15
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_oboparser.py +15 -0
- py_semtools-1.3.3/src/py_semtools/templates/makeTermFreqTable.txt +0 -4
- py_semtools-1.3.3/src/py_semtools/templates/ontoICdist.txt +0 -8
- py_semtools-1.3.3/src/py_semtools/templates/ontodist.txt +0 -10
- py_semtools-1.3.3/src/py_semtools/templates/plotProfRed.txt +0 -6
- {py_semtools-1.3.3 → py_semtools-1.3.4}/.coveragerc +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/.gitignore +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/.readthedocs.yml +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/AUTHORS.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/CHANGELOG.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/CONTRIBUTING.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/LICENSE.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/MANIFEST.in +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/README.md +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/README.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/Makefile +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/_static/.gitignore +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/authors.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/changelog.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/conf.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/contributing.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/index.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/license.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/readme.rst +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/docs/requirements.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/pyproject.toml +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/setup.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/__init__.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/external_data/ontologies.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parallelizer.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/ont/file_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/ont/json_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_pubmed_abstract_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_pubmed_paper_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/parsers/text/text_pubmed_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/sim_handler.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/ontoplot.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/plotClust.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/report.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/similarity_heatmap.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/similarity_matrix.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools/templates/stEngine.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/dependency_links.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/not-zip-safe +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/src/py_semtools.egg-info/top_level.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/__init__.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/Report.html +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/example +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/launch.sh +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/styles.css +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/cli_examples/template.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/conftest.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/branched.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/circular_sample.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/enrichment_ontology.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/enrichment_ontology2.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/enrichment_ontology3.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_profs/report.html +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_profs/report.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/filter_query_parentals.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/filter_target_and_query_parentals.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/filter_target_parentals.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/no_filter_limit_2.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/expected/no_filter_no_limit.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/input_data/enrichment_ontology3.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/input_data/query_hps.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/get_sorted_suggestions/input_data/relations.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/go-basic_sample.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/go-onlyOne.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/hierarchical_alt_chain.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/hierarchical_compressed.obo.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/hierarchical_sample.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/profiles +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/profiles_2cols +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/profiles_with_removedTerms +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/string_values +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/terms +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/terms_for_xref +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/input_scripts/terms_list +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/only_header_sample.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/partial_go.json +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/partial_go.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/blacklisted.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/cleaned_profiles +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/cleaned_profiles_2cols +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/expanded_profiles +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/expected_IC_ont +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/parental_from_terms +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/profile_stats +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/profiles_IC_onto_freq +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/strsimnet +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/strsimnet_cutoff2 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/terms_attr +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/translated_profiles_names +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/translated_terms_codes +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/white_and_blacklisted.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/ref_output_scripts/whitelisted.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/short_hierarchical_sample.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/sparse2_sample.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/sparse_sample.obo +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981088 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981089 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981091 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/22981092 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/chunk1/None +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/abstracts/single_abs_text +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/18382669 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/31356151 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/35042469 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/38108203 +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/chunk1/None +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/raw_indexes/papers/single_pap_text +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/abstracts/single_abs_index_nonsplit +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/abstracts/single_abs_index_split +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/papers/single_pap_index_nonsplit +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/expected/ready_indexes/papers/single_pap_index_split +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/abs_chunk1.xml.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/abs_chunk2.xml.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/single_abstract.xml +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/abstracts/single_abstract.xml.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/blacklisted_words1.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/blacklisted_words2.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/blacklisted_words3.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/example.xml +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/example_zipped.xml.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/pap_chunk1.tar.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/pap_chunk2.tar.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/single_paper.tar.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/papers/single_paper.xml +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/prepared_indexes/abstracts/example1.txt.gz +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/prepared_indexes/queries/hpo_list +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/stEngine/inputs/prepared_indexes/queries/mondo_list +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/st_engine_report/abs_profiles.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/data/st_engine_report/pmids_and_titles.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/data/profiles.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/data/ref_profile.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/launch.sh +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/launch_plots.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/profiles_freqs.html +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/demo_examples/template.txt +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_go.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_jsonparser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_ontology.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_pubmed_abs_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_pubmed_pap_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_pubmed_text_parser.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_similitudes.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_stengine.py +0 -0
- {py_semtools-1.3.3 → py_semtools-1.3.4}/tests/test_text_indexer.py +0 -0
- {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
|
+
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:
|
|
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;
|
|
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'
|
|
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', "--
|
|
260
|
-
help="Use it if your corpus comes splitted in smaller parts
|
|
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', "--
|
|
296
|
-
help="Use it if your corpus comes splitted in smaller parts
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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["
|
|
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--
|
|
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--
|
|
74
|
-
|
|
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.
|
|
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["
|
|
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())
|