aiagents4pharma 1.44.0__py3-none-any.whl → 1.45.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. aiagents4pharma/__init__.py +2 -2
  2. aiagents4pharma/talk2aiagents4pharma/.dockerignore +13 -0
  3. aiagents4pharma/talk2aiagents4pharma/Dockerfile +105 -0
  4. aiagents4pharma/talk2aiagents4pharma/README.md +1 -0
  5. aiagents4pharma/talk2aiagents4pharma/__init__.py +4 -5
  6. aiagents4pharma/talk2aiagents4pharma/agents/__init__.py +3 -2
  7. aiagents4pharma/talk2aiagents4pharma/agents/main_agent.py +24 -23
  8. aiagents4pharma/talk2aiagents4pharma/configs/__init__.py +2 -2
  9. aiagents4pharma/talk2aiagents4pharma/configs/agents/__init__.py +2 -2
  10. aiagents4pharma/talk2aiagents4pharma/configs/agents/main_agent/default.yaml +2 -2
  11. aiagents4pharma/talk2aiagents4pharma/configs/config.yaml +1 -1
  12. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/.env.example +23 -0
  13. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/docker-compose.yml +93 -0
  14. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/.env.example +23 -0
  15. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/docker-compose.yml +108 -0
  16. aiagents4pharma/talk2aiagents4pharma/install.md +127 -0
  17. aiagents4pharma/talk2aiagents4pharma/states/__init__.py +3 -2
  18. aiagents4pharma/talk2aiagents4pharma/states/state_talk2aiagents4pharma.py +5 -3
  19. aiagents4pharma/talk2aiagents4pharma/tests/__init__.py +2 -2
  20. aiagents4pharma/talk2aiagents4pharma/tests/test_main_agent.py +72 -50
  21. aiagents4pharma/talk2biomodels/.dockerignore +13 -0
  22. aiagents4pharma/talk2biomodels/Dockerfile +104 -0
  23. aiagents4pharma/talk2biomodels/README.md +1 -0
  24. aiagents4pharma/talk2biomodels/__init__.py +4 -8
  25. aiagents4pharma/talk2biomodels/agents/__init__.py +3 -2
  26. aiagents4pharma/talk2biomodels/agents/t2b_agent.py +47 -42
  27. aiagents4pharma/talk2biomodels/api/__init__.py +4 -5
  28. aiagents4pharma/talk2biomodels/api/kegg.py +14 -10
  29. aiagents4pharma/talk2biomodels/api/ols.py +13 -10
  30. aiagents4pharma/talk2biomodels/api/uniprot.py +7 -6
  31. aiagents4pharma/talk2biomodels/configs/__init__.py +3 -4
  32. aiagents4pharma/talk2biomodels/configs/agents/__init__.py +2 -2
  33. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/__init__.py +2 -2
  34. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/default.yaml +1 -1
  35. aiagents4pharma/talk2biomodels/configs/config.yaml +1 -1
  36. aiagents4pharma/talk2biomodels/configs/tools/__init__.py +4 -5
  37. aiagents4pharma/talk2biomodels/configs/tools/ask_question/__init__.py +2 -2
  38. aiagents4pharma/talk2biomodels/configs/tools/ask_question/default.yaml +1 -2
  39. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/__init__.py +2 -2
  40. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/default.yaml +1 -1
  41. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/__init__.py +2 -2
  42. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/default.yaml +1 -1
  43. aiagents4pharma/talk2biomodels/install.md +63 -0
  44. aiagents4pharma/talk2biomodels/models/__init__.py +4 -4
  45. aiagents4pharma/talk2biomodels/models/basico_model.py +36 -28
  46. aiagents4pharma/talk2biomodels/models/sys_bio_model.py +13 -10
  47. aiagents4pharma/talk2biomodels/states/__init__.py +3 -2
  48. aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +12 -8
  49. aiagents4pharma/talk2biomodels/tests/BIOMD0000000449_url.xml +1585 -0
  50. aiagents4pharma/talk2biomodels/tests/__init__.py +2 -2
  51. aiagents4pharma/talk2biomodels/tests/article_on_model_537.pdf +0 -0
  52. aiagents4pharma/talk2biomodels/tests/test_api.py +18 -14
  53. aiagents4pharma/talk2biomodels/tests/test_ask_question.py +8 -9
  54. aiagents4pharma/talk2biomodels/tests/test_basico_model.py +15 -9
  55. aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +54 -55
  56. aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +28 -27
  57. aiagents4pharma/talk2biomodels/tests/test_integration.py +21 -33
  58. aiagents4pharma/talk2biomodels/tests/test_load_biomodel.py +14 -11
  59. aiagents4pharma/talk2biomodels/tests/test_param_scan.py +21 -20
  60. aiagents4pharma/talk2biomodels/tests/test_query_article.py +129 -29
  61. aiagents4pharma/talk2biomodels/tests/test_search_models.py +9 -13
  62. aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +16 -15
  63. aiagents4pharma/talk2biomodels/tests/test_steady_state.py +12 -22
  64. aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +33 -29
  65. aiagents4pharma/talk2biomodels/tools/__init__.py +15 -12
  66. aiagents4pharma/talk2biomodels/tools/ask_question.py +42 -32
  67. aiagents4pharma/talk2biomodels/tools/custom_plotter.py +51 -43
  68. aiagents4pharma/talk2biomodels/tools/get_annotation.py +99 -75
  69. aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +57 -51
  70. aiagents4pharma/talk2biomodels/tools/load_arguments.py +52 -32
  71. aiagents4pharma/talk2biomodels/tools/load_biomodel.py +8 -2
  72. aiagents4pharma/talk2biomodels/tools/parameter_scan.py +107 -90
  73. aiagents4pharma/talk2biomodels/tools/query_article.py +14 -13
  74. aiagents4pharma/talk2biomodels/tools/search_models.py +37 -26
  75. aiagents4pharma/talk2biomodels/tools/simulate_model.py +47 -37
  76. aiagents4pharma/talk2biomodels/tools/steady_state.py +76 -58
  77. aiagents4pharma/talk2biomodels/tools/utils.py +4 -3
  78. aiagents4pharma/talk2cells/README.md +1 -0
  79. aiagents4pharma/talk2cells/__init__.py +4 -5
  80. aiagents4pharma/talk2cells/agents/__init__.py +3 -2
  81. aiagents4pharma/talk2cells/agents/scp_agent.py +21 -19
  82. aiagents4pharma/talk2cells/states/__init__.py +3 -2
  83. aiagents4pharma/talk2cells/states/state_talk2cells.py +4 -2
  84. aiagents4pharma/talk2cells/tests/scp_agent/test_scp_agent.py +8 -9
  85. aiagents4pharma/talk2cells/tools/__init__.py +3 -2
  86. aiagents4pharma/talk2cells/tools/scp_agent/__init__.py +4 -4
  87. aiagents4pharma/talk2cells/tools/scp_agent/display_studies.py +5 -3
  88. aiagents4pharma/talk2cells/tools/scp_agent/search_studies.py +21 -22
  89. aiagents4pharma/talk2knowledgegraphs/.dockerignore +13 -0
  90. aiagents4pharma/talk2knowledgegraphs/Dockerfile +103 -0
  91. aiagents4pharma/talk2knowledgegraphs/README.md +1 -0
  92. aiagents4pharma/talk2knowledgegraphs/__init__.py +4 -7
  93. aiagents4pharma/talk2knowledgegraphs/agents/__init__.py +3 -2
  94. aiagents4pharma/talk2knowledgegraphs/agents/t2kg_agent.py +40 -30
  95. aiagents4pharma/talk2knowledgegraphs/configs/__init__.py +3 -6
  96. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/__init__.py +2 -2
  97. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/default.yaml +8 -8
  98. aiagents4pharma/talk2knowledgegraphs/configs/app/__init__.py +3 -2
  99. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/__init__.py +2 -2
  100. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/default.yaml +1 -1
  101. aiagents4pharma/talk2knowledgegraphs/configs/config.yaml +1 -1
  102. aiagents4pharma/talk2knowledgegraphs/configs/tools/__init__.py +4 -5
  103. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/__init__.py +2 -2
  104. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/default.yaml +1 -1
  105. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/__init__.py +2 -2
  106. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/default.yaml +1 -1
  107. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/__init__.py +2 -2
  108. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/default.yaml +1 -1
  109. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/ols_terms/default.yaml +1 -1
  110. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/reactome_pathways/default.yaml +1 -1
  111. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/uniprot_proteins/default.yaml +1 -1
  112. aiagents4pharma/talk2knowledgegraphs/configs/utils/pubchem_utils/default.yaml +1 -1
  113. aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py +4 -6
  114. aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py +115 -67
  115. aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py +2 -0
  116. aiagents4pharma/talk2knowledgegraphs/datasets/primekg.py +35 -24
  117. aiagents4pharma/talk2knowledgegraphs/datasets/starkqa_primekg.py +29 -21
  118. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/.env.example +23 -0
  119. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/docker-compose.yml +93 -0
  120. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/.env.example +23 -0
  121. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/docker-compose.yml +108 -0
  122. aiagents4pharma/talk2knowledgegraphs/entrypoint.sh +190 -0
  123. aiagents4pharma/talk2knowledgegraphs/install.md +140 -0
  124. aiagents4pharma/talk2knowledgegraphs/milvus_data_dump.py +31 -65
  125. aiagents4pharma/talk2knowledgegraphs/states/__init__.py +3 -2
  126. aiagents4pharma/talk2knowledgegraphs/states/state_talk2knowledgegraphs.py +1 -0
  127. aiagents4pharma/talk2knowledgegraphs/tests/test_agents_t2kg_agent.py +65 -40
  128. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_biobridge_primekg.py +54 -48
  129. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_dataset.py +4 -0
  130. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_primekg.py +17 -4
  131. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py +33 -24
  132. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_graphrag_reasoning.py +116 -69
  133. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_milvus_multimodal_subgraph_extraction.py +334 -216
  134. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_multimodal_subgraph_extraction.py +22 -15
  135. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_extraction.py +19 -12
  136. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_summarization.py +95 -48
  137. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py +4 -0
  138. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py +5 -0
  139. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_nim_molmim.py +13 -18
  140. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_ollama.py +10 -3
  141. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +4 -3
  142. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +3 -2
  143. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ols.py +1 -0
  144. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_pubchem.py +9 -4
  145. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_reactome.py +6 -6
  146. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_uniprot.py +4 -0
  147. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_extractions_milvus_multimodal_pcst.py +160 -97
  148. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_kg_utils.py +3 -4
  149. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_pubchem_utils.py +87 -13
  150. aiagents4pharma/talk2knowledgegraphs/tools/__init__.py +10 -7
  151. aiagents4pharma/talk2knowledgegraphs/tools/graphrag_reasoning.py +15 -20
  152. aiagents4pharma/talk2knowledgegraphs/tools/milvus_multimodal_subgraph_extraction.py +145 -142
  153. aiagents4pharma/talk2knowledgegraphs/tools/multimodal_subgraph_extraction.py +92 -90
  154. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_extraction.py +25 -37
  155. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_summarization.py +10 -13
  156. aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +4 -7
  157. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py +4 -7
  158. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py +4 -0
  159. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py +11 -14
  160. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/nim_molmim.py +7 -7
  161. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/ollama.py +12 -6
  162. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py +8 -6
  163. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +9 -6
  164. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +1 -0
  165. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +15 -9
  166. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ols_terms.py +23 -20
  167. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/pubchem_strings.py +12 -10
  168. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/reactome_pathways.py +16 -10
  169. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/uniprot_proteins.py +26 -18
  170. aiagents4pharma/talk2knowledgegraphs/utils/extractions/__init__.py +4 -5
  171. aiagents4pharma/talk2knowledgegraphs/utils/extractions/milvus_multimodal_pcst.py +14 -34
  172. aiagents4pharma/talk2knowledgegraphs/utils/extractions/multimodal_pcst.py +53 -47
  173. aiagents4pharma/talk2knowledgegraphs/utils/extractions/pcst.py +18 -14
  174. aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py +22 -23
  175. aiagents4pharma/talk2knowledgegraphs/utils/pubchem_utils.py +11 -10
  176. aiagents4pharma/talk2scholars/.dockerignore +13 -0
  177. aiagents4pharma/talk2scholars/Dockerfile +104 -0
  178. aiagents4pharma/talk2scholars/README.md +1 -0
  179. aiagents4pharma/talk2scholars/agents/__init__.py +1 -5
  180. aiagents4pharma/talk2scholars/agents/main_agent.py +6 -4
  181. aiagents4pharma/talk2scholars/agents/paper_download_agent.py +5 -4
  182. aiagents4pharma/talk2scholars/agents/pdf_agent.py +4 -2
  183. aiagents4pharma/talk2scholars/agents/s2_agent.py +2 -2
  184. aiagents4pharma/talk2scholars/agents/zotero_agent.py +10 -11
  185. aiagents4pharma/talk2scholars/configs/__init__.py +1 -3
  186. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py +1 -4
  187. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +1 -1
  188. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +1 -1
  189. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +8 -8
  190. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +7 -7
  191. aiagents4pharma/talk2scholars/configs/tools/__init__.py +8 -6
  192. aiagents4pharma/talk2scholars/docker-compose/cpu/.env.example +21 -0
  193. aiagents4pharma/talk2scholars/docker-compose/cpu/docker-compose.yml +90 -0
  194. aiagents4pharma/talk2scholars/docker-compose/gpu/.env.example +21 -0
  195. aiagents4pharma/talk2scholars/docker-compose/gpu/docker-compose.yml +105 -0
  196. aiagents4pharma/talk2scholars/install.md +122 -0
  197. aiagents4pharma/talk2scholars/state/state_talk2scholars.py +8 -8
  198. aiagents4pharma/talk2scholars/tests/{test_main_agent.py → test_agents_main_agent.py} +41 -23
  199. aiagents4pharma/talk2scholars/tests/{test_paper_download_agent.py → test_agents_paper_agents_download_agent.py} +10 -16
  200. aiagents4pharma/talk2scholars/tests/{test_pdf_agent.py → test_agents_pdf_agent.py} +6 -10
  201. aiagents4pharma/talk2scholars/tests/{test_s2_agent.py → test_agents_s2_agent.py} +8 -16
  202. aiagents4pharma/talk2scholars/tests/{test_zotero_agent.py → test_agents_zotero_agent.py} +5 -7
  203. aiagents4pharma/talk2scholars/tests/{test_s2_display_dataframe.py → test_s2_tools_display_dataframe.py} +6 -7
  204. aiagents4pharma/talk2scholars/tests/{test_s2_query_dataframe.py → test_s2_tools_query_dataframe.py} +5 -15
  205. aiagents4pharma/talk2scholars/tests/{test_paper_downloader.py → test_tools_paper_downloader.py} +25 -63
  206. aiagents4pharma/talk2scholars/tests/{test_question_and_answer_tool.py → test_tools_question_and_answer_tool.py} +2 -6
  207. aiagents4pharma/talk2scholars/tests/{test_s2_multi.py → test_tools_s2_multi.py} +5 -5
  208. aiagents4pharma/talk2scholars/tests/{test_s2_retrieve.py → test_tools_s2_retrieve.py} +2 -1
  209. aiagents4pharma/talk2scholars/tests/{test_s2_search.py → test_tools_s2_search.py} +5 -5
  210. aiagents4pharma/talk2scholars/tests/{test_s2_single.py → test_tools_s2_single.py} +5 -5
  211. aiagents4pharma/talk2scholars/tests/{test_arxiv_downloader.py → test_utils_arxiv_downloader.py} +16 -25
  212. aiagents4pharma/talk2scholars/tests/{test_base_paper_downloader.py → test_utils_base_paper_downloader.py} +25 -47
  213. aiagents4pharma/talk2scholars/tests/{test_biorxiv_downloader.py → test_utils_biorxiv_downloader.py} +14 -42
  214. aiagents4pharma/talk2scholars/tests/{test_medrxiv_downloader.py → test_utils_medrxiv_downloader.py} +15 -49
  215. aiagents4pharma/talk2scholars/tests/{test_nvidia_nim_reranker.py → test_utils_nvidia_nim_reranker.py} +6 -16
  216. aiagents4pharma/talk2scholars/tests/{test_pdf_answer_formatter.py → test_utils_pdf_answer_formatter.py} +1 -0
  217. aiagents4pharma/talk2scholars/tests/{test_pdf_batch_processor.py → test_utils_pdf_batch_processor.py} +6 -15
  218. aiagents4pharma/talk2scholars/tests/{test_pdf_collection_manager.py → test_utils_pdf_collection_manager.py} +34 -11
  219. aiagents4pharma/talk2scholars/tests/{test_pdf_document_processor.py → test_utils_pdf_document_processor.py} +2 -3
  220. aiagents4pharma/talk2scholars/tests/{test_pdf_generate_answer.py → test_utils_pdf_generate_answer.py} +3 -6
  221. aiagents4pharma/talk2scholars/tests/{test_pdf_gpu_detection.py → test_utils_pdf_gpu_detection.py} +5 -16
  222. aiagents4pharma/talk2scholars/tests/{test_pdf_rag_pipeline.py → test_utils_pdf_rag_pipeline.py} +7 -17
  223. aiagents4pharma/talk2scholars/tests/{test_pdf_retrieve_chunks.py → test_utils_pdf_retrieve_chunks.py} +4 -11
  224. aiagents4pharma/talk2scholars/tests/{test_pdf_singleton_manager.py → test_utils_pdf_singleton_manager.py} +26 -23
  225. aiagents4pharma/talk2scholars/tests/{test_pdf_vector_normalization.py → test_utils_pdf_vector_normalization.py} +1 -1
  226. aiagents4pharma/talk2scholars/tests/{test_pdf_vector_store.py → test_utils_pdf_vector_store.py} +27 -55
  227. aiagents4pharma/talk2scholars/tests/{test_pubmed_downloader.py → test_utils_pubmed_downloader.py} +31 -91
  228. aiagents4pharma/talk2scholars/tests/{test_read_helper_utils.py → test_utils_read_helper_utils.py} +2 -6
  229. aiagents4pharma/talk2scholars/tests/{test_s2_utils_ext_ids.py → test_utils_s2_utils_ext_ids.py} +5 -15
  230. aiagents4pharma/talk2scholars/tests/{test_zotero_human_in_the_loop.py → test_utils_zotero_human_in_the_loop.py} +6 -13
  231. aiagents4pharma/talk2scholars/tests/{test_zotero_path.py → test_utils_zotero_path.py} +53 -45
  232. aiagents4pharma/talk2scholars/tests/{test_zotero_read.py → test_utils_zotero_read.py} +30 -91
  233. aiagents4pharma/talk2scholars/tests/{test_zotero_write.py → test_utils_zotero_write.py} +6 -16
  234. aiagents4pharma/talk2scholars/tools/__init__.py +1 -4
  235. aiagents4pharma/talk2scholars/tools/paper_download/paper_downloader.py +20 -35
  236. aiagents4pharma/talk2scholars/tools/paper_download/utils/__init__.py +7 -5
  237. aiagents4pharma/talk2scholars/tools/paper_download/utils/arxiv_downloader.py +9 -11
  238. aiagents4pharma/talk2scholars/tools/paper_download/utils/base_paper_downloader.py +14 -21
  239. aiagents4pharma/talk2scholars/tools/paper_download/utils/biorxiv_downloader.py +14 -22
  240. aiagents4pharma/talk2scholars/tools/paper_download/utils/medrxiv_downloader.py +11 -13
  241. aiagents4pharma/talk2scholars/tools/paper_download/utils/pubmed_downloader.py +14 -28
  242. aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +4 -8
  243. aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +16 -14
  244. aiagents4pharma/talk2scholars/tools/pdf/utils/answer_formatter.py +4 -4
  245. aiagents4pharma/talk2scholars/tools/pdf/utils/batch_processor.py +15 -17
  246. aiagents4pharma/talk2scholars/tools/pdf/utils/collection_manager.py +2 -2
  247. aiagents4pharma/talk2scholars/tools/pdf/utils/document_processor.py +5 -5
  248. aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +4 -4
  249. aiagents4pharma/talk2scholars/tools/pdf/utils/get_vectorstore.py +2 -6
  250. aiagents4pharma/talk2scholars/tools/pdf/utils/gpu_detection.py +5 -9
  251. aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +4 -4
  252. aiagents4pharma/talk2scholars/tools/pdf/utils/paper_loader.py +2 -2
  253. aiagents4pharma/talk2scholars/tools/pdf/utils/rag_pipeline.py +6 -15
  254. aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +7 -15
  255. aiagents4pharma/talk2scholars/tools/pdf/utils/singleton_manager.py +2 -2
  256. aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +3 -4
  257. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_normalization.py +8 -17
  258. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +17 -33
  259. aiagents4pharma/talk2scholars/tools/s2/__init__.py +8 -6
  260. aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +3 -7
  261. aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +7 -6
  262. aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +5 -12
  263. aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +2 -4
  264. aiagents4pharma/talk2scholars/tools/s2/search.py +6 -6
  265. aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +5 -3
  266. aiagents4pharma/talk2scholars/tools/s2/utils/__init__.py +1 -3
  267. aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +12 -18
  268. aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +11 -18
  269. aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +11 -16
  270. aiagents4pharma/talk2scholars/tools/zotero/__init__.py +1 -4
  271. aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py +1 -4
  272. aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +21 -39
  273. aiagents4pharma/talk2scholars/tools/zotero/utils/review_helper.py +2 -6
  274. aiagents4pharma/talk2scholars/tools/zotero/utils/write_helper.py +8 -11
  275. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py +4 -12
  276. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_pdf_downloader.py +13 -27
  277. aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +4 -7
  278. aiagents4pharma/talk2scholars/tools/zotero/zotero_review.py +8 -10
  279. aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py +3 -2
  280. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/METADATA +115 -51
  281. aiagents4pharma-1.45.1.dist-info/RECORD +324 -0
  282. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/WHEEL +1 -2
  283. aiagents4pharma-1.44.0.dist-info/RECORD +0 -293
  284. aiagents4pharma-1.44.0.dist-info/top_level.txt +0 -1
  285. /aiagents4pharma/talk2scholars/tests/{test_state.py → test_states_state.py} +0 -0
  286. /aiagents4pharma/talk2scholars/tests/{test_pdf_paper_loader.py → test_utils_pdf_paper_loader.py} +0 -0
  287. /aiagents4pharma/talk2scholars/tests/{test_tool_helper_utils.py → test_utils_tool_helper_utils.py} +0 -0
  288. /aiagents4pharma/talk2scholars/tests/{test_zotero_pdf_downloader_utils.py → test_utils_zotero_pdf_downloader_utils.py} +0 -0
  289. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/licenses/LICENSE +0 -0
