aiagents4pharma 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aiagents4pharma/__init__.py +11 -0
- aiagents4pharma/talk2aiagents4pharma/.dockerignore +13 -0
- aiagents4pharma/talk2aiagents4pharma/Dockerfile +133 -0
- aiagents4pharma/talk2aiagents4pharma/README.md +1 -0
- aiagents4pharma/talk2aiagents4pharma/__init__.py +5 -0
- aiagents4pharma/talk2aiagents4pharma/agents/__init__.py +6 -0
- aiagents4pharma/talk2aiagents4pharma/agents/main_agent.py +70 -0
- aiagents4pharma/talk2aiagents4pharma/configs/__init__.py +5 -0
- aiagents4pharma/talk2aiagents4pharma/configs/agents/__init__.py +5 -0
- aiagents4pharma/talk2aiagents4pharma/configs/agents/main_agent/default.yaml +29 -0
- aiagents4pharma/talk2aiagents4pharma/configs/app/__init__.py +0 -0
- aiagents4pharma/talk2aiagents4pharma/configs/app/frontend/__init__.py +0 -0
- aiagents4pharma/talk2aiagents4pharma/configs/app/frontend/default.yaml +102 -0
- aiagents4pharma/talk2aiagents4pharma/configs/config.yaml +4 -0
- aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/.env.example +23 -0
- aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/docker-compose.yml +93 -0
- aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/.env.example +23 -0
- aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/docker-compose.yml +108 -0
- aiagents4pharma/talk2aiagents4pharma/install.md +154 -0
- aiagents4pharma/talk2aiagents4pharma/states/__init__.py +5 -0
- aiagents4pharma/talk2aiagents4pharma/states/state_talk2aiagents4pharma.py +18 -0
- aiagents4pharma/talk2aiagents4pharma/tests/__init__.py +3 -0
- aiagents4pharma/talk2aiagents4pharma/tests/test_main_agent.py +312 -0
- aiagents4pharma/talk2biomodels/.dockerignore +13 -0
- aiagents4pharma/talk2biomodels/Dockerfile +104 -0
- aiagents4pharma/talk2biomodels/README.md +1 -0
- aiagents4pharma/talk2biomodels/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/agents/__init__.py +6 -0
- aiagents4pharma/talk2biomodels/agents/t2b_agent.py +104 -0
- aiagents4pharma/talk2biomodels/api/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/api/ols.py +75 -0
- aiagents4pharma/talk2biomodels/api/uniprot.py +36 -0
- aiagents4pharma/talk2biomodels/configs/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/configs/agents/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/default.yaml +14 -0
- aiagents4pharma/talk2biomodels/configs/app/__init__.py +0 -0
- aiagents4pharma/talk2biomodels/configs/app/frontend/__init__.py +0 -0
- aiagents4pharma/talk2biomodels/configs/app/frontend/default.yaml +72 -0
- aiagents4pharma/talk2biomodels/configs/config.yaml +7 -0
- aiagents4pharma/talk2biomodels/configs/tools/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/configs/tools/ask_question/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/configs/tools/ask_question/default.yaml +30 -0
- aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/default.yaml +8 -0
- aiagents4pharma/talk2biomodels/configs/tools/get_annotation/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/configs/tools/get_annotation/default.yaml +8 -0
- aiagents4pharma/talk2biomodels/install.md +63 -0
- aiagents4pharma/talk2biomodels/models/__init__.py +5 -0
- aiagents4pharma/talk2biomodels/models/basico_model.py +125 -0
- aiagents4pharma/talk2biomodels/models/sys_bio_model.py +60 -0
- aiagents4pharma/talk2biomodels/states/__init__.py +6 -0
- aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +49 -0
- aiagents4pharma/talk2biomodels/tests/BIOMD0000000449_url.xml +1585 -0
- aiagents4pharma/talk2biomodels/tests/__init__.py +3 -0
- aiagents4pharma/talk2biomodels/tests/article_on_model_537.pdf +0 -0
- aiagents4pharma/talk2biomodels/tests/test_api.py +31 -0
- aiagents4pharma/talk2biomodels/tests/test_ask_question.py +42 -0
- aiagents4pharma/talk2biomodels/tests/test_basico_model.py +67 -0
- aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +190 -0
- aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +92 -0
- aiagents4pharma/talk2biomodels/tests/test_integration.py +116 -0
- aiagents4pharma/talk2biomodels/tests/test_load_biomodel.py +35 -0
- aiagents4pharma/talk2biomodels/tests/test_param_scan.py +71 -0
- aiagents4pharma/talk2biomodels/tests/test_query_article.py +184 -0
- aiagents4pharma/talk2biomodels/tests/test_save_model.py +47 -0
- aiagents4pharma/talk2biomodels/tests/test_search_models.py +35 -0
- aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +44 -0
- aiagents4pharma/talk2biomodels/tests/test_steady_state.py +86 -0
- aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +67 -0
- aiagents4pharma/talk2biomodels/tools/__init__.py +17 -0
- aiagents4pharma/talk2biomodels/tools/ask_question.py +125 -0
- aiagents4pharma/talk2biomodels/tools/custom_plotter.py +165 -0
- aiagents4pharma/talk2biomodels/tools/get_annotation.py +342 -0
- aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +159 -0
- aiagents4pharma/talk2biomodels/tools/load_arguments.py +134 -0
- aiagents4pharma/talk2biomodels/tools/load_biomodel.py +44 -0
- aiagents4pharma/talk2biomodels/tools/parameter_scan.py +310 -0
- aiagents4pharma/talk2biomodels/tools/query_article.py +64 -0
- aiagents4pharma/talk2biomodels/tools/save_model.py +98 -0
- aiagents4pharma/talk2biomodels/tools/search_models.py +96 -0
- aiagents4pharma/talk2biomodels/tools/simulate_model.py +137 -0
- aiagents4pharma/talk2biomodels/tools/steady_state.py +187 -0
- aiagents4pharma/talk2biomodels/tools/utils.py +23 -0
- aiagents4pharma/talk2cells/README.md +1 -0
- aiagents4pharma/talk2cells/__init__.py +5 -0
- aiagents4pharma/talk2cells/agents/__init__.py +6 -0
- aiagents4pharma/talk2cells/agents/scp_agent.py +87 -0
- aiagents4pharma/talk2cells/states/__init__.py +6 -0
- aiagents4pharma/talk2cells/states/state_talk2cells.py +15 -0
- aiagents4pharma/talk2cells/tests/scp_agent/test_scp_agent.py +22 -0
- aiagents4pharma/talk2cells/tools/__init__.py +6 -0
- aiagents4pharma/talk2cells/tools/scp_agent/__init__.py +6 -0
- aiagents4pharma/talk2cells/tools/scp_agent/display_studies.py +27 -0
- aiagents4pharma/talk2cells/tools/scp_agent/search_studies.py +78 -0
- aiagents4pharma/talk2knowledgegraphs/.dockerignore +13 -0
- aiagents4pharma/talk2knowledgegraphs/Dockerfile +131 -0
- aiagents4pharma/talk2knowledgegraphs/README.md +1 -0
- aiagents4pharma/talk2knowledgegraphs/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/agents/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/agents/t2kg_agent.py +99 -0
- aiagents4pharma/talk2knowledgegraphs/configs/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/__init__.py +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/default.yaml +62 -0
- aiagents4pharma/talk2knowledgegraphs/configs/app/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/__init__.py +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/default.yaml +79 -0
- aiagents4pharma/talk2knowledgegraphs/configs/config.yaml +13 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/__init__.py +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/default.yaml +24 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/multimodal_subgraph_extraction/__init__.py +0 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/multimodal_subgraph_extraction/default.yaml +33 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/__init__.py +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/default.yaml +43 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/__init__.py +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/default.yaml +9 -0
- aiagents4pharma/talk2knowledgegraphs/configs/utils/database/milvus/__init__.py +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/utils/database/milvus/default.yaml +61 -0
- aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/ols_terms/default.yaml +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/reactome_pathways/default.yaml +3 -0
- aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/uniprot_proteins/default.yaml +6 -0
- aiagents4pharma/talk2knowledgegraphs/configs/utils/pubchem_utils/default.yaml +5 -0
- aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py +607 -0
- aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py +25 -0
- aiagents4pharma/talk2knowledgegraphs/datasets/primekg.py +212 -0
- aiagents4pharma/talk2knowledgegraphs/datasets/starkqa_primekg.py +210 -0
- aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/.env.example +23 -0
- aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/docker-compose.yml +93 -0
- aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/.env.example +23 -0
- aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/docker-compose.yml +108 -0
- aiagents4pharma/talk2knowledgegraphs/entrypoint.sh +180 -0
- aiagents4pharma/talk2knowledgegraphs/install.md +165 -0
- aiagents4pharma/talk2knowledgegraphs/milvus_data_dump.py +886 -0
- aiagents4pharma/talk2knowledgegraphs/states/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/states/state_talk2knowledgegraphs.py +40 -0
- aiagents4pharma/talk2knowledgegraphs/tests/__init__.py +0 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_agents_t2kg_agent.py +318 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_biobridge_primekg.py +248 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_dataset.py +33 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_primekg.py +86 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py +125 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_graphrag_reasoning.py +257 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_milvus_multimodal_subgraph_extraction.py +1444 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_multimodal_subgraph_extraction.py +159 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_extraction.py +152 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_summarization.py +201 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_database_milvus_connection_manager.py +812 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py +51 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py +49 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_nim_molmim.py +59 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_ollama.py +63 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_sentencetransformer.py +47 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +40 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +94 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ols.py +70 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_pubchem.py +45 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_reactome.py +44 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_uniprot.py +48 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_extractions_milvus_multimodal_pcst.py +759 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_kg_utils.py +78 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_pubchem_utils.py +123 -0
- aiagents4pharma/talk2knowledgegraphs/tools/__init__.py +11 -0
- aiagents4pharma/talk2knowledgegraphs/tools/graphrag_reasoning.py +138 -0
- aiagents4pharma/talk2knowledgegraphs/tools/load_arguments.py +22 -0
- aiagents4pharma/talk2knowledgegraphs/tools/milvus_multimodal_subgraph_extraction.py +965 -0
- aiagents4pharma/talk2knowledgegraphs/tools/multimodal_subgraph_extraction.py +374 -0
- aiagents4pharma/talk2knowledgegraphs/tools/subgraph_extraction.py +291 -0
- aiagents4pharma/talk2knowledgegraphs/tools/subgraph_summarization.py +123 -0
- aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/utils/database/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/utils/database/milvus_connection_manager.py +586 -0
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py +81 -0
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py +111 -0
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/nim_molmim.py +54 -0
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/ollama.py +87 -0
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py +73 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +12 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +37 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +129 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ols_terms.py +89 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/pubchem_strings.py +78 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/reactome_pathways.py +71 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/uniprot_proteins.py +98 -0
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/__init__.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/milvus_multimodal_pcst.py +762 -0
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/multimodal_pcst.py +298 -0
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/pcst.py +229 -0
- aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py +67 -0
- aiagents4pharma/talk2knowledgegraphs/utils/pubchem_utils.py +104 -0
- aiagents4pharma/talk2scholars/.dockerignore +13 -0
- aiagents4pharma/talk2scholars/Dockerfile +104 -0
- aiagents4pharma/talk2scholars/README.md +1 -0
- aiagents4pharma/talk2scholars/__init__.py +7 -0
- aiagents4pharma/talk2scholars/agents/__init__.py +13 -0
- aiagents4pharma/talk2scholars/agents/main_agent.py +89 -0
- aiagents4pharma/talk2scholars/agents/paper_download_agent.py +96 -0
- aiagents4pharma/talk2scholars/agents/pdf_agent.py +101 -0
- aiagents4pharma/talk2scholars/agents/s2_agent.py +135 -0
- aiagents4pharma/talk2scholars/agents/zotero_agent.py +127 -0
- aiagents4pharma/talk2scholars/configs/__init__.py +7 -0
- aiagents4pharma/talk2scholars/configs/agents/__init__.py +7 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py +7 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +52 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/paper_download_agent/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/paper_download_agent/default.yaml +19 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +19 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +44 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +19 -0
- aiagents4pharma/talk2scholars/configs/app/__init__.py +7 -0
- aiagents4pharma/talk2scholars/configs/app/frontend/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/app/frontend/default.yaml +72 -0
- aiagents4pharma/talk2scholars/configs/config.yaml +16 -0
- aiagents4pharma/talk2scholars/configs/tools/__init__.py +21 -0
- aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/default.yaml +26 -0
- aiagents4pharma/talk2scholars/configs/tools/paper_download/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/paper_download/default.yaml +124 -0
- aiagents4pharma/talk2scholars/configs/tools/question_and_answer/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/question_and_answer/default.yaml +62 -0
- aiagents4pharma/talk2scholars/configs/tools/retrieve_semantic_scholar_paper_id/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/retrieve_semantic_scholar_paper_id/default.yaml +12 -0
- aiagents4pharma/talk2scholars/configs/tools/search/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/search/default.yaml +26 -0
- aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/default.yaml +26 -0
- aiagents4pharma/talk2scholars/configs/tools/zotero_read/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/zotero_read/default.yaml +57 -0
- aiagents4pharma/talk2scholars/configs/tools/zotero_write/__inti__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/zotero_write/default.yaml +55 -0
- aiagents4pharma/talk2scholars/docker-compose/cpu/.env.example +21 -0
- aiagents4pharma/talk2scholars/docker-compose/cpu/docker-compose.yml +90 -0
- aiagents4pharma/talk2scholars/docker-compose/gpu/.env.example +21 -0
- aiagents4pharma/talk2scholars/docker-compose/gpu/docker-compose.yml +105 -0
- aiagents4pharma/talk2scholars/install.md +122 -0
- aiagents4pharma/talk2scholars/state/__init__.py +7 -0
- aiagents4pharma/talk2scholars/state/state_talk2scholars.py +98 -0
- aiagents4pharma/talk2scholars/tests/__init__.py +3 -0
- aiagents4pharma/talk2scholars/tests/test_agents_main_agent.py +256 -0
- aiagents4pharma/talk2scholars/tests/test_agents_paper_agents_download_agent.py +139 -0
- aiagents4pharma/talk2scholars/tests/test_agents_pdf_agent.py +114 -0
- aiagents4pharma/talk2scholars/tests/test_agents_s2_agent.py +198 -0
- aiagents4pharma/talk2scholars/tests/test_agents_zotero_agent.py +160 -0
- aiagents4pharma/talk2scholars/tests/test_s2_tools_display_dataframe.py +91 -0
- aiagents4pharma/talk2scholars/tests/test_s2_tools_query_dataframe.py +191 -0
- aiagents4pharma/talk2scholars/tests/test_states_state.py +38 -0
- aiagents4pharma/talk2scholars/tests/test_tools_paper_downloader.py +507 -0
- aiagents4pharma/talk2scholars/tests/test_tools_question_and_answer_tool.py +105 -0
- aiagents4pharma/talk2scholars/tests/test_tools_s2_multi.py +307 -0
- aiagents4pharma/talk2scholars/tests/test_tools_s2_retrieve.py +67 -0
- aiagents4pharma/talk2scholars/tests/test_tools_s2_search.py +286 -0
- aiagents4pharma/talk2scholars/tests/test_tools_s2_single.py +298 -0
- aiagents4pharma/talk2scholars/tests/test_utils_arxiv_downloader.py +469 -0
- aiagents4pharma/talk2scholars/tests/test_utils_base_paper_downloader.py +598 -0
- aiagents4pharma/talk2scholars/tests/test_utils_biorxiv_downloader.py +669 -0
- aiagents4pharma/talk2scholars/tests/test_utils_medrxiv_downloader.py +500 -0
- aiagents4pharma/talk2scholars/tests/test_utils_nvidia_nim_reranker.py +117 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_answer_formatter.py +67 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_batch_processor.py +92 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_collection_manager.py +173 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_document_processor.py +68 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_generate_answer.py +72 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_gpu_detection.py +129 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_paper_loader.py +116 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_rag_pipeline.py +88 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_retrieve_chunks.py +190 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_singleton_manager.py +159 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_vector_normalization.py +121 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pdf_vector_store.py +406 -0
- aiagents4pharma/talk2scholars/tests/test_utils_pubmed_downloader.py +1007 -0
- aiagents4pharma/talk2scholars/tests/test_utils_read_helper_utils.py +106 -0
- aiagents4pharma/talk2scholars/tests/test_utils_s2_utils_ext_ids.py +403 -0
- aiagents4pharma/talk2scholars/tests/test_utils_tool_helper_utils.py +85 -0
- aiagents4pharma/talk2scholars/tests/test_utils_zotero_human_in_the_loop.py +266 -0
- aiagents4pharma/talk2scholars/tests/test_utils_zotero_path.py +496 -0
- aiagents4pharma/talk2scholars/tests/test_utils_zotero_pdf_downloader_utils.py +46 -0
- aiagents4pharma/talk2scholars/tests/test_utils_zotero_read.py +743 -0
- aiagents4pharma/talk2scholars/tests/test_utils_zotero_write.py +151 -0
- aiagents4pharma/talk2scholars/tools/__init__.py +9 -0
- aiagents4pharma/talk2scholars/tools/paper_download/__init__.py +12 -0
- aiagents4pharma/talk2scholars/tools/paper_download/paper_downloader.py +442 -0
- aiagents4pharma/talk2scholars/tools/paper_download/utils/__init__.py +22 -0
- aiagents4pharma/talk2scholars/tools/paper_download/utils/arxiv_downloader.py +207 -0
- aiagents4pharma/talk2scholars/tools/paper_download/utils/base_paper_downloader.py +336 -0
- aiagents4pharma/talk2scholars/tools/paper_download/utils/biorxiv_downloader.py +313 -0
- aiagents4pharma/talk2scholars/tools/paper_download/utils/medrxiv_downloader.py +196 -0
- aiagents4pharma/talk2scholars/tools/paper_download/utils/pubmed_downloader.py +323 -0
- aiagents4pharma/talk2scholars/tools/pdf/__init__.py +7 -0
- aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +170 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +37 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/answer_formatter.py +62 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/batch_processor.py +198 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/collection_manager.py +172 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/document_processor.py +76 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +97 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/get_vectorstore.py +59 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/gpu_detection.py +150 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +97 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/paper_loader.py +123 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/rag_pipeline.py +113 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +197 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/singleton_manager.py +140 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +86 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/vector_normalization.py +150 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +327 -0
- aiagents4pharma/talk2scholars/tools/s2/__init__.py +21 -0
- aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +110 -0
- aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +111 -0
- aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +233 -0
- aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +128 -0
- aiagents4pharma/talk2scholars/tools/s2/search.py +101 -0
- aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +102 -0
- aiagents4pharma/talk2scholars/tools/s2/utils/__init__.py +5 -0
- aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +223 -0
- aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +205 -0
- aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +216 -0
- aiagents4pharma/talk2scholars/tools/zotero/__init__.py +7 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py +7 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +270 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/review_helper.py +74 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/write_helper.py +194 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py +180 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_pdf_downloader.py +133 -0
- aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +105 -0
- aiagents4pharma/talk2scholars/tools/zotero/zotero_review.py +162 -0
- aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py +91 -0
- aiagents4pharma-0.0.0.dist-info/METADATA +335 -0
- aiagents4pharma-0.0.0.dist-info/RECORD +336 -0
- aiagents4pharma-0.0.0.dist-info/WHEEL +4 -0
- aiagents4pharma-0.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for Zotero path utility in zotero_path.py.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
from unittest.mock import MagicMock, patch
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path import (
|
|
11
|
+
fetch_papers_for_save,
|
|
12
|
+
find_or_create_collection,
|
|
13
|
+
get_all_collection_paths,
|
|
14
|
+
get_item_collections,
|
|
15
|
+
)
|
|
16
|
+
from aiagents4pharma.talk2scholars.tools.zotero.zotero_read import (
|
|
17
|
+
zotero_read,
|
|
18
|
+
)
|
|
19
|
+
from aiagents4pharma.talk2scholars.tools.zotero.zotero_write import (
|
|
20
|
+
zotero_write,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestGetItemCollections(unittest.TestCase):
|
|
25
|
+
"""Unit tests for the get_item_collections function."""
|
|
26
|
+
|
|
27
|
+
def test_basic_collections_mapping(self):
|
|
28
|
+
"""test_basic_collections_mapping"""
|
|
29
|
+
# Define fake collections with one parent-child relationship and one independent collection.
|
|
30
|
+
fake_collections = [
|
|
31
|
+
{"key": "A", "data": {"name": "Parent", "parentCollection": None}},
|
|
32
|
+
{"key": "B", "data": {"name": "Child", "parentCollection": "A"}},
|
|
33
|
+
{"key": "C", "data": {"name": "Independent", "parentCollection": None}},
|
|
34
|
+
]
|
|
35
|
+
# Define fake collection items for each collection:
|
|
36
|
+
# - Collection A returns one item with key "item1"
|
|
37
|
+
# - Collection B returns one item with key "item2"
|
|
38
|
+
# - Collection C returns two items: one duplicate ("item1") and one new ("item3")
|
|
39
|
+
fake_collection_items = {
|
|
40
|
+
"A": [{"data": {"key": "item1"}}],
|
|
41
|
+
"B": [{"data": {"key": "item2"}}],
|
|
42
|
+
"C": [{"data": {"key": "item1"}}, {"data": {"key": "item3"}}],
|
|
43
|
+
}
|
|
44
|
+
fake_zot = MagicMock()
|
|
45
|
+
fake_zot.collections.return_value = fake_collections
|
|
46
|
+
|
|
47
|
+
# When collection_items is called, return the appropriate list based on collection key.
|
|
48
|
+
def fake_collection_items_func(collection_key):
|
|
49
|
+
return fake_collection_items.get(collection_key, [])
|
|
50
|
+
|
|
51
|
+
fake_zot.collection_items.side_effect = fake_collection_items_func
|
|
52
|
+
|
|
53
|
+
# Expected full collection paths:
|
|
54
|
+
# - Collection A: "/Parent"
|
|
55
|
+
# - Collection B: "/Parent/Child" (child of A)
|
|
56
|
+
# - Collection C: "/Independent"
|
|
57
|
+
#
|
|
58
|
+
# Expected mapping for items:
|
|
59
|
+
# - "item1" appears in collections A and C → ["/Parent", "/Independent"]
|
|
60
|
+
# - "item2" appears in B → ["/Parent/Child"]
|
|
61
|
+
# - "item3" appears in C → ["/Independent"]
|
|
62
|
+
expected_mapping = {
|
|
63
|
+
"item1": ["/Parent", "/Independent"],
|
|
64
|
+
"item2": ["/Parent/Child"],
|
|
65
|
+
"item3": ["/Independent"],
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
result = get_item_collections(fake_zot)
|
|
69
|
+
self.assertEqual(result, expected_mapping)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TestFindOrCreateCollectionExtra(unittest.TestCase):
|
|
73
|
+
"""Extra tests for the find_or_create_collection function."""
|
|
74
|
+
|
|
75
|
+
def setUp(self):
|
|
76
|
+
"""Set up a fake Zotero client with some default collections."""
|
|
77
|
+
# Set up a fake Zotero client with some default collections.
|
|
78
|
+
self.fake_zot = MagicMock()
|
|
79
|
+
self.fake_zot.collections.return_value = [
|
|
80
|
+
{"key": "parent1", "data": {"name": "Parent", "parentCollection": None}},
|
|
81
|
+
{"key": "child1", "data": {"name": "Child", "parentCollection": "parent1"}},
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
def test_empty_path(self):
|
|
85
|
+
"""Test that an empty path returns None."""
|
|
86
|
+
result = find_or_create_collection(self.fake_zot, "", create_missing=False)
|
|
87
|
+
self.assertIsNone(result)
|
|
88
|
+
|
|
89
|
+
def test_create_collection_with_success_key(self):
|
|
90
|
+
"""
|
|
91
|
+
Test that when create_missing is True and the response contains a "success" key,
|
|
92
|
+
the function returns the new collection key.
|
|
93
|
+
"""
|
|
94
|
+
# Simulate no existing collections (so direct match fails)
|
|
95
|
+
self.fake_zot.collections.return_value = []
|
|
96
|
+
# Simulate create_collection returning a dict with a "success" key.
|
|
97
|
+
self.fake_zot.create_collection.return_value = {"success": {"0": "new_key_success"}}
|
|
98
|
+
result = find_or_create_collection(self.fake_zot, "/NewCollection", create_missing=True)
|
|
99
|
+
self.assertEqual(result, "new_key_success")
|
|
100
|
+
# Verify payload formatting: for a simple (non-nested) path, no parentCollection.
|
|
101
|
+
args, _ = self.fake_zot.create_collection.call_args
|
|
102
|
+
payload = args[0]
|
|
103
|
+
self.assertEqual(payload["name"], "newcollection")
|
|
104
|
+
self.assertNotIn("parentCollection", payload)
|
|
105
|
+
|
|
106
|
+
def test_create_collection_with_successful_key(self):
|
|
107
|
+
"""
|
|
108
|
+
Test that when create_missing is True and the response contains a "successful" key,
|
|
109
|
+
the function returns the new collection key.
|
|
110
|
+
"""
|
|
111
|
+
self.fake_zot.collections.return_value = []
|
|
112
|
+
self.fake_zot.create_collection.return_value = {
|
|
113
|
+
"successful": {"0": {"data": {"key": "new_key_successful"}}}
|
|
114
|
+
}
|
|
115
|
+
result = find_or_create_collection(self.fake_zot, "/NewCollection", create_missing=True)
|
|
116
|
+
self.assertEqual(result, "new_key_successful")
|
|
117
|
+
|
|
118
|
+
def test_create_collection_exception(self):
|
|
119
|
+
"""
|
|
120
|
+
Test that if create_collection raises an exception,
|
|
121
|
+
the function logs the error and returns None.
|
|
122
|
+
"""
|
|
123
|
+
self.fake_zot.collections.return_value = []
|
|
124
|
+
self.fake_zot.create_collection.side_effect = Exception("Creation error")
|
|
125
|
+
result = find_or_create_collection(self.fake_zot, "/NewCollection", create_missing=True)
|
|
126
|
+
self.assertIsNone(result)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class TestZoteroPath:
|
|
130
|
+
"""Tests for the zotero_path utility functions."""
|
|
131
|
+
|
|
132
|
+
def test_fetch_papers_for_save_no_papers(self):
|
|
133
|
+
"""Test that fetch_papers_for_save returns None when no papers are available."""
|
|
134
|
+
# Empty state
|
|
135
|
+
state = {}
|
|
136
|
+
assert fetch_papers_for_save(state) is None
|
|
137
|
+
|
|
138
|
+
# State with empty last_displayed_papers
|
|
139
|
+
state = {"last_displayed_papers": ""}
|
|
140
|
+
assert fetch_papers_for_save(state) is None
|
|
141
|
+
|
|
142
|
+
# State with last_displayed_papers pointing to non-existent key
|
|
143
|
+
state = {"last_displayed_papers": "nonexistent_key"}
|
|
144
|
+
assert fetch_papers_for_save(state) is None
|
|
145
|
+
|
|
146
|
+
def test_fetch_papers_for_save_with_papers(self):
|
|
147
|
+
"""Test that fetch_papers_for_save correctly retrieves papers from state."""
|
|
148
|
+
# State with direct papers
|
|
149
|
+
sample_papers = {"paper1": {"Title": "Test Paper"}}
|
|
150
|
+
state = {"last_displayed_papers": sample_papers}
|
|
151
|
+
assert fetch_papers_for_save(state) == sample_papers
|
|
152
|
+
|
|
153
|
+
# State with papers referenced by key
|
|
154
|
+
state = {"last_displayed_papers": "zotero_read", "zotero_read": sample_papers}
|
|
155
|
+
assert fetch_papers_for_save(state) == sample_papers
|
|
156
|
+
|
|
157
|
+
@patch("pyzotero.zotero.Zotero")
|
|
158
|
+
def test_find_or_create_collection_exact_match(self, mock_zotero):
|
|
159
|
+
"""Test that find_or_create_collection correctly finds an exact match."""
|
|
160
|
+
# Setup mock
|
|
161
|
+
mock_zot = MagicMock()
|
|
162
|
+
mock_zotero.return_value = mock_zot
|
|
163
|
+
|
|
164
|
+
# Setup collections
|
|
165
|
+
collections = [
|
|
166
|
+
{"key": "abc123", "data": {"name": "Curiosity", "parentCollection": None}},
|
|
167
|
+
{
|
|
168
|
+
"key": "def456",
|
|
169
|
+
"data": {"name": "Curiosity1", "parentCollection": "abc123"},
|
|
170
|
+
},
|
|
171
|
+
{"key": "ghi789", "data": {"name": "Random", "parentCollection": None}},
|
|
172
|
+
{"key": "rad123", "data": {"name": "radiation", "parentCollection": None}},
|
|
173
|
+
]
|
|
174
|
+
mock_zot.collections.return_value = collections
|
|
175
|
+
|
|
176
|
+
# Test finding "Curiosity"
|
|
177
|
+
result = find_or_create_collection(mock_zot, "/Curiosity")
|
|
178
|
+
assert result == "abc123"
|
|
179
|
+
|
|
180
|
+
# Test finding with different case
|
|
181
|
+
result = find_or_create_collection(mock_zot, "/curiosity")
|
|
182
|
+
assert result == "abc123"
|
|
183
|
+
|
|
184
|
+
# Test finding "radiation" - direct match
|
|
185
|
+
result = find_or_create_collection(mock_zot, "/radiation")
|
|
186
|
+
assert result == "rad123"
|
|
187
|
+
|
|
188
|
+
# Test finding without leading slash
|
|
189
|
+
result = find_or_create_collection(mock_zot, "radiation")
|
|
190
|
+
assert result == "rad123"
|
|
191
|
+
|
|
192
|
+
@patch("pyzotero.zotero.Zotero")
|
|
193
|
+
def test_find_or_create_collection_no_match(self, mock_zotero):
|
|
194
|
+
"""Test that find_or_create_collection returns None for non-existent collections."""
|
|
195
|
+
# Setup mock
|
|
196
|
+
mock_zot = MagicMock()
|
|
197
|
+
mock_zotero.return_value = mock_zot
|
|
198
|
+
|
|
199
|
+
# Setup collections
|
|
200
|
+
collections = [
|
|
201
|
+
{"key": "abc123", "data": {"name": "Curiosity", "parentCollection": None}},
|
|
202
|
+
{
|
|
203
|
+
"key": "def456",
|
|
204
|
+
"data": {"name": "Curiosity1", "parentCollection": "abc123"},
|
|
205
|
+
},
|
|
206
|
+
]
|
|
207
|
+
mock_zot.collections.return_value = collections
|
|
208
|
+
|
|
209
|
+
# Test finding non-existent "Curiosity2"
|
|
210
|
+
result = find_or_create_collection(mock_zot, "/Curiosity2")
|
|
211
|
+
assert result is None
|
|
212
|
+
|
|
213
|
+
# Test finding non-existent nested path
|
|
214
|
+
result = find_or_create_collection(mock_zot, "/Curiosity/Curiosity2")
|
|
215
|
+
assert result is None
|
|
216
|
+
|
|
217
|
+
@patch("pyzotero.zotero.Zotero")
|
|
218
|
+
def test_find_or_create_collection_with_creation(self, mock_zotero):
|
|
219
|
+
"""Test that find_or_create_collection creates collections when requested."""
|
|
220
|
+
# Setup mock
|
|
221
|
+
mock_zot = MagicMock()
|
|
222
|
+
mock_zotero.return_value = mock_zot
|
|
223
|
+
|
|
224
|
+
# Setup collections
|
|
225
|
+
collections = [{"key": "abc123", "data": {"name": "Curiosity", "parentCollection": None}}]
|
|
226
|
+
mock_zot.collections.return_value = collections
|
|
227
|
+
|
|
228
|
+
# Setup create_collection response
|
|
229
|
+
mock_zot.create_collection.return_value = {
|
|
230
|
+
"successful": {"0": {"data": {"key": "new_key"}}}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
# Test creating "Curiosity2" - note we're expecting lowercase in the call
|
|
234
|
+
result = find_or_create_collection(mock_zot, "/Curiosity2", create_missing=True)
|
|
235
|
+
assert result == "new_key"
|
|
236
|
+
# Use case-insensitive check for the collection name
|
|
237
|
+
mock_zot.create_collection.assert_called_once()
|
|
238
|
+
call_args = mock_zot.create_collection.call_args[0][0]
|
|
239
|
+
assert "name" in call_args
|
|
240
|
+
assert call_args["name"].lower() == "curiosity2"
|
|
241
|
+
|
|
242
|
+
# Test creating nested "Curiosity/Curiosity2"
|
|
243
|
+
mock_zot.create_collection.reset_mock()
|
|
244
|
+
result = find_or_create_collection(mock_zot, "/Curiosity/Curiosity2", create_missing=True)
|
|
245
|
+
assert result == "new_key"
|
|
246
|
+
# Check that the call includes parentCollection
|
|
247
|
+
mock_zot.create_collection.assert_called_once()
|
|
248
|
+
call_args = mock_zot.create_collection.call_args[0][0]
|
|
249
|
+
assert "name" in call_args
|
|
250
|
+
assert "parentCollection" in call_args
|
|
251
|
+
assert call_args["name"].lower() == "curiosity2"
|
|
252
|
+
assert call_args["parentCollection"] == "abc123"
|
|
253
|
+
|
|
254
|
+
@patch("pyzotero.zotero.Zotero")
|
|
255
|
+
def test_get_all_collection_paths(self, mock_zotero):
|
|
256
|
+
"""Test that get_all_collection_paths returns correct paths."""
|
|
257
|
+
# Setup mock
|
|
258
|
+
mock_zot = MagicMock()
|
|
259
|
+
mock_zotero.return_value = mock_zot
|
|
260
|
+
|
|
261
|
+
# Setup collections
|
|
262
|
+
collections = [
|
|
263
|
+
{"key": "abc123", "data": {"name": "Curiosity", "parentCollection": None}},
|
|
264
|
+
{
|
|
265
|
+
"key": "def456",
|
|
266
|
+
"data": {"name": "Curiosity1", "parentCollection": "abc123"},
|
|
267
|
+
},
|
|
268
|
+
{"key": "ghi789", "data": {"name": "Random", "parentCollection": None}},
|
|
269
|
+
]
|
|
270
|
+
mock_zot.collections.return_value = collections
|
|
271
|
+
|
|
272
|
+
# Test getting all paths
|
|
273
|
+
result = get_all_collection_paths(mock_zot)
|
|
274
|
+
assert "/Curiosity" in result
|
|
275
|
+
assert "/Random" in result
|
|
276
|
+
assert "/Curiosity/Curiosity1" in result
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class TestZoteroWrite:
|
|
280
|
+
"""Integration tests for zotero_write.py."""
|
|
281
|
+
|
|
282
|
+
@pytest.fixture
|
|
283
|
+
def mock_hydra(self):
|
|
284
|
+
"""Fixture to mock hydra configuration."""
|
|
285
|
+
with patch(
|
|
286
|
+
"aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.compose"
|
|
287
|
+
) as mock_compose:
|
|
288
|
+
cfg = MagicMock()
|
|
289
|
+
cfg.tools.zotero_write.user_id = "test_user"
|
|
290
|
+
cfg.tools.zotero_write.library_type = "user"
|
|
291
|
+
cfg.tools.zotero_write.api_key = "test_key"
|
|
292
|
+
cfg.tools.zotero_write.zotero = MagicMock()
|
|
293
|
+
cfg.tools.zotero_write.zotero.max_limit = 50
|
|
294
|
+
mock_compose.return_value = cfg
|
|
295
|
+
yield cfg
|
|
296
|
+
|
|
297
|
+
@pytest.fixture
|
|
298
|
+
def mock_zotero(self):
|
|
299
|
+
"""Fixture to mock Zotero client."""
|
|
300
|
+
with patch(
|
|
301
|
+
"aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.zotero.Zotero"
|
|
302
|
+
) as mock_zot_class:
|
|
303
|
+
mock_zot = MagicMock()
|
|
304
|
+
mock_zot_class.return_value = mock_zot
|
|
305
|
+
yield mock_zot
|
|
306
|
+
|
|
307
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save")
|
|
308
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.initialize")
|
|
309
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.compose")
|
|
310
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.zotero.Zotero")
|
|
311
|
+
def test_zotero_write_no_papers(self, mock_zotero_class, mock_compose, _, mock_fetch):
|
|
312
|
+
"""When no papers exist (even after approval), the function raises a ValueError."""
|
|
313
|
+
# Mock hydra configuration
|
|
314
|
+
cfg = MagicMock()
|
|
315
|
+
cfg.user_id = "test_user"
|
|
316
|
+
cfg.library_type = "user"
|
|
317
|
+
cfg.api_key = "test_key"
|
|
318
|
+
mock_compose.return_value = MagicMock()
|
|
319
|
+
mock_compose.return_value.tools.zotero_write = cfg
|
|
320
|
+
|
|
321
|
+
# Mock Zotero client
|
|
322
|
+
mock_zot = MagicMock()
|
|
323
|
+
mock_zotero_class.return_value = mock_zot
|
|
324
|
+
|
|
325
|
+
mock_fetch.return_value = None
|
|
326
|
+
|
|
327
|
+
state = {
|
|
328
|
+
"zotero_write_approval_status": {
|
|
329
|
+
"approved": True,
|
|
330
|
+
"collection_path": "/Curiosity",
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
with pytest.raises(ValueError) as excinfo:
|
|
335
|
+
zotero_write.run(
|
|
336
|
+
{
|
|
337
|
+
"tool_call_id": "test_id",
|
|
338
|
+
"collection_path": "/Curiosity",
|
|
339
|
+
"state": state,
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
assert "No fetched papers were found to save" in str(excinfo.value)
|
|
343
|
+
|
|
344
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save")
|
|
345
|
+
@patch(
|
|
346
|
+
"aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.find_or_create_collection"
|
|
347
|
+
)
|
|
348
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.initialize")
|
|
349
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.compose")
|
|
350
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.zotero.Zotero")
|
|
351
|
+
def test_zotero_write_invalid_collection(
|
|
352
|
+
self, mock_zotero_class, mock_compose, _, mock_find, mock_fetch
|
|
353
|
+
):
|
|
354
|
+
"""Saving to a nonexistent Zotero collection returns an error Command."""
|
|
355
|
+
# Mock hydra configuration
|
|
356
|
+
cfg = MagicMock()
|
|
357
|
+
cfg.user_id = "test_user"
|
|
358
|
+
cfg.library_type = "user"
|
|
359
|
+
cfg.api_key = "test_key"
|
|
360
|
+
mock_compose.return_value = MagicMock()
|
|
361
|
+
mock_compose.return_value.tools.zotero_write = cfg
|
|
362
|
+
|
|
363
|
+
# Mock Zotero client
|
|
364
|
+
mock_zot = MagicMock()
|
|
365
|
+
mock_zotero_class.return_value = mock_zot
|
|
366
|
+
mock_zot.collections.return_value = [
|
|
367
|
+
{"key": "k1", "data": {"name": "Curiosity"}},
|
|
368
|
+
{"key": "k2", "data": {"name": "Random"}},
|
|
369
|
+
]
|
|
370
|
+
|
|
371
|
+
sample = {"paper1": {"Title": "Test Paper"}}
|
|
372
|
+
mock_fetch.return_value = sample
|
|
373
|
+
mock_find.return_value = None
|
|
374
|
+
|
|
375
|
+
state = {
|
|
376
|
+
"zotero_write_approval_status": {
|
|
377
|
+
"approved": True,
|
|
378
|
+
"collection_path": "/NonExistent",
|
|
379
|
+
},
|
|
380
|
+
"last_displayed_papers": "papers",
|
|
381
|
+
"papers": sample,
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
result = zotero_write.run(
|
|
385
|
+
{
|
|
386
|
+
"tool_call_id": "test_id",
|
|
387
|
+
"collection_path": "/NonExistent",
|
|
388
|
+
"state": state,
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
msg = result.update["messages"][0].content
|
|
393
|
+
assert "does not exist in Zotero" in msg
|
|
394
|
+
assert "Curiosity, Random" in msg
|
|
395
|
+
|
|
396
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save")
|
|
397
|
+
@patch(
|
|
398
|
+
"aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.find_or_create_collection"
|
|
399
|
+
)
|
|
400
|
+
def test_zotero_write_success(self, mock_find, mock_fetch, mock_hydra, mock_zotero):
|
|
401
|
+
"""A valid approved save returns a success Command with summary."""
|
|
402
|
+
sample = {"paper1": {"Title": "Test Paper", "Authors": ["Test Author"]}}
|
|
403
|
+
mock_fetch.return_value = sample
|
|
404
|
+
mock_find.return_value = "abc123"
|
|
405
|
+
mock_zotero.collections.return_value = [{"key": "abc123", "data": {"name": "radiation"}}]
|
|
406
|
+
mock_zotero.create_items.return_value = {"successful": {"0": {"key": "item123"}}}
|
|
407
|
+
mock_hydra.tools.zotero_write.zotero.max_limit = 50
|
|
408
|
+
|
|
409
|
+
state = {
|
|
410
|
+
"zotero_write_approval_status": {
|
|
411
|
+
"approved": True,
|
|
412
|
+
"collection_path": "/radiation",
|
|
413
|
+
},
|
|
414
|
+
"last_displayed_papers": "papers",
|
|
415
|
+
"papers": sample,
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
result = zotero_write.run(
|
|
419
|
+
{
|
|
420
|
+
"tool_call_id": "test_id",
|
|
421
|
+
"collection_path": "/radiation",
|
|
422
|
+
"state": state,
|
|
423
|
+
}
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
msg = result.update["messages"][0].content
|
|
427
|
+
assert "Save was successful" in msg
|
|
428
|
+
assert "radiation" in msg
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
class TestZoteroRead:
|
|
432
|
+
"""Integration tests for zotero_read.py."""
|
|
433
|
+
|
|
434
|
+
@pytest.fixture
|
|
435
|
+
def mock_hydra(self):
|
|
436
|
+
"""Fixture to mock hydra configuration."""
|
|
437
|
+
with (
|
|
438
|
+
patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.initialize"),
|
|
439
|
+
patch(
|
|
440
|
+
"aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.compose"
|
|
441
|
+
) as mock_compose,
|
|
442
|
+
):
|
|
443
|
+
cfg = MagicMock()
|
|
444
|
+
cfg.tools.zotero_read.user_id = "test_user"
|
|
445
|
+
cfg.tools.zotero_read.library_type = "user"
|
|
446
|
+
cfg.tools.zotero_read.api_key = "test_key"
|
|
447
|
+
cfg.tools.zotero_read.zotero = MagicMock()
|
|
448
|
+
cfg.tools.zotero_read.zotero.max_limit = 50
|
|
449
|
+
cfg.tools.zotero_read.zotero.filter_item_types = [
|
|
450
|
+
"journalArticle",
|
|
451
|
+
"conferencePaper",
|
|
452
|
+
]
|
|
453
|
+
mock_compose.return_value = cfg
|
|
454
|
+
yield cfg
|
|
455
|
+
|
|
456
|
+
@pytest.fixture
|
|
457
|
+
def mock_zotero(self):
|
|
458
|
+
"""Fixture to mock Zotero client."""
|
|
459
|
+
with patch(
|
|
460
|
+
"aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.zotero.Zotero"
|
|
461
|
+
) as mock_zot_class:
|
|
462
|
+
mock_zot = MagicMock()
|
|
463
|
+
mock_zot_class.return_value = mock_zot
|
|
464
|
+
yield mock_zot
|
|
465
|
+
|
|
466
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
|
|
467
|
+
def test_zotero_read_item_collections_error(
|
|
468
|
+
self, mock_get_collections, mock_hydra, mock_zotero
|
|
469
|
+
):
|
|
470
|
+
"""Test that zotero_read handles errors in get_item_collections."""
|
|
471
|
+
|
|
472
|
+
mock_get_collections.side_effect = Exception("Test error")
|
|
473
|
+
|
|
474
|
+
mock_zotero.items.return_value = [
|
|
475
|
+
{
|
|
476
|
+
"data": {
|
|
477
|
+
"key": "paper1",
|
|
478
|
+
"title": "Test Paper",
|
|
479
|
+
"itemType": "journalArticle",
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
]
|
|
483
|
+
mock_hydra.tools.zotero_read.zotero.max_limit = 50
|
|
484
|
+
|
|
485
|
+
result = zotero_read.run(
|
|
486
|
+
{
|
|
487
|
+
"query": "test",
|
|
488
|
+
"only_articles": True,
|
|
489
|
+
"tool_call_id": "test_id",
|
|
490
|
+
"limit": 2,
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
assert result is not None
|
|
495
|
+
assert isinstance(result.update, dict)
|
|
496
|
+
assert "article_data" in result.update
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for Zotero PDF downloader utilities.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import unittest
|
|
7
|
+
from unittest.mock import MagicMock, patch
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_pdf_downloader import (
|
|
12
|
+
download_pdfs_in_parallel,
|
|
13
|
+
download_zotero_pdf,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestZoteroPDFDownloaderUtils(unittest.TestCase):
|
|
18
|
+
"""Tests for zotero_pdf_downloader module."""
|
|
19
|
+
|
|
20
|
+
@patch("requests.Session.get")
|
|
21
|
+
def test_download_zotero_pdf_default_filename(self, mock_get):
|
|
22
|
+
"""Test download_zotero_pdf returns default filename when header has no filename."""
|
|
23
|
+
# Mock response without Content-Disposition filename
|
|
24
|
+
mock_response = MagicMock()
|
|
25
|
+
mock_response.raise_for_status = lambda: None
|
|
26
|
+
mock_response.iter_content = lambda chunk_size: [b"fakepdf"]
|
|
27
|
+
mock_response.headers = {}
|
|
28
|
+
mock_get.return_value = mock_response
|
|
29
|
+
|
|
30
|
+
session = requests.Session()
|
|
31
|
+
result = download_zotero_pdf(session, "user123", "apikey", "attach123")
|
|
32
|
+
# Should return a tuple (file_path, filename)
|
|
33
|
+
self.assertIsNotNone(result)
|
|
34
|
+
file_path, filename = result
|
|
35
|
+
# File should exist
|
|
36
|
+
self.assertTrue(os.path.isfile(file_path))
|
|
37
|
+
# Filename should default to 'downloaded.pdf'
|
|
38
|
+
self.assertEqual(filename, "downloaded.pdf")
|
|
39
|
+
# Clean up temp file
|
|
40
|
+
os.remove(file_path)
|
|
41
|
+
|
|
42
|
+
def test_download_pdfs_in_parallel_empty(self):
|
|
43
|
+
"""Test that download_pdfs_in_parallel returns empty dict on empty input."""
|
|
44
|
+
session = requests.Session()
|
|
45
|
+
result = download_pdfs_in_parallel(session, "user123", "apikey", {})
|
|
46
|
+
self.assertEqual(result, {})
|