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,98 @@
1
+ """
2
+ State management for the Talk2Scholars agent.
3
+
4
+ This module defines the state class `Talk2Scholars`, which maintains the conversation
5
+ context, retrieved papers, and other relevant metadata. The state ensures consistency
6
+ across agent interactions.
7
+ """
8
+
9
+ import logging
10
+ from collections.abc import Mapping
11
+ from typing import Annotated, Any
12
+
13
+ from langchain_core.embeddings import Embeddings
14
+ from langchain_core.language_models import BaseChatModel
15
+ from langgraph.prebuilt.chat_agent_executor import AgentState
16
+
17
+ # Configure logging
18
+ logging.basicConfig(level=logging.INFO)
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def merge_dict(existing: dict[str, Any], new: dict[str, Any]) -> dict[str, Any]:
23
+ """
24
+ Merges the existing dictionary with a new dictionary.
25
+
26
+ This function logs the state merge and ensures that the new values
27
+ are appended to the existing state without overwriting other entries.
28
+ Args:
29
+ existing (Dict[str, Any]): The current dictionary state.
30
+ new (Dict[str, Any]): The new dictionary state to merge.
31
+ Returns:
32
+ Dict[str, Any]: The merged dictionary state.
33
+ """
34
+ merged = dict(existing) if existing else {}
35
+ merged.update(new or {})
36
+ return merged
37
+
38
+
39
+ def replace_dict(existing: dict[str, Any], new: Any) -> Any:
40
+ """
41
+ Replaces the existing dictionary with a new dictionary.
42
+
43
+ This function logs the state update and ensures that the old state is replaced
44
+ with the new one.
45
+
46
+ Args:
47
+ existing (Dict[str, Any]): The current dictionary state.
48
+ new (Dict[str, Any]): The new dictionary state to replace the existing one.
49
+
50
+ Returns:
51
+ Dict[str, Any]: The updated dictionary state.
52
+
53
+ Example:
54
+ >>> old_state = {"papers": {"id1": "Paper 1"}}
55
+ >>> new_state = {"papers": {"id2": "Paper 2"}}
56
+ >>> updated_state = replace_dict(old_state, new_state)
57
+ >>> print(updated_state)
58
+ {"papers": {"id2": "Paper 2"}}
59
+ """
60
+ # If new is not a mapping, just replace existing value outright
61
+ if not isinstance(new, Mapping):
62
+ return new
63
+ # In-place replace: clear existing mapping and update with new entries
64
+ existing.clear()
65
+ existing.update(new)
66
+ return existing
67
+
68
+
69
+ class Talk2Scholars(AgentState):
70
+ """
71
+ Represents the state of the Talk2Scholars agent.
72
+ This class extends `AgentState` to maintain conversation history, retrieved papers,
73
+ and interactions with the language model.
74
+ Attributes:
75
+ last_displayed_papers (Dict[str, Any]): Stores the most recently displayed papers.
76
+ papers (Dict[str, Any]): Stores the research papers retrieved from the agent's queries.
77
+ multi_papers (Dict[str, Any]): Stores multiple recommended papers from various sources.
78
+ article_data (Dict[str, Any]): Stores the papers retrieved from Zotero and the pdf
79
+ download agent with their metadata.
80
+ zotero_write_approval_status (Dict[str, Any]): Stores the approval status and collection
81
+ path for Zotero save operations.
82
+ llm_model (BaseChatModel): The language model instance used for generating responses.
83
+ text_embedding_model (Embeddings): The text embedding model used for
84
+ similarity calculations.
85
+ """
86
+
87
+ # Agent state fields
88
+ # Key controlling UI display: always replace to reference latest output
89
+ # Stores the most recently displayed papers metadata
90
+ last_displayed_papers: Annotated[dict[str, Any], replace_dict]
91
+ # Accumulative keys: merge new entries into existing state
92
+ papers: Annotated[dict[str, Any], merge_dict]
93
+ multi_papers: Annotated[dict[str, Any], merge_dict]
94
+ article_data: Annotated[dict[str, Any], merge_dict]
95
+ # Approval status: always replace to reflect latest operation
96
+ zotero_write_approval_status: Annotated[dict[str, Any], replace_dict]
97
+ llm_model: BaseChatModel
98
+ text_embedding_model: Embeddings
@@ -0,0 +1,3 @@
1
+ """
2
+ This module contains the test cases.
3
+ """
@@ -0,0 +1,256 @@
1
+ """
2
+ Unit tests for main agent functionality.
3
+ Tests the supervisor agent's routing logic and state management.
4
+ """
5
+
6
+ from types import SimpleNamespace
7
+
8
+ import hydra
9
+ import pytest
10
+ from langchain_core.language_models.chat_models import BaseChatModel
11
+ from langchain_core.messages import AIMessage, HumanMessage
12
+ from langchain_core.outputs import ChatGeneration, ChatResult
13
+ from langchain_openai import ChatOpenAI
14
+ from pydantic import Field
15
+
16
+ from aiagents4pharma.talk2scholars.agents.main_agent import get_app
17
+
18
+ # --- Dummy LLM Implementation ---
19
+
20
+
21
+ class DummyLLM(BaseChatModel):
22
+ """A dummy language model implementation for testing purposes."""
23
+
24
+ model_name: str = Field(...)
25
+
26
+ def _generate(self, messages, stop=None, run_manager=None, **kwargs):
27
+ """generate a dummy response based on the input messages."""
28
+ DummyLLM.called_prompt = messages[0].content
29
+ message = AIMessage(content="dummy output")
30
+ generation = ChatGeneration(message=message)
31
+ return ChatResult(generations=[generation])
32
+
33
+ @property
34
+ def _llm_type(self):
35
+ """Return the type of the language model."""
36
+ return "dummy"
37
+
38
+ # NEW: public shim to avoid protected access in tests
39
+ def public_llm_type(self) -> str:
40
+ """public method to access the LLM type."""
41
+ return self._llm_type
42
+
43
+
44
+ # --- Dummy Workflow and Sub-agent Functions ---
45
+
46
+
47
+ class DummyWorkflow:
48
+ """A dummy workflow class that records arguments for verification."""
49
+
50
+ def __init__(self, supervisor_args=None):
51
+ """Initialize the workflow with the given supervisor arguments."""
52
+ self.supervisor_args = supervisor_args or {}
53
+ self.checkpointer = None
54
+ self.name = None
55
+
56
+ def compile(self, checkpointer, name):
57
+ """Compile the workflow with the given checkpointer and name."""
58
+ self.checkpointer = checkpointer
59
+ self.name = name
60
+ return self
61
+
62
+ def get_supervisor_args(self):
63
+ """Return the supervisor arguments stored in this workflow."""
64
+ return self.supervisor_args
65
+
66
+
67
+ def dummy_s2_agent(uniq_id, llm_model):
68
+ """Return a DummyWorkflow for the S2 agent."""
69
+ dummy_s2_agent.called_uniq_id = uniq_id
70
+ dummy_s2_agent.called_llm_model = llm_model
71
+ return DummyWorkflow(supervisor_args={"agent": "s2", "uniq_id": uniq_id})
72
+
73
+
74
+ def dummy_zotero_agent(uniq_id, llm_model):
75
+ """Return a DummyWorkflow for the Zotero agent."""
76
+ dummy_zotero_agent.called_uniq_id = uniq_id
77
+ dummy_zotero_agent.called_llm_model = llm_model
78
+ return DummyWorkflow(supervisor_args={"agent": "zotero", "uniq_id": uniq_id})
79
+
80
+
81
+ def dummy_question_and_answer_agent(uniq_id, llm_model):
82
+ """Return a DummyWorkflow for the PDF agent."""
83
+ dummy_question_and_answer_agent.called_uniq_id = uniq_id
84
+ dummy_question_and_answer_agent.called_llm_model = llm_model
85
+ return DummyWorkflow(supervisor_args={"agent": "pdf", "uniq_id": uniq_id})
86
+
87
+
88
+ def dummy_create_supervisor(apps, model, state_schema, **kwargs):
89
+ """Return a DummyWorkflow for the supervisor."""
90
+ dummy_create_supervisor.called_kwargs = kwargs
91
+ return DummyWorkflow(
92
+ supervisor_args={
93
+ "apps": apps,
94
+ "model": model,
95
+ "state_schema": state_schema,
96
+ **kwargs,
97
+ }
98
+ )
99
+
100
+
101
+ # --- Dummy Hydra Configuration Setup ---
102
+
103
+
104
+ class DummyHydraContext:
105
+ """A dummy context manager for mocking Hydra's initialize and compose functions."""
106
+
107
+ def __enter__(self):
108
+ """Return None when entering the context."""
109
+ return None
110
+
111
+ def __exit__(self, exc_type, exc_val, traceback):
112
+ """Exit function that does nothing."""
113
+ return None
114
+
115
+
116
+ def dict_to_namespace(d):
117
+ """Convert a dictionary to a SimpleNamespace object."""
118
+ return SimpleNamespace(
119
+ **{key: dict_to_namespace(val) if isinstance(val, dict) else val for key, val in d.items()}
120
+ )
121
+
122
+
123
+ dummy_config = {
124
+ "agents": {"talk2scholars": {"main_agent": {"system_prompt": "Dummy system prompt"}}}
125
+ }
126
+
127
+
128
+ class DummyHydraCompose:
129
+ """A dummy class that returns a namespace from a dummy config dictionary."""
130
+
131
+ def __init__(self, config):
132
+ """Constructor that stores the dummy config."""
133
+ self.config = config
134
+
135
+ def __getattr__(self, item):
136
+ """Return a namespace from the dummy config."""
137
+ return dict_to_namespace(self.config.get(item, {}))
138
+
139
+ def get_config(self):
140
+ """Get the raw dummy configuration dictionary."""
141
+ return self.config
142
+
143
+
144
+ # --- Pytest Fixtures to Patch Dependencies ---
145
+
146
+
147
+ @pytest.fixture(autouse=True)
148
+ def patch_hydra(monkeypatch):
149
+ """Patch the hydra.initialize and hydra.compose functions to return dummy objects."""
150
+ monkeypatch.setattr(hydra, "initialize", lambda version_base, config_path: DummyHydraContext())
151
+ monkeypatch.setattr(
152
+ hydra, "compose", lambda config_name, overrides: DummyHydraCompose(dummy_config)
153
+ )
154
+
155
+
156
+ def dummy_paper_download_agent(uniq_id, llm_model):
157
+ """Return a DummyWorkflow for the paper download agent."""
158
+ dummy_paper_download_agent.called_uniq_id = uniq_id
159
+ dummy_paper_download_agent.called_llm_model = llm_model
160
+ return DummyWorkflow(supervisor_args={"agent": "paper_download", "uniq_id": uniq_id})
161
+
162
+
163
+ @pytest.fixture(autouse=True)
164
+ def patch_sub_agents_and_supervisor(monkeypatch):
165
+ """Patch the sub-agents and supervisor creation functions."""
166
+ monkeypatch.setattr(
167
+ "aiagents4pharma.talk2scholars.agents.main_agent.get_app_s2", dummy_s2_agent
168
+ )
169
+ monkeypatch.setattr(
170
+ "aiagents4pharma.talk2scholars.agents.main_agent.get_app_zotero",
171
+ dummy_zotero_agent,
172
+ )
173
+ monkeypatch.setattr(
174
+ "aiagents4pharma.talk2scholars.agents.main_agent.get_app_pdf",
175
+ dummy_question_and_answer_agent,
176
+ )
177
+ monkeypatch.setattr(
178
+ "aiagents4pharma.talk2scholars.agents.main_agent.get_app_paper_download",
179
+ dummy_paper_download_agent,
180
+ )
181
+ monkeypatch.setattr(
182
+ "aiagents4pharma.talk2scholars.agents.main_agent.create_supervisor",
183
+ dummy_create_supervisor,
184
+ )
185
+
186
+
187
+ # --- Test Cases ---
188
+
189
+
190
+ def test_dummy_llm_generate():
191
+ """Test the dummy LLM's generate function through public interface."""
192
+ dummy = DummyLLM(model_name="test-model")
193
+ # Test that the dummy LLM can be used (testing the class works)
194
+ assert dummy.model_name == "test-model"
195
+ # Test through public interface that internally calls _generate (covers lines 26-27)
196
+ # Use invoke which internally calls _generate
197
+ messages = [HumanMessage(content="test prompt")]
198
+ result = dummy.invoke(messages)
199
+ # Verify the internal state was set
200
+ assert hasattr(DummyLLM, "called_prompt")
201
+ assert result is not None
202
+ assert DummyLLM.called_prompt == "test prompt"
203
+
204
+
205
+ def test_dummy_llm_llm_type():
206
+ """Test the dummy LLM's type identification."""
207
+ dummy = DummyLLM(model_name="test-model")
208
+
209
+ # Use public shim instead of protected attribute access
210
+ llm_type = dummy.public_llm_type()
211
+ assert llm_type == "dummy"
212
+
213
+ # Also test the public string representation
214
+ assert "DummyLLM" in str(dummy.__class__.__name__)
215
+
216
+
217
+ def test_get_app_with_gpt4o_mini():
218
+ """
219
+ Test that get_app replaces a 'gpt-4o-mini' LLM with a new ChatOpenAI instance.
220
+ """
221
+ uniq_id = "test_thread"
222
+ dummy_llm = DummyLLM(model_name="gpt-4o-mini")
223
+ app = get_app(uniq_id, dummy_llm)
224
+
225
+ supervisor_args = getattr(app, "supervisor_args", {})
226
+ assert isinstance(supervisor_args.get("model"), ChatOpenAI)
227
+ assert supervisor_args.get("prompt") == "Dummy system prompt"
228
+ assert getattr(app, "name", "") == "Talk2Scholars_MainAgent"
229
+
230
+
231
+ def test_get_app_with_other_model():
232
+ """
233
+ Test that get_app does not replace the LLM if its model_name is not 'gpt-4o-mini'.
234
+ """
235
+ uniq_id = "test_thread_2"
236
+ dummy_llm = DummyLLM(model_name="other-model")
237
+ app = get_app(uniq_id, dummy_llm)
238
+
239
+ supervisor_args = getattr(app, "supervisor_args", {})
240
+ assert supervisor_args.get("model") is dummy_llm
241
+ assert supervisor_args.get("prompt") == "Dummy system prompt"
242
+ assert getattr(app, "name", "") == "Talk2Scholars_MainAgent"
243
+
244
+
245
+ def test_dummy_workflow_get_supervisor_args():
246
+ """Test that DummyWorkflow.get_supervisor_args returns the stored args."""
247
+ dummy_args = {"agent": "test", "uniq_id": "id123"}
248
+ wf = DummyWorkflow(supervisor_args=dummy_args)
249
+ assert wf.get_supervisor_args() is dummy_args
250
+
251
+
252
+ def test_dummy_hydra_compose_get_config():
253
+ """Test that DummyHydraCompose.get_config returns the raw config."""
254
+ config_dict = {"agents": {"test": {"key": "value"}}}
255
+ compose = DummyHydraCompose(config_dict)
256
+ assert compose.get_config() is config_dict
@@ -0,0 +1,139 @@
1
+ """Unit tests for the paper download agent in Talk2Scholars."""
2
+
3
+ from unittest import mock
4
+
5
+ import pytest
6
+ from langchain_core.language_models.chat_models import BaseChatModel
7
+ from langchain_core.messages import AIMessage, HumanMessage
8
+
9
+ from ..agents.paper_download_agent import get_app
10
+ from ..state.state_talk2scholars import Talk2Scholars
11
+
12
+
13
+ @pytest.fixture(autouse=True)
14
+ def mock_hydra_fixture():
15
+ """Mocks Hydra configuration for tests."""
16
+ with mock.patch("hydra.initialize"), mock.patch("hydra.compose") as mock_compose:
17
+ cfg_mock = mock.MagicMock()
18
+ cfg_mock.agents.talk2scholars.paper_download_agent.paper_download_agent = "Test prompt"
19
+ mock_compose.return_value = cfg_mock
20
+ yield mock_compose
21
+
22
+
23
+ @pytest.fixture
24
+ def mock_tools_fixture():
25
+ """Mocks paper download tools to prevent real HTTP calls."""
26
+ with mock.patch(
27
+ "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.download_papers"
28
+ ) as mock_download_papers:
29
+ mock_download_papers.return_value = {"article_data": {"dummy_key": "dummy_value"}}
30
+ yield [mock_download_papers]
31
+
32
+
33
+ @pytest.mark.usefixtures("mock_hydra_fixture")
34
+ def test_paper_download_agent_initialization():
35
+ """Ensures the paper download agent initializes properly with a prompt."""
36
+ thread_id = "test_thread_paper_dl"
37
+ llm_mock = mock.Mock(spec=BaseChatModel) # Mock LLM
38
+
39
+ with mock.patch(
40
+ "aiagents4pharma.talk2scholars.agents.paper_download_agent.create_react_agent"
41
+ ) as mock_create_agent:
42
+ mock_create_agent.return_value = mock.Mock()
43
+
44
+ app = get_app(thread_id, llm_mock)
45
+ assert app is not None, "The agent app should be successfully created."
46
+ assert mock_create_agent.called
47
+
48
+
49
+ def test_paper_download_agent_invocation():
50
+ """Verifies agent processes queries and updates state correctly."""
51
+ _ = mock_tools_fixture # Prevents unused-argument warning
52
+ thread_id = "test_thread_paper_dl"
53
+ mock_state = Talk2Scholars(messages=[HumanMessage(content="Download paper 1234.5678")])
54
+ llm_mock = mock.Mock(spec=BaseChatModel)
55
+
56
+ with mock.patch(
57
+ "aiagents4pharma.talk2scholars.agents.paper_download_agent.create_react_agent"
58
+ ) as mock_create_agent:
59
+ mock_agent = mock.Mock()
60
+ mock_create_agent.return_value = mock_agent
61
+ mock_agent.invoke.return_value = {
62
+ "messages": [AIMessage(content="Here is the paper")],
63
+ "article_data": {"file_bytes": b"FAKE_PDF_CONTENTS"},
64
+ }
65
+
66
+ app = get_app(thread_id, llm_mock)
67
+ result = app.invoke(
68
+ mock_state,
69
+ config={
70
+ "configurable": {
71
+ "thread_id": thread_id,
72
+ "checkpoint_ns": "test_ns",
73
+ "checkpoint_id": "test_checkpoint",
74
+ }
75
+ },
76
+ )
77
+
78
+ assert "messages" in result
79
+ assert "article_data" in result
80
+
81
+
82
+ def test_paper_download_agent_tools_assignment(
83
+ request,
84
+ ):
85
+ """Checks correct tool assignment (download_papers tool)."""
86
+ thread_id = "test_thread_paper_dl"
87
+ request.getfixturevalue("mock_tools_fixture")
88
+ llm_mock = mock.Mock(spec=BaseChatModel)
89
+
90
+ with (
91
+ mock.patch(
92
+ "aiagents4pharma.talk2scholars.agents.paper_download_agent.create_react_agent"
93
+ ) as mock_create_agent,
94
+ mock.patch(
95
+ "aiagents4pharma.talk2scholars.agents.paper_download_agent.ToolNode"
96
+ ) as mock_toolnode,
97
+ ):
98
+ mock_agent = mock.Mock()
99
+ mock_create_agent.return_value = mock_agent
100
+ mock_tool_instance = mock.Mock()
101
+ mock_toolnode.return_value = mock_tool_instance
102
+
103
+ get_app(thread_id, llm_mock)
104
+ # Verify ToolNode was called with download_papers function
105
+ assert mock_toolnode.called
106
+ # Check that ToolNode was called with a list containing the download_papers tool
107
+ call_args = mock_toolnode.call_args[0][0] # Get first positional argument (the tools list)
108
+ assert len(call_args) == 1
109
+ # The tool should be a StructuredTool with name 'download_papers'
110
+ tool = call_args[0]
111
+ assert hasattr(tool, "name")
112
+ assert tool.name == "download_papers"
113
+
114
+
115
+ def test_paper_download_agent_hydra_failure():
116
+ """Confirms the agent gracefully handles exceptions if Hydra fails."""
117
+ thread_id = "test_thread_paper_dl"
118
+ llm_mock = mock.Mock(spec=BaseChatModel)
119
+
120
+ with mock.patch("hydra.initialize", side_effect=Exception("Mock Hydra failure")):
121
+ with pytest.raises(Exception) as exc_info:
122
+ get_app(thread_id, llm_mock)
123
+ assert "Mock Hydra failure" in str(exc_info.value)
124
+
125
+
126
+ def test_paper_download_agent_model_failure():
127
+ """Ensures agent handles model-related failures gracefully."""
128
+ thread_id = "test_thread_paper_dl"
129
+ llm_mock = mock.Mock(spec=BaseChatModel)
130
+
131
+ with mock.patch(
132
+ "aiagents4pharma.talk2scholars.agents.paper_download_agent.create_react_agent",
133
+ side_effect=Exception("Mock model failure"),
134
+ ):
135
+ with pytest.raises(Exception) as exc_info:
136
+ get_app(thread_id, llm_mock)
137
+ assert "Mock model failure" in str(exc_info.value), (
138
+ "Model initialization failure should raise an exception."
139
+ )
@@ -0,0 +1,114 @@
1
+ """
2
+ Unit Tests for the PDF agent.
3
+ """
4
+
5
+ from unittest import mock
6
+
7
+ import pytest
8
+ from langchain_core.messages import AIMessage, HumanMessage
9
+
10
+ from ..agents.pdf_agent import get_app
11
+ from ..state.state_talk2scholars import Talk2Scholars
12
+
13
+
14
+ @pytest.fixture(autouse=True)
15
+ def mock_hydra_fixture():
16
+ """Mock Hydra configuration to prevent external dependencies."""
17
+ with mock.patch("hydra.initialize"), mock.patch("hydra.compose") as mock_compose:
18
+ # Create a mock configuration with a pdf_agent section.
19
+ cfg_mock = mock.MagicMock()
20
+ # The pdf_agent config will be accessed as cfg.agents.talk2scholars.pdf_agent in get_app.
21
+ cfg_mock.agents.talk2scholars.pdf_agent.some_property = "Test prompt"
22
+ mock_compose.return_value = cfg_mock
23
+ yield mock_compose
24
+
25
+
26
+ @pytest.fixture
27
+ def mock_tools_fixture():
28
+ """Mock PDF agent tools to prevent execution of real API calls."""
29
+ with (
30
+ mock.patch(
31
+ "aiagents4pharma.talk2scholars.agents.pdf_agent.question_and_answer"
32
+ ) as mock_question_and_answer,
33
+ ):
34
+ mock_question_and_answer.return_value = {"result": "Mock Question and Answer Result"}
35
+ yield [mock_question_and_answer]
36
+
37
+
38
+ @pytest.fixture(name="mock_llm")
39
+ def llm_fixture():
40
+ """Provide a dummy language model to pass into get_app."""
41
+ return mock.Mock()
42
+
43
+
44
+ @pytest.mark.usefixtures("mock_hydra_fixture")
45
+ def test_pdf_agent_initialization(mock_llm):
46
+ """Test that PDF agent initializes correctly with mock configuration."""
47
+ thread_id = "test_thread"
48
+ with mock.patch(
49
+ "aiagents4pharma.talk2scholars.agents.pdf_agent.create_react_agent"
50
+ ) as mock_create:
51
+ mock_create.return_value = mock.Mock()
52
+ app = get_app(thread_id, mock_llm)
53
+ assert app is not None
54
+ assert mock_create.called
55
+
56
+
57
+ def test_pdf_agent_invocation(mock_llm):
58
+ """Test that the PDF agent processes user input and returns a valid response."""
59
+ thread_id = "test_thread"
60
+ # Create a sample state with a human message.
61
+ mock_state = Talk2Scholars(messages=[HumanMessage(content="Extract key data from PDF")])
62
+ with mock.patch(
63
+ "aiagents4pharma.talk2scholars.agents.pdf_agent.create_react_agent"
64
+ ) as mock_create:
65
+ mock_agent = mock.Mock()
66
+ mock_create.return_value = mock_agent
67
+ # Simulate a response from the PDF agent.
68
+ mock_agent.invoke.return_value = {
69
+ "messages": [AIMessage(content="PDF content extracted successfully")],
70
+ "article_data": {"page": 1, "text": "Sample PDF text"},
71
+ }
72
+ app = get_app(thread_id, mock_llm)
73
+ result = app.invoke(
74
+ mock_state,
75
+ config={
76
+ "configurable": {
77
+ "thread_id": thread_id,
78
+ "checkpoint_ns": "test_ns",
79
+ "checkpoint_id": "test_checkpoint",
80
+ }
81
+ },
82
+ )
83
+ assert "messages" in result
84
+ assert "article_data" in result
85
+ assert result["article_data"]["page"] == 1
86
+
87
+
88
+ def test_pdf_agent_tools_assignment(request, mock_llm):
89
+ """Ensure that the correct tools are assigned to the PDF agent."""
90
+ thread_id = "test_thread"
91
+ mock_tools = request.getfixturevalue("mock_tools_fixture")
92
+ with (
93
+ mock.patch(
94
+ "aiagents4pharma.talk2scholars.agents.pdf_agent.create_react_agent"
95
+ ) as mock_create,
96
+ mock.patch("aiagents4pharma.talk2scholars.agents.pdf_agent.ToolNode") as mock_toolnode,
97
+ ):
98
+ mock_agent = mock.Mock()
99
+ mock_create.return_value = mock_agent
100
+ mock_tool_instance = mock.Mock()
101
+ mock_tool_instance.tools = mock_tools
102
+ mock_toolnode.return_value = mock_tool_instance
103
+ get_app(thread_id, mock_llm)
104
+ assert mock_toolnode.called
105
+ assert len(mock_tool_instance.tools) == 1
106
+
107
+
108
+ def test_pdf_agent_hydra_failure(mock_llm):
109
+ """Test exception handling when Hydra fails to load config for PDF agent."""
110
+ thread_id = "test_thread"
111
+ with mock.patch("hydra.initialize", side_effect=Exception("Hydra error")):
112
+ with pytest.raises(Exception) as exc_info:
113
+ get_app(thread_id, mock_llm)
114
+ assert "Hydra error" in str(exc_info.value)