@@ -4,11 +4,15 @@ Tests the supervisor agent's routing logic and state management.
4
4
  """
5
5
 
6
6
  from types import SimpleNamespace
7
- import pytest
7
+
8
8
  import hydra
9
+ import pytest
9
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
10
13
  from langchain_openai import ChatOpenAI
11
14
  from pydantic import Field
15
+
12
16
  from aiagents4pharma.talk2scholars.agents.main_agent import get_app
13
17
 
14
18
  # --- Dummy LLM Implementation ---
@@ -19,16 +23,23 @@ class DummyLLM(BaseChatModel):
19
23
 
20
24
  model_name: str = Field(...)
21
25
 
22
- def _generate(self, prompt, stop=None):
23
- """Generate a response given a prompt."""
24
- DummyLLM.called_prompt = prompt
25
- return "dummy output"
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])
26
32
 
27
33
  @property
28
34
  def _llm_type(self):
29
35
  """Return the type of the language model."""
30
36
  return "dummy"
31
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
+
32
43
 
33
44
  # --- Dummy Workflow and Sub-agent Functions ---
34
45
 
@@ -105,17 +116,12 @@ class DummyHydraContext:
105
116
  def dict_to_namespace(d):
106
117
  """Convert a dictionary to a SimpleNamespace object."""
107
118
  return SimpleNamespace(
108
- **{
109
- key: dict_to_namespace(val) if isinstance(val, dict) else val
110
- for key, val in d.items()
111
- }
119
+ **{key: dict_to_namespace(val) if isinstance(val, dict) else val for key, val in d.items()}
112
120
  )
113
121
 
114
122
 
115
123
  dummy_config = {
116
- "agents": {
117
- "talk2scholars": {"main_agent": {"system_prompt": "Dummy system prompt"}}
118
- }
124
+ "agents": {"talk2scholars": {"main_agent": {"system_prompt": "Dummy system prompt"}}}
119
125
  }
120
126
 
121
127
 
@@ -141,9 +147,7 @@ class DummyHydraCompose:
141
147
  @pytest.fixture(autouse=True)
142
148
  def patch_hydra(monkeypatch):
143
149
  """Patch the hydra.initialize and hydra.compose functions to return dummy objects."""
144
- monkeypatch.setattr(
145
- hydra, "initialize", lambda version_base, config_path: DummyHydraContext()
146
- )
150
+ monkeypatch.setattr(hydra, "initialize", lambda version_base, config_path: DummyHydraContext())
147
151
  monkeypatch.setattr(
148
152
  hydra, "compose", lambda config_name, overrides: DummyHydraCompose(dummy_config)
149
153
  )
@@ -153,9 +157,7 @@ def dummy_paper_download_agent(uniq_id, llm_model):
153
157
  """Return a DummyWorkflow for the paper download agent."""
154
158
  dummy_paper_download_agent.called_uniq_id = uniq_id
155
159
  dummy_paper_download_agent.called_llm_model = llm_model
156
- return DummyWorkflow(
157
- supervisor_args={"agent": "paper_download", "uniq_id": uniq_id}
158
- )
160
+ return DummyWorkflow(supervisor_args={"agent": "paper_download", "uniq_id": uniq_id})
159
161
 
160
162
 
161
163
  @pytest.fixture(autouse=True)
@@ -186,16 +188,30 @@ def patch_sub_agents_and_supervisor(monkeypatch):
186
188
 
187
189
 
188
190
  def test_dummy_llm_generate():
189
- """Test the dummy LLM's generate function."""
191
+ """Test the dummy LLM's generate function through public interface."""
190
192
  dummy = DummyLLM(model_name="test-model")
