aiagents4pharma 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. aiagents4pharma/__init__.py +11 -0
  2. aiagents4pharma/talk2aiagents4pharma/.dockerignore +13 -0
  3. aiagents4pharma/talk2aiagents4pharma/Dockerfile +133 -0
  4. aiagents4pharma/talk2aiagents4pharma/README.md +1 -0
  5. aiagents4pharma/talk2aiagents4pharma/__init__.py +5 -0
  6. aiagents4pharma/talk2aiagents4pharma/agents/__init__.py +6 -0
  7. aiagents4pharma/talk2aiagents4pharma/agents/main_agent.py +70 -0
  8. aiagents4pharma/talk2aiagents4pharma/configs/__init__.py +5 -0
  9. aiagents4pharma/talk2aiagents4pharma/configs/agents/__init__.py +5 -0
  10. aiagents4pharma/talk2aiagents4pharma/configs/agents/main_agent/default.yaml +29 -0
  11. aiagents4pharma/talk2aiagents4pharma/configs/app/__init__.py +0 -0
  12. aiagents4pharma/talk2aiagents4pharma/configs/app/frontend/__init__.py +0 -0
  13. aiagents4pharma/talk2aiagents4pharma/configs/app/frontend/default.yaml +102 -0
  14. aiagents4pharma/talk2aiagents4pharma/configs/config.yaml +4 -0
  15. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/.env.example +23 -0
  16. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/docker-compose.yml +93 -0
  17. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/.env.example +23 -0
  18. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/docker-compose.yml +108 -0
  19. aiagents4pharma/talk2aiagents4pharma/install.md +154 -0
  20. aiagents4pharma/talk2aiagents4pharma/states/__init__.py +5 -0
  21. aiagents4pharma/talk2aiagents4pharma/states/state_talk2aiagents4pharma.py +18 -0
  22. aiagents4pharma/talk2aiagents4pharma/tests/__init__.py +3 -0
  23. aiagents4pharma/talk2aiagents4pharma/tests/test_main_agent.py +312 -0
  24. aiagents4pharma/talk2biomodels/.dockerignore +13 -0
  25. aiagents4pharma/talk2biomodels/Dockerfile +104 -0
  26. aiagents4pharma/talk2biomodels/README.md +1 -0
  27. aiagents4pharma/talk2biomodels/__init__.py +5 -0
  28. aiagents4pharma/talk2biomodels/agents/__init__.py +6 -0
  29. aiagents4pharma/talk2biomodels/agents/t2b_agent.py +104 -0
  30. aiagents4pharma/talk2biomodels/api/__init__.py +5 -0
  31. aiagents4pharma/talk2biomodels/api/ols.py +75 -0
  32. aiagents4pharma/talk2biomodels/api/uniprot.py +36 -0
  33. aiagents4pharma/talk2biomodels/configs/__init__.py +5 -0
  34. aiagents4pharma/talk2biomodels/configs/agents/__init__.py +5 -0
  35. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/__init__.py +3 -0
  36. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/default.yaml +14 -0
  37. aiagents4pharma/talk2biomodels/configs/app/__init__.py +0 -0
  38. aiagents4pharma/talk2biomodels/configs/app/frontend/__init__.py +0 -0
  39. aiagents4pharma/talk2biomodels/configs/app/frontend/default.yaml +72 -0
  40. aiagents4pharma/talk2biomodels/configs/config.yaml +7 -0
  41. aiagents4pharma/talk2biomodels/configs/tools/__init__.py +5 -0
  42. aiagents4pharma/talk2biomodels/configs/tools/ask_question/__init__.py +3 -0
  43. aiagents4pharma/talk2biomodels/configs/tools/ask_question/default.yaml +30 -0
  44. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/__init__.py +3 -0
  45. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/default.yaml +8 -0
  46. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/__init__.py +3 -0
  47. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/default.yaml +8 -0
  48. aiagents4pharma/talk2biomodels/install.md +63 -0
  49. aiagents4pharma/talk2biomodels/models/__init__.py +5 -0
  50. aiagents4pharma/talk2biomodels/models/basico_model.py +125 -0
  51. aiagents4pharma/talk2biomodels/models/sys_bio_model.py +60 -0
  52. aiagents4pharma/talk2biomodels/states/__init__.py +6 -0
  53. aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +49 -0
  54. aiagents4pharma/talk2biomodels/tests/BIOMD0000000449_url.xml +1585 -0
  55. aiagents4pharma/talk2biomodels/tests/__init__.py +3 -0
  56. aiagents4pharma/talk2biomodels/tests/article_on_model_537.pdf +0 -0
  57. aiagents4pharma/talk2biomodels/tests/test_api.py +31 -0
  58. aiagents4pharma/talk2biomodels/tests/test_ask_question.py +42 -0
  59. aiagents4pharma/talk2biomodels/tests/test_basico_model.py +67 -0
  60. aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +190 -0
  61. aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +92 -0
  62. aiagents4pharma/talk2biomodels/tests/test_integration.py +116 -0
  63. aiagents4pharma/talk2biomodels/tests/test_load_biomodel.py +35 -0
  64. aiagents4pharma/talk2biomodels/tests/test_param_scan.py +71 -0
  65. aiagents4pharma/talk2biomodels/tests/test_query_article.py +184 -0
  66. aiagents4pharma/talk2biomodels/tests/test_save_model.py +47 -0
  67. aiagents4pharma/talk2biomodels/tests/test_search_models.py +35 -0
  68. aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +44 -0
  69. aiagents4pharma/talk2biomodels/tests/test_steady_state.py +86 -0
  70. aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +67 -0
  71. aiagents4pharma/talk2biomodels/tools/__init__.py +17 -0
  72. aiagents4pharma/talk2biomodels/tools/ask_question.py +125 -0
  73. aiagents4pharma/talk2biomodels/tools/custom_plotter.py +165 -0
  74. aiagents4pharma/talk2biomodels/tools/get_annotation.py +342 -0
  75. aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +159 -0
  76. aiagents4pharma/talk2biomodels/tools/load_arguments.py +134 -0
  77. aiagents4pharma/talk2biomodels/tools/load_biomodel.py +44 -0
  78. aiagents4pharma/talk2biomodels/tools/parameter_scan.py +310 -0
  79. aiagents4pharma/talk2biomodels/tools/query_article.py +64 -0
  80. aiagents4pharma/talk2biomodels/tools/save_model.py +98 -0
  81. aiagents4pharma/talk2biomodels/tools/search_models.py +96 -0
  82. aiagents4pharma/talk2biomodels/tools/simulate_model.py +137 -0
  83. aiagents4pharma/talk2biomodels/tools/steady_state.py +187 -0
  84. aiagents4pharma/talk2biomodels/tools/utils.py +23 -0
  85. aiagents4pharma/talk2cells/README.md +1 -0
  86. aiagents4pharma/talk2cells/__init__.py +5 -0
  87. aiagents4pharma/talk2cells/agents/__init__.py +6 -0
  88. aiagents4pharma/talk2cells/agents/scp_agent.py +87 -0
  89. aiagents4pharma/talk2cells/states/__init__.py +6 -0
  90. aiagents4pharma/talk2cells/states/state_talk2cells.py +15 -0
  91. aiagents4pharma/talk2cells/tests/scp_agent/test_scp_agent.py +22 -0
  92. aiagents4pharma/talk2cells/tools/__init__.py +6 -0
  93. aiagents4pharma/talk2cells/tools/scp_agent/__init__.py +6 -0
  94. aiagents4pharma/talk2cells/tools/scp_agent/display_studies.py +27 -0
  95. aiagents4pharma/talk2cells/tools/scp_agent/search_studies.py +78 -0
  96. aiagents4pharma/talk2knowledgegraphs/.dockerignore +13 -0
  97. aiagents4pharma/talk2knowledgegraphs/Dockerfile +131 -0
  98. aiagents4pharma/talk2knowledgegraphs/README.md +1 -0
  99. aiagents4pharma/talk2knowledgegraphs/__init__.py +5 -0
  100. aiagents4pharma/talk2knowledgegraphs/agents/__init__.py +5 -0
  101. aiagents4pharma/talk2knowledgegraphs/agents/t2kg_agent.py +99 -0
  102. aiagents4pharma/talk2knowledgegraphs/configs/__init__.py +5 -0
  103. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/__init__.py +3 -0
  104. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/default.yaml +62 -0
  105. aiagents4pharma/talk2knowledgegraphs/configs/app/__init__.py +5 -0
  106. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/__init__.py +3 -0
  107. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/default.yaml +79 -0
  108. aiagents4pharma/talk2knowledgegraphs/configs/config.yaml +13 -0
  109. aiagents4pharma/talk2knowledgegraphs/configs/tools/__init__.py +5 -0
  110. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/__init__.py +3 -0
  111. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/default.yaml +24 -0
  112. aiagents4pharma/talk2knowledgegraphs/configs/tools/multimodal_subgraph_extraction/__init__.py +0 -0
  113. aiagents4pharma/talk2knowledgegraphs/configs/tools/multimodal_subgraph_extraction/default.yaml +33 -0
  114. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/__init__.py +3 -0
  115. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/default.yaml +43 -0
  116. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/__init__.py +3 -0
  117. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/default.yaml +9 -0
  118. aiagents4pharma/talk2knowledgegraphs/configs/utils/database/milvus/__init__.py +3 -0
  119. aiagents4pharma/talk2knowledgegraphs/configs/utils/database/milvus/default.yaml +61 -0
  120. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/ols_terms/default.yaml +3 -0
  121. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/reactome_pathways/default.yaml +3 -0
  122. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/uniprot_proteins/default.yaml +6 -0
  123. aiagents4pharma/talk2knowledgegraphs/configs/utils/pubchem_utils/default.yaml +5 -0
  124. aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py +5 -0
  125. aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py +607 -0
  126. aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py +25 -0
  127. aiagents4pharma/talk2knowledgegraphs/datasets/primekg.py +212 -0
  128. aiagents4pharma/talk2knowledgegraphs/datasets/starkqa_primekg.py +210 -0
  129. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/.env.example +23 -0
  130. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/docker-compose.yml +93 -0
  131. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/.env.example +23 -0
  132. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/docker-compose.yml +108 -0
  133. aiagents4pharma/talk2knowledgegraphs/entrypoint.sh +180 -0
  134. aiagents4pharma/talk2knowledgegraphs/install.md +165 -0
  135. aiagents4pharma/talk2knowledgegraphs/milvus_data_dump.py +886 -0
  136. aiagents4pharma/talk2knowledgegraphs/states/__init__.py +5 -0
  137. aiagents4pharma/talk2knowledgegraphs/states/state_talk2knowledgegraphs.py +40 -0
  138. aiagents4pharma/talk2knowledgegraphs/tests/__init__.py +0 -0
  139. aiagents4pharma/talk2knowledgegraphs/tests/test_agents_t2kg_agent.py +318 -0
  140. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_biobridge_primekg.py +248 -0
  141. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_dataset.py +33 -0
  142. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_primekg.py +86 -0
  143. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py +125 -0
  144. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_graphrag_reasoning.py +257 -0
  145. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_milvus_multimodal_subgraph_extraction.py +1444 -0
  146. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_multimodal_subgraph_extraction.py +159 -0
  147. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_extraction.py +152 -0
  148. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_summarization.py +201 -0
  149. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_database_milvus_connection_manager.py +812 -0
  150. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py +51 -0
  151. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py +49 -0
  152. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_nim_molmim.py +59 -0
  153. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_ollama.py +63 -0
  154. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_sentencetransformer.py +47 -0
  155. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +40 -0
  156. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +94 -0
  157. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ols.py +70 -0
  158. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_pubchem.py +45 -0
  159. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_reactome.py +44 -0
  160. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_uniprot.py +48 -0
  161. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_extractions_milvus_multimodal_pcst.py +759 -0
  162. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_kg_utils.py +78 -0
  163. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_pubchem_utils.py +123 -0
  164. aiagents4pharma/talk2knowledgegraphs/tools/__init__.py +11 -0
  165. aiagents4pharma/talk2knowledgegraphs/tools/graphrag_reasoning.py +138 -0
  166. aiagents4pharma/talk2knowledgegraphs/tools/load_arguments.py +22 -0
  167. aiagents4pharma/talk2knowledgegraphs/tools/milvus_multimodal_subgraph_extraction.py +965 -0
  168. aiagents4pharma/talk2knowledgegraphs/tools/multimodal_subgraph_extraction.py +374 -0
  169. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_extraction.py +291 -0
  170. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_summarization.py +123 -0
  171. aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +5 -0
  172. aiagents4pharma/talk2knowledgegraphs/utils/database/__init__.py +5 -0
  173. aiagents4pharma/talk2knowledgegraphs/utils/database/milvus_connection_manager.py +586 -0
  174. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py +5 -0
  175. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py +81 -0
  176. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py +111 -0
  177. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/nim_molmim.py +54 -0
  178. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/ollama.py +87 -0
  179. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py +73 -0
  180. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +12 -0
  181. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +37 -0
  182. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +129 -0
  183. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ols_terms.py +89 -0
  184. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/pubchem_strings.py +78 -0
  185. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/reactome_pathways.py +71 -0
  186. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/uniprot_proteins.py +98 -0
  187. aiagents4pharma/talk2knowledgegraphs/utils/extractions/__init__.py +5 -0
  188. aiagents4pharma/talk2knowledgegraphs/utils/extractions/milvus_multimodal_pcst.py +762 -0
  189. aiagents4pharma/talk2knowledgegraphs/utils/extractions/multimodal_pcst.py +298 -0
  190. aiagents4pharma/talk2knowledgegraphs/utils/extractions/pcst.py +229 -0
  191. aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py +67 -0
  192. aiagents4pharma/talk2knowledgegraphs/utils/pubchem_utils.py +104 -0
  193. aiagents4pharma/talk2scholars/.dockerignore +13 -0
  194. aiagents4pharma/talk2scholars/Dockerfile +104 -0
  195. aiagents4pharma/talk2scholars/README.md +1 -0
  196. aiagents4pharma/talk2scholars/__init__.py +7 -0
  197. aiagents4pharma/talk2scholars/agents/__init__.py +13 -0
  198. aiagents4pharma/talk2scholars/agents/main_agent.py +89 -0
  199. aiagents4pharma/talk2scholars/agents/paper_download_agent.py +96 -0
  200. aiagents4pharma/talk2scholars/agents/pdf_agent.py +101 -0
  201. aiagents4pharma/talk2scholars/agents/s2_agent.py +135 -0
  202. aiagents4pharma/talk2scholars/agents/zotero_agent.py +127 -0
  203. aiagents4pharma/talk2scholars/configs/__init__.py +7 -0
  204. aiagents4pharma/talk2scholars/configs/agents/__init__.py +7 -0
  205. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py +7 -0
  206. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/__init__.py +3 -0
  207. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +52 -0
  208. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/paper_download_agent/__init__.py +3 -0
  209. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/paper_download_agent/default.yaml +19 -0
  210. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/__init__.py +3 -0
  211. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +19 -0
  212. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/__init__.py +3 -0
  213. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +44 -0
  214. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/__init__.py +3 -0
  215. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +19 -0
  216. aiagents4pharma/talk2scholars/configs/app/__init__.py +7 -0
  217. aiagents4pharma/talk2scholars/configs/app/frontend/__init__.py +3 -0
  218. aiagents4pharma/talk2scholars/configs/app/frontend/default.yaml +72 -0
  219. aiagents4pharma/talk2scholars/configs/config.yaml +16 -0
  220. aiagents4pharma/talk2scholars/configs/tools/__init__.py +21 -0
  221. aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/__init__.py +3 -0
  222. aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/default.yaml +26 -0
  223. aiagents4pharma/talk2scholars/configs/tools/paper_download/__init__.py +3 -0
  224. aiagents4pharma/talk2scholars/configs/tools/paper_download/default.yaml +124 -0
  225. aiagents4pharma/talk2scholars/configs/tools/question_and_answer/__init__.py +3 -0
  226. aiagents4pharma/talk2scholars/configs/tools/question_and_answer/default.yaml +62 -0
  227. aiagents4pharma/talk2scholars/configs/tools/retrieve_semantic_scholar_paper_id/__init__.py +3 -0
  228. aiagents4pharma/talk2scholars/configs/tools/retrieve_semantic_scholar_paper_id/default.yaml +12 -0
  229. aiagents4pharma/talk2scholars/configs/tools/search/__init__.py +3 -0
  230. aiagents4pharma/talk2scholars/configs/tools/search/default.yaml +26 -0
  231. aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/__init__.py +3 -0
  232. aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/default.yaml +26 -0
  233. aiagents4pharma/talk2scholars/configs/tools/zotero_read/__init__.py +3 -0
  234. aiagents4pharma/talk2scholars/configs/tools/zotero_read/default.yaml +57 -0
  235. aiagents4pharma/talk2scholars/configs/tools/zotero_write/__inti__.py +3 -0
  236. aiagents4pharma/talk2scholars/configs/tools/zotero_write/default.yaml +55 -0
  237. aiagents4pharma/talk2scholars/docker-compose/cpu/.env.example +21 -0
  238. aiagents4pharma/talk2scholars/docker-compose/cpu/docker-compose.yml +90 -0
  239. aiagents4pharma/talk2scholars/docker-compose/gpu/.env.example +21 -0
  240. aiagents4pharma/talk2scholars/docker-compose/gpu/docker-compose.yml +105 -0
  241. aiagents4pharma/talk2scholars/install.md +122 -0
  242. aiagents4pharma/talk2scholars/state/__init__.py +7 -0
  243. aiagents4pharma/talk2scholars/state/state_talk2scholars.py +98 -0
  244. aiagents4pharma/talk2scholars/tests/__init__.py +3 -0
  245. aiagents4pharma/talk2scholars/tests/test_agents_main_agent.py +256 -0
  246. aiagents4pharma/talk2scholars/tests/test_agents_paper_agents_download_agent.py +139 -0
  247. aiagents4pharma/talk2scholars/tests/test_agents_pdf_agent.py +114 -0
  248. aiagents4pharma/talk2scholars/tests/test_agents_s2_agent.py +198 -0
  249. aiagents4pharma/talk2scholars/tests/test_agents_zotero_agent.py +160 -0
  250. aiagents4pharma/talk2scholars/tests/test_s2_tools_display_dataframe.py +91 -0
  251. aiagents4pharma/talk2scholars/tests/test_s2_tools_query_dataframe.py +191 -0
  252. aiagents4pharma/talk2scholars/tests/test_states_state.py +38 -0
  253. aiagents4pharma/talk2scholars/tests/test_tools_paper_downloader.py +507 -0
  254. aiagents4pharma/talk2scholars/tests/test_tools_question_and_answer_tool.py +105 -0
  255. aiagents4pharma/talk2scholars/tests/test_tools_s2_multi.py +307 -0
  256. aiagents4pharma/talk2scholars/tests/test_tools_s2_retrieve.py +67 -0
  257. aiagents4pharma/talk2scholars/tests/test_tools_s2_search.py +286 -0
  258. aiagents4pharma/talk2scholars/tests/test_tools_s2_single.py +298 -0
  259. aiagents4pharma/talk2scholars/tests/test_utils_arxiv_downloader.py +469 -0
  260. aiagents4pharma/talk2scholars/tests/test_utils_base_paper_downloader.py +598 -0
  261. aiagents4pharma/talk2scholars/tests/test_utils_biorxiv_downloader.py +669 -0
  262. aiagents4pharma/talk2scholars/tests/test_utils_medrxiv_downloader.py +500 -0
  263. aiagents4pharma/talk2scholars/tests/test_utils_nvidia_nim_reranker.py +117 -0
  264. aiagents4pharma/talk2scholars/tests/test_utils_pdf_answer_formatter.py +67 -0
  265. aiagents4pharma/talk2scholars/tests/test_utils_pdf_batch_processor.py +92 -0
  266. aiagents4pharma/talk2scholars/tests/test_utils_pdf_collection_manager.py +173 -0
  267. aiagents4pharma/talk2scholars/tests/test_utils_pdf_document_processor.py +68 -0
  268. aiagents4pharma/talk2scholars/tests/test_utils_pdf_generate_answer.py +72 -0
  269. aiagents4pharma/talk2scholars/tests/test_utils_pdf_gpu_detection.py +129 -0
  270. aiagents4pharma/talk2scholars/tests/test_utils_pdf_paper_loader.py +116 -0
  271. aiagents4pharma/talk2scholars/tests/test_utils_pdf_rag_pipeline.py +88 -0
  272. aiagents4pharma/talk2scholars/tests/test_utils_pdf_retrieve_chunks.py +190 -0
  273. aiagents4pharma/talk2scholars/tests/test_utils_pdf_singleton_manager.py +159 -0
  274. aiagents4pharma/talk2scholars/tests/test_utils_pdf_vector_normalization.py +121 -0
  275. aiagents4pharma/talk2scholars/tests/test_utils_pdf_vector_store.py +406 -0
  276. aiagents4pharma/talk2scholars/tests/test_utils_pubmed_downloader.py +1007 -0
  277. aiagents4pharma/talk2scholars/tests/test_utils_read_helper_utils.py +106 -0
  278. aiagents4pharma/talk2scholars/tests/test_utils_s2_utils_ext_ids.py +403 -0
  279. aiagents4pharma/talk2scholars/tests/test_utils_tool_helper_utils.py +85 -0
  280. aiagents4pharma/talk2scholars/tests/test_utils_zotero_human_in_the_loop.py +266 -0
  281. aiagents4pharma/talk2scholars/tests/test_utils_zotero_path.py +496 -0
  282. aiagents4pharma/talk2scholars/tests/test_utils_zotero_pdf_downloader_utils.py +46 -0
  283. aiagents4pharma/talk2scholars/tests/test_utils_zotero_read.py +743 -0
  284. aiagents4pharma/talk2scholars/tests/test_utils_zotero_write.py +151 -0
  285. aiagents4pharma/talk2scholars/tools/__init__.py +9 -0
  286. aiagents4pharma/talk2scholars/tools/paper_download/__init__.py +12 -0
  287. aiagents4pharma/talk2scholars/tools/paper_download/paper_downloader.py +442 -0
  288. aiagents4pharma/talk2scholars/tools/paper_download/utils/__init__.py +22 -0
  289. aiagents4pharma/talk2scholars/tools/paper_download/utils/arxiv_downloader.py +207 -0
  290. aiagents4pharma/talk2scholars/tools/paper_download/utils/base_paper_downloader.py +336 -0
  291. aiagents4pharma/talk2scholars/tools/paper_download/utils/biorxiv_downloader.py +313 -0
  292. aiagents4pharma/talk2scholars/tools/paper_download/utils/medrxiv_downloader.py +196 -0
  293. aiagents4pharma/talk2scholars/tools/paper_download/utils/pubmed_downloader.py +323 -0
  294. aiagents4pharma/talk2scholars/tools/pdf/__init__.py +7 -0
  295. aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +170 -0
  296. aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +37 -0
  297. aiagents4pharma/talk2scholars/tools/pdf/utils/answer_formatter.py +62 -0
  298. aiagents4pharma/talk2scholars/tools/pdf/utils/batch_processor.py +198 -0
  299. aiagents4pharma/talk2scholars/tools/pdf/utils/collection_manager.py +172 -0
  300. aiagents4pharma/talk2scholars/tools/pdf/utils/document_processor.py +76 -0
  301. aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +97 -0
  302. aiagents4pharma/talk2scholars/tools/pdf/utils/get_vectorstore.py +59 -0
  303. aiagents4pharma/talk2scholars/tools/pdf/utils/gpu_detection.py +150 -0
  304. aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +97 -0
  305. aiagents4pharma/talk2scholars/tools/pdf/utils/paper_loader.py +123 -0
  306. aiagents4pharma/talk2scholars/tools/pdf/utils/rag_pipeline.py +113 -0
  307. aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +197 -0
  308. aiagents4pharma/talk2scholars/tools/pdf/utils/singleton_manager.py +140 -0
  309. aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +86 -0
  310. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_normalization.py +150 -0
  311. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +327 -0
  312. aiagents4pharma/talk2scholars/tools/s2/__init__.py +21 -0
  313. aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +110 -0
  314. aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +111 -0
  315. aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +233 -0
  316. aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +128 -0
  317. aiagents4pharma/talk2scholars/tools/s2/search.py +101 -0
  318. aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +102 -0
  319. aiagents4pharma/talk2scholars/tools/s2/utils/__init__.py +5 -0
  320. aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +223 -0
  321. aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +205 -0
  322. aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +216 -0
  323. aiagents4pharma/talk2scholars/tools/zotero/__init__.py +7 -0
  324. aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py +7 -0
  325. aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +270 -0
  326. aiagents4pharma/talk2scholars/tools/zotero/utils/review_helper.py +74 -0
  327. aiagents4pharma/talk2scholars/tools/zotero/utils/write_helper.py +194 -0
  328. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py +180 -0
  329. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_pdf_downloader.py +133 -0
  330. aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +105 -0
  331. aiagents4pharma/talk2scholars/tools/zotero/zotero_review.py +162 -0
  332. aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py +91 -0
  333. aiagents4pharma-0.0.0.dist-info/METADATA +335 -0
  334. aiagents4pharma-0.0.0.dist-info/RECORD +336 -0
  335. aiagents4pharma-0.0.0.dist-info/WHEEL +4 -0
  336. aiagents4pharma-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,743 @@
