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,327 @@
1
+ """
2
+ Vectorstore class for managing PDF embeddings with Milvus.
3
+ Manages GPU normalization and similarity search and MMR operations.
4
+ With automatic handling of COSINE to IP conversion for GPU compatibility.
5
+ Supports both GPU and CPU configurations.
6
+ """
7
+
8
+ import logging
9
+ import os
10
+ import time
11
+ from typing import Any
12
+
13
+ from langchain_core.documents import Document
14
+ from langchain_core.embeddings import Embeddings
15
+ from langchain_milvus import Milvus
16
+
17
+ from .collection_manager import ensure_collection_exists
18
+ from .gpu_detection import (
19
+ detect_nvidia_gpu,
20
+ get_optimal_index_config,
21
+ log_index_configuration,
22
+ )
23
+ from .singleton_manager import VectorstoreSingleton
24
+ from .vector_normalization import wrap_embedding_model_if_needed
25
+
26
+ # Set up logging with configurable level
27
+ log_level = os.environ.get("LOG_LEVEL", "INFO")
28
+ logging.basicConfig(level=getattr(logging, log_level))
29
+ logger = logging.getLogger(__name__)
30
+ logger.setLevel(getattr(logging, log_level))
31
+
32
+
33
+ class Vectorstore:
34
+ """
35
+ Enhanced Vectorstore class with GPU normalization support.
36
+ Automatically handles COSINE -> IP conversion for GPU compatibility.
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ embedding_model: Embeddings,
42
+ metadata_fields: list[str] | None = None,
43
+ config: Any = None,
44
+ ):
45
+ """
46
+ Initialize the document store with Milvus and GPU optimization.
47
+
48
+ Args:
49
+ embedding_model: The embedding model to use
50
+ metadata_fields: Fields to include in document metadata
51
+ config: Configuration object containing Milvus connection details
52
+ """
53
+ self.config = config
54
+ self.metadata_fields = metadata_fields or [
55
+ "title",
56
+ "paper_id",
57
+ "page",
58
+ "chunk_id",
59
+ ]
60
+ self.initialization_time = time.time()
61
+
62
+ # GPU detection with config override (SINGLE CALL)
63
+ self.has_gpu = detect_nvidia_gpu(config)
64
+
65
+ # Additional check for force CPU mode
66
+ if (
67
+ config
68
+ and hasattr(config, "gpu_detection")
69
+ and getattr(config.gpu_detection, "force_cpu_mode", False)
70
+ ):
71
+ logger.info("Running in forced CPU mode (config override)")
72
+ self.has_gpu = False
73
+
74
+ # Determine if we want to use COSINE similarity
75
+ self.use_cosine = True # Default preference
76
+ if config and hasattr(config, "similarity_metric"):
77
+ self.use_cosine = getattr(config.similarity_metric, "use_cosine", True)
78
+
79
+ # Wrap embedding model with normalization if needed for GPU
80
+ self.original_embedding_model = embedding_model
81
+ self.embedding_model = wrap_embedding_model_if_needed(
82
+ embedding_model, self.has_gpu, self.use_cosine
83
+ )
84
+
85
+ # Configure index parameters AFTER determining GPU usage and normalization
86
+ embedding_dim = config.milvus.embedding_dim if config else 768
87
+ self.index_params, self.search_params = get_optimal_index_config(
88
+ self.has_gpu, embedding_dim, self.use_cosine
89
+ )
90
+
91
+ # Log the configuration
92
+ log_index_configuration(self.index_params, self.search_params, self.use_cosine)
93
+
94
+ # Track loaded papers to prevent duplicate loading
95
+ self.loaded_papers = set()
96
+
97
+ # Initialize Milvus connection parameters with environment variable fallback
98
+ self.connection_args = {
99
+ "host": (config.milvus.host if config else os.getenv("MILVUS_HOST", "127.0.0.1")),
100
+ "port": (config.milvus.port if config else int(os.getenv("MILVUS_PORT", "19530"))),
101
+ }
102
+ # Log the connection parameters being used
103
+ logger.info(
104
+ "Using Milvus connection: %s:%s",
105
+ self.connection_args["host"],
106
+ self.connection_args["port"],
107
+ )
108
+ self.collection_name = config.milvus.collection_name if config else "pdf_rag_documents"
109
+ self.db_name = config.milvus.db_name if config else "pdf_rag_db"
110
+
111
+ # Get singleton instance
112
+ self._singleton = VectorstoreSingleton()
113
+
114
+ # Connect to Milvus (reuses existing connection if available)
115
+ self._connect_milvus()
116
+
117
+ # Create collection with proper metric type
118
+ self.collection = ensure_collection_exists(
119
+ self.collection_name, self.config, self.index_params, self.has_gpu
120
+ )
121
+
122
+ # Initialize the LangChain Milvus vector store
123
+ self.vector_store = self._initialize_vector_store()
124
+
125
+ # Load existing papers AFTER vector store is ready
126
+ self._load_existing_paper_ids()
127
+
128
+ # CRITICAL: Load collection into memory/GPU after any existing data is identified
129
+ logger.info(
130
+ "Calling _ensure_collection_loaded() for %s processing...",
131
+ "GPU" if self.has_gpu else "CPU",
132
+ )
133
+ self._ensure_collection_loaded()
134
+
135
+ # Store for document metadata (keeping for compatibility)
136
+ self.documents: dict[str, Document] = {}
137
+ self.paper_metadata: dict[str, dict[str, Any]] = {}
138
+
139
+ # Log final configuration
140
+ metric_info = (
141
+ "IP (normalized for COSINE)"
142
+ if self.has_gpu and self.use_cosine
143
+ else self.index_params["metric_type"]
144
+ )
145
+
146
+ logger.info(
147
+ "Milvus vector store initialized with collection: %s (GPU: %s, Metric: %s)",
148
+ self.collection_name,
149
+ "enabled" if self.has_gpu else "disabled",
150
+ metric_info,
151
+ )
152
+
153
+ def _connect_milvus(self) -> None:
154
+ """Establish connection to Milvus server using singleton."""
155
+ self._singleton.get_connection(
156
+ self.connection_args["host"], self.connection_args["port"], self.db_name
157
+ )
158
+
159
+ def _initialize_vector_store(self) -> Milvus:
160
+ """Initialize or load the Milvus vector store with proper embedding model."""
161
+ # Use the wrapped embedding model (with normalization if needed)
162
+ vector_store = self._singleton.get_vector_store(
163
+ self.collection_name, self.embedding_model, self.connection_args
164
+ )
165
+
166
+ return vector_store
167
+
168
+ def _load_existing_paper_ids(self):
169
+ """Load already embedded paper IDs using LangChain's collection access."""
170
+ logger.info("Checking for existing papers via LangChain collection...")
171
+
172
+ # Access the collection through LangChain's wrapper
173
+ langchain_collection = getattr(self.vector_store, "col", None)
174
+
175
+ if langchain_collection is None:
176
+ langchain_collection = getattr(self.vector_store, "collection", None)
177
+
178
+ if langchain_collection is None:
179
+ logger.warning("No LangChain collection found, proceeding with empty loaded_papers")
180
+ return
181
+
182
+ # Force flush and check entity count
183
+ langchain_collection.flush()
184
+ num_entities = langchain_collection.num_entities
185
+
186
+ logger.info("LangChain collection entity count: %d", num_entities)
187
+
188
+ if num_entities > 0:
189
+ logger.info("Loading existing paper IDs from LangChain collection...")
190
+
191
+ results = langchain_collection.query(
192
+ expr="", # No filter - get all
193
+ output_fields=["paper_id"],
194
+ limit=16384, # Max limit
195
+ consistency_level="Strong",
196
+ )
197
+
198
+ # Extract unique paper IDs
199
+ existing_paper_ids = {result["paper_id"] for result in results}
200
+ self.loaded_papers.update(existing_paper_ids)
201
+
202
+ logger.info("Found %d unique papers in collection", len(existing_paper_ids))
203
+ else:
204
+ logger.info("Collection is empty - no existing papers")
205
+
206
+ def similarity_search(self, query: str, **kwargs: Any) -> list[Document]:
207
+ """
208
+ Perform similarity search on the vector store.
209
+ Query embedding will be automatically normalized if using GPU with COSINE.
210
+ Keyword args:
211
+ k: int = 4
212
+ filter: Optional[Dict[str, Any]] = None
213
+ plus any other kwargs to pass through to the underlying vector_store.
214
+ """
215
+ # Extract our parameters
216
+ k: int = kwargs.pop("k", 4)
217
+ filter_: dict[str, Any] | None = kwargs.pop("filter", None)
218
+
219
+ # Build Milvus expr from filter_, if present
220
+ expr = None
221
+ if filter_:
222
+ conditions = []
223
+ for key, value in filter_.items():
224
+ if isinstance(value, str):
225
+ conditions.append(f'{key} == "{value}"')
226
+ elif isinstance(value, list):
227
+ vals = ", ".join(f'"{v}"' if isinstance(v, str) else str(v) for v in value)
228
+ conditions.append(f"{key} in [{vals}]")
229
+ else:
230
+ conditions.append(f"{key} == {value}")
231
+ expr = " and ".join(conditions)
232
+
233
+ # Delegate to the wrapped store
234
+ return self.vector_store.similarity_search(query=query, k=k, expr=expr, **kwargs)
235
+
236
+ def max_marginal_relevance_search(self, query: str, **kwargs: Any) -> list[Document]:
237
+ """
238
+ Perform MMR search on the vector store.
239
+ Query embedding will be automatically normalized if using GPU with COSINE.
240
+ Keyword args:
241
+ k: int = 4
242
+ fetch_k: int = 20
243
+ lambda_mult: float = 0.5
244
+ filter: Optional[Dict[str, Any]] = None
245
+ plus any other kwargs to pass through.
246
+ """
247
+ # Extract our parameters
248
+ k: int = kwargs.pop("k", 4)
249
+ fetch_k: int = kwargs.pop("fetch_k", 20)
250
+ lambda_mult: float = kwargs.pop("lambda_mult", 0.5)
251
+ filter_: dict[str, Any] | None = kwargs.pop("filter", None)
252
+
253
+ # Build Milvus expr from filter_, if present
254
+ expr = None
255
+ if filter_:
256
+ conditions = []
257
+ for key, value in filter_.items():
258
+ if isinstance(value, str):
259
+ conditions.append(f'{key} == "{value}"')
260
+ elif isinstance(value, list):
261
+ vals = ", ".join(f'"{v}"' if isinstance(v, str) else str(v) for v in value)
262
+ conditions.append(f"{key} in [{vals}]")
263
+ else:
264
+ conditions.append(f"{key} == {value}")
265
+ expr = " and ".join(conditions)
266
+
267
+ # Delegate to the wrapped store
268
+ return self.vector_store.max_marginal_relevance_search(
269
+ query=query,
270
+ k=k,
271
+ fetch_k=fetch_k,
272
+ lambda_mult=lambda_mult,
273
+ expr=expr,
274
+ **kwargs,
275
+ )
276
+
277
+ def _ensure_collection_loaded(self):
278
+ """Ensure collection is loaded into memory/GPU after data insertion."""
279
+ # Get the collection
280
+ collection = getattr(self.vector_store, "col", None)
281
+ if collection is None:
282
+ collection = getattr(self.vector_store, "collection", None)
283
+
284
+ if collection is None:
285
+ logger.warning("Cannot access collection for loading")
286
+ return
287
+
288
+ # Force flush to ensure we see all data
289
+ logger.info("Flushing collection to ensure data visibility...")
290
+ collection.flush()
291
+
292
+ # Check entity count after flush
293
+ num_entities = collection.num_entities
294
+ logger.info("Collection entity count after flush: %d", num_entities)
295
+
296
+ if num_entities > 0:
297
+ hardware_type = "GPU" if self.has_gpu else "CPU"
298
+ logger.info(
299
+ "Loading collection with %d entities into %s memory...",
300
+ num_entities,
301
+ hardware_type,
302
+ )
303
+
304
+ # Load collection into memory (CPU or GPU)
305
+ collection.load()
306
+
307
+ # Verify loading was successful
308
+ final_count = collection.num_entities
309
+ logger.info(
310
+ "Collection successfully loaded into %s memory with %d entities",
311
+ hardware_type,
312
+ final_count,
313
+ )
314
+ else:
315
+ logger.info("Collection is empty, skipping load operation")
316
+
317
+ def get_embedding_info(self) -> dict[str, Any]:
318
+ """Get information about the embedding configuration."""
319
+ return {
320
+ "has_gpu": self.has_gpu,
321
+ "use_cosine": self.use_cosine,
322
+ "metric_type": self.index_params["metric_type"],
323
+ "index_type": self.index_params["index_type"],
324
+ "normalization_enabled": hasattr(self.embedding_model, "normalize_for_gpu"),
325
+ "original_model_type": type(self.original_embedding_model).__name__,
326
+ "wrapped_model_type": type(self.embedding_model).__name__,
327
+ }
@@ -0,0 +1,21 @@
1
+ """
2
+ This file is used to import all the modules in the package.
3
+ """
4
+
5
+ from . import (
6
+ display_dataframe,
7
+ multi_paper_rec,
8
+ query_dataframe,
9
+ retrieve_semantic_scholar_paper_id,
10
+ search,
11
+ single_paper_rec,
12
+ )
13
+
14
+ __all__ = [
15
+ "display_dataframe",
16
+ "multi_paper_rec",
17
+ "search",
18
+ "single_paper_rec",
19
+ "query_dataframe",
20
+ "retrieve_semantic_scholar_paper_id",
21
+ ]
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """
5
+ Tool for rendering the most recently displayed papers as a DataFrame artifact for the front-end.
6
+
7
+ Call this tool when you need to present the current set of retrieved papers to the user
8
+ (e.g., "show me the papers", "display results"). It reads the 'last_displayed_papers'
9
+ dictionary from the agent state and returns it as an artifact that the UI will render
10
+ as a pandas DataFrame. This tool does not perform any new searches or filtering; it
11
+ only displays the existing list. If no papers are available, it raises NoPapersFoundError
12
+ to signal that a search or recommendation must be executed first.
13
+ """
14
+
15
+ import logging
16
+ from typing import Annotated
17
+
18
+ from langchain_core.messages import ToolMessage
19
+ from langchain_core.tools import tool
20
+ from langchain_core.tools.base import InjectedToolCallId
21
+ from langgraph.prebuilt import InjectedState
22
+ from langgraph.types import Command
23
+ from pydantic import BaseModel, Field
24
+
25
+ # Configure logging
26
+ logging.basicConfig(level=logging.INFO)
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class NoPapersFoundError(Exception):
31
+ """
32
+ Exception raised when no research papers are found in the agent's state.
33
+
34
+ This exception helps the language model determine whether a new search
35
+ or recommendation should be initiated.
36
+
37
+ Example:
38
+ >>> if not papers:
39
+ >>> raise NoPapersFoundError("No papers found. A search is needed.")
40
+ """
41
+
42
+
43
+ class DisplayDataFrameInput(BaseModel):
44
+ """
45
+ Pydantic schema for displaying the last set of papers as a DataFrame artifact.
46
+
47
+ Fields:
48
+ state: Agent state dict containing the 'last_displayed_papers' key.
49
+ tool_call_id: LangGraph-injected identifier for this tool invocation.
50
+ """
51
+
52
+ state: Annotated[dict, InjectedState] = Field(
53
+ ..., description="Agent state containing the 'last_displayed_papers' reference."
54
+ )
55
+ tool_call_id: Annotated[str, InjectedToolCallId] = Field(
56
+ ..., description="LangGraph-injected identifier for this tool call."
57
+ )
58
+
59
+
60
+ @tool(
61
+ "display_dataframe",
62
+ args_schema=DisplayDataFrameInput,
63
+ parse_docstring=True,
64
+ )
65
+ def display_dataframe(
66
+ tool_call_id: str,
67
+ state: dict,
68
+ ) -> Command:
69
+ """
70
+ Render the last set of retrieved papers as a DataFrame in the front-end.
71
+
72
+ This function reads the 'last_displayed_papers' key from state, fetches the
73
+ corresponding metadata dictionary, and returns a Command with a ToolMessage
74
+ containing the artifact (dictionary) for the front-end to render as a DataFrame.
75
+ If no papers are found in state, it raises a NoPapersFoundError to indicate
76
+ that a search or recommendation must be performed first.
77
+
78
+ Args:
79
+ tool_call_id (str): LangGraph-injected unique ID for this tool call.
80
+ state (dict): The agent's state containing the 'last_displayed_papers' reference.
81
+
82
+ Returns:
83
+ Command: A command whose update contains a ToolMessage with the artifact
84
+ (papers dict) for DataFrame rendering in the UI.
85
+
86
+ Raises:
87
+ NoPapersFoundError: If no entries exist under 'last_displayed_papers' in state.
88
+ """
89
+ logger.info("Displaying papers from 'last_displayed_papers'")
90
+ context_val = state.get("last_displayed_papers")
91
+ # Support both key reference (str) and direct mapping
92
+ if isinstance(context_val, dict):
93
+ artifact = context_val
94
+ else:
95
+ artifact = state.get(context_val)
96
+ if not artifact:
97
+ logger.info("No papers found in state, raising NoPapersFoundError")
98
+ raise NoPapersFoundError("No papers found. A search/rec needs to be performed first.")
99
+ content = f"{len(artifact)} papers found. Papers are attached as an artifact."
100
+ return Command(
101
+ update={
102
+ "messages": [
103
+ ToolMessage(
104
+ content=content,
105
+ tool_call_id=tool_call_id,
106
+ artifact=artifact,
107
+ )
108
+ ],
109
+ }
110
+ )
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Recommend research papers related to a set of input papers using Semantic Scholar.
5
+
6
+ Given a list of Semantic Scholar paper IDs, this tool aggregates related works
7
+ (citations and references) from each input paper and returns a consolidated list
8
+ of recommended papers.
9
+ """
10
+
11
+ import logging
12
+ from typing import Annotated, Any
13
+
14
+ from langchain_core.messages import ToolMessage
15
+ from langchain_core.tools import tool
16
+ from langchain_core.tools.base import InjectedToolCallId
17
+ from langgraph.types import Command
18
+ from pydantic import BaseModel, Field
19
+
20
+ from .utils.multi_helper import MultiPaperRecData
21
+
22
+ # Configure logging
23
+ logging.basicConfig(level=logging.INFO)
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class MultiPaperRecInput(BaseModel):
28
+ """Defines the input schema for the multi-paper recommendation tool.
29
+
30
+ Attributes:
31
+ paper_ids: List of 40-character Semantic Scholar Paper IDs (provide at least two).
32
+ limit: Maximum total number of recommendations to return (1-500).
33
+ year: Optional publication year filter; supports formats:
34
+ 'YYYY', 'YYYY-', '-YYYY', 'YYYY:YYYY'.
35
+ tool_call_id: Internal tool call identifier injected by the system.
36
+ """
37
+
38
+ paper_ids: list[str] = Field(
39
+ description="List of 40-character Semantic Scholar Paper IDs"
40
+ "(at least two) to base recommendations on"
41
+ )
42
+ limit: int = Field(
43
+ default=10,
44
+ description="Maximum total number of recommendations to return (1-500)",
45
+ ge=1,
46
+ le=500,
47
+ )
48
+ year: str | None = Field(
49
+ default=None,
50
+ description="Publication year filter; supports formats:"
51
+ "'YYYY', 'YYYY-', '-YYYY', 'YYYY:YYYY'",
52
+ )
53
+ tool_call_id: Annotated[str, InjectedToolCallId]
54
+
55
+ model_config = {"arbitrary_types_allowed": True}
56
+
57
+
58
+ @tool(
59
+ args_schema=MultiPaperRecInput,
60
+ parse_docstring=True,
61
+ )
62
+ def get_multi_paper_recommendations(
63
+ paper_ids: list[str],
64
+ tool_call_id: Annotated[str, InjectedToolCallId],
65
+ limit: int = 10,
66
+ year: str | None = None,
67
+ ) -> Command[Any]:
68
+ """
69
+ Recommend related research papers using the Semantic Scholar API.
70
+
71
+ This tool is designed to suggest relevant papers based on a list of
72
+ input Semantic Scholar paper IDs.
73
+
74
+ It fetches citations and references for each input paper and aggregates
75
+ them to generate a set of
76
+ recommended papers.
77
+
78
+ Args:
79
+ paper_ids (List[str]): List of 40-character Semantic Scholar paper IDs.
80
+ Provide at least two IDs to improve the relevance of recommendations.
81
+ tool_call_id (str): Internal tool call identifier injected by the system.
82
+ limit (int, optional): Maximum number of recommendations to return. Defaults to 10.
83
+ year (str, optional): Filter recommendations by publication year.
84
+ Supports formats: 'YYYY', 'YYYY-', '-YYYY', or 'YYYY:YYYY'. Defaults to None.
85
+
86
+ Returns:
87
+ Command: A Command object containing:
88
+ - multi_papers: List of recommended papers.
89
+ - last_displayed_papers: Same list for display purposes.
90
+ - messages: List containing a ToolMessage with recommendation details.
91
+ """
92
+ # Create recommendation data object to organize variables
93
+ rec_data = MultiPaperRecData(paper_ids, limit, year, tool_call_id)
94
+
95
+ # Process the recommendations
96
+ results = rec_data.process_recommendations()
97
+
98
+ return Command(
99
+ update={
100
+ "multi_papers": results["papers"],
101
+ # Store the latest multi-paper results mapping directly for display
102
+ "last_displayed_papers": results["papers"],
103
+ "messages": [
104
+ ToolMessage(
105
+ content=results["content"],
106
+ tool_call_id=tool_call_id,
107
+ artifact=results["papers"],
108
+ )
109
+ ],
110
+ }
111
+ )