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
@@ -38,7 +38,7 @@ def test_normalize_vectors_batch_normal_case():
38
38
  [0.6, 0.8],
39
39
  [0.6, 0.8],
40
40
  ]
41
- for r, e in zip(result, expected):
41
+ for r, e in zip(result, expected, strict=False):
42
42
  assert pytest.approx(r) == e
43
43
 
44
44
 
@@ -180,29 +180,20 @@ def test_load_existing_papers_with_exception(mock_embedding, mock_config):
180
180
  patch(f"{MODULE}.log_index_configuration"),
181
181
  ):
182
182
  mock_singleton = MagicMock()
183
- safe_store = MagicMock()
184
- safe_collection = MagicMock()
185
- safe_collection.num_entities = 0
186
- safe_collection.flush.return_value = None
187
- safe_store.col = safe_collection
188
- safe_store.collection = safe_collection
189
- mock_singleton.get_vector_store.return_value = safe_store
190
- mock_singleton.get_connection.return_value = None
191
- singleton_cls.return_value = mock_singleton
192
-
193
- vs = Vectorstore(embedding_model=mock_embedding, config=mock_config)
194
-
195
- # now replace with failing store
183
+ # Set up failing store directly for initialization
196
184
  bad_collection = MagicMock()
197
185
  bad_collection.num_entities = 0
198
186
  bad_collection.flush.side_effect = Exception("flush failed")
199
187
  bad_store = MagicMock()
200
188
  bad_store.col = bad_collection
201
189
  bad_store.collection = bad_collection
202
- vs.vector_store = bad_store
190
+ mock_singleton.get_vector_store.return_value = bad_store
191
+ mock_singleton.get_connection.return_value = None
192
+ singleton_cls.return_value = mock_singleton
203
193
 
194
+ # Test error propagation through initialization that calls _load_existing_paper_ids
204
195
  with pytest.raises(Exception) as excinfo:
205
- getattr(vs, "_load_existing_paper_ids")()
196
+ Vectorstore(embedding_model=mock_embedding, config=mock_config)
206
197
  assert "flush failed" in str(excinfo.value)
207
198
 
208
199
 
@@ -232,10 +223,8 @@ def test_ensure_collection_loaded_with_entities(mock_embedding, mock_config):
232
223
  mock_singleton.get_connection.return_value = None
233
224
  singleton_cls.return_value = mock_singleton
234
225
 
235
- vs = Vectorstore(embedding_model=mock_embedding, config=mock_config)
236
- vs.vector_store = mock_store
237
- getattr(vs, "_ensure_collection_loaded")()
238
-
226
+ # Test through initialization which calls _ensure_collection_loaded
227
+ Vectorstore(embedding_model=mock_embedding, config=mock_config)
239
228
  assert mock_collection.load.called
240
229
 
241
230
 
@@ -255,29 +244,20 @@ def test_ensure_collection_loaded_handles_exception(mock_embedding, mock_config)
255
244
  patch(f"{MODULE}.log_index_configuration"),
256
245
  ):
257
246
  mock_singleton = MagicMock()
258
- safe_store = MagicMock()
259
- safe_collection = MagicMock()
260
- safe_collection.num_entities = 0
261
- safe_collection.flush.return_value = None
262
- safe_store.col = safe_collection
263
- safe_store.collection = safe_collection
264
- mock_singleton.get_vector_store.return_value = safe_store
265
- mock_singleton.get_connection.return_value = None
266
- singleton_cls.return_value = mock_singleton
267
-
268
- vs = Vectorstore(embedding_model=mock_embedding, config=mock_config)
269
-
270
- # override with failing store
247
+ # Set up failing store directly for initialization
271
248
  bad_collection = MagicMock()
272
249
  bad_collection.num_entities = 0
273
250
  bad_collection.flush.side_effect = Exception("flush error")
274
251
  bad_store = MagicMock()
275
252
  bad_store.col = bad_collection
276
253
  bad_store.collection = bad_collection