1
+ """
2
+ Unit tests for Zotero search tool in zotero_read.py.
3
+ """
4
+
5
+ import unittest
6
+ from types import SimpleNamespace
7
+ from unittest.mock import MagicMock, patch
8
+
9
+ import requests
10
+ from langgraph.types import Command
11
+
12
+ from aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper import (
13
+ ZoteroSearchData,
14
+ )
15
+ from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_pdf_downloader import (
16
+ download_zotero_pdf,
17
+ )
18
+ from aiagents4pharma.talk2scholars.tools.zotero.zotero_read import zotero_read
19
+
20
+ # Dummy Hydra configuration to be used in tests
21
+ dummy_zotero_read_config = SimpleNamespace(
22
+ user_id="dummy_user",
23
+ library_type="user",
24
+ api_key="dummy_api_key",
25
+ zotero=SimpleNamespace(
26
+ max_limit=5,
27
+ filter_item_types=["journalArticle", "conferencePaper"],
28
+ ),
29
+ )
30
+ dummy_cfg = SimpleNamespace(tools=SimpleNamespace(zotero_read=dummy_zotero_read_config))
31
+
32
+
33
+ class TestZoteroSearchTool(unittest.TestCase):
34
+ """Tests for Zotero search tool."""
35
+
36
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
37
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
38
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
39
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
40
+ def test_valid_query(
41
+ self,
42
+ mock_hydra_init,
43
+ mock_hydra_compose,
44
+ mock_zotero_class,
45
+ mock_get_item_collections,
46
+ ):
47
+ """Test valid query returns correct Command output."""
48
+ # Setup Hydra mocks
49
+ mock_hydra_compose.return_value = dummy_cfg
50
+ mock_hydra_init.return_value.__enter__.return_value = None
51
+
52
+ # Create a fake Zotero client that returns two valid items
53
+ fake_zot = MagicMock()
54
+ fake_items = [
55
+ {
56
+ "data": {
57
+ "key": "paper1",
58
+ "title": "Paper 1",
59
+ "abstractNote": "Abstract 1",
60
+ "date": "2021",
61
+ "url": "http://example.com",
62
+ "itemType": "journalArticle",
63
+ }
64
+ },
65
+ {
66
+ "data": {
67
+ "key": "paper2",
68
+ "title": "Paper 2",
69
+ "abstractNote": "Abstract 2",
70
+ "date": "2022",
71
+ "url": "http://example2.com",
72
+ "itemType": "conferencePaper",
73
+ }
74
+ },
75
+ ]
76
+ fake_zot.items.return_value = fake_items
77
+ mock_zotero_class.return_value = fake_zot
78
+
79
+ # Fake mapping for collection paths
80
+ mock_get_item_collections.return_value = {
81
+ "paper1": ["/Test Collection"],
82
+ "paper2": ["/Test Collection"],
83
+ }
84
+
85
+ # Call the tool with a valid query using .run() with a dictionary input
86
+ tool_call_id = "test_id_1"
87
+ tool_input = {
88
+ "query": "test",
89
+ "only_articles": True,
90
+ "tool_call_id": tool_call_id,
91
+ "limit": 2,
92
+ }
93
+ result = zotero_read.run(tool_input)
94
+
95
+ # Verify the Command update structure and contents
96
+ self.assertIsInstance(result, Command)
97
+ update = result.update
98
+ self.assertIn("article_data", update)
99
+ self.assertIn("last_displayed_papers", update)
100
+ self.assertIn("messages", update)
101
+
102
+ filtered_papers = update["article_data"]
103
+ self.assertIn("paper1", filtered_papers)
104
+ self.assertIn("paper2", filtered_papers)
105
+ message_content = update["messages"][0].content
106
+ self.assertIn("Query: test", message_content)
107
+ self.assertIn("Number of papers found: 2", message_content)
108
+
109
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
110
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
111
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
112
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
113
+ def test_empty_query_fetch_all_items(
114
+ self,
115
+ mock_hydra_init,
116
+ mock_hydra_compose,
117
+ mock_zotero_class,
118
+ mock_get_item_collections,
119
+ ):
120
+ """Test empty query fetches all items."""
121
+ mock_hydra_compose.return_value = dummy_cfg
122
+ mock_hydra_init.return_value.__enter__.return_value = None
123
+
124
+ fake_zot = MagicMock()
125
+ fake_items = [
126
+ {
127
+ "data": {
128
+ "key": "paper1",
129
+ "title": "Paper 1",
130
+ "abstractNote": "Abstract 1",
131
+ "date": "2021",
132
+ "url": "http://example.com",
133
+ "itemType": "journalArticle",
134
+ }
135
+ },
136
+ ]
137
+ fake_zot.items.return_value = fake_items
138
+ mock_zotero_class.return_value = fake_zot
139
+ mock_get_item_collections.return_value = {"paper1": ["/Test Collection"]}
140
+
141
+ tool_call_id = "test_id_2"
142
+ tool_input = {
143
+ "query": " ",
144
+ "only_articles": True,
145
+ "tool_call_id": tool_call_id,
146
+ "limit": 2,
147
+ }
148
+ result = zotero_read.run(tool_input)
149
+
150
+ update = result.update
151
+ filtered_papers = update["article_data"]
152
+ self.assertIn("paper1", filtered_papers)
153
+ fake_zot.items.assert_called_with(limit=dummy_cfg.tools.zotero_read.zotero.max_limit)
154
+
155
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
156
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
157
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
158
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
159
+ def test_no_items_returned(
160
+ self,
161
+ mock_hydra_init,
162
+ mock_hydra_compose,
163
+ mock_zotero_class,
164
+ mock_get_item_collections,
165
+ ):
166
+ """Test no items returned from Zotero."""
167
+ mock_hydra_compose.return_value = dummy_cfg
168
+ mock_hydra_init.return_value.__enter__.return_value = None
169
+
170
+ fake_zot = MagicMock()
171
+ fake_zot.items.return_value = []
172
+ mock_zotero_class.return_value = fake_zot
173
+ mock_get_item_collections.return_value = {}
174
+
175
+ tool_call_id = "test_id_3"
176
+ tool_input = {
177
+ "query": "nonexistent",
178
+ "only_articles": True,
179
+ "tool_call_id": tool_call_id,
180
+ "limit": 2,
181
+ }
182
+ with self.assertRaises(RuntimeError) as context:
183
+ zotero_read.run(tool_input)
184
+ self.assertIn("No items returned from Zotero", str(context.exception))
185
+
186
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
187
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
188
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
189
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
190
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.download_pdfs_in_parallel")
191
+ def test_filtering_no_matching_papers(self, *mocks):
192
+ """Testing filtering when no paper matching"""
193
+ (
194
+ mock_batch_download,
195
+ mock_hydra_init,
196
+ mock_hydra_compose,
197
+ mock_zotero_class,
198
+ mock_get_item_collections,
199
+ ) = mocks
200
+ mock_hydra_compose.return_value = dummy_cfg
201
+ mock_hydra_init.return_value.__enter__.return_value = None
202
+
203
+ fake_zot = MagicMock()
204
+ fake_items = [
205
+ {
206
+ "data": {
207
+ "key": "paper1",
208
+ "title": "Paper 1",
209
+ "abstractNote": "Abstract 1",
210
+ "date": "2021",
211
+ "url": "http://example.com",
212
+ "itemType": "attachment",
213
+ "contentType": "application/pdf", # orphaned
214
+ "filename": "paper1.pdf",
215
+ }
216
+ },
217
+ {
218
+ "data": {
219
+ "key": "paper2",
220
+ "title": "Paper 2",
221
+ "abstractNote": "Abstract 2",
222
+ "date": "2022",
223
+ "url": "http://example2.com",
224
+ "itemType": "note",
225
+ }
226
+ },
227
+ ]
228
+ fake_zot.items.return_value = fake_items
229
+ mock_zotero_class.return_value = fake_zot
230
+ mock_get_item_collections.return_value = {
231
+ "paper1": ["/Test Collection"],
232
+ "paper2": ["/Test Collection"],
233
+ }
234
+
235
+ mock_batch_download.return_value = {
236
+ "paper1": ("/tmp/fake_path.pdf", "paper1.pdf", "paper1")
237
+ }
238
+
239
+ tool_input = {
240
+ "query": "test",
241
+ "only_articles": False,
242
+ "tool_call_id": "test_id_4",
243
+ "limit": 2,
244
+ "download_pdfs": True,
245
+ }
246
+
247
+ result = zotero_read.run(tool_input)
248
+ filtered_papers = result.update["article_data"]
249
+
250
+ self.assertIn("paper1", filtered_papers)
251
+ self.assertIn("paper2", filtered_papers)
252
+ self.assertEqual(filtered_papers["paper1"]["filename"], "paper1.pdf")
253
+ self.assertEqual(filtered_papers["paper1"]["pdf_url"], "/tmp/fake_path.pdf")
254
+ self.assertEqual(filtered_papers["paper1"]["source"], "zotero")
255
+
256
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
257
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
258
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
259
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
260
+ def test_items_api_exception(
261
+ self,
262
+ mock_hydra_init,
263
+ mock_hydra_compose,
264
+ mock_zotero_class,
265
+ mock_get_item_collections,
266
+ ):
267
+ """Test items API exception is properly raised."""
268
+ mock_hydra_compose.return_value = dummy_cfg
269
+ mock_hydra_init.return_value.__enter__.return_value = None
270
+ mock_get_item_collections.return_value = {}
271
+
272
+ fake_zot = MagicMock()
273
+ fake_zot.items.side_effect = Exception("API error")
274
+ mock_zotero_class.return_value = fake_zot
275
+
276
+ tool_call_id = "test_id_5"
277
+ tool_input = {
278
+ "query": "test",
279
+ "only_articles": True,
280
+ "tool_call_id": tool_call_id,
281
+ "limit": 2,
282
+ }
283
+ with self.assertRaises(RuntimeError) as context:
284
+ zotero_read.run(tool_input)
285
+ self.assertIn("Failed to fetch items from Zotero", str(context.exception))
286
+
287
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
288
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
289
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
290
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
291
+ def test_missing_key_in_item(
292
+ self,
293
+ mock_hydra_init,
294
+ mock_hydra_compose,
295
+ mock_zotero_class,
296
+ mock_get_item_collections,
297
+ ):
298
+ """
299
+ Test that an item with a valid 'data' structure but missing the 'key' field is skipped.
300
+ """
301
+ mock_hydra_compose.return_value = dummy_cfg
302
+ mock_hydra_init.return_value.__enter__.return_value = None
303
+
304
+ fake_zot = MagicMock()
305
+ fake_items = [
306
+ {
307
+ "data": {
308
+ "title": "No Key Paper",
309
+ "abstractNote": "Abstract",
310
+ "date": "2021",
311
+ "url": "http://example.com",
312
+ "itemType": "journalArticle",
313
+ }
314
+ }, # Missing 'key' field
315
+ {
316
+ "data": {
317
+ "key": "paper_valid",
318
+ "title": "Valid Paper",
319
+ "abstractNote": "Valid Abstract",
320
+ "date": "2021",
321
+ "url": "http://example.com",
322
+ "itemType": "journalArticle",
323
+ }
324
+ },
325
+ ]
326
+ fake_zot.items.return_value = fake_items
327
+ mock_zotero_class.return_value = fake_zot
328
+ mock_get_item_collections.return_value = {"paper_valid": ["/Test Collection"]}
329
+
330
+ tool_call_id = "test_id_6"
331
+ tool_input = {
332
+ "query": "dummy",
333
+ "only_articles": True,
334
+ "tool_call_id": tool_call_id,
335
+ "limit": 2,
336
+ }
337
+ result = zotero_read.run(tool_input)
338
+
339
+ update = result.update
340
+ filtered_papers = update["article_data"]
341
+ self.assertIn("paper_valid", filtered_papers)
342
+ self.assertEqual(len(filtered_papers), 1)
343
+
344
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
345
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
346
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
347
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
348
+ def test_item_not_dict(
349
+ self,
350
+ mock_hydra_init,
351
+ mock_hydra_compose,
352
+ mock_zotero_class,
353
+ mock_get_item_collections,
354
+ ):
355
+ """
356
+ Test that if the items list contains an element that is not a dict, it is skipped.
357
+ """
358
+ mock_hydra_compose.return_value = dummy_cfg
359
+ mock_hydra_init.return_value.__enter__.return_value = None
360
+
361
+ fake_zot = MagicMock()
362
+ # Supply one item that is not a dict.
363
+ fake_items = ["this is not a dict"]
364
+ fake_zot.items.return_value = fake_items
365
+ mock_zotero_class.return_value = fake_zot
366
+ # Mapping doesn't matter here.
367
+ mock_get_item_collections.return_value = {}
368
+
369
+ tool_call_id = "test_id_7"
370
+ tool_input = {
371
+ "query": "dummy",
372
+ "only_articles": True,
373
+ "tool_call_id": tool_call_id,
374
+ "limit": 2,
375
+ }
376
+ with self.assertRaises(RuntimeError) as context:
377
+ zotero_read.run(tool_input)
378
+ self.assertIn("No matching papers returned from Zotero", str(context.exception))
379
+
380
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
381
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
382
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
383
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
384
+ def test_data_not_dict(
385
+ self,
386
+ mock_hydra_init,
387
+ mock_hydra_compose,
388
+ mock_zotero_class,
389
+ mock_get_item_collections,
390
+ ):
391
+ """Test for no dict"""
392
+ mock_hydra_compose.return_value = dummy_cfg
393
+ mock_hydra_init.return_value.__enter__.return_value = None
394
+
395
+ fake_zot = MagicMock()
396
+ # Make the item itself non-dict (not just `data`)
397
+ fake_items = ["this is not a dict"]
398
+ fake_zot.items.return_value = fake_items
399
+ mock_zotero_class.return_value = fake_zot
400
+ mock_get_item_collections.return_value = {}
401
+
402
+ tool_input = {
403
+ "query": "dummy",
404
+ "only_articles": True,
405
+ "tool_call_id": "test_id_8",
406
+ "limit": 2,
407
+ }
408
+
409
+ with self.assertRaises(RuntimeError) as context:
410
+ zotero_read.run(tool_input)
411
+ self.assertIn("No matching papers returned from Zotero", str(context.exception))
412
+
413
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
414
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
415
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
416
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
417
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.requests.Session.get")
418
+ def test_pdf_attachment_success(self, *mocks):
419
+ """Test for pdf attachment success"""
420
+ (
421
+ mock_session_get,
422
+ mock_hydra_init,
423
+ mock_hydra_compose,
424
+ mock_zotero_class,
425
+ mock_get_item_collections,
426
+ ) = mocks
427
+ mock_hydra_compose.return_value = dummy_cfg
428
+ mock_hydra_init.return_value.__enter__.return_value = None
429
+
430
+ fake_zot = MagicMock()
431
+ fake_zot.items.return_value = [
432
+ {
433
+ "data": {
434
+ "key": "paper1",
435
+ "title": "Paper 1",
436
+ "abstractNote": "Abstract 1",
437
+ "date": "2021",
438
+ "url": "http://example.com",
439
+ "itemType": "journalArticle",
440
+ "creators": [
441
+ {
442
+ "firstName": "John",
443
+ "lastName": "Doe",
444
+ "creatorType": "author",
445
+ }
446
+ ],
447
+ }
448
+ }
449
+ ]
450
+
451
+ fake_pdf_child = {
452
+ "data": {
453
+ "key": "attachment1",
454
+ "filename": "file1.pdf",
455
+ "contentType": "application/pdf",
456
+ }
457
+ }
458
+ fake_zot.children.return_value = [fake_pdf_child]
459
+ mock_zotero_class.return_value = fake_zot
460
+ mock_get_item_collections.return_value = {"paper1": ["/Test Collection"]}
461
+
462
+ # Mock successful PDF download via session
463
+ mock_response = MagicMock()
464
+ mock_response.status_code = 200
465
+ mock_response.iter_content = lambda chunk_size: [b"fake pdf content"]
466
+ mock_response.headers = {"Content-Disposition": 'attachment; filename="file1.pdf"'}
467
+ mock_response.raise_for_status = lambda: None
468
+ mock_session_get.return_value = mock_response
469
+
470
+ tool_input = {
471
+ "query": "pdf test",
472
+ "only_articles": True,
473
+ "tool_call_id": "test_pdf_success",
474
+ "limit": 1,
475
+ "download_pdfs": True,
476
+ }
477
+
478
+ result = zotero_read.run(tool_input)
479
+ paper = result.update["article_data"]["paper1"]
480
+
481
+ self.assertIn("pdf_url", paper)
482
+ self.assertTrue(paper["pdf_url"].endswith(".pdf"))
483
+ self.assertEqual(paper["filename"], "file1.pdf")
484
+ self.assertEqual(paper["attachment_key"], "attachment1")
485
+
486
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
487
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
488
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
489
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
490
+ def test_pdf_attachment_children_exception(
491
+ self,
492
+ mock_hydra_init,
493
+ mock_hydra_compose,
494
+ mock_zotero_class,
495
+ mock_get_item_collections,
496
+ ):
497
+ """Test that when children() raises an exception, PDF info is not added."""
498
+ mock_hydra_compose.return_value = dummy_cfg
499
+ mock_hydra_init.return_value.__enter__.return_value = None
500
+
501
+ fake_zot = MagicMock()
502
+ fake_items = [
503
+ {
504
+ "data": {
505
+ "key": "paper1",
506
+ "title": "Paper 1",
507
+ "abstractNote": "Abstract 1",
508
+ "date": "2021",
509
+ "url": "http://example.com",
510
+ "itemType": "journalArticle",
511
+ "creators": [
512
+ {
513
+ "firstName": "John",
514
+ "lastName": "Doe",
515
+ "creatorType": "author",
516
+ }
517
+ ],
518
+ }
519
+ },
520
+ ]
521
+ fake_zot.items.return_value = fake_items
522
+
523
+ # Simulate children() raising an exception
524
+ fake_zot.children.side_effect = Exception("Child fetch error")
525
+ mock_zotero_class.return_value = fake_zot
526
+ mock_get_item_collections.return_value = {"paper1": ["/Test Collection"]}
527
+
528
+ tool_call_id = "test_pdf_children_exception"
529
+ tool_input = {
530
+ "query": "pdf test exception",
531
+ "only_articles": True,
532
+ "tool_call_id": tool_call_id,
533
+ "limit": 1,
534
+ }
535
+ result = zotero_read.run(tool_input)
536
+ filtered_papers = result.update["article_data"]
537
+
538
+ # Ensure no PDF-related keys are added
539
+ self.assertIn("paper1", filtered_papers)
540
+ self.assertNotIn("pdf_url", filtered_papers["paper1"])
541
+ self.assertNotIn("filename", filtered_papers["paper1"])
542
+ self.assertNotIn("attachment_key", filtered_papers["paper1"])
543
+
544
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
545
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
546
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
547
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
548
+ def test_pdf_attachment_missing_key(
549
+ self,
550
+ mock_hydra_init,
551
+ mock_hydra_compose,
552
+ mock_zotero_class,
553
+ mock_get_item_collections,
554
+ ):
555
+ """Test for pdf attachment missing"""
556
+ mock_hydra_compose.return_value = dummy_cfg
557
+ mock_hydra_init.return_value.__enter__.return_value = None
558
+
559
+ fake_zot = MagicMock()
560
+ fake_zot.items.return_value = [
561
+ {
562
+ "data": {
563
+ "key": "paper1",
564
+ "title": "Paper 1",
565
+ "abstractNote": "Abstract 1",
566
+ "date": "2021",
567
+ "url": "http://example.com",
568
+ "itemType": "journalArticle",
569
+ "creators": [
570
+ {
571
+ "firstName": "Alice",
572
+ "lastName": "Smith",
573
+ "creatorType": "author",
574
+ }
575
+ ],
576
+ }
577
+ },
578
+ ]
579
+
580
+ fake_pdf_child = {
581
+ "data": {
582
+ "filename": "no_key.pdf",
583
+ "contentType": "application/pdf",
584
+ }
585
+ }
586
+ fake_zot.children.return_value = [fake_pdf_child]
587
+ mock_zotero_class.return_value = fake_zot
588
+ mock_get_item_collections.return_value = {"paper1": ["/Test Collection"]}
589
+
590
+ tool_input = {
591
+ "query": "missing key test",
592
+ "only_articles": True,
593
+ "tool_call_id": "test_pdf_missing_key",
594
+ "limit": 1,
595
+ }
596
+
597
+ result = zotero_read.run(tool_input)
598
+ paper = result.update["article_data"]["paper1"]
599
+
600
+ self.assertNotIn("pdf_url", paper)
601
+ self.assertNotIn("attachment_key", paper)
602
+
603
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
604
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
605
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
606
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
607
+ def test_pdf_attachment_outer_exception(
608
+ self,
609
+ mock_hydra_init,
610
+ mock_hydra_compose,
611
+ mock_zotero_class,
612
+ mock_get_item_collections,
613
+ ):
614
+ """Test that if children() returns a non-iterable (causing an exception),
615
+ PDF info is not added."""
616
+ mock_hydra_compose.return_value = dummy_cfg
617
+ mock_hydra_init.return_value.__enter__.return_value = None
618
+
619
+ fake_zot = MagicMock()
620
+ fake_items = [
621
+ {
622
+ "data": {
623
+ "key": "paper1",
624
+ "title": "Paper 1",
625
+ "abstractNote": "Abstract 1",
626
+ "date": "2021",
627
+ "url": "http://example.com",
628
+ "itemType": "journalArticle",
629
+ "creators": [
630
+ {
631
+ "firstName": "Bob",
632
+ "lastName": "Jones",
633
+ "creatorType": "author",
634
+ }
635
+ ],
636
+ }
637
+ },
638
+ ]
639
+ fake_zot.items.return_value = fake_items
640
+
641
+ # Simulate children() returning None to trigger an exception in list comprehension.
642
+ fake_zot.children.return_value = None
643
+
644
+ mock_zotero_class.return_value = fake_zot
645
+ mock_get_item_collections.return_value = {"paper1": ["/Test Collection"]}
646
+
647
+ tool_call_id = "test_pdf_outer_exception"
648
+ tool_input = {
649
+ "query": "outer exception test",
650
+ "only_articles": True,
651
+ "tool_call_id": tool_call_id,
652
+ "limit": 1,
653
+ }
654
+ result = zotero_read.run(tool_input)
655
+ filtered_papers = result.update["article_data"]
656
+
657
+ # Ensure no PDF-related keys are added if an exception occurs
658
+ self.assertIn("paper1", filtered_papers)
659
+ self.assertNotIn("pdf_url", filtered_papers["paper1"])
660
+ self.assertNotIn("filename", filtered_papers["paper1"])
661
+ self.assertNotIn("attachment_key", filtered_papers["paper1"])
662
+
663
+ @patch(
664
+ "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_pdf_downloader."
665
+ "requests.Session.get"
666
+ )
667
+ def test_download_zotero_pdf_exception(self, mock_session_get):
668
+ """Test that download_zotero_pdf returns None and logs error on request exception."""
669
+ # Simulate a session.get exception during PDF download
670
+ mock_session_get.side_effect = requests.exceptions.RequestException(
671
+ "Simulated download failure"
672
+ )
673
+ # Create a session for testing
674
+ session = requests.Session()
675
+ # Call the module-level download function
676
+ result = download_zotero_pdf(
677
+ session,
678
+ dummy_cfg.tools.zotero_read.user_id,
679
+ dummy_cfg.tools.zotero_read.api_key,
680
+ "FAKE_ATTACHMENT_KEY",
681
+ )
682
+ # Should return None on failure
683
+ self.assertIsNone(result)
684
+
685
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path.get_item_collections")
686
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.zotero.Zotero")
687
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.compose")
688
+ @patch("aiagents4pharma.talk2scholars.tools.zotero.utils.read_helper.hydra.initialize")
689
+ def test_download_pdf_exception_logging(
690
+ self,
691
+ mock_hydra_init,
692
+ mock_hydra_compose,
693
+ mock_zotero_class,
694
+ mock_get_item_collections,
695
+ ):
696
+ """Test that a failed download logs the error and does not break the pipeline."""
697
+ mock_hydra_compose.return_value = dummy_cfg
698
+ mock_hydra_init.return_value.__enter__.return_value = None
699
+
700
+ fake_zot = MagicMock()
701
+ fake_zot.items.return_value = [
702
+ {
703
+ "data": {
704
+ "key": "paper1",
705
+ "title": "Fake Title",
706
+ "itemType": "journalArticle",
707
+ }
708
+ }
709
+ ]
710
+ # Simulate an attachment
711
+ fake_zot.children.return_value = [
712
+ {
713
+ "data": {
714
+ "key": "attachment1",
715
+ "filename": "file1.pdf",
716
+ "contentType": "application/pdf",
717
+ }
718
+ }
719
+ ]
720
+
721
+ mock_zotero_class.return_value = fake_zot
722
+ mock_get_item_collections.return_value = {"paper1": ["/Fake Collection"]}
723
+
724
+ # Patch the module-level download_zotero_pdf to raise an exception
725
+ with patch(
726
+ "aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_pdf_downloader."
727
+ "download_zotero_pdf"
728
+ ) as mock_download_pdf:
729
+ mock_download_pdf.side_effect = requests.exceptions.RequestException(
730
+ "Simulated download error"
731
+ )
732
+
733
+ search = ZoteroSearchData(
734
+ query="failure test",
735
+ only_articles=True,
736
+ limit=1,
737
+ tool_call_id="fail_test",
738
+ )
739
+ search.process_search()
740
+
741
+ article_data = search.get_search_results()["article_data"]
742
+ assert "paper1" in article_data
743
+ assert "pdf_url" not in article_data["paper1"] # download failed, no URL