191
- output = getattr(dummy, "_generate")("any prompt")
192
- assert output == "dummy output"
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"
193
203
 
194
204
 
195
205
  def test_dummy_llm_llm_type():
196
- """Test the dummy LLM's _llm_type property."""
206
+ """Test the dummy LLM's type identification."""
197
207
  dummy = DummyLLM(model_name="test-model")
198
- assert getattr(dummy, "_llm_type") == "dummy"
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__)
199
215
 
200
216
 
201
217
  def test_get_app_with_gpt4o_mini():
@@ -225,12 +241,14 @@ def test_get_app_with_other_model():
225
241
  assert supervisor_args.get("prompt") == "Dummy system prompt"
226
242
  assert getattr(app, "name", "") == "Talk2Scholars_MainAgent"
227
243
 
244
+
228
245
  def test_dummy_workflow_get_supervisor_args():
229
246
  """Test that DummyWorkflow.get_supervisor_args returns the stored args."""
230
247
  dummy_args = {"agent": "test", "uniq_id": "id123"}
231
248
  wf = DummyWorkflow(supervisor_args=dummy_args)
232
249
  assert wf.get_supervisor_args() is dummy_args
233
250
 
251
+
234
252
  def test_dummy_hydra_compose_get_config():
235
253
  """Test that DummyHydraCompose.get_config returns the raw config."""
