aiagents4pharma 1.44.0__py3-none-any.whl → 1.45.1__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.
Files changed (289) hide show
  1. aiagents4pharma/__init__.py +2 -2
  2. aiagents4pharma/talk2aiagents4pharma/.dockerignore +13 -0
  3. aiagents4pharma/talk2aiagents4pharma/Dockerfile +105 -0
  4. aiagents4pharma/talk2aiagents4pharma/README.md +1 -0
  5. aiagents4pharma/talk2aiagents4pharma/__init__.py +4 -5
  6. aiagents4pharma/talk2aiagents4pharma/agents/__init__.py +3 -2
  7. aiagents4pharma/talk2aiagents4pharma/agents/main_agent.py +24 -23
  8. aiagents4pharma/talk2aiagents4pharma/configs/__init__.py +2 -2
  9. aiagents4pharma/talk2aiagents4pharma/configs/agents/__init__.py +2 -2
  10. aiagents4pharma/talk2aiagents4pharma/configs/agents/main_agent/default.yaml +2 -2
  11. aiagents4pharma/talk2aiagents4pharma/configs/config.yaml +1 -1
  12. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/.env.example +23 -0
  13. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/docker-compose.yml +93 -0
  14. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/.env.example +23 -0
  15. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/docker-compose.yml +108 -0
  16. aiagents4pharma/talk2aiagents4pharma/install.md +127 -0
  17. aiagents4pharma/talk2aiagents4pharma/states/__init__.py +3 -2
  18. aiagents4pharma/talk2aiagents4pharma/states/state_talk2aiagents4pharma.py +5 -3
  19. aiagents4pharma/talk2aiagents4pharma/tests/__init__.py +2 -2
  20. aiagents4pharma/talk2aiagents4pharma/tests/test_main_agent.py +72 -50
  21. aiagents4pharma/talk2biomodels/.dockerignore +13 -0
  22. aiagents4pharma/talk2biomodels/Dockerfile +104 -0
  23. aiagents4pharma/talk2biomodels/README.md +1 -0
  24. aiagents4pharma/talk2biomodels/__init__.py +4 -8
  25. aiagents4pharma/talk2biomodels/agents/__init__.py +3 -2
  26. aiagents4pharma/talk2biomodels/agents/t2b_agent.py +47 -42
  27. aiagents4pharma/talk2biomodels/api/__init__.py +4 -5
  28. aiagents4pharma/talk2biomodels/api/kegg.py +14 -10
  29. aiagents4pharma/talk2biomodels/api/ols.py +13 -10
  30. aiagents4pharma/talk2biomodels/api/uniprot.py +7 -6
  31. aiagents4pharma/talk2biomodels/configs/__init__.py +3 -4
  32. aiagents4pharma/talk2biomodels/configs/agents/__init__.py +2 -2
  33. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/__init__.py +2 -2
  34. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/default.yaml +1 -1
  35. aiagents4pharma/talk2biomodels/configs/config.yaml +1 -1
  36. aiagents4pharma/talk2biomodels/configs/tools/__init__.py +4 -5
  37. aiagents4pharma/talk2biomodels/configs/tools/ask_question/__init__.py +2 -2
  38. aiagents4pharma/talk2biomodels/configs/tools/ask_question/default.yaml +1 -2
  39. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/__init__.py +2 -2
  40. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/default.yaml +1 -1
  41. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/__init__.py +2 -2
  42. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/default.yaml +1 -1
  43. aiagents4pharma/talk2biomodels/install.md +63 -0
  44. aiagents4pharma/talk2biomodels/models/__init__.py +4 -4
  45. aiagents4pharma/talk2biomodels/models/basico_model.py +36 -28
  46. aiagents4pharma/talk2biomodels/models/sys_bio_model.py +13 -10
  47. aiagents4pharma/talk2biomodels/states/__init__.py +3 -2
  48. aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +12 -8
  49. aiagents4pharma/talk2biomodels/tests/BIOMD0000000449_url.xml +1585 -0
  50. aiagents4pharma/talk2biomodels/tests/__init__.py +2 -2
  51. aiagents4pharma/talk2biomodels/tests/article_on_model_537.pdf +0 -0
  52. aiagents4pharma/talk2biomodels/tests/test_api.py +18 -14
  53. aiagents4pharma/talk2biomodels/tests/test_ask_question.py +8 -9
  54. aiagents4pharma/talk2biomodels/tests/test_basico_model.py +15 -9
  55. aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +54 -55
  56. aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +28 -27
  57. aiagents4pharma/talk2biomodels/tests/test_integration.py +21 -33
  58. aiagents4pharma/talk2biomodels/tests/test_load_biomodel.py +14 -11
  59. aiagents4pharma/talk2biomodels/tests/test_param_scan.py +21 -20
  60. aiagents4pharma/talk2biomodels/tests/test_query_article.py +129 -29
  61. aiagents4pharma/talk2biomodels/tests/test_search_models.py +9 -13
  62. aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +16 -15
  63. aiagents4pharma/talk2biomodels/tests/test_steady_state.py +12 -22
  64. aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +33 -29
  65. aiagents4pharma/talk2biomodels/tools/__init__.py +15 -12
  66. aiagents4pharma/talk2biomodels/tools/ask_question.py +42 -32
  67. aiagents4pharma/talk2biomodels/tools/custom_plotter.py +51 -43
  68. aiagents4pharma/talk2biomodels/tools/get_annotation.py +99 -75
  69. aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +57 -51
  70. aiagents4pharma/talk2biomodels/tools/load_arguments.py +52 -32
  71. aiagents4pharma/talk2biomodels/tools/load_biomodel.py +8 -2
  72. aiagents4pharma/talk2biomodels/tools/parameter_scan.py +107 -90
  73. aiagents4pharma/talk2biomodels/tools/query_article.py +14 -13
  74. aiagents4pharma/talk2biomodels/tools/search_models.py +37 -26
  75. aiagents4pharma/talk2biomodels/tools/simulate_model.py +47 -37
  76. aiagents4pharma/talk2biomodels/tools/steady_state.py +76 -58
  77. aiagents4pharma/talk2biomodels/tools/utils.py +4 -3
  78. aiagents4pharma/talk2cells/README.md +1 -0
  79. aiagents4pharma/talk2cells/__init__.py +4 -5
  80. aiagents4pharma/talk2cells/agents/__init__.py +3 -2
  81. aiagents4pharma/talk2cells/agents/scp_agent.py +21 -19
  82. aiagents4pharma/talk2cells/states/__init__.py +3 -2
  83. aiagents4pharma/talk2cells/states/state_talk2cells.py +4 -2
  84. aiagents4pharma/talk2cells/tests/scp_agent/test_scp_agent.py +8 -9
  85. aiagents4pharma/talk2cells/tools/__init__.py +3 -2
  86. aiagents4pharma/talk2cells/tools/scp_agent/__init__.py +4 -4
  87. aiagents4pharma/talk2cells/tools/scp_agent/display_studies.py +5 -3
  88. aiagents4pharma/talk2cells/tools/scp_agent/search_studies.py +21 -22
  89. aiagents4pharma/talk2knowledgegraphs/.dockerignore +13 -0
  90. aiagents4pharma/talk2knowledgegraphs/Dockerfile +103 -0
  91. aiagents4pharma/talk2knowledgegraphs/README.md +1 -0
  92. aiagents4pharma/talk2knowledgegraphs/__init__.py +4 -7
  93. aiagents4pharma/talk2knowledgegraphs/agents/__init__.py +3 -2
  94. aiagents4pharma/talk2knowledgegraphs/agents/t2kg_agent.py +40 -30
  95. aiagents4pharma/talk2knowledgegraphs/configs/__init__.py +3 -6
  96. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/__init__.py +2 -2
  97. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/default.yaml +8 -8
  98. aiagents4pharma/talk2knowledgegraphs/configs/app/__init__.py +3 -2
  99. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/__init__.py +2 -2
  100. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/default.yaml +1 -1
  101. aiagents4pharma/talk2knowledgegraphs/configs/config.yaml +1 -1
  102. aiagents4pharma/talk2knowledgegraphs/configs/tools/__init__.py +4 -5
  103. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/__init__.py +2 -2
  104. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/default.yaml +1 -1
  105. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/__init__.py +2 -2
  106. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/default.yaml +1 -1
  107. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/__init__.py +2 -2
  108. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/default.yaml +1 -1
  109. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/ols_terms/default.yaml +1 -1
  110. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/reactome_pathways/default.yaml +1 -1
  111. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/uniprot_proteins/default.yaml +1 -1
  112. aiagents4pharma/talk2knowledgegraphs/configs/utils/pubchem_utils/default.yaml +1 -1
  113. aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py +4 -6
  114. aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py +115 -67
  115. aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py +2 -0
  116. aiagents4pharma/talk2knowledgegraphs/datasets/primekg.py +35 -24
  117. aiagents4pharma/talk2knowledgegraphs/datasets/starkqa_primekg.py +29 -21
  118. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/.env.example +23 -0
  119. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/docker-compose.yml +93 -0
  120. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/.env.example +23 -0
  121. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/docker-compose.yml +108 -0
  122. aiagents4pharma/talk2knowledgegraphs/entrypoint.sh +190 -0
  123. aiagents4pharma/talk2knowledgegraphs/install.md +140 -0
  124. aiagents4pharma/talk2knowledgegraphs/milvus_data_dump.py +31 -65
  125. aiagents4pharma/talk2knowledgegraphs/states/__init__.py +3 -2
  126. aiagents4pharma/talk2knowledgegraphs/states/state_talk2knowledgegraphs.py +1 -0
  127. aiagents4pharma/talk2knowledgegraphs/tests/test_agents_t2kg_agent.py +65 -40
  128. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_biobridge_primekg.py +54 -48
  129. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_dataset.py +4 -0
  130. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_primekg.py +17 -4
  131. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py +33 -24
  132. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_graphrag_reasoning.py +116 -69
  133. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_milvus_multimodal_subgraph_extraction.py +334 -216
  134. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_multimodal_subgraph_extraction.py +22 -15
  135. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_extraction.py +19 -12
  136. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_summarization.py +95 -48
  137. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py +4 -0
  138. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py +5 -0
  139. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_nim_molmim.py +13 -18
  140. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_ollama.py +10 -3
  141. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +4 -3
  142. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +3 -2
  143. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ols.py +1 -0
  144. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_pubchem.py +9 -4
  145. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_reactome.py +6 -6
  146. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_uniprot.py +4 -0
  147. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_extractions_milvus_multimodal_pcst.py +160 -97
  148. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_kg_utils.py +3 -4
  149. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_pubchem_utils.py +87 -13
  150. aiagents4pharma/talk2knowledgegraphs/tools/__init__.py +10 -7
  151. aiagents4pharma/talk2knowledgegraphs/tools/graphrag_reasoning.py +15 -20
  152. aiagents4pharma/talk2knowledgegraphs/tools/milvus_multimodal_subgraph_extraction.py +145 -142
  153. aiagents4pharma/talk2knowledgegraphs/tools/multimodal_subgraph_extraction.py +92 -90
  154. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_extraction.py +25 -37
  155. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_summarization.py +10 -13
  156. aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +4 -7
  157. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py +4 -7
  158. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py +4 -0
  159. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py +11 -14
  160. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/nim_molmim.py +7 -7
  161. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/ollama.py +12 -6
  162. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py +8 -6
  163. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +9 -6
  164. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +1 -0
  165. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +15 -9
  166. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ols_terms.py +23 -20
  167. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/pubchem_strings.py +12 -10
  168. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/reactome_pathways.py +16 -10
  169. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/uniprot_proteins.py +26 -18
  170. aiagents4pharma/talk2knowledgegraphs/utils/extractions/__init__.py +4 -5
  171. aiagents4pharma/talk2knowledgegraphs/utils/extractions/milvus_multimodal_pcst.py +14 -34
  172. aiagents4pharma/talk2knowledgegraphs/utils/extractions/multimodal_pcst.py +53 -47
  173. aiagents4pharma/talk2knowledgegraphs/utils/extractions/pcst.py +18 -14
  174. aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py +22 -23
  175. aiagents4pharma/talk2knowledgegraphs/utils/pubchem_utils.py +11 -10
  176. aiagents4pharma/talk2scholars/.dockerignore +13 -0
  177. aiagents4pharma/talk2scholars/Dockerfile +104 -0
  178. aiagents4pharma/talk2scholars/README.md +1 -0
  179. aiagents4pharma/talk2scholars/agents/__init__.py +1 -5
  180. aiagents4pharma/talk2scholars/agents/main_agent.py +6 -4
  181. aiagents4pharma/talk2scholars/agents/paper_download_agent.py +5 -4
  182. aiagents4pharma/talk2scholars/agents/pdf_agent.py +4 -2
  183. aiagents4pharma/talk2scholars/agents/s2_agent.py +2 -2
  184. aiagents4pharma/talk2scholars/agents/zotero_agent.py +10 -11
  185. aiagents4pharma/talk2scholars/configs/__init__.py +1 -3
  186. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py +1 -4
  187. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +1 -1
  188. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +1 -1
  189. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +8 -8
  190. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +7 -7
  191. aiagents4pharma/talk2scholars/configs/tools/__init__.py +8 -6
  192. aiagents4pharma/talk2scholars/docker-compose/cpu/.env.example +21 -0
  193. aiagents4pharma/talk2scholars/docker-compose/cpu/docker-compose.yml +90 -0
  194. aiagents4pharma/talk2scholars/docker-compose/gpu/.env.example +21 -0
  195. aiagents4pharma/talk2scholars/docker-compose/gpu/docker-compose.yml +105 -0
  196. aiagents4pharma/talk2scholars/install.md +122 -0
  197. aiagents4pharma/talk2scholars/state/state_talk2scholars.py +8 -8
  198. aiagents4pharma/talk2scholars/tests/{test_main_agent.py → test_agents_main_agent.py} +41 -23
  199. aiagents4pharma/talk2scholars/tests/{test_paper_download_agent.py → test_agents_paper_agents_download_agent.py} +10 -16
  200. aiagents4pharma/talk2scholars/tests/{test_pdf_agent.py → test_agents_pdf_agent.py} +6 -10
  201. aiagents4pharma/talk2scholars/tests/{test_s2_agent.py → test_agents_s2_agent.py} +8 -16
  202. aiagents4pharma/talk2scholars/tests/{test_zotero_agent.py → test_agents_zotero_agent.py} +5 -7
  203. aiagents4pharma/talk2scholars/tests/{test_s2_display_dataframe.py → test_s2_tools_display_dataframe.py} +6 -7
  204. aiagents4pharma/talk2scholars/tests/{test_s2_query_dataframe.py → test_s2_tools_query_dataframe.py} +5 -15
  205. aiagents4pharma/talk2scholars/tests/{test_paper_downloader.py → test_tools_paper_downloader.py} +25 -63
  206. aiagents4pharma/talk2scholars/tests/{test_question_and_answer_tool.py → test_tools_question_and_answer_tool.py} +2 -6
  207. aiagents4pharma/talk2scholars/tests/{test_s2_multi.py → test_tools_s2_multi.py} +5 -5
  208. aiagents4pharma/talk2scholars/tests/{test_s2_retrieve.py → test_tools_s2_retrieve.py} +2 -1
  209. aiagents4pharma/talk2scholars/tests/{test_s2_search.py → test_tools_s2_search.py} +5 -5
  210. aiagents4pharma/talk2scholars/tests/{test_s2_single.py → test_tools_s2_single.py} +5 -5
  211. aiagents4pharma/talk2scholars/tests/{test_arxiv_downloader.py → test_utils_arxiv_downloader.py} +16 -25
  212. aiagents4pharma/talk2scholars/tests/{test_base_paper_downloader.py → test_utils_base_paper_downloader.py} +25 -47
  213. aiagents4pharma/talk2scholars/tests/{test_biorxiv_downloader.py → test_utils_biorxiv_downloader.py} +14 -42
  214. aiagents4pharma/talk2scholars/tests/{test_medrxiv_downloader.py → test_utils_medrxiv_downloader.py} +15 -49
  215. aiagents4pharma/talk2scholars/tests/{test_nvidia_nim_reranker.py → test_utils_nvidia_nim_reranker.py} +6 -16
  216. aiagents4pharma/talk2scholars/tests/{test_pdf_answer_formatter.py → test_utils_pdf_answer_formatter.py} +1 -0
  217. aiagents4pharma/talk2scholars/tests/{test_pdf_batch_processor.py → test_utils_pdf_batch_processor.py} +6 -15
  218. aiagents4pharma/talk2scholars/tests/{test_pdf_collection_manager.py → test_utils_pdf_collection_manager.py} +34 -11
  219. aiagents4pharma/talk2scholars/tests/{test_pdf_document_processor.py → test_utils_pdf_document_processor.py} +2 -3
  220. aiagents4pharma/talk2scholars/tests/{test_pdf_generate_answer.py → test_utils_pdf_generate_answer.py} +3 -6
  221. aiagents4pharma/talk2scholars/tests/{test_pdf_gpu_detection.py → test_utils_pdf_gpu_detection.py} +5 -16
  222. aiagents4pharma/talk2scholars/tests/{test_pdf_rag_pipeline.py → test_utils_pdf_rag_pipeline.py} +7 -17
  223. aiagents4pharma/talk2scholars/tests/{test_pdf_retrieve_chunks.py → test_utils_pdf_retrieve_chunks.py} +4 -11
  224. aiagents4pharma/talk2scholars/tests/{test_pdf_singleton_manager.py → test_utils_pdf_singleton_manager.py} +26 -23
  225. aiagents4pharma/talk2scholars/tests/{test_pdf_vector_normalization.py → test_utils_pdf_vector_normalization.py} +1 -1
  226. aiagents4pharma/talk2scholars/tests/{test_pdf_vector_store.py → test_utils_pdf_vector_store.py} +27 -55
  227. aiagents4pharma/talk2scholars/tests/{test_pubmed_downloader.py → test_utils_pubmed_downloader.py} +31 -91
  228. aiagents4pharma/talk2scholars/tests/{test_read_helper_utils.py → test_utils_read_helper_utils.py} +2 -6
  229. aiagents4pharma/talk2scholars/tests/{test_s2_utils_ext_ids.py → test_utils_s2_utils_ext_ids.py} +5 -15
  230. aiagents4pharma/talk2scholars/tests/{test_zotero_human_in_the_loop.py → test_utils_zotero_human_in_the_loop.py} +6 -13
  231. aiagents4pharma/talk2scholars/tests/{test_zotero_path.py → test_utils_zotero_path.py} +53 -45
  232. aiagents4pharma/talk2scholars/tests/{test_zotero_read.py → test_utils_zotero_read.py} +30 -91
  233. aiagents4pharma/talk2scholars/tests/{test_zotero_write.py → test_utils_zotero_write.py} +6 -16
  234. aiagents4pharma/talk2scholars/tools/__init__.py +1 -4
  235. aiagents4pharma/talk2scholars/tools/paper_download/paper_downloader.py +20 -35
  236. aiagents4pharma/talk2scholars/tools/paper_download/utils/__init__.py +7 -5
  237. aiagents4pharma/talk2scholars/tools/paper_download/utils/arxiv_downloader.py +9 -11
  238. aiagents4pharma/talk2scholars/tools/paper_download/utils/base_paper_downloader.py +14 -21
  239. aiagents4pharma/talk2scholars/tools/paper_download/utils/biorxiv_downloader.py +14 -22
  240. aiagents4pharma/talk2scholars/tools/paper_download/utils/medrxiv_downloader.py +11 -13
  241. aiagents4pharma/talk2scholars/tools/paper_download/utils/pubmed_downloader.py +14 -28
  242. aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +4 -8
  243. aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +16 -14
  244. aiagents4pharma/talk2scholars/tools/pdf/utils/answer_formatter.py +4 -4
  245. aiagents4pharma/talk2scholars/tools/pdf/utils/batch_processor.py +15 -17
  246. aiagents4pharma/talk2scholars/tools/pdf/utils/collection_manager.py +2 -2
  247. aiagents4pharma/talk2scholars/tools/pdf/utils/document_processor.py +5 -5
  248. aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +4 -4
  249. aiagents4pharma/talk2scholars/tools/pdf/utils/get_vectorstore.py +2 -6
  250. aiagents4pharma/talk2scholars/tools/pdf/utils/gpu_detection.py +5 -9
  251. aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +4 -4
  252. aiagents4pharma/talk2scholars/tools/pdf/utils/paper_loader.py +2 -2
  253. aiagents4pharma/talk2scholars/tools/pdf/utils/rag_pipeline.py +6 -15
  254. aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +7 -15
  255. aiagents4pharma/talk2scholars/tools/pdf/utils/singleton_manager.py +2 -2
  256. aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +3 -4
  257. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_normalization.py +8 -17
  258. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +17 -33
  259. aiagents4pharma/talk2scholars/tools/s2/__init__.py +8 -6
  260. aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +3 -7
  261. aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +7 -6
  262. aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +5 -12
  263. aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +2 -4
  264. aiagents4pharma/talk2scholars/tools/s2/search.py +6 -6
  265. aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +5 -3
  266. aiagents4pharma/talk2scholars/tools/s2/utils/__init__.py +1 -3
  267. aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +12 -18
  268. aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +11 -18
  269. aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +11 -16
  270. aiagents4pharma/talk2scholars/tools/zotero/__init__.py +1 -4
  271. aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py +1 -4
  272. aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +21 -39
  273. aiagents4pharma/talk2scholars/tools/zotero/utils/review_helper.py +2 -6
  274. aiagents4pharma/talk2scholars/tools/zotero/utils/write_helper.py +8 -11
  275. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py +4 -12
  276. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_pdf_downloader.py +13 -27
  277. aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +4 -7
  278. aiagents4pharma/talk2scholars/tools/zotero/zotero_review.py +8 -10
  279. aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py +3 -2
  280. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/METADATA +115 -51
  281. aiagents4pharma-1.45.1.dist-info/RECORD +324 -0
  282. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/WHEEL +1 -2
  283. aiagents4pharma-1.44.0.dist-info/RECORD +0 -293
  284. aiagents4pharma-1.44.0.dist-info/top_level.txt +0 -1
  285. /aiagents4pharma/talk2scholars/tests/{test_state.py → test_states_state.py} +0 -0
  286. /aiagents4pharma/talk2scholars/tests/{test_pdf_paper_loader.py → test_utils_pdf_paper_loader.py} +0 -0
  287. /aiagents4pharma/talk2scholars/tests/{test_tool_helper_utils.py → test_utils_tool_helper_utils.py} +0 -0
  288. /aiagents4pharma/talk2scholars/tests/{test_zotero_pdf_downloader_utils.py → test_utils_zotero_pdf_downloader_utils.py} +0 -0
  289. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/licenses/LICENSE +0 -0