277
- vs.vector_store = bad_store
254
+ mock_singleton.get_vector_store.return_value = bad_store
255
+ mock_singleton.get_connection.return_value = None
256
+ singleton_cls.return_value = mock_singleton
278
257
 
258
+ # Test error propagation through initialization that calls _ensure_collection_loaded
279
259
  with pytest.raises(Exception) as excinfo:
280
- getattr(vs, "_ensure_collection_loaded")()
260
+ Vectorstore(embedding_model=mock_embedding, config=mock_config)
281
261
  assert "flush error" in str(excinfo.value)
282
262
 
283
263
 
@@ -313,9 +293,7 @@ def test_force_cpu_mode_logs_override(mock_config, mock_embedding):
313
293
  assert not vs.has_gpu
314
294
 
315
295
 
316
- def test_similarity_metric_override(
317
- dummy_embedding, dummy_config, dummy_vectorstore_components
318
- ):
296
+ def test_similarity_metric_override(dummy_embedding, dummy_config, dummy_vectorstore_components):
319
297
  """
320
298
  Test setting of use_cosine from config.similarity_metric.
321
299
  """
@@ -338,8 +316,7 @@ def test_load_existing_paper_ids_fallback_to_collection(
338
316
  delattr(mock_vector_store, attr)
339
317
 
340
318
  vs = Vectorstore(dummy_embedding, config=dummy_config)
341
- vs.vector_store = mock_vector_store
342
- getattr(vs, "_load_existing_paper_ids")()
319
+ # The loaded_papers is set during initialization via _load_existing_paper_ids
343
320
  assert isinstance(vs.loaded_papers, set)
344
321
 
345
322
 
@@ -356,8 +333,7 @@ def test_load_existing_papers_collection_empty_logs(
356
333
  mock_vector_store.col = mock_collection
357
334
 
358
335
  vs = Vectorstore(dummy_embedding, config=dummy_config)
359
- vs.vector_store = mock_vector_store
360
- getattr(vs, "_load_existing_paper_ids")()
336
+ # The loaded_papers is set during initialization via _load_existing_paper_ids
361
337
  assert len(vs.loaded_papers) == 0
362
338
 
363
339
 
@@ -382,16 +358,12 @@ def test_similarity_search_filter_paths(
382
358
  assert isinstance(result, list)
383
359
 
384
360
 
385
- def test_mmr_search_filter_paths(
386
- dummy_embedding, dummy_config, dummy_vectorstore_components
387
- ):
361
+ def test_mmr_search_filter_paths(dummy_embedding, dummy_config, dummy_vectorstore_components):
388
362
  """
389
363
  Test filter expression generation in max_marginal_relevance_search.
390
364
  """
391
365
  _, mock_vector_store = dummy_vectorstore_components
392
- mock_vector_store.max_marginal_relevance_search.return_value = [
393
- Document(page_content="test")
394
- ]
366
+ mock_vector_store.max_marginal_relevance_search.return_value = [Document(page_content="test")]
395
367
  vs = Vectorstore(dummy_embedding, config=dummy_config)
396
368
  vs.vector_store = mock_vector_store
397
369
 
@@ -411,10 +383,10 @@ def test_ensure_collection_loaded_no_col_and_no_collection(
411
383
  if hasattr(mock_vector_store, attr):
412
384
  delattr(mock_vector_store, attr)
413
385
 
414
- vs = Vectorstore(dummy_embedding, config=dummy_config)
415
- vs.vector_store = mock_vector_store
416
- getattr(vs, "_ensure_collection_loaded")()
417
- # no exception
386
+ # Test initialization succeeds without exception
387
+ Vectorstore(dummy_embedding, config=dummy_config)
388
+ # Collection loading is handled during initialization via _ensure_collection_loaded
389
+ # no exception if we got this far
418
390
 
419
391
 
420
392
  def test_ensure_collection_loaded_empty_logs(
@@ -428,7 +400,7 @@ def test_ensure_collection_loaded_empty_logs(
428
400
  mock_collection.num_entities = 0
429
401
  mock_vector_store.col = mock_collection
430
402
 
431
- vs = Vectorstore(dummy_embedding, config=dummy_config)
432
- vs.vector_store = mock_vector_store
433
- getattr(vs, "_ensure_collection_loaded")()
434
- # no exception
403
+ # Test initialization succeeds without exception
404
+ Vectorstore(dummy_embedding, config=dummy_config)
405
+ # Collection loading is handled during initialization via _ensure_collection_loaded
406
+ # no exception if we got this far
@@ -139,8 +139,7 @@ class TestPubmedDownloaderBasics(unittest.TestCase):
139
139
  """<error> node -> return empty string."""
140
140
  resp = Mock()
141
141
  resp.text = (
142
- '<?xml version="1.0"?><OA><error code="idDoesNotExist">'
143
- "Invalid PMC ID</error></OA>"
142
+ '<?xml version="1.0"?><OA><error code="idDoesNotExist">Invalid PMC ID</error></OA>'
144
143
  )
145
144
  resp.raise_for_status = Mock()
146
145
  mock_get.return_value = resp
@@ -299,10 +298,7 @@ class TestPubmedDownloaderEuropePMC(unittest.TestCase):
299
298
  resp.status_code = 200
300
299
  mock_head.return_value = resp
301
300
  result = self.downloader.try_europe_pmc_public("PMC123456")
302
- expected = (
303
- "https://www.ebi.ac.uk/europepmc/webservices/rest"
304
- "?accid=PMC123456&blobtype=pdf"
305
- )
301
+ expected = "https://www.ebi.ac.uk/europepmc/webservices/rest?accid=PMC123456&blobtype=pdf"
306
302
  mock_head.assert_called_once_with(expected, timeout=30)
307
303
  self.assertEqual(result, expected)
308
304
 
@@ -356,18 +352,14 @@ class TestPubmedDownloaderPMCScrape(unittest.TestCase):
356
352
 
357
353
  expected_url = "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC123456/"
358
354
  expected_headers = {"User-Agent": "Mozilla/5.0 (compatible; test-agent)"}
359
- mock_get.assert_called_once_with(
360
- expected_url, headers=expected_headers, timeout=30
361
- )
362
- self.assertEqual(
363
- result, "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC123456/pdf/test.pdf"
364
- )
355
+ mock_get.assert_called_once_with(expected_url, headers=expected_headers, timeout=30)
356
+ self.assertEqual(result, "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC123456/pdf/test.pdf")
365
357
 
366
358
  @patch("requests.get")
367
359
  def test_try_pmc_page_scraping_no_pdf(self, mock_get):
368
360
  """Test PMC page scraping with no PDF found."""
369
361
  resp = Mock()
370
- resp.content = "<html><head></head></html>".encode()
362
+ resp.content = b"<html><head></head></html>"
371
363
  resp.raise_for_status = Mock()
372
364
  mock_get.return_value = resp
373
365
  self.assertEqual(self.downloader.try_pmc_page_scraping_public("PMC123456"), "")
@@ -486,17 +478,13 @@ class TestPubmedDownloaderConstructAndFallbacks(unittest.TestCase):
486
478
  """Test fallback chain through multiple sources."""
487
479
  with (
488
480
  patch.object(self.downloader, "try_oa_api_public", return_value="") as m_oa,
489
- patch.object(
490
- self.downloader, "try_europe_pmc_public", return_value=""
491
- ) as m_eu,
481
+ patch.object(self.downloader, "try_europe_pmc_public", return_value="") as m_eu,
492
482
  patch.object(
493
483
  self.downloader,
494
484
  "try_pmc_page_scraping_public",
495
485
  return_value="http://test.pdf",
496
486
  ) as m_scr,
497
- patch.object(
498
- self.downloader, "try_direct_pmc_url_public", return_value=""
499
- ) as m_dir,
487
+ patch.object(self.downloader, "try_direct_pmc_url_public", return_value="") as m_dir,
500
488
  ):
501
489
  out = self.downloader.fetch_pdf_url_with_fallbacks_public("PMC123456")
502
490
  self.assertEqual(out, "http://test.pdf")
@@ -509,12 +497,8 @@ class TestPubmedDownloaderConstructAndFallbacks(unittest.TestCase):
509
497
  """Test fallback chain with direct PMC success."""
510
498
  with (
511
499
  patch.object(self.downloader, "try_oa_api_public", return_value="") as m_oa,
512
- patch.object(
513
- self.downloader, "try_europe_pmc_public", return_value=""
514
- ) as m_eu,
515
- patch.object(
516
- self.downloader, "try_pmc_page_scraping_public", return_value=""
517
- ) as m_scr,
500
+ patch.object(self.downloader, "try_europe_pmc_public", return_value="") as m_eu,
501
+ patch.object(self.downloader, "try_pmc_page_scraping_public", return_value="") as m_scr,
518
502
  patch.object(
519
503
  self.downloader,
520
504
  "try_direct_pmc_url_public",
@@ -532,15 +516,9 @@ class TestPubmedDownloaderConstructAndFallbacks(unittest.TestCase):
532
516
  """Test fallback chain when all sources fail."""
533
517
  with (
534
518
  patch.object(self.downloader, "try_oa_api_public", return_value="") as m_oa,
535
- patch.object(
536
- self.downloader, "try_europe_pmc_public", return_value=""
537
- ) as m_eu,
538
- patch.object(
539
- self.downloader, "try_pmc_page_scraping_public", return_value=""
540
- ) as m_scr,
541
- patch.object(
542
- self.downloader, "try_direct_pmc_url_public", return_value=""
543
- ) as m_dir,
519
+ patch.object(self.downloader, "try_europe_pmc_public", return_value="") as m_eu,
520
+ patch.object(self.downloader, "try_pmc_page_scraping_public", return_value="") as m_scr,
521
+ patch.object(self.downloader, "try_direct_pmc_url_public", return_value="") as m_dir,
544
522
  ):
545
523
  out = self.downloader.fetch_pdf_url_with_fallbacks_public("PMC123456")
546
524
  self.assertEqual(out, "")
@@ -587,9 +565,7 @@ class TestPubmedDownloaderIntegration(unittest.TestCase):
587
565
  """Test full workflow from PMID to PDF URL."""
588
566
  metadata_response = Mock()
589
567
  metadata_response.json.return_value = {
590
- "records": [
591
- {"pmid": "12345678", "pmcid": "PMC123456", "doi": "10.1234/test"}
592
- ]
568
+ "records": [{"pmid": "12345678", "pmcid": "PMC123456", "doi": "10.1234/test"}]
593
569
  }
594
570
  metadata_response.raise_for_status = Mock()
595
571
 
@@ -631,9 +607,7 @@ class TestPubmedDownloaderIntegration(unittest.TestCase):
631
607
  """Test workflow with fallback to alternative sources."""
632
608
  metadata_response = Mock()
633
609
  metadata_response.json.return_value = {
634
- "records": [
635
- {"pmid": "12345678", "pmcid": "PMC123456", "doi": "10.1234/test"}
636
- ]
610
+ "records": [{"pmid": "12345678", "pmcid": "PMC123456", "doi": "10.1234/test"}]
637
611
  }
638
612
  metadata_response.raise_for_status = Mock()
639
613
 
@@ -780,9 +754,7 @@ class TestPubmedDownloaderHelpers(unittest.TestCase):
780
754
  """Test that placeholder abstracts return empty snippets."""
781
755
  self.assertEqual(self.downloader.get_snippet(""), "")
782
756
  self.assertEqual(self.downloader.get_snippet("N/A"), "")
783
- self.assertEqual(
784
- self.downloader.get_snippet("Abstract available in PubMed"), ""
785
- )
757
+ self.assertEqual(self.downloader.get_snippet("Abstract available in PubMed"), "")
786
758
 
787
759
  def test_get_snippet_non_placeholder_delegates_to_base(self):
788
760
  """Test that non-placeholder abstracts delegate to base class."""
@@ -793,9 +765,7 @@ class TestPubmedDownloaderHelpers(unittest.TestCase):
793
765
 
794
766
  def test_get_paper_identifier_info_without_pmcid_line(self):
795
767
  """Test paper identifier info formatting without PMCID."""
796
- info = self.downloader.get_paper_identifier_info_public(
797
- {"PMID": "999", "PMCID": "N/A"}
798
- )
768
+ info = self.downloader.get_paper_identifier_info_public({"PMID": "999", "PMCID": "N/A"})
799
769
  self.assertIn("(PMID: 999)", info)
800
770
  self.assertNotIn("PMCID:", info)
801
771
 
@@ -861,14 +831,10 @@ class TestPubmedDownloaderMissingLineCoverage(unittest.TestCase):
861
831
  patch.object(self.downloader, "_try_pmc_page_scraping", return_value=""),
862
832
  patch.object(self.downloader, "_try_direct_pmc_url", return_value=""),
863
833
  patch(
864
- "aiagents4pharma.talk2scholars.tools.paper_download.utils."
865
- "pubmed_downloader.logger"
834
+ "aiagents4pharma.talk2scholars.tools.paper_download.utils.pubmed_downloader.logger"
866
835
  ) as mock_logger,
867
836
  ):
868
-
869
- result = self.downloader.fetch_pdf_url_with_fallbacks_production(
870
- "PMC123456"
871
- )
837
+ result = self.downloader.fetch_pdf_url_with_fallbacks_production("PMC123456")
872
838
 
873
839
  self.assertEqual(result, "")
874
840
  # Verify the warning log is called
@@ -879,21 +845,15 @@ class TestPubmedDownloaderMissingLineCoverage(unittest.TestCase):
879
845
  def test_fetch_pdf_url_with_fallbacks_oa_api_success_early_return(self):
880
846
  """Test _fetch_pdf_url_with_fallbacks when OA API succeeds on first try."""
881
847
  with (
882
- patch.object(
883
- self.downloader, "_try_oa_api", return_value="http://oa.pdf"
884
- ) as mock_oa,
848
+ patch.object(self.downloader, "_try_oa_api", return_value="http://oa.pdf") as mock_oa,
885
849
  patch.object(self.downloader, "_try_europe_pmc") as mock_eu,
886
850
  patch.object(self.downloader, "_try_pmc_page_scraping") as mock_scr,
887
851
  patch.object(self.downloader, "_try_direct_pmc_url") as mock_dir,
888
852
  patch(
889
- "aiagents4pharma.talk2scholars.tools.paper_download.utils."
890
- "pubmed_downloader.logger"
853
+ "aiagents4pharma.talk2scholars.tools.paper_download.utils.pubmed_downloader.logger"
891
854
  ) as mock_logger,
892
855
  ):
893
-
894
- result = self.downloader.fetch_pdf_url_with_fallbacks_production(
895
- "PMC123456"
896
- )
856
+ result = self.downloader.fetch_pdf_url_with_fallbacks_production("PMC123456")
897
857
 
898
858
  self.assertEqual(result, "http://oa.pdf")
899
859
  mock_oa.assert_called_once_with("PMC123456")
@@ -901,9 +861,7 @@ class TestPubmedDownloaderMissingLineCoverage(unittest.TestCase):
901
861
  mock_scr.assert_not_called()
902
862
  mock_dir.assert_not_called()
903
863
  # Verify the initial info log is called
904
- mock_logger.info.assert_called_with(
905
- "Fetching PDF URL for PMCID: %s", "PMC123456"
906
- )
864
+ mock_logger.info.assert_called_with("Fetching PDF URL for PMCID: %s", "PMC123456")
907
865
 
908
866
  def test_fetch_pdf_url_with_fallbacks_europe_pmc_success_after_oa_fail(self):
909
867
  """Test _fetch_pdf_url_with_fallbacks when Europe PMC succeeds after OA API fails."""
@@ -915,10 +873,7 @@ class TestPubmedDownloaderMissingLineCoverage(unittest.TestCase):
915
873
  patch.object(self.downloader, "_try_pmc_page_scraping") as mock_scr,
916
874
  patch.object(self.downloader, "_try_direct_pmc_url") as mock_dir,
917
875
  ):
918
-
919
- result = self.downloader.fetch_pdf_url_with_fallbacks_production(
920
- "PMC123456"
921
- )
876
+ result = self.downloader.fetch_pdf_url_with_fallbacks_production("PMC123456")
922
877
 
923
878
  self.assertEqual(result, "http://eu.pdf")
924
879
  mock_oa.assert_called_once_with("PMC123456")
@@ -932,18 +887,13 @@ class TestPubmedDownloaderMissingLineCoverage(unittest.TestCase):
932
887
  """Test _fetch_pdf_url_with_fallbacks when PMC scraping succeeds."""
933
888
  with (
934
889
  patch.object(self.downloader, "_try_oa_api", return_value="") as mock_oa,
935
- patch.object(
936
- self.downloader, "_try_europe_pmc", return_value=""
937
- ) as mock_eu,
890
+ patch.object(self.downloader, "_try_europe_pmc", return_value="") as mock_eu,
938
891
  patch.object(
939
892
  self.downloader, "_try_pmc_page_scraping", return_value="http://scr.pdf"
940
893
  ) as mock_scr,
941
894
  patch.object(self.downloader, "_try_direct_pmc_url") as mock_dir,
942
895
  ):
943
-
944
- result = self.downloader.fetch_pdf_url_with_fallbacks_production(
945
- "PMC123456"
946
- )
896
+ result = self.downloader.fetch_pdf_url_with_fallbacks_production("PMC123456")
947
897
 
948
898
  self.assertEqual(result, "http://scr.pdf")
949
899
  mock_oa.assert_called_once_with("PMC123456")
@@ -955,20 +905,13 @@ class TestPubmedDownloaderMissingLineCoverage(unittest.TestCase):
955
905
  """Test _fetch_pdf_url_with_fallbacks when direct PMC succeeds as last resort."""
956
906
  with (
957
907
  patch.object(self.downloader, "_try_oa_api", return_value="") as mock_oa,
958
- patch.object(
959
- self.downloader, "_try_europe_pmc", return_value=""
960
- ) as mock_eu,
961
- patch.object(
962
- self.downloader, "_try_pmc_page_scraping", return_value=""
963
- ) as mock_scr,
908
+ patch.object(self.downloader, "_try_europe_pmc", return_value="") as mock_eu,
909
+ patch.object(self.downloader, "_try_pmc_page_scraping", return_value="") as mock_scr,
964
910
  patch.object(
965
911
  self.downloader, "_try_direct_pmc_url", return_value="http://dir.pdf"
966
912
  ) as mock_dir,
967
913
  ):
968
-
969
- result = self.downloader.fetch_pdf_url_with_fallbacks_production(
970
- "PMC123456"
971
- )
914
+ result = self.downloader.fetch_pdf_url_with_fallbacks_production("PMC123456")
972
915
 
973
916
  self.assertEqual(result, "http://dir.pdf")
974
917
  mock_oa.assert_called_once_with("PMC123456")
@@ -1012,8 +955,7 @@ class TestPubmedDownloaderProductionConstructPdfUrl(unittest.TestCase):
1012
955
  """Test production construct_pdf_url with missing pmcid key."""
1013
956
  metadata = {"records": [{"doi": "10.1/x"}]}
1014
957
  with patch(
1015
- "aiagents4pharma.talk2scholars.tools.paper_download.utils."
1016
- "pubmed_downloader.logger"
958
+ "aiagents4pharma.talk2scholars.tools.paper_download.utils.pubmed_downloader.logger"
1017
959
  ) as mock_logger:
1018
960
  result = self.downloader.construct_pdf_url(metadata, "12345678")
1019
961
 
@@ -1027,8 +969,7 @@ class TestPubmedDownloaderProductionConstructPdfUrl(unittest.TestCase):
1027
969
  """Test production construct_pdf_url with empty pmcid."""
1028
970
  metadata = {"records": [{"pmcid": "", "doi": "10.1/x"}]}
1029
971
  with patch(
1030
- "aiagents4pharma.talk2scholars.tools.paper_download.utils."
1031
- "pubmed_downloader.logger"
972
+ "aiagents4pharma.talk2scholars.tools.paper_download.utils.pubmed_downloader.logger"
1032
973
  ) as mock_logger:
1033
974
  result = self.downloader.construct_pdf_url(metadata, "12345678")
1034
975
 
@@ -1042,8 +983,7 @@ class TestPubmedDownloaderProductionConstructPdfUrl(unittest.TestCase):
1042
983
  """Test production construct_pdf_url with N/A pmcid."""
1043
984
  metadata = {"records": [{"pmcid": "N/A", "doi": "10.1/x"}]}
1044
985
  with patch(
1045
- "aiagents4pharma.talk2scholars.tools.paper_download.utils."
1046
- "pubmed_downloader.logger"
986
+ "aiagents4pharma.talk2scholars.tools.paper_download.utils.pubmed_downloader.logger"
1047
987
  ) as mock_logger:
1048
988
  result = self.downloader.construct_pdf_url(metadata, "12345678")
1049
989
 
@@ -27,14 +27,10 @@ dummy_cfg = SimpleNamespace(tools=SimpleNamespace(zotero_read=dummy_zotero_read_
27
27
  class TestReadHelperDownloadsFalse(unittest.TestCase):
28
28
  """Tests for read_helper download_pdfs=False branches."""
29
29
 
30
- @patch(
31
- "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections"
32
- )
30
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
33
31
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
34
32
  @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
35
- @patch(
36
- "aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize"
37
- )
33
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
38
34
  def test_download_pdfs_false_branches(
39
35
  self,
40
36
  mock_hydra_init,
@@ -49,9 +49,7 @@ def patch_hydra(monkeypatch):
49
49
  ),
50
50
  )
51
51
  )
52
- monkeypatch.setattr(
53
- hydra, "initialize", lambda version_base, config_path: DummyHydraContext()
54
- )
52
+ monkeypatch.setattr(hydra, "initialize", lambda version_base, config_path: DummyHydraContext())
55
53
  monkeypatch.setattr(hydra, "compose", lambda config_name, overrides: dummy_cfg)
56
54
 
57
55
 
@@ -69,9 +67,7 @@ def test_multi_helper_pmc_and_doi_ids(monkeypatch):
69
67
  }
70
68
  ]
71
69
  }
72
- response = SimpleNamespace(
73
- status_code=200, json=lambda: data, raise_for_status=lambda: None
74
- )
70
+ response = SimpleNamespace(status_code=200, json=lambda: data, raise_for_status=lambda: None)
75
71
  monkeypatch.setattr(requests, "post", lambda *args, **kwargs: response)
76
72
  results = rec.process_recommendations()
77
73
  ids_list = results["papers"]["p1"]["paper_ids"]
@@ -91,9 +87,7 @@ def test_search_helper_pmc_and_doi_ids(monkeypatch):
91
87
  }
92
88
  ]
93
89
  }