236
254
  config_dict = {"agents": {"test": {"key": "value"}}}
@@ -1,9 +1,11 @@
1
1
  """Unit tests for the paper download agent in Talk2Scholars."""
2
2
 
3
3
  from unittest import mock
4
+
4
5
  import pytest
5
- from langchain_core.messages import HumanMessage, AIMessage
6
6
  from langchain_core.language_models.chat_models import BaseChatModel
7
+ from langchain_core.messages import AIMessage, HumanMessage
8
+
7
9
  from ..agents.paper_download_agent import get_app
8
10
  from ..state.state_talk2scholars import Talk2Scholars
9
11
 
@@ -13,9 +15,7 @@ def mock_hydra_fixture():
13
15
  """Mocks Hydra configuration for tests."""
14
16
  with mock.patch("hydra.initialize"), mock.patch("hydra.compose") as mock_compose:
15
17
  cfg_mock = mock.MagicMock()
16
- cfg_mock.agents.talk2scholars.paper_download_agent.paper_download_agent = (
17
- "Test prompt"
18
- )
18
+ cfg_mock.agents.talk2scholars.paper_download_agent.paper_download_agent = "Test prompt"
19
19
  mock_compose.return_value = cfg_mock
20
20
  yield mock_compose
21
21
 
@@ -26,9 +26,7 @@ def mock_tools_fixture():
26
26
  with mock.patch(
27
27
  "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.download_papers"
28
28
  ) as mock_download_papers:
