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,223 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Utility for fetching recommendations based on multiple papers.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ from typing import Any
10
+
11
+ import hydra
12
+ import requests
13
+
14
+ # Configure logging
15
+ logging.basicConfig(level=logging.INFO)
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class MultiPaperRecData:
20
+ """Helper class to organize multi-paper recommendation data."""
21
+
22
+ def __init__(
23
+ self,
24
+ paper_ids: list[str],
25
+ limit: int,
26
+ year: str | None,
27
+ tool_call_id: str,
28
+ ):
29
+ self.paper_ids = paper_ids
30
+ self.limit = limit
31
+ self.year = year
32
+ self.tool_call_id = tool_call_id
33
+ self.cfg = self._load_config()
34
+ self.endpoint = self.cfg.api_endpoint
35
+ self.headers = self.cfg.headers
36
+ self.payload = {"positivePaperIds": paper_ids, "negativePaperIds": []}
37
+ self.params = self._create_params()
38
+ self.response = None
39
+ self.data = None
40
+ self.recommendations = []
41
+ self.filtered_papers = {}
42
+ self.content = ""
43
+
44
+ def _load_config(self) -> Any:
45
+ """Load hydra configuration."""
46
+ with hydra.initialize(version_base=None, config_path="../../../configs"):
47
+ cfg = hydra.compose(
48
+ config_name="config",
49
+ overrides=["tools/multi_paper_recommendation=default"],
50
+ )
51
+ logger.info("Loaded configuration for multi-paper recommendation tool")
52
+ return cfg.tools.multi_paper_recommendation
53
+
54
+ def _create_params(self) -> dict[str, Any]:
55
+ """Create parameters for the API request."""
56
+ params = {
57
+ "limit": min(self.limit, 500),
58
+ "fields": ",".join(self.cfg.api_fields),
59
+ }
60
+ if self.year:
61
+ params["year"] = self.year
62
+ return params
63
+
64
+ def _fetch_recommendations(self) -> None:
65
+ """Fetch recommendations from Semantic Scholar API."""
66
+ logger.info(
67
+ "Starting multi-paper recommendations search with paper IDs: %s",
68
+ self.paper_ids,
69
+ )
70
+
71
+ # Wrap API call in try/except to catch connectivity issues and validate response format
72
+ for attempt in range(10):
73
+ try:
74
+ self.response = requests.post(
75
+ self.endpoint,
76
+ headers=self.headers,
77
+ params=self.params,
78
+ data=json.dumps(self.payload),
79
+ timeout=self.cfg.request_timeout,
80
+ )
81
+ self.response.raise_for_status() # Raises HTTPError for bad responses
82
+ break # Exit loop if request is successful
83
+ except requests.exceptions.RequestException as e:
84
+ logger.error(
85
+ "Attempt %d: Failed to connect to Semantic Scholar API for "
86
+ "multi-paper recommendations: %s",
87
+ attempt + 1,
88
+ e,
89
+ )
90
+ if attempt == 9: # Last attempt
91
+ raise RuntimeError(
92
+ "Failed to connect to Semantic Scholar API after 10 attempts."
93
+ "Please retry the same query."
94
+ ) from e
95
+
96
+ if self.response is None:
97
+ raise RuntimeError("Failed to obtain a response from the Semantic Scholar API.")
98
+
99
+ logger.info(
100
+ "API Response Status for multi-paper recommendations: %s",
101
+ self.response.status_code,
102
+ )
103
+ logger.info("Request params: %s", self.params)
104
+
105
+ self.data = self.response.json()
106
+
107
+ # Check for expected data format
108
+ if "recommendedPapers" not in self.data:
109
+ logger.error("Unexpected API response format: %s", self.data)
110
+ raise RuntimeError(
111
+ "Unexpected response from Semantic Scholar API. The results could not be "
112
+ "retrieved due to an unexpected format. "
113
+ "Please modify your search query and try again."
114
+ )
115
+
116
+ self.recommendations = self.data.get("recommendedPapers", [])
117
+ if not self.recommendations:
118
+ logger.error("No recommendations returned from API for paper IDs: %s", self.paper_ids)
119
+ raise RuntimeError(
120
+ "No recommendations were found for your query. Consider refining your search "
121
+ "by using more specific keywords or different terms."
122
+ )
123
+
124
+ def _filter_papers(self) -> None:
125
+ """Filter and format papers."""
126
+ # Build filtered recommendations with unified paper_ids
127
+ filtered: dict[str, Any] = {}
128
+ for paper in self.recommendations:
129
+ if not paper.get("title") or not paper.get("authors"):
130
+ continue
131
+ ext = paper.get("externalIds", {}) or {}
132
+ ids: list[str] = []
133
+ arxiv = ext.get("ArXiv")
134
+ if arxiv:
135
+ ids.append(f"arxiv:{arxiv}")
136
+ pubmed = ext.get("PubMed")
137
+ if pubmed:
138
+ ids.append(f"pubmed:{pubmed}")
139
+ pmc = ext.get("PubMedCentral")
140
+ if pmc:
141
+ ids.append(f"pmc:{pmc}")
142
+ doi_id = ext.get("DOI")
143
+ if doi_id:
144
+ ids.append(f"doi:{doi_id}")
145
+ metadata = {
146
+ "semantic_scholar_paper_id": paper["paperId"],
147
+ "Title": paper.get("title", "N/A"),
148
+ "Abstract": paper.get("abstract", "N/A"),
149
+ "Year": paper.get("year", "N/A"),
150
+ "Publication Date": paper.get("publicationDate", "N/A"),
151
+ "Venue": paper.get("venue", "N/A"),
152
+ "Journal Name": (paper.get("journal") or {}).get("name", "N/A"),
153
+ "Citation Count": paper.get("citationCount", "N/A"),
154
+ "Authors": [
155
+ f"{author.get('name', 'N/A')} (ID: {author.get('authorId', 'N/A')})"
156
+ for author in paper.get("authors", [])
157
+ ],
158
+ "URL": paper.get("url", "N/A"),
159
+ "arxiv_id": arxiv or "N/A",
160
+ "pm_id": pubmed or "N/A",
161
+ "pmc_id": pmc or "N/A",
162
+ "doi": doi_id or "N/A",
163
+ "paper_ids": ids,
164
+ "source": "semantic_scholar",
165
+ }
166
+ filtered[paper["paperId"]] = metadata
167
+ self.filtered_papers = filtered
168
+
169
+ logger.info("Filtered %d papers", len(self.filtered_papers))
170
+
171
+ def _get_snippet(self, abstract: str) -> str:
172
+ """Extract the first one or two sentences from an abstract."""
173
+ if not abstract or abstract == "N/A":
174
+ return ""
175
+ sentences = abstract.split(". ")
176
+ snippet_sentences = sentences[:2]
177
+ snippet = ". ".join(snippet_sentences)
178
+ if not snippet.endswith("."):
179
+ snippet += "."
180
+ return snippet
181
+
182
+ def _create_content(self) -> None:
183
+ """Create the content message for the response."""
184
+ top_papers = list(self.filtered_papers.values())[:3]
185
+ entries: list[str] = []
186
+ for i, paper in enumerate(top_papers):
187
+ title = paper.get("Title", "N/A")
188
+ year = paper.get("Year", "N/A")
189
+ snippet = self._get_snippet(paper.get("Abstract", ""))
190
+ entry = f"{i + 1}. {title} ({year})"
191
+ if snippet:
192
+ entry += f"\n Abstract snippet: {snippet}"
193
+ entries.append(entry)
194
+ top_papers_info = "\n".join(entries)
195
+
196
+ self.content = (
197
+ "Recommendations based on multiple papers were successful. "
198
+ "Papers are attached as an artifact."
199
+ )
200
+ self.content += " Here is a summary of the recommendations:\n"
201
+ self.content += f"Number of recommended papers found: {self.get_paper_count()}\n"
202
+ self.content += f"Query Paper IDs: {', '.join(self.paper_ids)}\n"
203
+ self.content += f"Year: {self.year}\n" if self.year else ""
204
+ self.content += "Here are a few of these papers:\n" + top_papers_info
205
+
206
+ def process_recommendations(self) -> dict[str, Any]:
207
+ """Process the recommendations request and return results."""
208
+ self._fetch_recommendations()
209
+ self._filter_papers()
210
+ self._create_content()
211
+
212
+ return {
213
+ "papers": self.filtered_papers,
214
+ "content": self.content,
215
+ }
216
+
217
+ def get_paper_count(self) -> int:
218
+ """Get the number of recommended papers.
219
+
220
+ Returns:
221
+ int: The number of papers in the filtered papers dictionary.
222
+ """
223
+ return len(self.filtered_papers)
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Utility for fetching recommendations based on a single paper.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any
9
+
10
+ import hydra
11
+ import requests
12
+
13
+ # Configure logging
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class SearchData:
19
+ """Helper class to organize search-related data."""
20
+
21
+ def __init__(
22
+ self,
23
+ query: str,
24
+ limit: int,
25
+ year: str | None,
26
+ tool_call_id: str,
27
+ ):
28
+ self.query = query
29
+ self.limit = limit
30
+ self.year = year
31
+ self.tool_call_id = tool_call_id
32
+ self.cfg = self._load_config()
33
+ self.endpoint = self.cfg.api_endpoint
34
+ self.params = self._create_params()
35
+ self.response = None
36
+ self.data = None
37
+ self.papers = []
38
+ self.filtered_papers = {}
39
+ self.content = ""
40
+
41
+ def _load_config(self) -> Any:
42
+ """Load hydra configuration."""
43
+ with hydra.initialize(version_base=None, config_path="../../../configs"):
44
+ cfg = hydra.compose(config_name="config", overrides=["tools/search=default"])
45
+ logger.info("Loaded configuration for search tool")
46
+ return cfg.tools.search
47
+
48
+ def _create_params(self) -> dict[str, Any]:
49
+ """Create parameters for the API request."""
50
+ params = {
51
+ "query": self.query,
52
+ "limit": min(self.limit, 100),
53
+ "fields": ",".join(self.cfg.api_fields),
54
+ }
55
+ if self.year:
56
+ params["year"] = self.year
57
+ return params
58
+
59
+ def _fetch_papers(self) -> None:
60
+ """Fetch papers from Semantic Scholar API."""
61
+ logger.info("Searching for papers on %s", self.query)
62
+
63
+ # Wrap API call in try/except to catch connectivity issues
64
+ for attempt in range(10):
65
+ try:
66
+ self.response = requests.get(self.endpoint, params=self.params, timeout=10)
67
+ self.response.raise_for_status() # Raises HTTPError for bad responses
68
+ break # Exit loop if request is successful
69
+ except requests.exceptions.RequestException as e:
70
+ logger.error(
71
+ "Attempt %d: Failed to connect to Semantic Scholar API: %s",
72
+ attempt + 1,
73
+ e,
74
+ )
75
+ if attempt == 9: # Last attempt
76
+ raise RuntimeError(
77
+ "Failed to connect to Semantic Scholar API after 10 attempts."
78
+ "Please retry the same query."
79
+ ) from e
80
+
81
+ if self.response is None:
82
+ raise RuntimeError("Failed to obtain a response from the Semantic Scholar API.")
83
+
84
+ self.data = self.response.json()
85
+
86
+ # Check for expected data format
87
+ if "data" not in self.data:
88
+ logger.error("Unexpected API response format: %s", self.data)
89
+ raise RuntimeError(
90
+ "Unexpected response from Semantic Scholar API. The results could not be "
91
+ "retrieved due to an unexpected format. "
92
+ "Please modify your search query and try again."
93
+ )
94
+
95
+ self.papers = self.data.get("data", [])
96
+ if not self.papers:
97
+ logger.error("No papers returned from Semantic Scholar API for query: %s", self.query)
98
+ raise RuntimeError(
99
+ "No papers were found for your query. Consider refining your search "
100
+ "by using more specific keywords or different terms."
101
+ )
102
+
103
+ def _filter_papers(self) -> None:
104
+ """Filter and format papers."""
105
+ # Build filtered papers mapping with unified paper_ids list
106
+ filtered: dict[str, Any] = {}
107
+ for paper in self.papers:
108
+ if not paper.get("title") or not paper.get("authors"):
109
+ continue
110
+ ext = paper.get("externalIds", {}) or {}
111
+ # Prioritized list of IDs: arXiv, PubMed, PubMedCentral, DOI
112
+ ids: list[str] = []
113
+ arxiv = ext.get("ArXiv")
114
+ if arxiv:
115
+ ids.append(f"arxiv:{arxiv}")
116
+ pubmed = ext.get("PubMed")
117
+ if pubmed:
118
+ ids.append(f"pubmed:{pubmed}")
119
+ pmc = ext.get("PubMedCentral")
120
+ if pmc:
121
+ ids.append(f"pmc:{pmc}")
122
+ doi_id = ext.get("DOI")
123
+ if doi_id:
124
+ ids.append(f"doi:{doi_id}")
125
+ # Compose metadata dict
126
+ metadata = {
127
+ "semantic_scholar_paper_id": paper["paperId"],
128
+ "Title": paper.get("title", "N/A"),
129
+ "Abstract": paper.get("abstract", "N/A"),
130
+ "Year": paper.get("year", "N/A"),
131
+ "Publication Date": paper.get("publicationDate", "N/A"),
132
+ "Venue": paper.get("venue", "N/A"),
133
+ "Journal Name": (paper.get("journal") or {}).get("name", "N/A"),
134
+ "Citation Count": paper.get("citationCount", "N/A"),
135
+ "Authors": [
136
+ f"{author.get('name', 'N/A')} (ID: {author.get('authorId', 'N/A')})"
137
+ for author in paper.get("authors", [])
138
+ ],
139
+ "URL": paper.get("url", "N/A"),
140
+ "arxiv_id": arxiv or "N/A",
141
+ "pmc_id": pmc or "N/A",
142
+ "pm_id": pubmed or "N/A",
143
+ "doi": doi_id or "N/A",
144
+ "paper_ids": ids,
145
+ "source": "semantic_scholar",
146
+ }
147
+ filtered[paper["paperId"]] = metadata
148
+ self.filtered_papers = filtered
149
+
150
+ logger.info("Filtered %d papers", len(self.filtered_papers))
151
+
152
+ def _get_snippet(self, abstract: str) -> str:
153
+ """Extract the first one or two sentences from an abstract."""
154
+ if not abstract or abstract == "N/A":
155
+ return ""
156
+ sentences = abstract.split(". ")
157
+ snippet_sentences = sentences[:2]
158
+ snippet = ". ".join(snippet_sentences)
159
+ if not snippet.endswith("."):
160
+ snippet += "."
161
+ return snippet
162
+
163
+ def _create_content(self) -> None:
164
+ """Create the content message for the response."""
165
+ top_papers = list(self.filtered_papers.values())[:3]
166
+ entries = []
167
+ for i, paper in enumerate(top_papers):
168
+ title = paper.get("Title", "N/A")
169
+ year = paper.get("Year", "N/A")
170
+ snippet = self._get_snippet(paper.get("Abstract", ""))
171
+ entry = f"{i + 1}. {title} ({year})"
172
+ if snippet:
173
+ entry += f"\n Abstract snippet: {snippet}"
174
+ entries.append(entry)
175
+ top_papers_info = "\n".join(entries)
176
+
177
+ logger.info("-----------Filtered %d papers", self.get_paper_count())
178
+
179
+ self.content = (
180
+ "Search was successful. Papers are attached as an artifact. "
181
+ "Here is a summary of the search results:\n"
182
+ )
183
+ self.content += f"Number of papers found: {self.get_paper_count()}\n"
184
+ self.content += f"Query: {self.query}\n"
185
+ self.content += f"Year: {self.year}\n" if self.year else ""
186
+ self.content += "Top 3 papers:\n" + top_papers_info
187
+
188
+ def process_search(self) -> dict[str, Any]:
189
+ """Process the search request and return results."""
190
+ self._fetch_papers()
191
+ self._filter_papers()
192
+ self._create_content()
193
+
194
+ return {
195
+ "papers": self.filtered_papers,
196
+ "content": self.content,
197
+ }
198
+
199
+ def get_paper_count(self) -> int:
200
+ """Get the number of papers found in the search.
201
+
202
+ Returns:
203
+ int: The number of papers in the filtered papers dictionary.
204
+ """
205
+ return len(self.filtered_papers)
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Utility for fetching recommendations based on a single paper.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any
9
+
10
+ import hydra
11
+ import requests
12
+
13
+ # Configure logging
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class SinglePaperRecData:
19
+ """Helper class to organize single paper recommendation data."""
20
+
21
+ def __init__(
22
+ self,
23
+ paper_id: str,
24
+ limit: int,
25
+ year: str | None,
26
+ tool_call_id: str,
27
+ ):
28
+ self.paper_id = paper_id
29
+ self.limit = limit
30
+ self.year = year
31
+ self.tool_call_id = tool_call_id
32
+ self.cfg = self._load_config()
33
+ self.endpoint = f"{self.cfg.api_endpoint}/{paper_id}"
34
+ self.params = self._create_params()
35
+ self.response = None
36
+ self.data = None
37
+ self.recommendations = []
38
+ self.filtered_papers = {}
39
+ self.content = ""
40
+
41
+ def _load_config(self) -> Any:
42
+ """Load hydra configuration."""
43
+ with hydra.initialize(version_base=None, config_path="../../../configs"):
44
+ cfg = hydra.compose(
45
+ config_name="config",
46
+ overrides=["tools/single_paper_recommendation=default"],
47
+ )
48
+ logger.info("Loaded configuration for single paper recommendation tool")
49
+ return cfg.tools.single_paper_recommendation
50
+
51
+ def _create_params(self) -> dict[str, Any]:
52
+ """Create parameters for the API request."""
53
+ params = {
54
+ "limit": min(self.limit, 500), # Max 500 per API docs
55
+ "fields": ",".join(self.cfg.api_fields),
56
+ "from": self.cfg.recommendation_params.from_pool,
57
+ }
58
+ if self.year:
59
+ params["year"] = self.year
60
+ return params
61
+
62
+ def _fetch_recommendations(self) -> None:
63
+ """Fetch recommendations from Semantic Scholar API."""
64
+ logger.info(
65
+ "Starting single paper recommendations search with paper ID: %s",
66
+ self.paper_id,
67
+ )
68
+
69
+ # Wrap API call in try/except to catch connectivity issues and check response format
70
+ for attempt in range(10):
71
+ try:
72
+ self.response = requests.get(
73
+ self.endpoint, params=self.params, timeout=self.cfg.request_timeout
74
+ )
75
+ self.response.raise_for_status() # Raises HTTPError for bad responses
76
+ break # Exit loop if request is successful
77
+ except requests.exceptions.RequestException as e:
78
+ logger.error(
79
+ "Attempt %d: Failed to connect to Semantic Scholar API for recommendations: %s",
80
+ attempt + 1,
81
+ e,
82
+ )
83
+ if attempt == 9: # Last attempt
84
+ raise RuntimeError(
85
+ "Failed to connect to Semantic Scholar API after 10 attempts."
86
+ "Please retry the same query."
87
+ ) from e
88
+
89
+ if self.response is None:
90
+ raise RuntimeError("Failed to obtain a response from the Semantic Scholar API.")
91
+
92
+ logger.info(
93
+ "API Response Status for recommendations of paper %s: %s",
94
+ self.paper_id,
95
+ self.response.status_code,
96
+ )
97
+ logger.info("Request params: %s", self.params)
98
+
99
+ self.data = self.response.json()
100
+
101
+ # Check for expected data format
102
+ if "recommendedPapers" not in self.data:
103
+ logger.error("Unexpected API response format: %s", self.data)
104
+ raise RuntimeError(
105
+ "Unexpected response from Semantic Scholar API. The results could not be "
106
+ "retrieved due to an unexpected format. "
107
+ "Please modify your search query and try again."
108
+ )
109
+
110
+ self.recommendations = self.data.get("recommendedPapers", [])
111
+ if not self.recommendations:
112
+ logger.error("No recommendations returned from API for paper: %s", self.paper_id)
113
+ raise RuntimeError(
114
+ "No recommendations were found for your query. Consider refining your search "
115
+ "by using more specific keywords or different terms."
116
+ )
117
+
118
+ def _filter_papers(self) -> None:
119
+ """Filter and format papers."""
120
+ # Build filtered recommendations with unified paper_ids
121
+ filtered: dict[str, Any] = {}
122
+ for paper in self.recommendations:
123
+ if not paper.get("title") or not paper.get("authors"):
124
+ continue
125
+ ext = paper.get("externalIds", {}) or {}
126
+ ids: list[str] = []
127
+ arxiv = ext.get("ArXiv")
128
+ if arxiv:
129
+ ids.append(f"arxiv:{arxiv}")
130
+ pubmed = ext.get("PubMed")
131
+ if pubmed:
132
+ ids.append(f"pubmed:{pubmed}")
133
+ pmc = ext.get("PubMedCentral")
134
+ if pmc:
135
+ ids.append(f"pmc:{pmc}")
136
+ doi_id = ext.get("DOI")
137
+ if doi_id:
138
+ ids.append(f"doi:{doi_id}")
139
+ metadata = {
140
+ "semantic_scholar_paper_id": paper["paperId"],
141
+ "Title": paper.get("title", "N/A"),
142
+ "Abstract": paper.get("abstract", "N/A"),
143
+ "Year": paper.get("year", "N/A"),
144
+ "Publication Date": paper.get("publicationDate", "N/A"),
145
+ "Venue": paper.get("venue", "N/A"),
146
+ "Journal Name": (paper.get("journal") or {}).get("name", "N/A"),
147
+ "Citation Count": paper.get("citationCount", "N/A"),
148
+ "Authors": [
149
+ f"{author.get('name', 'N/A')} (ID: {author.get('authorId', 'N/A')})"
150
+ for author in paper.get("authors", [])
151
+ ],
152
+ "URL": paper.get("url", "N/A"),
153
+ "arxiv_id": arxiv or "N/A",
154
+ "pm_id": pubmed or "N/A",
155
+ "pmc_id": pmc or "N/A",
156
+ "doi": doi_id or "N/A",
157
+ "paper_ids": ids,
158
+ "source": "semantic_scholar",
159
+ }
160
+ filtered[paper["paperId"]] = metadata
161
+ self.filtered_papers = filtered
162
+
163
+ logger.info("Filtered %d papers", len(self.filtered_papers))
164
+
165
+ def _get_snippet(self, abstract: str) -> str:
166
+ """Extract the first one or two sentences from an abstract."""
167
+ if not abstract or abstract == "N/A":
168
+ return ""
169
+ sentences = abstract.split(". ")
170
+ snippet_sentences = sentences[:2]
171
+ snippet = ". ".join(snippet_sentences)
172
+ if not snippet.endswith("."):
173
+ snippet += "."
174
+ return snippet
175
+
176
+ def _create_content(self) -> None:
177
+ """Create the content message for the response."""
178
+ top_papers = list(self.filtered_papers.values())[:3]
179
+ entries: list[str] = []
180
+ for i, paper in enumerate(top_papers):
181
+ title = paper.get("Title", "N/A")
182
+ year = paper.get("Year", "N/A")
183
+ snippet = self._get_snippet(paper.get("Abstract", ""))
184
+ entry = f"{i + 1}. {title} ({year})"
185
+ if snippet:
186
+ entry += f"\n Abstract snippet: {snippet}"
187
+ entries.append(entry)
188
+ top_papers_info = "\n".join(entries)
189
+
190
+ self.content = (
191
+ "Recommendations based on the single paper were successful. "
192
+ "Papers are attached as an artifact. "
193
+ "Here is a summary of the recommendations:\n"
194
+ )
195
+ self.content += f"Number of recommended papers found: {self.get_paper_count()}\n"
196
+ self.content += f"Query Paper ID: {self.paper_id}\n"
197
+ self.content += "Here are a few of these papers:\n" + top_papers_info
198
+
199
+ def process_recommendations(self) -> dict[str, Any]:
200
+ """Process the recommendations request and return results."""
201
+ self._fetch_recommendations()
202
+ self._filter_papers()
203
+ self._create_content()
204
+
205
+ return {
206
+ "papers": self.filtered_papers,
207
+ "content": self.content,
208
+ }
209
+
210
+ def get_paper_count(self) -> int:
211
+ """Get the number of recommended papers.
212
+
213
+ Returns:
214
+ int: The number of papers in the filtered papers dictionary.
215
+ """
216
+ return len(self.filtered_papers)
@@ -0,0 +1,7 @@
1
+ """
2
+ Import statements
3
+ """
4
+
5
+ from . import utils, zotero_read, zotero_review, zotero_write
6
+
7
+ __all__ = ["zotero_read", "zotero_write", "utils", "zotero_review"]
@@ -0,0 +1,7 @@
1
+ """
2
+ Import statements
3
+ """
4
+
5
+ from . import read_helper, review_helper, write_helper, zotero_path
6
+
7
+ __all__ = ["zotero_path", "read_helper", "write_helper", "review_helper"]