94
- response = SimpleNamespace(
95
- status_code=200, json=lambda: data, raise_for_status=lambda: None
96
- )
90
+ response = SimpleNamespace(status_code=200, json=lambda: data, raise_for_status=lambda: None)
97
91
  monkeypatch.setattr(requests, "get", lambda *args, **kwargs: response)
98
92
  results = sd.process_search()
99
93
  ids_list = results["papers"]["s1"]["paper_ids"]
@@ -113,9 +107,7 @@ def test_single_helper_pmc_and_doi_ids(monkeypatch):
113
107
  }
114
108
  ]
115
109
  }
116
- response = SimpleNamespace(
117
- status_code=200, json=lambda: data, raise_for_status=lambda: None
118
- )
110
+ response = SimpleNamespace(status_code=200, json=lambda: data, raise_for_status=lambda: None)
119
111
  monkeypatch.setattr(requests, "get", lambda *args, **kwargs: response)
120
112
  results = sp.process_recommendations()
121
113
  ids_list = results["papers"]["x1"]["paper_ids"]
@@ -395,9 +387,7 @@ def test_single_helper_create_content_snippet(monkeypatch):
395
387
 
396
388
  def test_multi_helper_create_content_snippet(monkeypatch):
397
389
  """Test that MultiPaperRecData._create_content includes snippets appropriately."""