@@ -4,7 +4,9 @@ Unit tests for Zotero path utility in zotero_path.py.
4
4
 
5
5
  import unittest
6
6
  from unittest.mock import MagicMock, patch
7
+
7
8
  import pytest
9
+
8
10
  from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path import (
9
11
  fetch_papers_for_save,
10
12
  find_or_create_collection,
@@ -92,12 +94,8 @@ class TestFindOrCreateCollectionExtra(unittest.TestCase):
92
94
  # Simulate no existing collections (so direct match fails)
93
95
  self.fake_zot.collections.return_value = []
94
96
  # Simulate create_collection returning a dict with a "success" key.
95
- self.fake_zot.create_collection.return_value = {
96
- "success": {"0": "new_key_success"}
97
- }
98
- result = find_or_create_collection(
99
- self.fake_zot, "/NewCollection", create_missing=True
100
- )
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)
101
99
  self.assertEqual(result, "new_key_success")
102
100
  # Verify payload formatting: for a simple (non-nested) path, no parentCollection.
103
101
  args, _ = self.fake_zot.create_collection.call_args
@@ -114,9 +112,7 @@ class TestFindOrCreateCollectionExtra(unittest.TestCase):
114
112
  self.fake_zot.create_collection.return_value = {
115
113
  "successful": {"0": {"data": {"key": "new_key_successful"}}}
116
114
  }