29
- mock_download_papers.return_value = {
30
- "article_data": {"dummy_key": "dummy_value"}
31
- }
29
+ mock_download_papers.return_value = {"article_data": {"dummy_key": "dummy_value"}}
32
30
  yield [mock_download_papers]
33
31
 
34
32
 
@@ -52,9 +50,7 @@ def test_paper_download_agent_invocation():
52
50
  """Verifies agent processes queries and updates state correctly."""
53
51
  _ = mock_tools_fixture # Prevents unused-argument warning
54
52
  thread_id = "test_thread_paper_dl"
55
- mock_state = Talk2Scholars(
56
- messages=[HumanMessage(content="Download paper 1234.5678")]
57
- )
53
+ mock_state = Talk2Scholars(messages=[HumanMessage(content="Download paper 1234.5678")])
58
54
  llm_mock = mock.Mock(spec=BaseChatModel)
59
55
 
60
56
  with mock.patch(
@@ -108,9 +104,7 @@ def test_paper_download_agent_tools_assignment(
108
104
  # Verify ToolNode was called with download_papers function
109
105
  assert mock_toolnode.called
110
106
  # Check that ToolNode was called with a list containing the download_papers tool
111
- call_args = mock_toolnode.call_args[0][
112
- 0
113
- ] # Get first positional argument (the tools list)
107
+ call_args = mock_toolnode.call_args[0][0] # Get first positional argument (the tools list)
114
108
  assert len(call_args) == 1
115
109
  # The tool should be a StructuredTool with name 'download_papers'
116
110
  tool = call_args[0]
@@ -140,6 +134,6 @@ def test_paper_download_agent_model_failure():
140
134
  ):
141
135
  with pytest.raises(Exception) as exc_info:
142
136
  get_app(thread_id, llm_mock)
143
- assert "Mock model failure" in str(
144
- exc_info.value
145
- ), "Model initialization failure should raise an exception."
137
+ assert "Mock model failure" in str(exc_info.value), (
138
+ "Model initialization failure should raise an exception."
139
+ )
@@ -3,8 +3,10 @@ Unit Tests for the PDF agent.
3
3
  """
4
4
 
5
5
  from unittest import mock
6
+
6
7
  import pytest
7
- from langchain_core.messages import HumanMessage, AIMessage
8
+ from langchain_core.messages import AIMessage, HumanMessage
9
+
8
10
  from ..agents.pdf_agent import get_app
9
11
  from ..state.state_talk2scholars import Talk2Scholars
10
12
 
@@ -29,9 +31,7 @@ def mock_tools_fixture():
29
31
  "aiagents4pharma.talk2scholars.agents.pdf_agent.question_and_answer"
30
32
  ) as mock_question_and_answer,
31
33
  ):
32
- mock_question_and_answer.return_value = {
33
- "result": "Mock Question and Answer Result"
34
- }
34
+ mock_question_and_answer.return_value = {"result": "Mock Question and Answer Result"}
35
35
  yield [mock_question_and_answer]
36
36
 
37
37
 
@@ -58,9 +58,7 @@ def test_pdf_agent_invocation(mock_llm):
58
58
  """Test that the PDF agent processes user input and returns a valid response."""
59
59
  thread_id = "test_thread"
60
60
  # Create a sample state with a human message.
61
- mock_state = Talk2Scholars(
62
- messages=[HumanMessage(content="Extract key data from PDF")]
63
- )
61
+ mock_state = Talk2Scholars(messages=[HumanMessage(content="Extract key data from PDF")])
64
62
  with mock.patch(
65
63
  "aiagents4pharma.talk2scholars.agents.pdf_agent.create_react_agent"
66
64
  ) as mock_create:
@@ -95,9 +93,7 @@ def test_pdf_agent_tools_assignment(request, mock_llm):
95
93
  mock.patch(
96
94
  "aiagents4pharma.talk2scholars.agents.pdf_agent.create_react_agent"
97
95
  ) as mock_create,
98
- mock.patch(
99
- "aiagents4pharma.talk2scholars.agents.pdf_agent.ToolNode"
100
- ) as mock_toolnode,
96
+ mock.patch("aiagents4pharma.talk2scholars.agents.pdf_agent.ToolNode") as mock_toolnode,
101
97
  ):
102
98
  mock_agent = mock.Mock()
103
99
  mock_create.return_value = mock_agent
@@ -3,9 +3,11 @@ Updated Unit Tests for the S2 agent (Semantic Scholar sub-agent).
3
3
  """
4
4
 
5
5
  from unittest import mock
6
+
6
7
  import pytest
7
- from langchain_core.messages import HumanMessage, AIMessage
8
+ from langchain_core.messages import AIMessage, HumanMessage
8
9
  from langchain_openai import ChatOpenAI
10
+
9
11
  from ..agents.s2_agent import get_app
10
12
  from ..state.state_talk2scholars import Talk2Scholars
11
13
 
@@ -27,9 +29,7 @@ def mock_hydra_fixture():
27
29
  def mock_tools_fixture():
28
30
  """Mock tools to prevent execution of real API calls."""
29
31
  with (
30
- mock.patch(
31
- "aiagents4pharma.talk2scholars.tools.s2.search.search_tool"
32
- ) as mock_s2_search,
32
+ mock.patch("aiagents4pharma.talk2scholars.tools.s2.search.search_tool") as mock_s2_search,
33
33
  mock.patch(
34
34
  "aiagents4pharma.talk2scholars.tools.s2.display_dataframe.display_dataframe"
35
35
  ) as mock_s2_display,
@@ -115,9 +115,7 @@ def test_s2_agent_tools_assignment(request):
115
115
  mock.patch(
116
116
  "aiagents4pharma.talk2scholars.agents.s2_agent.create_react_agent"
117
117
  ) as mock_create,
118
- mock.patch(
119
- "aiagents4pharma.talk2scholars.agents.s2_agent.ToolNode"
120
- ) as mock_toolnode,
118
+ mock.patch("aiagents4pharma.talk2scholars.agents.s2_agent.ToolNode") as mock_toolnode,
121
119
  ):
122
120
  mock_agent = mock.Mock()
123
121
  mock_create.return_value = mock_agent
@@ -132,9 +130,7 @@ def test_s2_agent_tools_assignment(request):
132
130
  def test_s2_query_dataframe_tool():
133
131
  """Test if the query_dataframe tool is correctly utilized by the agent."""
134
132
  thread_id = "test_thread"
135
- mock_state = Talk2Scholars(
136
- messages=[HumanMessage(content="Query results for AI papers")]
137
- )
133
+ mock_state = Talk2Scholars(messages=[HumanMessage(content="Query results for AI papers")])
138
134
  with mock.patch(
139
135
  "aiagents4pharma.talk2scholars.agents.s2_agent.create_react_agent"
140
136
  ) as mock_create:
@@ -166,9 +162,7 @@ def test_s2_query_dataframe_tool():
166
162
  def test_s2_retrieve_id_tool():
167
163
  """Test if the retrieve_semantic_scholar_paper_id tool is correctly utilized by the agent."""
168
164
  thread_id = "test_thread"
169
- mock_state = Talk2Scholars(
170
- messages=[HumanMessage(content="Retrieve paper ID for AI research")]
171
- )
165
+ mock_state = Talk2Scholars(messages=[HumanMessage(content="Retrieve paper ID for AI research")])
172
166
  with mock.patch(
173
167
  "aiagents4pharma.talk2scholars.agents.s2_agent.create_react_agent"
174
168
  ) as mock_create:
@@ -177,9 +171,7 @@ def test_s2_retrieve_id_tool():
177
171
  mock_agent.invoke.return_value = {
178
172
  "messages": [HumanMessage(content="Retrieve paper ID for AI research")],
179
173
  "last_displayed_papers": {},
180
- "papers": {
181
- "paper_id": "MockPaper123"
182
- }, # Ensure 'paper_id' is inside 'papers'
174
+ "papers": {"paper_id": "MockPaper123"}, # Ensure 'paper_id' is inside 'papers'
183
175
  "multi_papers": {},
184
176
  }
185
177
  app = get_app(thread_id, llm_model=LLM_MODEL)
@@ -3,9 +3,11 @@ Updated Unit Tests for the Zotero agent (Zotero Library Managent sub-agent).
3
3
  """
4
4
 
5
5
  from unittest import mock
6
+
6
7
  import pytest
7
- from langchain_core.messages import HumanMessage, AIMessage
8
+ from langchain_core.messages import AIMessage, HumanMessage
8
9
  from langchain_openai import ChatOpenAI
10
+
9
11
  from ..agents.zotero_agent import get_app
10
12
  from ..state.state_talk2scholars import Talk2Scholars
11
13
 
@@ -105,9 +107,7 @@ def test_zotero_agent_tools_assignment(request):
105
107
  mock.patch(
106
108
  "aiagents4pharma.talk2scholars.agents.zotero_agent.create_react_agent"
107
109
  ) as mock_create,
108
- mock.patch(
109
- "aiagents4pharma.talk2scholars.agents.zotero_agent.ToolNode"
110
- ) as mock_toolnode,
110
+ mock.patch("aiagents4pharma.talk2scholars.agents.zotero_agent.ToolNode") as mock_toolnode,
111
111
  ):
112
112
  mock_agent = mock.Mock()
113
113
  mock_create.return_value = mock_agent
@@ -122,9 +122,7 @@ def test_zotero_agent_tools_assignment(request):
122
122
  def test_s2_query_dataframe_tool():
123
123
  """Test if the query_dataframe tool is correctly utilized by the agent."""
124
124
  thread_id = "test_thread"
125
- mock_state = Talk2Scholars(
126
- messages=[HumanMessage(content="Query results for AI papers")]
127
- )
125
+ mock_state = Talk2Scholars(messages=[HumanMessage(content="Query results for AI papers")])
128
126
  with mock.patch(
129
127
  "aiagents4pharma.talk2scholars.agents.zotero_agent.create_react_agent"
130
128
  ) as mock_create:
@@ -4,10 +4,13 @@ Unit tests for S2 tools functionality.
4
4
 
5
5
  import pytest
6
6
  from langgraph.types import Command
7
+
7
8
  from ..tools.s2.display_dataframe import (
8
- display_dataframe,
9
9
  NoPapersFoundError as raised_error,
10
10
  )
11
+ from ..tools.s2.display_dataframe import (
12
+ display_dataframe,
13
+ )
11
14
 
12
15
 
13
16
  @pytest.fixture(name="initial_state")
@@ -51,9 +54,7 @@ class TestS2Tools:
51
54
  raised_error,
52
55
  match="No papers found. A search/rec needs to be performed first.",
53
56
  ):
54
- display_dataframe.invoke(
55
- {"state": initial_state, "tool_call_id": "test123"}
56
- )
57
+ display_dataframe.invoke({"state": initial_state, "tool_call_id": "test123"})
57
58
 
58
59
  def test_display_dataframe_shows_papers(self, initial_state):
59
60
  """Verifies display_dataframe tool correctly returns papers from state"""
@@ -61,9 +62,7 @@ class TestS2Tools:
61
62
  state["last_displayed_papers"] = "papers"
62
63
  state["papers"] = MOCK_STATE_PAPER
63
64
 
64
- result = display_dataframe.invoke(
65
- input={"state": state, "tool_call_id": "test123"}
66
- )
65
+ result = display_dataframe.invoke(input={"state": state, "tool_call_id": "test123"})
67
66
 
68
67
  assert isinstance(result, Command) # Expect a Command object
69
68
  assert isinstance(result.update, dict) # Ensure update is a dictionary
@@ -59,9 +59,7 @@ class TestS2Tools:
59
59
  ):
60
60
  query_dataframe.run(tool_input)
61
61
 
62
- @patch(
63
- "aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent"
64
- )
62
+ @patch("aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent")
65
63
  def test_query_dataframe_with_papers(self, mock_create_agent, initial_state):
66
64
  """Tests querying papers when data is available."""
67
65
  state = initial_state.copy()
@@ -72,9 +70,7 @@ class TestS2Tools:
72
70
  mock_agent = MagicMock()
73
71
  mock_agent.invoke.return_value = {"output": "Mocked response"}
74
72
 
75
- mock_create_agent.return_value = (
76
- mock_agent # Mock the function returning the agent
77
- )
73
+ mock_create_agent.return_value = mock_agent # Mock the function returning the agent
78
74
 
79
75
  # Ensure that the output of query_dataframe is correctly structured
80
76
  # Invoke the tool with a test tool_call_id
@@ -94,9 +90,7 @@ class TestS2Tools:
94
90
  assert isinstance(msg, ToolMessage)
95
91
  assert msg.content == "Mocked response"
96
92
 
97
- @patch(
98
- "aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent"
99
- )
93
+ @patch("aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent")
100
94
  def test_query_dataframe_direct_mapping(self, mock_create_agent, initial_state):
101
95
  """Tests query_dataframe when last_displayed_papers is a direct dict mapping."""
102
96
  # Prepare state with direct mapping
@@ -143,9 +137,7 @@ class TestS2Tools:
143
137
  query_dataframe.run(tool_input)
144
138
  assert "Could not resolve a valid metadata dictionary" in str(exc.value)
145
139
 
146
- @patch(
147
- "aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent"
148
- )
140
+ @patch("aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent")
149
141
  def test_query_dataframe_extract_ids(self, mock_create_agent):
150
142
  """Test extract_ids returns the raw list or single element correctly."""
151
143
  # Prepare state with fake paper_ids column
@@ -159,9 +151,7 @@ class TestS2Tools:
159
151
  state[state_key] = dic # simulate indirect mapping
160
152
  # Mock agent to echo the Python expression
161
153
  mock_agent = MagicMock()
162
- mock_agent.invoke.side_effect = lambda args, stream_mode=None: {
163
- "output": args["input"]
164
- }
154
+ mock_agent.invoke.side_effect = lambda args, stream_mode=None: {"output": args["input"]}
165
155
  mock_create_agent.return_value = mock_agent
166
156
  # Test full list
167
157
  tool_input = {
@@ -33,12 +33,14 @@ from aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader import
33
33
  # --- tiny helpers to manipulate factory state without protected-access lint ---
34
34
  def _set_cached_config(value):
35
35
  """set cached config in the factory for testing purposes."""
36
- setattr(PaperDownloaderFactory, "_cached_config", value)
36
+ attr_name = "_cached_config"
37
+ setattr(PaperDownloaderFactory, attr_name, value)
37
38
 
38
39
 
39
40
  def _set_config_lock(lock_obj):
40
41
  """set the config lock object in the factory for testing purposes."""
41
- setattr(PaperDownloaderFactory, "_config_lock", lock_obj)
42
+ attr_name = "_config_lock"
43
+ setattr(PaperDownloaderFactory, attr_name, lock_obj)
42
44
 
43
45
 
44
46
  class PaperDownloaderFactoryTestShim(PaperDownloaderFactory):
@@ -121,16 +123,10 @@ class TestPaperDownloaderFactory(unittest.TestCase):
121
123
  """tear down after each test."""
122
124
  PaperDownloaderFactory.clear_cache()
123
125
 
124
- @patch(
125
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.ArxivDownloader"
126
- )
126
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.ArxivDownloader")
127
127
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
128
- @patch(
129
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
130
- )
131
- def test_create_arxiv_and_cached_config(
132
- self, mock_global_hydra, mock_hydra, mock_arxiv
133
- ):
128
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
129
+ def test_create_arxiv_and_cached_config(self, mock_global_hydra, mock_hydra, mock_arxiv):
134
130
  """First create loads config, second create returns cached config (no re-init)."""
135
131
  # First call: GlobalHydra not initialized
136
132
  mock_global_hydra.return_value.is_initialized.return_value = False
@@ -156,16 +152,10 @@ class TestPaperDownloaderFactory(unittest.TestCase):
156
152
  mock_hydra.initialize.assert_not_called()
157
153
  mock_hydra.compose.assert_not_called()
158
154
 
159
- @patch(
160
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.MedrxivDownloader"
161
- )
155
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.MedrxivDownloader")
162
156
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
163
- @patch(
164
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
165
- )
166
- @patch(
167
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.OmegaConf"
168
- )
157
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
158
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.OmegaConf")
169
159
  def test_create_medrxiv_omegaconf_and_clear_existing(
170
160
  self, mock_omegaconf, mock_global_hydra, mock_hydra, mock_medrxiv
171
161
  ):
@@ -195,16 +185,10 @@ class TestPaperDownloaderFactory(unittest.TestCase):
195
185
  self.assertEqual(cfg_d["api_url"], "https://med")
196
186
  self.assertEqual(cfg_d["pdf_url_template"], "T")
197
187
 
198
- @patch(
199
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.BiorxivDownloader"
200
- )
188
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.BiorxivDownloader")
201
189
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
202
- @patch(
203
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
204
- )
205
- def test_create_biorxiv_dir_fallback(
206
- self, mock_global_hydra, mock_hydra, mock_biorxiv
207
- ):
190
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
191
+ def test_create_biorxiv_dir_fallback(self, mock_global_hydra, mock_hydra, mock_biorxiv):
208
192
  """dir() fallback path with __slots__ object should populate public, skip private."""
209
193
  mock_global_hydra.return_value.is_initialized.return_value = False
210
194
  common_obj = _SlotsSource(public_val=30, private_val="hide")
@@ -221,13 +205,9 @@ class TestPaperDownloaderFactory(unittest.TestCase):
221
205
  # Ensure private key not present
222
206
  self.assertNotIn("_private", cfg_d)
223
207
 
224
- @patch(
225
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.PubmedDownloader"
226
- )
208
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.PubmedDownloader")
227
209
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
228
- @patch(
229
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
230
- )
210
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
231
211
  def test_create_pubmed_apply_config_warning_path(
232
212
  self, mock_global_hydra, mock_hydra, mock_pubmed
233
213
  ):
@@ -236,9 +216,7 @@ class TestPaperDownloaderFactory(unittest.TestCase):
236
216
  # First (common) will raise inside _extract_from_items -> warning
237
217
  common_obj = _ExplodingItemsSlots()
238
218
  # Service path is sane to still build config
239
- svc_obj = SimpleNamespace(
240
- api_url="https://pubmed", request_timeout=55, chunk_size=1024
241
- )
219
+ svc_obj = SimpleNamespace(api_url="https://pubmed", request_timeout=55, chunk_size=1024)
242
220
  mock_hydra.compose.return_value = _cfg_obj(common_obj, {"pubmed": svc_obj})
243
221
 
244
222
  with patch(
@@ -255,26 +233,18 @@ class TestPaperDownloaderFactory(unittest.TestCase):
255
233
  self.assertEqual(cfg_d["chunk_size"], 1024)
256
234
 
257
235
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
258
- @patch(
259
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
260
- )
236
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
261
237
  def test_create_missing_service_error_message(self, mock_global_hydra, mock_hydra):
262
238
  """Missing service should raise ValueError with 'Service ... not found' message."""
263
239
  mock_global_hydra.return_value.is_initialized.return_value = False
264
240
  mock_hydra.compose.return_value = _cfg_obj(SimpleNamespace(), {"arxiv": {}})
265
241
  with self.assertRaises(ValueError) as ctx:
266
242
  PaperDownloaderFactory.create("unsupported")
267
- self.assertIn(
268
- "Service 'unsupported' not found in configuration", str(ctx.exception)
269
- )
243
+ self.assertIn("Service 'unsupported' not found in configuration", str(ctx.exception))
270
244
 
271
245
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
272
- @patch(
273
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
274
- )
275
- def test_get_unified_config_failure_raises_runtimeerror(
276
- self, mock_global_hydra, mock_hydra
277
- ):
246
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
247
+ def test_get_unified_config_failure_raises_runtimeerror(self, mock_global_hydra, mock_hydra):
278
248
  """Hydra initialize failure should surface as RuntimeError from create()."""
279
249
  PaperDownloaderFactory.clear_cache()
280
250
  mock_global_hydra.return_value.is_initialized.return_value = False
@@ -400,9 +370,7 @@ class TestDownloadPapersFunction(unittest.TestCase):
400
370
  self.assertTrue(result.update["ok"])
401
371
 
402
372
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
403
- @patch(
404
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
405
- )
373
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
406
374
  def test_get_default_service_functionality(self, mock_global_hydra, mock_hydra):
407
375
  """Test get_default_service method with various configurations."""
408
376
  mock_global_hydra.return_value.is_initialized.return_value = False
@@ -411,14 +379,12 @@ class TestDownloadPapersFunction(unittest.TestCase):
411
379
  common_cfg = SimpleNamespace(request_timeout=30, chunk_size=8192)
412
380
  services = {
413
381
  "arxiv": SimpleNamespace(api_url="https://arxiv.org"),
414
- "pubmed": SimpleNamespace(id_converter_url="https://pmc.ncbi.nlm.nih.gov")
382
+ "pubmed": SimpleNamespace(id_converter_url="https://pmc.ncbi.nlm.nih.gov"),
415
383
  }
416
384
  tool_cfg = SimpleNamespace(default_service="arxiv")
417
385
  mock_hydra.compose.return_value = SimpleNamespace(
418
386
  tools=SimpleNamespace(
419
- paper_download=SimpleNamespace(
420
- tool=tool_cfg, common=common_cfg, services=services
421
- )
387
+ paper_download=SimpleNamespace(tool=tool_cfg, common=common_cfg, services=services)
422
388
  )
423
389
  )
424
390
 
@@ -449,9 +415,7 @@ class TestDownloadPapersFunction(unittest.TestCase):
449
415
  tool_cfg.default_service = "medrxiv"
450
416
  mock_hydra.compose.return_value = SimpleNamespace(
451
417
  tools=SimpleNamespace(
452
- paper_download=SimpleNamespace(
453
- tool=tool_cfg, common=common_cfg, services=services
454
- )
418
+ paper_download=SimpleNamespace(tool=tool_cfg, common=common_cfg, services=services)
455
419
  )
456
420
  )
457
421
  PaperDownloaderFactory.clear_cache()
@@ -469,9 +433,7 @@ class TestUnifiedConfigDoubleCheck(unittest.TestCase):
469
433
  """Covers the double-check return branch in _get_unified_config."""
470
434
 
471
435
  @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.hydra")
472
- @patch(
473
- "aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra"
474
- )
436
+ @patch("aiagents4pharma.talk2scholars.tools.paper_download.paper_downloader.GlobalHydra")
475
437
  def test_double_check_inside_lock(self, mock_global_hydra, _mock_hydra):
476
438
  """tests the double-check branch in _get_unified_config using public create()."""
477
439
  # avoid real hydra init path if we accidentally go there
@@ -73,9 +73,7 @@ def test_question_and_answer_success(dependencies_fixture, input_fixture):
73
73
  dependencies_fixture["mock_helper_cls"].return_value = mock_helper
74
74
  dependencies_fixture["mock_load_config"].return_value = {"config_key": "value"}
75
75
  dependencies_fixture["mock_get_vectorstore"].return_value = MagicMock()
76
- dependencies_fixture["mock_retrieve_rerank"].return_value = [
77
- {"chunk": "relevant content"}
78
- ]
76
+ dependencies_fixture["mock_retrieve_rerank"].return_value = [{"chunk": "relevant content"}]
79
77
  dependencies_fixture["mock_format_answer"].return_value = "Here is your answer."
80
78
 
81
79
  result = question_and_answer.invoke(input_fixture)
@@ -99,9 +97,7 @@ def test_question_and_answer_no_reranked_chunks(dependencies_fixture, input_fixt
99
97
  dependencies_fixture["mock_load_config"].return_value = {"config_key": "value"}
100
98
  dependencies_fixture["mock_get_vectorstore"].return_value = MagicMock()
101
99
  dependencies_fixture["mock_retrieve_rerank"].return_value = []
102
- dependencies_fixture["mock_format_answer"].return_value = (
103
- "No relevant information found."
104
- )
100
+ dependencies_fixture["mock_format_answer"].return_value = "No relevant information found."
105
101
 
106
102
  result = question_and_answer.invoke(input_fixture)
107
103