398
- mr = MultiPaperRecData(
399
- paper_ids=["a", "b"], limit=2, year="2021", tool_call_id="tid"
400
- )
390
+ mr = MultiPaperRecData(paper_ids=["a", "b"], limit=2, year="2021", tool_call_id="tid")
401
391
  mr.filtered_papers = {
402
392
  "m1": {"Title": "MTitle1", "Year": "2017", "Abstract": "MOne. MTwo. MThree."},
403
393
  "m2": {"Title": "MTitle2", "Year": "2016", "Abstract": ""},
@@ -3,7 +3,8 @@ Unit tests for Zotero human in the loop in zotero_review.py with structured outp
3
3
  """
4
4
 
5
5
  import unittest
6
- from unittest.mock import patch, MagicMock
6
+ from unittest.mock import MagicMock, patch
7
+
7
8
  from aiagents4pharma.talk2scholars.tools.zotero.zotero_review import zotero_review
8
9
 
9
10
 
@@ -185,22 +186,16 @@ class TestZoteroReviewTool(unittest.TestCase):
185
186
 
186
187
  upd = result.update
187
188
  self.assertEqual(upd["zotero_write_approval_status"], {"approved": False})
188
- self.assertIn(
189
- "Human rejected saving papers to Zotero", upd["messages"][0].content
190
- )
189
+ self.assertIn("Human rejected saving papers to Zotero", upd["messages"][0].content)
191
190
  mock_fetch.assert_called_once()
192
191
  mock_interrupt.assert_called_once()
193
192
 
194
- @patch(
195
- "aiagents4pharma.talk2scholars.tools.zotero.zotero_review.fetch_papers_for_save"
196
- )
193
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_review.fetch_papers_for_save")
197
194
  @patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_review.interrupt")
198
195
  def test_structured_processing_failure(self, mock_interrupt, mock_fetch):
199
196
  """Test fallback when structured review processing fails."""
200
197
  # Simulate valid fetched papers with multiple entries.
201
- papers = {
202
- f"p{i}": {"Title": f"Title{i}", "Authors": [f"A{i}"]} for i in range(1, 8)
203
- }
198
+ papers = {f"p{i}": {"Title": f"Title{i}", "Authors": [f"A{i}"]} for i in range(1, 8)}
204
199
  mock_fetch.return_value = papers
205
200
  mock_interrupt.return_value = "dummy_response"
206
201
  # Provide a fake llm_model whose invoke() raises an exception.
@@ -234,9 +229,7 @@ class TestZoteroReviewTool(unittest.TestCase):
234
229
 
235
230
  @patch(
236
231
  "aiagents4pharma.talk2scholars.tools.zotero.zotero_review.fetch_papers_for_save",
237
- return_value={
238
- "p1": {"Title": "Test Paper", "Authors": ["Alice", "Bob", "Charlie"]}
239
- },
232
+ return_value={"p1": {"Title": "Test Paper", "Authors": ["Alice", "Bob", "Charlie"]}},
240
233
  )
241
234
  @patch(
242
235
  "aiagents4pharma.talk2scholars.tools.zotero.zotero_review.interrupt",