117
- result = find_or_create_collection(
118
- self.fake_zot, "/NewCollection", create_missing=True
119
- )
115
+ result = find_or_create_collection(self.fake_zot, "/NewCollection", create_missing=True)
120
116
  self.assertEqual(result, "new_key_successful")
121
117
 
122
118
  def test_create_collection_exception(self):
@@ -126,9 +122,7 @@ class TestFindOrCreateCollectionExtra(unittest.TestCase):
126
122
  """
127
123
  self.fake_zot.collections.return_value = []
128
124
  self.fake_zot.create_collection.side_effect = Exception("Creation error")
129
- result = find_or_create_collection(
130
- self.fake_zot, "/NewCollection", create_missing=True
131
- )
125
+ result = find_or_create_collection(self.fake_zot, "/NewCollection", create_missing=True)
132
126
  self.assertIsNone(result)
133
127
 
134
128
 
@@ -228,9 +222,7 @@ class TestZoteroPath:
228
222
  mock_zotero.return_value = mock_zot
229
223
 
230
224
  # Setup collections
231
- collections = [
232
- {"key": "abc123", "data": {"name": "Curiosity", "parentCollection": None}}
233
- ]
225
+ collections = [{"key": "abc123", "data": {"name": "Curiosity", "parentCollection": None}}]
234
226
  mock_zot.collections.return_value = collections
235
227
 
236
228
  # Setup create_collection response
@@ -249,9 +241,7 @@ class TestZoteroPath:
249
241
 
250
242
  # Test creating nested "Curiosity/Curiosity2"
251
243
  mock_zot.create_collection.reset_mock()
252
- result = find_or_create_collection(
253
- mock_zot, "/Curiosity/Curiosity2", create_missing=True
254
- )
244
+ result = find_or_create_collection(mock_zot, "/Curiosity/Curiosity2", create_missing=True)
255
245
  assert result == "new_key"
256
246
  # Check that the call includes parentCollection
257
247
  mock_zot.create_collection.assert_called_once()
@@ -314,11 +304,24 @@ class TestZoteroWrite:
314
304
  mock_zot_class.return_value = mock_zot
315
305
  yield mock_zot
316
306
 
317
- @patch(
318
- "aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save"
319
- )
320
- def test_zotero_write_no_papers(self, mock_fetch):
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):
321
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
+
322
325
  mock_fetch.return_value = None
323
326
 
324
327
  state = {
@@ -338,22 +341,37 @@ class TestZoteroWrite:
338
341
  )
339
342
  assert "No fetched papers were found to save" in str(excinfo.value)
340
343
 
341
- @patch(
342
- "aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save"
343
- )
344
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save")
344
345
  @patch(
345
346
  "aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.find_or_create_collection"
346
347
  )
347
- def test_zotero_write_invalid_collection(self, mock_find, mock_fetch, mock_zotero):
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
+ ):
348
354
  """Saving to a nonexistent Zotero collection returns an error Command."""
349
- sample = {"paper1": {"Title": "Test Paper"}}
350
- mock_fetch.return_value = sample
351
- mock_find.return_value = None
352
- mock_zotero.collections.return_value = [
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 = [
353
367
  {"key": "k1", "data": {"name": "Curiosity"}},
354
368
  {"key": "k2", "data": {"name": "Random"}},
355
369
  ]
356
370
 
371
+ sample = {"paper1": {"Title": "Test Paper"}}
372
+ mock_fetch.return_value = sample
373
+ mock_find.return_value = None
374
+
357
375
  state = {
358
376
  "zotero_write_approval_status": {
359
377
  "approved": True,
@@ -375,9 +393,7 @@ class TestZoteroWrite:
375
393
  assert "does not exist in Zotero" in msg
376
394
  assert "Curiosity, Random" in msg
377
395
 
378
- @patch(
379
- "aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save"
380
- )
396
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.fetch_papers_for_save")
381
397
  @patch(
382
398
  "aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.find_or_create_collection"
383
399
  )
@@ -386,12 +402,8 @@ class TestZoteroWrite:
386
402
  sample = {"paper1": {"Title": "Test Paper", "Authors": ["Test Author"]}}
387
403
  mock_fetch.return_value = sample
388
404
  mock_find.return_value = "abc123"
389
- mock_zotero.collections.return_value = [
390
- {"key": "abc123", "data": {"name": "radiation"}}
391
- ]
392
- mock_zotero.create_items.return_value = {
393
- "successful": {"0": {"key": "item123"}}
394
- }
405
+ mock_zotero.collections.return_value = [{"key": "abc123", "data": {"name": "radiation"}}]
406
+ mock_zotero.create_items.return_value = {"successful": {"0": {"key": "item123"}}}
395
407
  mock_hydra.tools.zotero_write.zotero.max_limit = 50
396
408
 
397
409
  state = {
@@ -423,9 +435,7 @@ class TestZoteroRead:
423
435
  def mock_hydra(self):
424
436
  """Fixture to mock hydra configuration."""
425
437
  with (
426
- patch(
427
- "aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.initialize"
428
- ),
438
+ patch("aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.initialize"),
429
439
  patch(
430
440
  "aiagents4pharma.talk2scholars.tools.zotero.utils.write_helper.hydra.compose"
431
441
  ) as mock_compose,
@@ -453,9 +463,7 @@ class TestZoteroRead:
453
463
  mock_zot_class.return_value = mock_zot
454
464
  yield mock_zot
455
465
 
456
- @patch(
457
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
458
- )
466
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
459
467
  def test_zotero_read_item_collections_error(
460
468
  self, mock_get_collections, mock_hydra, mock_zotero
461
469
  ):
@@ -17,7 +17,6 @@ from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_pdf_downloader impo
17
17
  )
18
18
  from aiagents4pharma.talk2scholars.tools.zotero.zotero_read import zotero_read
19
19
 
20
-
21
20
  # Dummy Hydra configuration to be used in tests
22
21
  dummy_zotero_read_config = SimpleNamespace(
23
22
  user_id="dummy_user",
@@ -34,14 +33,10 @@ dummy_cfg = SimpleNamespace(tools=SimpleNamespace(zotero_read=dummy_zotero_read_
34
33
  class TestZoteroSearchTool(unittest.TestCase):
35
34
  """Tests for Zotero search tool."""
36
35
 
37
- @patch(
38
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
39
- )
36
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
40
37
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
41
38
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
42
- @patch(
43
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
44
- )
39
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
45
40
  def test_valid_query(
46
41
  self,
47
42
  mock_hydra_init,
@@ -111,14 +106,10 @@ class TestZoteroSearchTool(unittest.TestCase):
111
106
  self.assertIn("Query: test", message_content)
112
107
  self.assertIn("Number of papers found: 2", message_content)
113
108
 
114
- @patch(
115
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
116
- )
109
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
117
110
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
118
111
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
119
- @patch(
120
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
121
- )
112
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
122
113
  def test_empty_query_fetch_all_items(
123
114
  self,
124
115
  mock_hydra_init,
@@ -159,18 +150,12 @@ class TestZoteroSearchTool(unittest.TestCase):
159
150
  update = result.update
160
151
  filtered_papers = update["article_data"]
161
152
  self.assertIn("paper1", filtered_papers)
162
- fake_zot.items.assert_called_with(
163
- limit=dummy_cfg.tools.zotero_read.zotero.max_limit
164
- )
153
+ fake_zot.items.assert_called_with(limit=dummy_cfg.tools.zotero_read.zotero.max_limit)
165
154
 
166
- @patch(
167
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
168
- )
155
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
169
156
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
170
157
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
171
- @patch(
172
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
173
- )
158
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
174
159
  def test_no_items_returned(
175
160
  self,
176
161
  mock_hydra_init,
@@ -198,17 +183,11 @@ class TestZoteroSearchTool(unittest.TestCase):
198
183
  zotero_read.run(tool_input)
199
184
  self.assertIn("No items returned from Zotero", str(context.exception))
200
185
 
201
- @patch(
202
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
203
- )
186
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
204
187
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
205
188
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
206
- @patch(
207
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
208
- )
209
- @patch(
210
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.download_pdfs_in_parallel"
211
- )
189
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
190
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.download_pdfs_in_parallel")
212
191
  def test_filtering_no_matching_papers(self, *mocks):
213
192
  """Testing filtering when no paper matching"""
214
193
  (
@@ -274,14 +253,10 @@ class TestZoteroSearchTool(unittest.TestCase):
274
253
  self.assertEqual(filtered_papers["paper1"]["pdf_url"], "/tmp/fake_path.pdf")
275
254
  self.assertEqual(filtered_papers["paper1"]["source"], "zotero")
276
255
 
277
- @patch(
278
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
279
- )
256
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
280
257
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
281
258
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
282
- @patch(
283
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
284
- )
259
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
285
260
  def test_items_api_exception(
286
261
  self,
287
262
  mock_hydra_init,
@@ -309,14 +284,10 @@ class TestZoteroSearchTool(unittest.TestCase):
309
284
  zotero_read.run(tool_input)
310
285
  self.assertIn("Failed to fetch items from Zotero", str(context.exception))
311
286
 
312
- @patch(
313
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
314
- )
287
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
315
288
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
316
289
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
317
- @patch(
318
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
319
- )
290
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
320
291
  def test_missing_key_in_item(
321
292
  self,
322
293
  mock_hydra_init,
@@ -370,14 +341,10 @@ class TestZoteroSearchTool(unittest.TestCase):
370
341
  self.assertIn("paper_valid", filtered_papers)
371
342
  self.assertEqual(len(filtered_papers), 1)
372
343
 
373
- @patch(
374
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
375
- )
344
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
376
345
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
377
346
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
378
- @patch(
379
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
380
- )
347
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
381
348
  def test_item_not_dict(
382
349
  self,
383
350
  mock_hydra_init,
@@ -410,14 +377,10 @@ class TestZoteroSearchTool(unittest.TestCase):
410
377
  zotero_read.run(tool_input)
411
378
  self.assertIn("No matching papers returned from Zotero", str(context.exception))
412
379
 
413
- @patch(
414
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
415
- )
380
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
416
381
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
417
382
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
418
- @patch(
419
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
420
- )
383
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
421
384
  def test_data_not_dict(
422
385
  self,
423
386
  mock_hydra_init,
@@ -447,17 +410,11 @@ class TestZoteroSearchTool(unittest.TestCase):
447
410
  zotero_read.run(tool_input)
448
411
  self.assertIn("No matching papers returned from Zotero", str(context.exception))
449
412
 
450
- @patch(
451
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
452
- )
413
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
453
414
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
454
415
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
455
- @patch(
456
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
457
- )
458
- @patch(
459
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.requests.Session.get"
460
- )
416
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
417
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.requests.Session.get")
461
418
  def test_pdf_attachment_success(self, *mocks):
462
419
  """Test for pdf attachment success"""
463
420
  (
@@ -506,9 +463,7 @@ class TestZoteroSearchTool(unittest.TestCase):
506
463
  mock_response = MagicMock()
507
464
  mock_response.status_code = 200
508
465
  mock_response.iter_content = lambda chunk_size: [b"fake pdf content"]
509
- mock_response.headers = {
510
- "Content-Disposition": 'attachment; filename="file1.pdf"'
511
- }
466
+ mock_response.headers = {"Content-Disposition": 'attachment; filename="file1.pdf"'}
512
467
  mock_response.raise_for_status = lambda: None
513
468
  mock_session_get.return_value = mock_response
514
469
 
@@ -528,14 +483,10 @@ class TestZoteroSearchTool(unittest.TestCase):
528
483
  self.assertEqual(paper["filename"], "file1.pdf")
529
484
  self.assertEqual(paper["attachment_key"], "attachment1")
530
485
 
531
- @patch(
532
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
533
- )
486
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
534
487
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
535
488
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
536
- @patch(
537
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
538
- )
489
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
539
490
  def test_pdf_attachment_children_exception(
540
491
  self,
541
492
  mock_hydra_init,
@@ -590,14 +541,10 @@ class TestZoteroSearchTool(unittest.TestCase):
590
541
  self.assertNotIn("filename", filtered_papers["paper1"])
591
542
  self.assertNotIn("attachment_key", filtered_papers["paper1"])
592
543
 
593
- @patch(
594
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
595
- )
544
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
596
545
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
597
546
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
598
- @patch(
599
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
600
- )
547
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
601
548
  def test_pdf_attachment_missing_key(
602
549
  self,
603
550
  mock_hydra_init,
@@ -653,14 +600,10 @@ class TestZoteroSearchTool(unittest.TestCase):
653
600
  self.assertNotIn("pdf_url", paper)
654
601
  self.assertNotIn("attachment_key", paper)
655
602
 
656
- @patch(
657
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
658
- )
603
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
659
604
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
660
605
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
661
- @patch(
662
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
663
- )
606
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
664
607
  def test_pdf_attachment_outer_exception(
665
608
  self,
666
609
  mock_hydra_init,
@@ -739,14 +682,10 @@ class TestZoteroSearchTool(unittest.TestCase):
739
682
  # Should return None on failure
740
683
  self.assertIsNone(result)
741
684
 
742
- @patch(
743
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
744
- )
685
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
745
686
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
746
687
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
747
- @patch(
748
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
749
- )
688
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
750
689
  def test_download_pdf_exception_logging(
751
690
  self,
752
691
  mock_hydra_init,
@@ -4,16 +4,12 @@ Unit tests for Zotero write tool in zotero_write.py.
4
4
 
5
5
  import unittest
6
6
  from types import SimpleNamespace
7
- from unittest.mock import patch, MagicMock
7
+ from unittest.mock import MagicMock, patch
8
8
 
9
9
  from aiagents4pharma.talk2scholars.tools.zotero.zotero_write import zotero_write
10
10
 
11
- dummy_zotero_write_config = SimpleNamespace(
12
- user_id="dummy", library_type="user", api_key="dummy"
13
- )
14
- dummy_cfg = SimpleNamespace(
15
- tools=SimpleNamespace(zotero_write=dummy_zotero_write_config)
16
- )
11
+ dummy_zotero_write_config = SimpleNamespace(user_id="dummy", library_type="user", api_key="dummy")
12
+ dummy_cfg = SimpleNamespace(tools=SimpleNamespace(zotero_write=dummy_zotero_write_config))
17
13
 
18
14
 
19
15
  class TestZoteroSaveTool(unittest.TestCase):
@@ -49,9 +45,7 @@ class TestZoteroSaveTool(unittest.TestCase):
49
45
  }
50
46
  if papers is not None:
51
47
  # When papers is provided as dict, use it directly.
52
- state["last_displayed_papers"] = (
53
- papers if isinstance(papers, dict) else "papers"
54
- )
48
+ state["last_displayed_papers"] = papers if isinstance(papers, dict) else "papers"
55
49
  if isinstance(papers, dict):
56
50
  state["papers"] = papers
57
51
  return state
@@ -83,9 +77,7 @@ class TestZoteroSaveTool(unittest.TestCase):
83
77
  )
84
78
  def test_invalid_collection(self, mock_find, mock_fetch):
85
79
  """Test when collection path is invalid"""
86
- self.fake_zot.collections.return_value = [
87
- {"key": "k1", "data": {"name": "Existing"}}
88
- ]
80
+ self.fake_zot.collections.return_value = [{"key": "k1", "data": {"name": "Existing"}}]
89
81
  # Provide a valid papers dict so we don't hit the no-papers error.
90
82
  state = self.make_state({"p1": {"Title": "X"}}, True)
91
83
  result = zotero_write.run(
@@ -143,9 +135,7 @@ class TestZoteroSaveTool(unittest.TestCase):
143
135
  self.fake_zot.collections.return_value = [
144
136
  {"key": "colKey", "data": {"name": "Test Collection"}}
145
137
  ]
146
- self.fake_zot.create_items.return_value = {
147
- "successful": {"0": {"key": "item1"}}
148
- }
138
+ self.fake_zot.create_items.return_value = {"successful": {"0": {"key": "item1"}}}
149
139
  mock_fetch.return_value = {"p1": {"Title": "X"}}
150
140
  mock_find.return_value = "colKey"
151
141
 
@@ -4,9 +4,6 @@
4
4
  Import statements
5
5
  """
