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,88 @@
1
+ """pdf rag pipeline tests."""
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ import pytest
6
+ from langchain_core.documents import Document
7
+
8
+ from aiagents4pharma.talk2scholars.tools.pdf.utils.rag_pipeline import (
9
+ retrieve_and_rerank_chunks,
10
+ )
11
+
12
+
13
+ @pytest.fixture(name="base_config_fixture")
14
+ def _base_config_fixture():
15
+ """Provides a config-like object for testing."""
16
+ config = MagicMock()
17
+ config.get.side_effect = lambda key, default=None: {
18
+ "initial_retrieval_k": 120,
19
+ "mmr_diversity": 0.7,
20
+ }.get(key, default)
21
+ config.top_k_chunks = 5
22
+ return config
23
+
24
+
25
+ @pytest.fixture(name="mock_docs_fixture")
26
+ def _mock_docs_fixture():
27
+ """Simulates PDF document chunks."""
28
+ return [
29
+ Document(page_content=f"chunk {i}", metadata={"paper_id": f"P{i % 2}"}) for i in range(10)
30
+ ]
31
+
32
+
33
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.rag_pipeline.rerank_chunks")
34
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.rag_pipeline.retrieve_relevant_chunks")
35
+ def test_rag_pipeline_gpu_path(mock_retrieve, mock_rerank, base_config_fixture, mock_docs_fixture):
36
+ """test RAG pipeline with GPU path."""
37
+ mock_retrieve.return_value = mock_docs_fixture
38
+ mock_rerank.return_value = mock_docs_fixture[:5]
39
+
40
+ result = retrieve_and_rerank_chunks(
41
+ vector_store=MagicMock(),
42
+ query="Explain AI.",
43
+ config=base_config_fixture,
44
+ call_id="gpu_test",
45
+ has_gpu=True,
46
+ )
47
+
48
+ assert result == mock_docs_fixture[:5]
49
+ mock_retrieve.assert_called_once()
50
+ mock_rerank.assert_called_once()
51
+
52
+
53
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.rag_pipeline.rerank_chunks")
54
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.rag_pipeline.retrieve_relevant_chunks")
55
+ def test_rag_pipeline_cpu_path(mock_retrieve, mock_rerank, base_config_fixture, mock_docs_fixture):
56
+ """rag pipeline with CPU path."""
57
+ mock_retrieve.return_value = mock_docs_fixture
58
+ mock_rerank.return_value = mock_docs_fixture[:5]
59
+
60
+ result = retrieve_and_rerank_chunks(
61
+ vector_store=MagicMock(),
62
+ query="Explain quantum physics.",
63
+ config=base_config_fixture,
64
+ call_id="cpu_test",
65
+ has_gpu=False,
66
+ )
67
+
68
+ assert result == mock_docs_fixture[:5]
69
+ mock_retrieve.assert_called_once()
70
+ mock_rerank.assert_called_once()
71
+
72
+
73
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.rag_pipeline.rerank_chunks")
74
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.rag_pipeline.retrieve_relevant_chunks")
75
+ def test_rag_pipeline_empty_results(mock_retrieve, mock_rerank, base_config_fixture):
76
+ """rag pipeline with no results."""
77
+ mock_retrieve.return_value = []
78
+
79
+ result = retrieve_and_rerank_chunks(
80
+ vector_store=MagicMock(),
81
+ query="No match?",
82
+ config=base_config_fixture,
83
+ call_id="empty_test",
84
+ has_gpu=False,
85
+ )
86
+
87
+ assert result == []
88
+ mock_rerank.assert_not_called()
@@ -0,0 +1,190 @@
1
+ """retrieve_chunks for PDF tool tests"""
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ import pytest
6
+ from langchain_core.documents import Document
7
+
8
+ from aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks import (
9
+ retrieve_relevant_chunks,
10
+ retrieve_relevant_chunks_with_scores,
11
+ )
12
+
13
+
14
+ @pytest.fixture
15
+ def mock_vector_store():
16
+ """Fixture to simulate a vector store."""
17
+ return MagicMock()
18
+
19
+
20
+ @pytest.fixture
21
+ def mock_chunks():
22
+ """Fixture to simulate PDF chunks."""
23
+ return [
24
+ Document(page_content=f"chunk {i}", metadata={"paper_id": f"P{i % 2}"}) for i in range(5)
25
+ ]
26
+
27
+
28
+ @pytest.fixture
29
+ def mock_scored_chunks():
30
+ """Fixture to simulate scored PDF chunks."""
31
+ return [
32
+ (Document(page_content=f"chunk {i}", metadata={}), score)
33
+ for i, score in enumerate([0.9, 0.8, 0.4, 0.95])
34
+ ]
35
+
36
+
37
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
38
+ def test_retrieve_chunks_cpu_success(mock_logger, request):
39
+ """Test retrieve_relevant_chunks with CPU path."""
40
+ vector_store = request.getfixturevalue("mock_vector_store")
41
+ chunks = request.getfixturevalue("mock_chunks")
42
+ vector_store.has_gpu = False
43
+ mock_logger.debug = MagicMock()
44
+ vector_store.max_marginal_relevance_search.return_value = chunks
45
+
46
+ results = retrieve_relevant_chunks(vector_store, query="AI", top_k=5)
47
+
48
+ assert results == chunks
49
+ vector_store.max_marginal_relevance_search.assert_called_once()
50
+
51
+
52
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
53
+ def test_retrieve_chunks_gpu_success(mock_logger, request):
54
+ """Test retrieve_relevant_chunks with GPU path."""
55
+ vector_store = request.getfixturevalue("mock_vector_store")
56
+ chunks = request.getfixturevalue("mock_chunks")
57
+ vector_store.has_gpu = True
58
+ mock_logger.debug = MagicMock()
59
+ vector_store.max_marginal_relevance_search.return_value = chunks
60
+
61
+ results = retrieve_relevant_chunks(vector_store, query="AI", top_k=5)
62
+
63
+ assert results == chunks
64
+ vector_store.max_marginal_relevance_search.assert_called_once()
65
+
66
+
67
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
68
+ def test_retrieve_chunks_with_filter(mock_logger, request):
69
+ """Test retrieve_relevant_chunks with paper_id filter."""
70
+ vector_store = request.getfixturevalue("mock_vector_store")
71
+ chunks = request.getfixturevalue("mock_chunks")
72
+ vector_store.has_gpu = False
73
+ mock_logger.debug = MagicMock()
74
+ vector_store.max_marginal_relevance_search.return_value = chunks
75
+
76
+ results = retrieve_relevant_chunks(vector_store, query="filter test", paper_ids=["P1"], top_k=3)
77
+ assert results == chunks
78
+ args, kwargs = vector_store.max_marginal_relevance_search.call_args
79
+ assert len(args) == 0
80
+ assert kwargs["filter"] == {"paper_id": ["P1"]}
81
+
82
+
83
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
84
+ def test_retrieve_chunks_no_vector_store(mock_logger):
85
+ """Test when vector store is None."""
86
+ result = retrieve_relevant_chunks(vector_store=None, query="irrelevant")
87
+ assert result == []
88
+ mock_logger.error.assert_called_with("Vector store is not initialized")
89
+
90
+
91
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
92
+ def test_retrieve_chunks_with_scores_no_vector_store(mock_logger):
93
+ """Test retrieve_relevant_chunks_with_scores when vector store is None."""
94
+ result = retrieve_relevant_chunks_with_scores(vector_store=None, query="none")
95
+ assert result == []
96
+ mock_logger.error.assert_called_with("Vector store is not initialized")
97
+
98
+
99
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
100
+ def test_retrieve_chunks_default_search_params(mock_logger, request):
101
+ """Test default search params used when not defined."""
102
+ vector_store = request.getfixturevalue("mock_vector_store")
103
+ chunks = request.getfixturevalue("mock_chunks")
104
+ vector_store.has_gpu = False
105
+ delattr(vector_store, "search_params")
106
+ vector_store.max_marginal_relevance_search.return_value = chunks
107
+
108
+ results = retrieve_relevant_chunks(
109
+ vector_store,
110
+ query="default search param test",
111
+ top_k=5,
112
+ )
113
+
114
+ assert results == chunks
115
+ mock_logger.debug.assert_any_call("Using default search parameters (no hardware optimization)")
116
+
117
+
118
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
119
+ def test_retrieve_chunks_with_scores_paper_filter(mock_logger, request):
120
+ """Test retrieve_relevant_chunks_with_scores applies paper_id filter."""
121
+ vector_store = request.getfixturevalue("mock_vector_store")
122
+ scored_chunks = request.getfixturevalue("mock_scored_chunks")
123
+ vector_store.similarity_search_with_score.return_value = scored_chunks
124
+ mock_logger.debug = MagicMock()
125
+
126
+ results = retrieve_relevant_chunks_with_scores(
127
+ vector_store=vector_store,
128
+ query="filtered score",
129
+ paper_ids=["P123"],
130
+ top_k=5,
131
+ score_threshold=0.0,
132
+ )
133
+
134
+ assert isinstance(results, list)
135
+ assert vector_store.similarity_search_with_score.call_args[1]["filter"] == {
136
+ "paper_id": ["P123"]
137
+ }
138
+
139
+
140
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
141
+ def test_retrieve_chunks_with_scores_gpu_debug(mock_logger, request):
142
+ """Test GPU debug log and correct return in retrieve_relevant_chunks_with_scores."""
143
+ vector_store = request.getfixturevalue("mock_vector_store")
144
+ scored_chunks = request.getfixturevalue("mock_scored_chunks")
145
+ vector_store.has_gpu = True
146
+ vector_store.similarity_search_with_score.return_value = scored_chunks
147
+ mock_logger.debug = MagicMock()
148
+
149
+ results = retrieve_relevant_chunks_with_scores(
150
+ vector_store=vector_store, query="gpu test", top_k=4, score_threshold=0.0
151
+ )
152
+
153
+ # Should return all scored_chunks since threshold=0.0
154
+ assert results == scored_chunks
155
+ mock_logger.debug.assert_called_with("GPU-accelerated similarity search enabled")
156
+
157
+
158
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
159
+ def test_retrieve_chunks_with_scores_cpu_debug(mock_logger, request):
160
+ """Test CPU debug log and correct return in retrieve_relevant_chunks_with_scores."""
161
+ vector_store = request.getfixturevalue("mock_vector_store")
162
+ scored_chunks = request.getfixturevalue("mock_scored_chunks")
163
+ vector_store.has_gpu = False
164
+ vector_store.similarity_search_with_score.return_value = scored_chunks
165
+ mock_logger.debug = MagicMock()
166
+
167
+ results = retrieve_relevant_chunks_with_scores(
168
+ vector_store=vector_store, query="cpu test", top_k=2, score_threshold=0.0
169
+ )
170
+
171
+ assert results == scored_chunks
172
+ mock_logger.debug.assert_called_with("Standard CPU similarity search")
173
+
174
+
175
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.retrieve_chunks.logger")
176
+ def test_retrieve_chunks_with_scores_not_implemented(mock_logger, request):
177
+ """Test NotImplementedError path when similarity_search_with_score is missing."""
178
+ vector_store = request.getfixturevalue("mock_vector_store")
179
+ vector_store.has_gpu = True
180
+ # Remove the method to trigger NotImplementedError
181
+ if hasattr(vector_store, "similarity_search_with_score"):
182
+ delattr(vector_store, "similarity_search_with_score")
183
+ mock_logger.debug = MagicMock()
184
+
185
+ with pytest.raises(NotImplementedError) as excinfo:
186
+ retrieve_relevant_chunks_with_scores(
187
+ vector_store=vector_store, query="fail test", top_k=1, score_threshold=0.0
188
+ )
189
+ assert "Vector store does not support similarity_search_with_score" in str(excinfo.value)
190
+ mock_logger.debug.assert_called_with("GPU-accelerated similarity search enabled")
@@ -0,0 +1,159 @@
1
+ """
2
+ Tests for singleton_manager: manages vector store connections and event loops.
3
+ """
4
+
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ import pytest
8
+ from pymilvus.exceptions import MilvusException
9
+
10
+ from aiagents4pharma.talk2scholars.tools.pdf.utils.get_vectorstore import (
11
+ get_vectorstore,
12
+ )
13
+ from aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager import (
14
+ VectorstoreSingleton,
15
+ )
16
+
17
+
18
+ def test_singleton_instance_identity():
19
+ """Singleton should return the same instance."""
20
+ a = VectorstoreSingleton()
21
+ b = VectorstoreSingleton()
22
+ assert a is b
23
+
24
+
25
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.detect_nvidia_gpu")
26
+ def test_detect_gpu_once(mock_detect, monkeypatch):
27
+ """Ensure GPU detection is cached."""
28
+ mock_detect.return_value = True
29
+ singleton = VectorstoreSingleton()
30
+
31
+ # Reset GPU detection cache safely
32
+ monkeypatch.setattr(VectorstoreSingleton, "_gpu_detected", None, raising=False)
33
+
34
+ result = singleton.detect_gpu_once()
35
+ assert result is True
36
+
37
+ # Second call should use cached value; detect_nvidia_gpu called only once
38
+ result2 = singleton.detect_gpu_once()
39
+ assert result2 is True
40
+ mock_detect.assert_called_once()
41
+
42
+
43
+ def test_get_event_loop_reuses_existing():
44
+ """get_event_loop should return the same loop if it exists."""
45
+ singleton = VectorstoreSingleton()
46
+ loop1 = singleton.get_event_loop()
47
+ loop2 = singleton.get_event_loop()
48
+ assert loop1 is loop2
49
+
50
+
51
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.connections")
52
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.db")
53
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.utility")
54
+ def test_get_connection_creates_connection(_, mock_db, mock_conns):
55
+ """get_connection should create a new connection if none exists."""
56
+ singleton = VectorstoreSingleton()
57
+ mock_conns.has_connection.return_value = True
58
+ mock_db.list_database.return_value = []
59
+
60
+ conn_key = singleton.get_connection("localhost", 19530, "test_db")
61
+ assert conn_key == "default"
62
+ mock_conns.remove_connection.assert_called_once()
63
+ mock_conns.connect.assert_called_once()
64
+ mock_db.create_database.assert_called_once_with("test_db")
65
+ mock_db.using_database.assert_called_once_with("test_db")
66
+
67
+
68
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.Milvus")
69
+ def test_get_vector_store_creates_if_missing(mock_milvus, monkeypatch):
70
+ """get_vector_store should create a new vector store if missing."""
71
+ singleton = VectorstoreSingleton()
72
+
73
+ # Clear caches safely
74
+ monkeypatch.setattr(VectorstoreSingleton, "_vector_stores", {}, raising=False)
75
+ monkeypatch.setattr(VectorstoreSingleton, "_event_loops", {}, raising=False)
76
+
77
+ mock_embed = MagicMock()
78
+ connection_args = {"host": "localhost", "port": 19530}
79
+
80
+ vs = singleton.get_vector_store("collection1", mock_embed, connection_args)
81
+
82
+ assert vs is not None
83
+ mock_milvus.assert_called_once()
84
+
85
+
86
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.get_vectorstore.Vectorstore")
87
+ def test_get_vectorstore_factory(mock_vectorstore_cls):
88
+ """get_vectorstore should reuse or create Vectorstore."""
89
+ mock_config = MagicMock()
90
+ mock_config.milvus.collection_name = "demo"
91
+ mock_config.milvus.embedding_dim = 768
92
+ mock_embed = MagicMock()
93
+
94
+ result1 = get_vectorstore(mock_embed, mock_config, force_new=True)
95
+ assert result1 == mock_vectorstore_cls.return_value
96
+
97
+ result2 = get_vectorstore(mock_embed, mock_config)
98
+ assert result2 == result1
99
+
100
+
101
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.get_vectorstore.Vectorstore")
102
+ def test_get_vectorstore_force_new(mock_vectorstore_cls):
103
+ """get_vectorstore should return a new instance if force_new=True."""
104
+ mock_vs1 = MagicMock(name="Vectorstore1")
105
+ mock_vs2 = MagicMock(name="Vectorstore2")
106
+ mock_vectorstore_cls.side_effect = [mock_vs1, mock_vs2]
107
+
108
+ dummy_config = MagicMock()
109
+ dummy_config.milvus.collection_name = "my_test_collection"
110
+ dummy_config.milvus.embedding_dim = 768
111
+
112
+ vs1 = get_vectorstore(mock_vs1, dummy_config)
113
+ vs2 = get_vectorstore(mock_vs2, dummy_config, force_new=True)
114
+
115
+ assert vs1 is mock_vs1
116
+ assert vs2 is mock_vs2
117
+ assert vs1 != vs2
118
+
119
+
120
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.connections.connect")
121
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.connections.has_connection")
122
+ @patch("aiagents4pharma.talk2scholars.tools.pdf.utils.singleton_manager.db")
123
+ def test_get_connection_milvus_error(_, mock_has_connection, mock_connect, monkeypatch):
124
+ """get_connection should raise MilvusException on connection failure."""
125
+ manager = VectorstoreSingleton()
126
+
127
+ # Reset connections cache safely
128
+ monkeypatch.setattr(VectorstoreSingleton, "_connections", {}, raising=False)
129
+
130
+ mock_has_connection.return_value = False
131
+ mock_connect.side_effect = MilvusException("Connection failed")
132
+
133
+ with pytest.raises(MilvusException, match="Connection failed"):
134
+ manager.get_connection("localhost", 19530, "test_db")
135
+
136
+
137
+ def test_get_event_loop_creates_new_loop_on_closed(monkeypatch):
138
+ """Ensure get_event_loop creates a new loop if current one is closed."""
139
+ manager = VectorstoreSingleton()
140
+
141
+ # Clear event loops safely
142
+ monkeypatch.setattr(VectorstoreSingleton, "_event_loops", {}, raising=False)
143
+
144
+ mock_loop = MagicMock()
145
+ mock_loop.is_closed.return_value = True
146
+
147
+ with (
148
+ patch("asyncio.get_event_loop", return_value=mock_loop),
149
+ patch("asyncio.new_event_loop") as mock_new_loop,
150
+ patch("asyncio.set_event_loop") as mock_set_loop,
151
+ ):
152
+ new_loop = MagicMock()
153
+ mock_new_loop.return_value = new_loop
154
+
155
+ result_loop = manager.get_event_loop()
156
+
157
+ mock_new_loop.assert_called_once()
158
+ mock_set_loop.assert_called_once_with(new_loop)
159
+ assert result_loop == new_loop
@@ -0,0 +1,121 @@
1
+ """Unit tests for vector normalization utilities for GPU COSINE support."""
2
+
3
+ import logging
4
+
5
+ import pytest
6
+ from langchain_core.embeddings import Embeddings
7
+
8
+ from aiagents4pharma.talk2scholars.tools.pdf.utils import vector_normalization as vn
9
+
10
+
11
+ def test_normalize_vector_nonzero():
12
+ """Test normalizing a non-zero vector."""
13
+ vec = [3.0, 4.0]
14
+ result = vn.normalize_vector(vec)
15
+ expected = [0.6, 0.8]
16
+ assert pytest.approx(result) == expected
17
+
18
+
19
+ def test_normalize_vector_zero_logs_warning(caplog):
20
+ """Test normalizing a zero vector logs a warning."""
21
+ with caplog.at_level(logging.WARNING):
22
+ result = vn.normalize_vector([0.0, 0.0])
23
+ assert result == [0.0, 0.0]
24
+ assert "Zero vector encountered" in caplog.text
25
+
26
+
27
+ def test_normalize_vectors_batch_empty():
28
+ """Test that an empty batch returns unchanged."""
29
+ result = vn.normalize_vectors_batch([])
30
+ assert result == []
31
+
32
+
33
+ def test_normalize_vectors_batch_normal_case():
34
+ """Test batch normalization of valid vectors with equal dimensions."""
35
+ vectors = [[3, 4], [6, 8]]
36
+ result = vn.normalize_vectors_batch(vectors)
37
+ expected = [
38
+ [0.6, 0.8],
39
+ [0.6, 0.8],
40
+ ]
41
+ for r, e in zip(result, expected, strict=False):
42
+ assert pytest.approx(r) == e
43
+
44
+
45
+ def test_normalize_vectors_batch_with_zero_vector(caplog):
46
+ """Test that zero vectors are handled and logged."""
47
+ vectors = [[0.0, 0.0], [1.0, 0.0]]
48
+ with caplog.at_level(logging.WARNING):
49
+ result = vn.normalize_vectors_batch(vectors)
50
+ assert len(result) == 2
51
+ assert "zero vectors during batch normalization" in caplog.text
52
+ assert pytest.approx(result[1]) == [1.0, 0.0]
53
+
54
+
55
+ class DummyEmbedding(Embeddings):
56
+ """A dummy embedding class for testing normalization wrapper."""
57
+
58
+ def __init__(self):
59
+ self.test_attr = "test"
60
+
61
+ def embed_documents(self, texts):
62
+ return [[3.0, 4.0] for _ in texts]
63
+
64
+ def embed_query(self, text):
65
+ return [3.0, 4.0]
66
+
67
+
68
+ def test_normalizing_embeddings_embed_documents():
69
+ """Test that document embeddings are normalized."""
70
+ model = vn.NormalizingEmbeddings(DummyEmbedding())
71
+ result = model.embed_documents(["doc1", "doc2"])
72
+ assert len(result) == 2
73
+ assert pytest.approx(result[0]) == [0.6, 0.8]
74
+
75
+
76
+ def test_normalizing_embeddings_embed_query():
77
+ """Test that query embeddings are normalized."""
78
+ model = vn.NormalizingEmbeddings(DummyEmbedding())
79
+ result = model.embed_query("query")
80
+ assert pytest.approx(result) == [0.6, 0.8]
81
+
82
+
83
+ def test_normalizing_embeddings_passthrough():
84
+ """Test attribute delegation to base embedding model."""
85
+ dummy = DummyEmbedding()
86
+ model = vn.NormalizingEmbeddings(dummy)
87
+ assert model.test_attr == "test"
88
+
89
+
90
+ @pytest.mark.parametrize(
91
+ "has_gpu,use_cosine,expected_log",
92
+ [
93
+ (True, True, "ENABLED"),
94
+ (False, True, "DISABLED"),
95
+ (True, False, "DISABLED"),
96
+ (False, False, "DISABLED"),
97
+ ],
98
+ )
99
+ def test_should_normalize_vectors_logging(has_gpu, use_cosine, expected_log, caplog):
100
+ """Test should_normalize_vectors decision logic and logging."""
101
+ with caplog.at_level(logging.INFO):
102
+ result = vn.should_normalize_vectors(has_gpu, use_cosine)
103
+ if has_gpu and use_cosine:
104
+ assert result is True
105
+ else:
106
+ assert result is False
107
+ assert expected_log in caplog.text
108
+
109
+
110
+ def test_wrap_embedding_model_if_needed_enabled():
111
+ """Test that wrapping is applied when needed."""
112
+ base = DummyEmbedding()
113
+ wrapped = vn.wrap_embedding_model_if_needed(base, has_gpu=True, use_cosine=True)
114
+ assert isinstance(wrapped, vn.NormalizingEmbeddings)
115
+
116
+
117
+ def test_wrap_embedding_model_if_needed_disabled():
118
+ """Test that original model is returned when normalization not needed."""
119
+ base = DummyEmbedding()
120
+ wrapped = vn.wrap_embedding_model_if_needed(base, has_gpu=False, use_cosine=True)
121
+ assert wrapped is base