6
6
 
7
- from . import s2
8
- from . import pdf
9
- from . import zotero
10
- from . import paper_download
7
+ from . import paper_download, pdf, s2, zotero
11
8
 
12
9
  __all__ = ["s2", "pdf", "zotero", "paper_download"]
@@ -6,16 +6,17 @@ Supports downloading papers from arXiv, medRxiv, bioRxiv, and PubMed through a s
6
6
 
7
7
  import logging
8
8
  import threading
9
- from typing import Annotated, Any, List, Literal, Optional
9
+ from typing import Annotated, Any, Literal
10
10
 
11
11
  import hydra
12
12
  from hydra.core.global_hydra import GlobalHydra
13
- from omegaconf import OmegaConf
14
13
  from langchain_core.messages import ToolMessage
15
14
  from langchain_core.tools import tool
16
15
  from langchain_core.tools.base import InjectedToolCallId
17
16
  from langgraph.types import Command
17
+ from omegaconf import OmegaConf
18
18
  from pydantic import BaseModel, Field
19
+
19
20
  from .utils.arxiv_downloader import ArxivDownloader
20
21
  from .utils.base_paper_downloader import BasePaperDownloader
21
22
  from .utils.biorxiv_downloader import BiorxivDownloader
@@ -30,14 +31,14 @@ logger = logging.getLogger(__name__)
30
31
  class UnifiedPaperDownloadInput(BaseModel):
31
32
  """Input schema for the unified paper download tool."""
32
33
 
33
- service: Optional[Literal["arxiv", "medrxiv", "biorxiv", "pubmed"]] = Field(
34
+ service: Literal["arxiv", "medrxiv", "biorxiv", "pubmed"] | None = Field(
34
35
  default=None,
35
36
  description=(
36
37
  "Paper service to download from: 'arxiv', 'medrxiv', 'biorxiv', or 'pubmed'. "
37
38
  "If not specified, uses the configured default service."
38
39
  ),
39
40
  )
40
- identifiers: List[str] = Field(
41
+ identifiers: list[str] = Field(
41
42
  description=(
42
43
  "List of paper identifiers. Format depends on service:\n"
43
44
  "- arxiv: arXiv IDs (e.g., ['1234.5678', '2301.12345'])\n"
@@ -140,12 +141,9 @@ class PaperDownloaderFactory:
140
141
  return PaperDownloaderFactory._cached_config
141
142
 
142
143
  try:
143
-
144
144
  # Clear if already initialized
145
145
  if GlobalHydra().is_initialized():
146
- logger.info(
147
- "GlobalHydra already initialized, clearing for config load"
148
- )
146
+ logger.info("GlobalHydra already initialized, clearing for config load")
149
147
  GlobalHydra.instance().clear()
150
148
 
151
149
  # Load configuration
@@ -156,16 +154,12 @@ class PaperDownloaderFactory:
156
154
 
157
155
  # Cache the configuration
158
156
  PaperDownloaderFactory._cached_config = cfg.tools.paper_download
159
- logger.info(
160
- "Successfully loaded and cached paper download configuration"
161
- )
157
+ logger.info("Successfully loaded and cached paper download configuration")
162
158
 
163
159
  return PaperDownloaderFactory._cached_config
164
160
 
165
161
  except Exception as e:
166
- logger.error(
167
- "Failed to load unified paper download configuration: %s", e
168
- )
162
+ logger.error("Failed to load unified paper download configuration: %s", e)
169
163
  raise RuntimeError(f"Configuration loading failed: {e}") from e
170
164
 
171
165
  @staticmethod
@@ -181,10 +175,7 @@ class PaperDownloaderFactory:
181
175
  Returns:
182
176
  Service-specific configuration object
183
177
  """
184
- if (
185
- not hasattr(unified_config, "services")
186
- or service not in unified_config.services
187
- ):
178
+ if not hasattr(unified_config, "services") or service not in unified_config.services:
188
179
  raise ValueError(f"Service '{service}' not found in configuration")
189
180
 
190
181
  # Create a simple config object that combines common and service-specific settings
@@ -202,14 +193,10 @@ class PaperDownloaderFactory:
202
193
  config_obj = ServiceConfig()
203
194
 
204
195
  # Handle common config (using helper method to reduce branches)
205
- PaperDownloaderFactory._apply_config(
206
- config_obj, unified_config.common, "common"
207
- )
196
+ PaperDownloaderFactory._apply_config(config_obj, unified_config.common, "common")
208
197
 
209
198
  # Handle service-specific config (using helper method to reduce branches)
210
- PaperDownloaderFactory._apply_config(
211
- config_obj, unified_config.services[service], service
212
- )
199
+ PaperDownloaderFactory._apply_config(config_obj, unified_config.services[service], service)
213
200
 
214
201
  return config_obj
215
202
 
@@ -239,9 +226,7 @@ class PaperDownloaderFactory:
239
226
 
240
227
  # Method 2: Try direct attribute access
241
228
  if hasattr(source_config, "__dict__"):
242
- PaperDownloaderFactory._extract_from_dict(
243
- config_obj, source_config.__dict__
244
- )
229
+ PaperDownloaderFactory._extract_from_dict(config_obj, source_config.__dict__)
245
230
  return
246
231
 
247
232
  # Method 3: Try items() method
@@ -290,8 +275,8 @@ class PaperDownloaderFactory:
290
275
  parse_docstring=True,
291
276
  )
292
277
  def download_papers(
293
- service: Optional[Literal["arxiv", "medrxiv", "biorxiv", "pubmed"]],
294
- identifiers: List[str],
278
+ service: Literal["arxiv", "medrxiv", "biorxiv", "pubmed"] | None,
279
+ identifiers: list[str],
295
280
  tool_call_id: Annotated[str, InjectedToolCallId],
296
281
  ) -> Command[Any]:
297
282
  """
@@ -334,36 +319,36 @@ def download_papers(
334
319
  # Convenience functions for backward compatibility (optional)
335
320
  # These functions explicitly specify the service, bypassing the default service config
336
321
  def download_arxiv_papers(
337
- arxiv_ids: List[str], tool_call_id: Annotated[str, InjectedToolCallId]
322
+ arxiv_ids: list[str], tool_call_id: Annotated[str, InjectedToolCallId]
338
323
  ) -> Command[Any]:
339
324
  """Convenience function for downloading arXiv papers (explicitly uses arXiv service)."""
340
325
  return _download_papers_impl("arxiv", arxiv_ids, tool_call_id)
341
326
 
342
327
 
343
328
  def download_medrxiv_papers(
344
- dois: List[str], tool_call_id: Annotated[str, InjectedToolCallId]
329
+ dois: list[str], tool_call_id: Annotated[str, InjectedToolCallId]
345
330
  ) -> Command[Any]:
346
331
  """Convenience function for downloading medRxiv papers (explicitly uses medRxiv service)."""
347
332
  return _download_papers_impl("medrxiv", dois, tool_call_id)
348
333
 
349
334
 
350
335
  def download_biorxiv_papers(
351
- dois: List[str], tool_call_id: Annotated[str, InjectedToolCallId]
336
+ dois: list[str], tool_call_id: Annotated[str, InjectedToolCallId]
352
337
  ) -> Command[Any]:
353
338
  """Convenience function for downloading bioRxiv papers (explicitly uses bioRxiv service)."""
354
339
  return _download_papers_impl("biorxiv", dois, tool_call_id)
355
340
 
356
341
 
357
342
  def download_pubmed_papers(
358
- pmids: List[str], tool_call_id: Annotated[str, InjectedToolCallId]
343
+ pmids: list[str], tool_call_id: Annotated[str, InjectedToolCallId]
359
344
  ) -> Command[Any]:
360
345
  """Convenience function for downloading PubMed papers (explicitly uses PubMed service)."""
361
346
  return _download_papers_impl("pubmed", pmids, tool_call_id)
362
347
 
363
348
 
364
349
  def _download_papers_impl(
365
- service: Optional[Literal["arxiv", "medrxiv", "biorxiv", "pubmed"]],
366
- identifiers: List[str],
350
+ service: Literal["arxiv", "medrxiv", "biorxiv", "pubmed"] | None,
351
+ identifiers: list[str],
367
352
  tool_call_id: str,
368
353
  ) -> Command[Any]:
369
354
  """