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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. aiagents4pharma/__init__.py +2 -2
  2. aiagents4pharma/talk2aiagents4pharma/.dockerignore +13 -0
  3. aiagents4pharma/talk2aiagents4pharma/Dockerfile +105 -0
  4. aiagents4pharma/talk2aiagents4pharma/README.md +1 -0
  5. aiagents4pharma/talk2aiagents4pharma/__init__.py +4 -5
  6. aiagents4pharma/talk2aiagents4pharma/agents/__init__.py +3 -2
  7. aiagents4pharma/talk2aiagents4pharma/agents/main_agent.py +24 -23
  8. aiagents4pharma/talk2aiagents4pharma/configs/__init__.py +2 -2
  9. aiagents4pharma/talk2aiagents4pharma/configs/agents/__init__.py +2 -2
  10. aiagents4pharma/talk2aiagents4pharma/configs/agents/main_agent/default.yaml +2 -2
  11. aiagents4pharma/talk2aiagents4pharma/configs/config.yaml +1 -1
  12. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/.env.example +23 -0
  13. aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/docker-compose.yml +93 -0
  14. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/.env.example +23 -0
  15. aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/docker-compose.yml +108 -0
  16. aiagents4pharma/talk2aiagents4pharma/install.md +127 -0
  17. aiagents4pharma/talk2aiagents4pharma/states/__init__.py +3 -2
  18. aiagents4pharma/talk2aiagents4pharma/states/state_talk2aiagents4pharma.py +5 -3
  19. aiagents4pharma/talk2aiagents4pharma/tests/__init__.py +2 -2
  20. aiagents4pharma/talk2aiagents4pharma/tests/test_main_agent.py +72 -50
  21. aiagents4pharma/talk2biomodels/.dockerignore +13 -0
  22. aiagents4pharma/talk2biomodels/Dockerfile +104 -0
  23. aiagents4pharma/talk2biomodels/README.md +1 -0
  24. aiagents4pharma/talk2biomodels/__init__.py +4 -8
  25. aiagents4pharma/talk2biomodels/agents/__init__.py +3 -2
  26. aiagents4pharma/talk2biomodels/agents/t2b_agent.py +47 -42
  27. aiagents4pharma/talk2biomodels/api/__init__.py +4 -5
  28. aiagents4pharma/talk2biomodels/api/kegg.py +14 -10
  29. aiagents4pharma/talk2biomodels/api/ols.py +13 -10
  30. aiagents4pharma/talk2biomodels/api/uniprot.py +7 -6
  31. aiagents4pharma/talk2biomodels/configs/__init__.py +3 -4
  32. aiagents4pharma/talk2biomodels/configs/agents/__init__.py +2 -2
  33. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/__init__.py +2 -2
  34. aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/default.yaml +1 -1
  35. aiagents4pharma/talk2biomodels/configs/config.yaml +1 -1
  36. aiagents4pharma/talk2biomodels/configs/tools/__init__.py +4 -5
  37. aiagents4pharma/talk2biomodels/configs/tools/ask_question/__init__.py +2 -2
  38. aiagents4pharma/talk2biomodels/configs/tools/ask_question/default.yaml +1 -2
  39. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/__init__.py +2 -2
  40. aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/default.yaml +1 -1
  41. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/__init__.py +2 -2
  42. aiagents4pharma/talk2biomodels/configs/tools/get_annotation/default.yaml +1 -1
  43. aiagents4pharma/talk2biomodels/install.md +63 -0
  44. aiagents4pharma/talk2biomodels/models/__init__.py +4 -4
  45. aiagents4pharma/talk2biomodels/models/basico_model.py +36 -28
  46. aiagents4pharma/talk2biomodels/models/sys_bio_model.py +13 -10
  47. aiagents4pharma/talk2biomodels/states/__init__.py +3 -2
  48. aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +12 -8
  49. aiagents4pharma/talk2biomodels/tests/BIOMD0000000449_url.xml +1585 -0
  50. aiagents4pharma/talk2biomodels/tests/__init__.py +2 -2
  51. aiagents4pharma/talk2biomodels/tests/article_on_model_537.pdf +0 -0
  52. aiagents4pharma/talk2biomodels/tests/test_api.py +18 -14
  53. aiagents4pharma/talk2biomodels/tests/test_ask_question.py +8 -9
  54. aiagents4pharma/talk2biomodels/tests/test_basico_model.py +15 -9
  55. aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +54 -55
  56. aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +28 -27
  57. aiagents4pharma/talk2biomodels/tests/test_integration.py +21 -33
  58. aiagents4pharma/talk2biomodels/tests/test_load_biomodel.py +14 -11
  59. aiagents4pharma/talk2biomodels/tests/test_param_scan.py +21 -20
  60. aiagents4pharma/talk2biomodels/tests/test_query_article.py +129 -29
  61. aiagents4pharma/talk2biomodels/tests/test_search_models.py +9 -13
  62. aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +16 -15
  63. aiagents4pharma/talk2biomodels/tests/test_steady_state.py +12 -22
  64. aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +33 -29
  65. aiagents4pharma/talk2biomodels/tools/__init__.py +15 -12
  66. aiagents4pharma/talk2biomodels/tools/ask_question.py +42 -32
  67. aiagents4pharma/talk2biomodels/tools/custom_plotter.py +51 -43
  68. aiagents4pharma/talk2biomodels/tools/get_annotation.py +99 -75
  69. aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +57 -51
  70. aiagents4pharma/talk2biomodels/tools/load_arguments.py +52 -32
  71. aiagents4pharma/talk2biomodels/tools/load_biomodel.py +8 -2
  72. aiagents4pharma/talk2biomodels/tools/parameter_scan.py +107 -90
  73. aiagents4pharma/talk2biomodels/tools/query_article.py +14 -13
  74. aiagents4pharma/talk2biomodels/tools/search_models.py +37 -26
  75. aiagents4pharma/talk2biomodels/tools/simulate_model.py +47 -37
  76. aiagents4pharma/talk2biomodels/tools/steady_state.py +76 -58
  77. aiagents4pharma/talk2biomodels/tools/utils.py +4 -3
  78. aiagents4pharma/talk2cells/README.md +1 -0
  79. aiagents4pharma/talk2cells/__init__.py +4 -5
  80. aiagents4pharma/talk2cells/agents/__init__.py +3 -2
  81. aiagents4pharma/talk2cells/agents/scp_agent.py +21 -19
  82. aiagents4pharma/talk2cells/states/__init__.py +3 -2
  83. aiagents4pharma/talk2cells/states/state_talk2cells.py +4 -2
  84. aiagents4pharma/talk2cells/tests/scp_agent/test_scp_agent.py +8 -9
  85. aiagents4pharma/talk2cells/tools/__init__.py +3 -2
  86. aiagents4pharma/talk2cells/tools/scp_agent/__init__.py +4 -4
  87. aiagents4pharma/talk2cells/tools/scp_agent/display_studies.py +5 -3
  88. aiagents4pharma/talk2cells/tools/scp_agent/search_studies.py +21 -22
  89. aiagents4pharma/talk2knowledgegraphs/.dockerignore +13 -0
  90. aiagents4pharma/talk2knowledgegraphs/Dockerfile +103 -0
  91. aiagents4pharma/talk2knowledgegraphs/README.md +1 -0
  92. aiagents4pharma/talk2knowledgegraphs/__init__.py +4 -7
  93. aiagents4pharma/talk2knowledgegraphs/agents/__init__.py +3 -2
  94. aiagents4pharma/talk2knowledgegraphs/agents/t2kg_agent.py +40 -30
  95. aiagents4pharma/talk2knowledgegraphs/configs/__init__.py +3 -6
  96. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/__init__.py +2 -2
  97. aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/default.yaml +8 -8
  98. aiagents4pharma/talk2knowledgegraphs/configs/app/__init__.py +3 -2
  99. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/__init__.py +2 -2
  100. aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/default.yaml +1 -1
  101. aiagents4pharma/talk2knowledgegraphs/configs/config.yaml +1 -1
  102. aiagents4pharma/talk2knowledgegraphs/configs/tools/__init__.py +4 -5
  103. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/__init__.py +2 -2
  104. aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/default.yaml +1 -1
  105. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/__init__.py +2 -2
  106. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/default.yaml +1 -1
  107. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/__init__.py +2 -2
  108. aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/default.yaml +1 -1
  109. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/ols_terms/default.yaml +1 -1
  110. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/reactome_pathways/default.yaml +1 -1
  111. aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/uniprot_proteins/default.yaml +1 -1
  112. aiagents4pharma/talk2knowledgegraphs/configs/utils/pubchem_utils/default.yaml +1 -1
  113. aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py +4 -6
  114. aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py +115 -67
  115. aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py +2 -0
  116. aiagents4pharma/talk2knowledgegraphs/datasets/primekg.py +35 -24
  117. aiagents4pharma/talk2knowledgegraphs/datasets/starkqa_primekg.py +29 -21
  118. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/.env.example +23 -0
  119. aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/docker-compose.yml +93 -0
  120. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/.env.example +23 -0
  121. aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/docker-compose.yml +108 -0
  122. aiagents4pharma/talk2knowledgegraphs/entrypoint.sh +190 -0
  123. aiagents4pharma/talk2knowledgegraphs/install.md +140 -0
  124. aiagents4pharma/talk2knowledgegraphs/milvus_data_dump.py +31 -65
  125. aiagents4pharma/talk2knowledgegraphs/states/__init__.py +3 -2
  126. aiagents4pharma/talk2knowledgegraphs/states/state_talk2knowledgegraphs.py +1 -0
  127. aiagents4pharma/talk2knowledgegraphs/tests/test_agents_t2kg_agent.py +65 -40
  128. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_biobridge_primekg.py +54 -48
  129. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_dataset.py +4 -0
  130. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_primekg.py +17 -4
  131. aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py +33 -24
  132. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_graphrag_reasoning.py +116 -69
  133. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_milvus_multimodal_subgraph_extraction.py +334 -216
  134. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_multimodal_subgraph_extraction.py +22 -15
  135. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_extraction.py +19 -12
  136. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_summarization.py +95 -48
  137. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py +4 -0
  138. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py +5 -0
  139. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_nim_molmim.py +13 -18
  140. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_ollama.py +10 -3
  141. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +4 -3
  142. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +3 -2
  143. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ols.py +1 -0
  144. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_pubchem.py +9 -4
  145. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_reactome.py +6 -6
  146. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_uniprot.py +4 -0
  147. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_extractions_milvus_multimodal_pcst.py +160 -97
  148. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_kg_utils.py +3 -4
  149. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_pubchem_utils.py +87 -13
  150. aiagents4pharma/talk2knowledgegraphs/tools/__init__.py +10 -7
  151. aiagents4pharma/talk2knowledgegraphs/tools/graphrag_reasoning.py +15 -20
  152. aiagents4pharma/talk2knowledgegraphs/tools/milvus_multimodal_subgraph_extraction.py +145 -142
  153. aiagents4pharma/talk2knowledgegraphs/tools/multimodal_subgraph_extraction.py +92 -90
  154. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_extraction.py +25 -37
  155. aiagents4pharma/talk2knowledgegraphs/tools/subgraph_summarization.py +10 -13
  156. aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +4 -7
  157. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py +4 -7
  158. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py +4 -0
  159. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py +11 -14
  160. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/nim_molmim.py +7 -7
  161. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/ollama.py +12 -6
  162. aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py +8 -6
  163. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +9 -6
  164. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +1 -0
  165. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +15 -9
  166. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ols_terms.py +23 -20
  167. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/pubchem_strings.py +12 -10
  168. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/reactome_pathways.py +16 -10
  169. aiagents4pharma/talk2knowledgegraphs/utils/enrichments/uniprot_proteins.py +26 -18
  170. aiagents4pharma/talk2knowledgegraphs/utils/extractions/__init__.py +4 -5
  171. aiagents4pharma/talk2knowledgegraphs/utils/extractions/milvus_multimodal_pcst.py +14 -34
  172. aiagents4pharma/talk2knowledgegraphs/utils/extractions/multimodal_pcst.py +53 -47
  173. aiagents4pharma/talk2knowledgegraphs/utils/extractions/pcst.py +18 -14
  174. aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py +22 -23
  175. aiagents4pharma/talk2knowledgegraphs/utils/pubchem_utils.py +11 -10
  176. aiagents4pharma/talk2scholars/.dockerignore +13 -0
  177. aiagents4pharma/talk2scholars/Dockerfile +104 -0
  178. aiagents4pharma/talk2scholars/README.md +1 -0
  179. aiagents4pharma/talk2scholars/agents/__init__.py +1 -5
  180. aiagents4pharma/talk2scholars/agents/main_agent.py +6 -4
  181. aiagents4pharma/talk2scholars/agents/paper_download_agent.py +5 -4
  182. aiagents4pharma/talk2scholars/agents/pdf_agent.py +4 -2
  183. aiagents4pharma/talk2scholars/agents/s2_agent.py +2 -2
  184. aiagents4pharma/talk2scholars/agents/zotero_agent.py +10 -11
  185. aiagents4pharma/talk2scholars/configs/__init__.py +1 -3
  186. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py +1 -4
  187. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +1 -1
  188. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +1 -1
  189. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +8 -8
  190. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +7 -7
  191. aiagents4pharma/talk2scholars/configs/tools/__init__.py +8 -6
  192. aiagents4pharma/talk2scholars/docker-compose/cpu/.env.example +21 -0
  193. aiagents4pharma/talk2scholars/docker-compose/cpu/docker-compose.yml +90 -0
  194. aiagents4pharma/talk2scholars/docker-compose/gpu/.env.example +21 -0
  195. aiagents4pharma/talk2scholars/docker-compose/gpu/docker-compose.yml +105 -0
  196. aiagents4pharma/talk2scholars/install.md +122 -0
  197. aiagents4pharma/talk2scholars/state/state_talk2scholars.py +8 -8
  198. aiagents4pharma/talk2scholars/tests/{test_main_agent.py → test_agents_main_agent.py} +41 -23
  199. aiagents4pharma/talk2scholars/tests/{test_paper_download_agent.py → test_agents_paper_agents_download_agent.py} +10 -16
  200. aiagents4pharma/talk2scholars/tests/{test_pdf_agent.py → test_agents_pdf_agent.py} +6 -10
  201. aiagents4pharma/talk2scholars/tests/{test_s2_agent.py → test_agents_s2_agent.py} +8 -16
  202. aiagents4pharma/talk2scholars/tests/{test_zotero_agent.py → test_agents_zotero_agent.py} +5 -7
  203. aiagents4pharma/talk2scholars/tests/{test_s2_display_dataframe.py → test_s2_tools_display_dataframe.py} +6 -7
  204. aiagents4pharma/talk2scholars/tests/{test_s2_query_dataframe.py → test_s2_tools_query_dataframe.py} +5 -15
  205. aiagents4pharma/talk2scholars/tests/{test_paper_downloader.py → test_tools_paper_downloader.py} +25 -63
  206. aiagents4pharma/talk2scholars/tests/{test_question_and_answer_tool.py → test_tools_question_and_answer_tool.py} +2 -6
  207. aiagents4pharma/talk2scholars/tests/{test_s2_multi.py → test_tools_s2_multi.py} +5 -5
  208. aiagents4pharma/talk2scholars/tests/{test_s2_retrieve.py → test_tools_s2_retrieve.py} +2 -1
  209. aiagents4pharma/talk2scholars/tests/{test_s2_search.py → test_tools_s2_search.py} +5 -5
  210. aiagents4pharma/talk2scholars/tests/{test_s2_single.py → test_tools_s2_single.py} +5 -5
  211. aiagents4pharma/talk2scholars/tests/{test_arxiv_downloader.py → test_utils_arxiv_downloader.py} +16 -25
  212. aiagents4pharma/talk2scholars/tests/{test_base_paper_downloader.py → test_utils_base_paper_downloader.py} +25 -47
  213. aiagents4pharma/talk2scholars/tests/{test_biorxiv_downloader.py → test_utils_biorxiv_downloader.py} +14 -42
  214. aiagents4pharma/talk2scholars/tests/{test_medrxiv_downloader.py → test_utils_medrxiv_downloader.py} +15 -49
  215. aiagents4pharma/talk2scholars/tests/{test_nvidia_nim_reranker.py → test_utils_nvidia_nim_reranker.py} +6 -16
  216. aiagents4pharma/talk2scholars/tests/{test_pdf_answer_formatter.py → test_utils_pdf_answer_formatter.py} +1 -0
  217. aiagents4pharma/talk2scholars/tests/{test_pdf_batch_processor.py → test_utils_pdf_batch_processor.py} +6 -15
  218. aiagents4pharma/talk2scholars/tests/{test_pdf_collection_manager.py → test_utils_pdf_collection_manager.py} +34 -11
  219. aiagents4pharma/talk2scholars/tests/{test_pdf_document_processor.py → test_utils_pdf_document_processor.py} +2 -3
  220. aiagents4pharma/talk2scholars/tests/{test_pdf_generate_answer.py → test_utils_pdf_generate_answer.py} +3 -6
  221. aiagents4pharma/talk2scholars/tests/{test_pdf_gpu_detection.py → test_utils_pdf_gpu_detection.py} +5 -16
  222. aiagents4pharma/talk2scholars/tests/{test_pdf_rag_pipeline.py → test_utils_pdf_rag_pipeline.py} +7 -17
  223. aiagents4pharma/talk2scholars/tests/{test_pdf_retrieve_chunks.py → test_utils_pdf_retrieve_chunks.py} +4 -11
  224. aiagents4pharma/talk2scholars/tests/{test_pdf_singleton_manager.py → test_utils_pdf_singleton_manager.py} +26 -23
  225. aiagents4pharma/talk2scholars/tests/{test_pdf_vector_normalization.py → test_utils_pdf_vector_normalization.py} +1 -1
  226. aiagents4pharma/talk2scholars/tests/{test_pdf_vector_store.py → test_utils_pdf_vector_store.py} +27 -55
  227. aiagents4pharma/talk2scholars/tests/{test_pubmed_downloader.py → test_utils_pubmed_downloader.py} +31 -91
  228. aiagents4pharma/talk2scholars/tests/{test_read_helper_utils.py → test_utils_read_helper_utils.py} +2 -6
  229. aiagents4pharma/talk2scholars/tests/{test_s2_utils_ext_ids.py → test_utils_s2_utils_ext_ids.py} +5 -15
  230. aiagents4pharma/talk2scholars/tests/{test_zotero_human_in_the_loop.py → test_utils_zotero_human_in_the_loop.py} +6 -13
  231. aiagents4pharma/talk2scholars/tests/{test_zotero_path.py → test_utils_zotero_path.py} +53 -45
  232. aiagents4pharma/talk2scholars/tests/{test_zotero_read.py → test_utils_zotero_read.py} +30 -91
  233. aiagents4pharma/talk2scholars/tests/{test_zotero_write.py → test_utils_zotero_write.py} +6 -16
  234. aiagents4pharma/talk2scholars/tools/__init__.py +1 -4
  235. aiagents4pharma/talk2scholars/tools/paper_download/paper_downloader.py +20 -35
  236. aiagents4pharma/talk2scholars/tools/paper_download/utils/__init__.py +7 -5
  237. aiagents4pharma/talk2scholars/tools/paper_download/utils/arxiv_downloader.py +9 -11
  238. aiagents4pharma/talk2scholars/tools/paper_download/utils/base_paper_downloader.py +14 -21
  239. aiagents4pharma/talk2scholars/tools/paper_download/utils/biorxiv_downloader.py +14 -22
  240. aiagents4pharma/talk2scholars/tools/paper_download/utils/medrxiv_downloader.py +11 -13
  241. aiagents4pharma/talk2scholars/tools/paper_download/utils/pubmed_downloader.py +14 -28
  242. aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +4 -8
  243. aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +16 -14
  244. aiagents4pharma/talk2scholars/tools/pdf/utils/answer_formatter.py +4 -4
  245. aiagents4pharma/talk2scholars/tools/pdf/utils/batch_processor.py +15 -17
  246. aiagents4pharma/talk2scholars/tools/pdf/utils/collection_manager.py +2 -2
  247. aiagents4pharma/talk2scholars/tools/pdf/utils/document_processor.py +5 -5
  248. aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +4 -4
  249. aiagents4pharma/talk2scholars/tools/pdf/utils/get_vectorstore.py +2 -6
  250. aiagents4pharma/talk2scholars/tools/pdf/utils/gpu_detection.py +5 -9
  251. aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +4 -4
  252. aiagents4pharma/talk2scholars/tools/pdf/utils/paper_loader.py +2 -2
  253. aiagents4pharma/talk2scholars/tools/pdf/utils/rag_pipeline.py +6 -15
  254. aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +7 -15
  255. aiagents4pharma/talk2scholars/tools/pdf/utils/singleton_manager.py +2 -2
  256. aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +3 -4
  257. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_normalization.py +8 -17
  258. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +17 -33
  259. aiagents4pharma/talk2scholars/tools/s2/__init__.py +8 -6
  260. aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +3 -7
  261. aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +7 -6
  262. aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +5 -12
  263. aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +2 -4
  264. aiagents4pharma/talk2scholars/tools/s2/search.py +6 -6
  265. aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +5 -3
  266. aiagents4pharma/talk2scholars/tools/s2/utils/__init__.py +1 -3
  267. aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +12 -18
  268. aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +11 -18
  269. aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +11 -16
  270. aiagents4pharma/talk2scholars/tools/zotero/__init__.py +1 -4
  271. aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py +1 -4
  272. aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +21 -39
  273. aiagents4pharma/talk2scholars/tools/zotero/utils/review_helper.py +2 -6
  274. aiagents4pharma/talk2scholars/tools/zotero/utils/write_helper.py +8 -11
  275. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py +4 -12
  276. aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_pdf_downloader.py +13 -27
  277. aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +4 -7
  278. aiagents4pharma/talk2scholars/tools/zotero/zotero_review.py +8 -10
  279. aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py +3 -2
  280. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/METADATA +115 -51
  281. aiagents4pharma-1.45.1.dist-info/RECORD +324 -0
  282. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/WHEEL +1 -2
  283. aiagents4pharma-1.44.0.dist-info/RECORD +0 -293
  284. aiagents4pharma-1.44.0.dist-info/top_level.txt +0 -1
  285. /aiagents4pharma/talk2scholars/tests/{test_state.py → test_states_state.py} +0 -0
  286. /aiagents4pharma/talk2scholars/tests/{test_pdf_paper_loader.py → test_utils_pdf_paper_loader.py} +0 -0
  287. /aiagents4pharma/talk2scholars/tests/{test_tool_helper_utils.py → test_utils_tool_helper_utils.py} +0 -0
  288. /aiagents4pharma/talk2scholars/tests/{test_zotero_pdf_downloader_utils.py → test_utils_zotero_pdf_downloader_utils.py} +0 -0
  289. {aiagents4pharma-1.44.0.dist-info → aiagents4pharma-1.45.1.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,7 @@ Utility for zotero read tool.
5
5
  """
6
6
 
7
7
  import logging
8
- from typing import Any, Dict, List
8
+ from typing import Any
9
9
 
10
10
  import hydra
11
11
  import requests
@@ -51,7 +51,7 @@ class ZoteroSearchData:
51
51
  self._filter_and_format_papers(items)
52
52
  self._create_content()
53
53
 
54
- def get_search_results(self) -> Dict[str, Any]:
54
+ def get_search_results(self) -> dict[str, Any]:
55
55
  """Get the search results and content."""
56
56
  return {
57
57
  "article_data": self.article_data,
@@ -61,9 +61,7 @@ class ZoteroSearchData:
61
61
  def _load_config(self) -> Any:
62
62
  """Load hydra configuration."""
63
63
  with hydra.initialize(version_base=None, config_path="../../../configs"):
64
- cfg = hydra.compose(
65
- config_name="config", overrides=["tools/zotero_read=default"]
66
- )
64
+ cfg = hydra.compose(config_name="config", overrides=["tools/zotero_read=default"])
67
65
  logger.info("Loaded configuration for Zotero search tool")
68
66
  return cfg.tools.zotero_read
69
67
 
@@ -77,7 +75,7 @@ class ZoteroSearchData:
77
75
  )
78
76
  return zotero.Zotero(self.cfg.user_id, self.cfg.library_type, self.cfg.api_key)
79
77
 
80
- def _fetch_items(self) -> List[Dict[str, Any]]:
78
+ def _fetch_items(self) -> list[dict[str, Any]]:
81
79
  """Fetch items from Zotero."""
82
80
  try:
83
81
  if self.query.strip() == "":
@@ -100,15 +98,13 @@ class ZoteroSearchData:
100
98
 
101
99
  if not items:
102
100
  logger.error("No items returned from Zotero for query: '%s'", self.query)
103
- raise RuntimeError(
104
- "No items returned from Zotero. Please retry the same query."
105
- )
101
+ raise RuntimeError("No items returned from Zotero. Please retry the same query.")
106
102
 
107
103
  return items
108
104
 
109
- def _collect_item_attachments(self) -> Dict[str, str]:
105
+ def _collect_item_attachments(self) -> dict[str, str]:
110
106
  """Collect PDF attachment keys for non-orphan items."""
111
- item_attachments: Dict[str, str] = {}
107
+ item_attachments: dict[str, str] = {}
112
108
  for item_key, item_data in self.article_data.items():
113
109
  if item_data.get("Type") == "orphan_attachment":
114
110
  continue
@@ -127,7 +123,7 @@ class ZoteroSearchData:
127
123
  logger.error("Failed to get attachments for item %s: %s", item_key, e)
128
124
  return item_attachments
129
125
 
130
- def _process_orphaned_pdfs(self, orphaned_pdfs: Dict[str, str]) -> None:
126
+ def _process_orphaned_pdfs(self, orphaned_pdfs: dict[str, str]) -> None:
131
127
  """Download or record orphaned PDF attachments."""
132
128
  if self.download_pdfs:
133
129
  logger.info("Downloading %d orphaned PDFs in parallel", len(orphaned_pdfs))
@@ -147,16 +143,14 @@ class ZoteroSearchData:
147
143
  logger.info("Skipping orphaned PDF downloads (download_pdfs=False)")
148
144
  for attachment_key in orphaned_pdfs:
149
145
  self.article_data[attachment_key]["attachment_key"] = attachment_key
150
- self.article_data[attachment_key]["filename"] = (
151
- self.article_data[attachment_key].get("Title", attachment_key)
152
- )
146
+ self.article_data[attachment_key]["filename"] = self.article_data[
147
+ attachment_key
148
+ ].get("Title", attachment_key)
153
149
 
154
- def _process_item_pdfs(self, item_attachments: Dict[str, str]) -> None:
150
+ def _process_item_pdfs(self, item_attachments: dict[str, str]) -> None:
155
151
  """Download or record regular item PDF attachments."""
156
152
  if self.download_pdfs:
157
- logger.info(
158
- "Downloading %d regular item PDFs in parallel", len(item_attachments)
159
- )
153
+ logger.info("Downloading %d regular item PDFs in parallel", len(item_attachments))
160
154
  results = download_pdfs_in_parallel(
161
155
  self.session,
162
156
  self.cfg.user_id,
@@ -175,15 +169,13 @@ class ZoteroSearchData:
175
169
  self.article_data[item_key]["attachment_key"] = attachment_key
176
170
  logger.info("Downloaded Zotero PDF to: %s", file_path)
177
171
 
178
- def _filter_and_format_papers(self, items: List[Dict[str, Any]]) -> None:
172
+ def _filter_and_format_papers(self, items: list[dict[str, Any]]) -> None:
179
173
  """Filter and format papers from Zotero items, including standalone PDFs."""
180
- filter_item_types = (
181
- self.cfg.zotero.filter_item_types if self.only_articles else []
182
- )
174
+ filter_item_types = self.cfg.zotero.filter_item_types if self.only_articles else []
183
175
  logger.debug("Filtering item types: %s", filter_item_types)
184
176
 
185
177
  # Maps to track attachments for batch processing
186
- orphaned_pdfs: Dict[str, str] = {} # attachment_key -> item key (same for orphans)
178
+ orphaned_pdfs: dict[str, str] = {} # attachment_key -> item key (same for orphans)
187
179
 
188
180
  # First pass: process all items without downloading PDFs
189
181
  for item in items:
@@ -214,17 +206,14 @@ class ZoteroSearchData:
214
206
  "Authors": [
215
207
  f"{creator.get('firstName', '')} {creator.get('lastName', '')}".strip()
216
208
  for creator in data.get("creators", [])
217
- if isinstance(creator, dict)
218
- and creator.get("creatorType") == "author"
209
+ if isinstance(creator, dict) and creator.get("creatorType") == "author"
219
210
  ],
220
211
  "source": "zotero",
221
212
  }
222
213
  # We'll collect attachment info in second pass
223
214
 
224
215
  # CASE 2: Standalone orphaned PDF attachment
225
- elif data.get("contentType") == "application/pdf" and not data.get(
226
- "parentItem"
227
- ):
216
+ elif data.get("contentType") == "application/pdf" and not data.get("parentItem"):
228
217
  attachment_key = key
229
218
  filename = data.get("filename", "unknown.pdf")
230
219
 
@@ -260,25 +249,18 @@ class ZoteroSearchData:
260
249
 
261
250
  # Ensure we have some results
262
251
  if not self.article_data:
263
- logger.error(
264
- "No matching papers returned from Zotero for query: '%s'", self.query
265
- )
252
+ logger.error("No matching papers returned from Zotero for query: '%s'", self.query)
266
253
  raise RuntimeError(
267
254
  "No matching papers returned from Zotero. Please retry the same query."
268
255
  )
269
256
 
270
- logger.info(
271
- "Filtered %d items (including orphaned attachments)", len(self.article_data)
272
- )
257
+ logger.info("Filtered %d items (including orphaned attachments)", len(self.article_data))
273
258
 
274
259
  def _create_content(self) -> None:
275
260
  """Create the content message for the response."""
276
261
  top_papers = list(self.article_data.values())[:2]
277
262
  top_papers_info = "\n".join(
278
- [
279
- f"{i+1}. {paper['Title']} ({paper['Type']})"
280
- for i, paper in enumerate(top_papers)
281
- ]
263
+ [f"{i + 1}. {paper['Title']} ({paper['Type']})" for i, paper in enumerate(top_papers)]
282
264
  )
283
265
 
284
266
  self.content = "Retrieval was successful. Papers are attached as an artifact."
@@ -5,7 +5,6 @@ Utility for reviewing papers and saving them to Zotero.
5
5
  """
6
6
 
7
7
  import logging
8
- from typing import List
9
8
 
10
9
  # Configure logging
11
10
  logging.basicConfig(level=logging.INFO)
@@ -40,12 +39,9 @@ class ReviewData:
40
39
 
41
40
  def get_custom_path_approval_message(self, custom_path: str) -> str:
42
41
  """Get the formatted approval message for a custom collection path."""
43
- return (
44
- f"Human approved saving papers to custom Zotero "
45
- f"collection '{custom_path}'."
46
- )
42
+ return f"Human approved saving papers to custom Zotero collection '{custom_path}'."
47
43
 
48
- def _create_papers_summary(self) -> List[str]:
44
+ def _create_papers_summary(self) -> list[str]:
49
45
  """Create a summary of papers for review."""
50
46
  summary = []
51
47
  for paper_id, paper in list(self.fetched_papers.items())[:5]:
@@ -5,15 +5,16 @@ Utility for zotero write tool.
5
5
  """
6
6
 
7
7
  import logging
8
- from typing import Any, Dict
8
+ from typing import Any
9
+
9
10
  import hydra
10
11
  from pyzotero import zotero
12
+
11
13
  from .zotero_path import (
12
- find_or_create_collection,
13
14
  fetch_papers_for_save,
15
+ find_or_create_collection,
14
16
  )
15
17
 
16
-
17
18
  # Configure logging
18
19
  logging.basicConfig(level=logging.INFO)
19
20
  logger = logging.getLogger(__name__)
@@ -41,9 +42,7 @@ class ZoteroWriteData:
41
42
  def _load_config(self) -> Any:
42
43
  """Load hydra configuration."""
43
44
  with hydra.initialize(version_base=None, config_path="../../../configs"):
44
- cfg = hydra.compose(
45
- config_name="config", overrides=["tools/zotero_write=default"]
46
- )
45
+ cfg = hydra.compose(config_name="config", overrides=["tools/zotero_write=default"])
47
46
  logger.info("Loaded configuration for Zotero write tool")
48
47
  return cfg.tools.zotero_write
49
48
 
@@ -107,9 +106,7 @@ class ZoteroWriteData:
107
106
  if " " in name
108
107
  else {"creatorType": "author", "lastName": name}
109
108
  )
110
- for name in [
111
- author.split(" (ID: ")[0] for author in paper.get("Authors", [])
112
- ]
109
+ for name in [author.split(" (ID: ")[0] for author in paper.get("Authors", [])]
113
110
  ]
114
111
 
115
112
  self.zotero_items.append(
@@ -152,13 +149,13 @@ class ZoteroWriteData:
152
149
  top_papers = list(self.fetched_papers.values())[:2]
153
150
  top_papers_info = "\n".join(
154
151
  [
155
- f"{i+1}. {paper.get('Title', 'N/A')} ({paper.get('URL', 'N/A')})"
152
+ f"{i + 1}. {paper.get('Title', 'N/A')} ({paper.get('URL', 'N/A')})"
156
153
  for i, paper in enumerate(top_papers)
157
154
  ]
158
155
  )
159
156
  self.content += "Here are a few of these articles:\n" + top_papers_info
160
157
 
161
- def process_write(self) -> Dict[str, Any]:
158
+ def process_write(self) -> dict[str, Any]:
162
159
  """Process the write operation and return results."""
163
160
  self._validate_papers()
164
161
  matched_collection_key = self._find_collection()
@@ -29,9 +29,7 @@ def get_item_collections(zot):
29
29
 
30
30
  # Create mappings: collection key → name and collection key → parent key
31
31
  collection_map = {col["key"]: col["data"]["name"] for col in collections}
32
- parent_map = {
33
- col["key"]: col["data"].get("parentCollection") for col in collections
34
- }
32
+ parent_map = {col["key"]: col["data"].get("parentCollection") for col in collections}
35
33
 
36
34
  # Build full paths for collections
37
35
  def build_collection_path(col_key):
@@ -49,9 +47,7 @@ def get_item_collections(zot):
49
47
 
50
48
  for collection in collections:
51
49
  collection_key = collection["key"]
52
- collection_items = zot.collection_items(
53
- collection_key
54
- ) # Fetch items in the collection
50
+ collection_items = zot.collection_items(collection_key) # Fetch items in the collection
55
51
 
56
52
  for item in collection_items:
57
53
  item_key = item["data"]["key"]
@@ -67,9 +63,7 @@ def get_item_collections(zot):
67
63
 
68
64
  def find_or_create_collection(zot, path, create_missing=False):
69
65
  """find collection or create if missing"""
70
- logger.info(
71
- "Finding collection for path: %s (create_missing=%s)", path, create_missing
72
- )
66
+ logger.info("Finding collection for path: %s (create_missing=%s)", path, create_missing)
73
67
  # Normalize path: remove leading/trailing slashes and convert to lowercase
74
68
  normalized = path.strip("/").lower()
75
69
  path_parts = normalized.split("/") if normalized else []
@@ -136,9 +130,7 @@ def get_all_collection_paths(zot):
136
130
 
137
131
  # Create mappings: collection key → name and collection key → parent key
138
132
  collection_map = {col["key"]: col["data"]["name"] for col in collections}
139
- parent_map = {
140
- col["key"]: col["data"].get("parentCollection") for col in collections
141
- }
133
+ parent_map = {col["key"]: col["data"].get("parentCollection") for col in collections}
142
134
 
143
135
  # Build full paths for collections
144
136
  def build_collection_path(col_key):
@@ -3,10 +3,10 @@
3
3
  Utility functions for downloading PDFs from Zotero.
4
4
  """
5
5
 
6
+ import concurrent.futures
6
7
  import logging
7
8
  import tempfile
8
- from typing import Optional, Tuple, Dict
9
- import concurrent.futures
9
+
10
10
  import requests
11
11
 
12
12
  logger = logging.getLogger(__name__)
@@ -18,7 +18,7 @@ def download_zotero_pdf(
18
18
  api_key: str,
19
19
  attachment_key: str,
20
20
  **kwargs,
21
- ) -> Optional[Tuple[str, str]]:
21
+ ) -> tuple[str, str] | None:
22
22
  """
23
23
  Download a PDF from Zotero by attachment key.
24
24
 
@@ -40,18 +40,12 @@ def download_zotero_pdf(
40
40
  # Log configured parameters for verification
41
41
  logger.info("download_zotero_pdf params -> timeout=%s, chunk_size=%s", timeout, chunk_size)
42
42
  # Log download start
43
- logger.info(
44
- "Downloading Zotero PDF for attachment %s from Zotero API", attachment_key
45
- )
46
- zotero_pdf_url = (
47
- f"https://api.zotero.org/users/{user_id}/items/" f"{attachment_key}/file"
48
- )
43
+ logger.info("Downloading Zotero PDF for attachment %s from Zotero API", attachment_key)
44
+ zotero_pdf_url = f"https://api.zotero.org/users/{user_id}/items/{attachment_key}/file"
49
45
  headers = {"Zotero-API-Key": api_key}
50
46
 
51
47
  try:
52
- response = session.get(
53
- zotero_pdf_url, headers=headers, stream=True, timeout=timeout
54
- )
48
+ response = session.get(zotero_pdf_url, headers=headers, stream=True, timeout=timeout)
55
49
  response.raise_for_status()
56
50
 
57
51
  # Download to a temporary file first
@@ -65,9 +59,7 @@ def download_zotero_pdf(
65
59
  # Determine filename from Content-Disposition header or default
66
60
  if "filename=" in response.headers.get("Content-Disposition", ""):
67
61
  filename = (
68
- response.headers.get("Content-Disposition", "")
69
- .split("filename=")[-1]
70
- .strip('"')
62
+ response.headers.get("Content-Disposition", "").split("filename=")[-1].strip('"')
71
63
  )
72
64
  else:
73
65
  filename = "downloaded.pdf"
@@ -75,9 +67,7 @@ def download_zotero_pdf(
75
67
  return temp_file_path, filename
76
68
 
77
69
  except (requests.exceptions.RequestException, OSError) as e:
78
- logger.error(
79
- "Failed to download Zotero PDF for attachment %s: %s", attachment_key, e
80
- )
70
+ logger.error("Failed to download Zotero PDF for attachment %s: %s", attachment_key, e)
81
71
  return None
82
72
 
83
73
 
@@ -85,9 +75,9 @@ def download_pdfs_in_parallel(
85
75
  session: requests.Session,
86
76
  user_id: str,
87
77
  api_key: str,
88
- attachment_item_map: Dict[str, str],
78
+ attachment_item_map: dict[str, str],
89
79
  **kwargs,
90
- ) -> Dict[str, Tuple[str, str, str]]:
80
+ ) -> dict[str, tuple[str, str, str]]:
91
81
  """
92
82
  Download multiple PDFs in parallel using ThreadPoolExecutor.
93
83
 
@@ -108,20 +98,16 @@ def download_pdfs_in_parallel(
108
98
  chunk_size = kwargs.get("chunk_size")
109
99
  # Log configured parameters for verification
110
100
  logger.info(
111
- "download_pdfs_in_parallel params -> max_workers=%s, chunk_size=%s",
101
+ "download_pdfs_in_parallel params -> max_workers=%s, chunk_size=%s",
112
102
  max_workers,
113
103
  chunk_size,
114
104
  )
115
- results: Dict[str, Tuple[str, str, str]] = {}
105
+ results: dict[str, tuple[str, str, str]] = {}
116
106
  if not attachment_item_map:
117
107
  return results
118
108
 
119
109
  with concurrent.futures.ThreadPoolExecutor(
120
- max_workers=(
121
- max_workers
122
- if max_workers is not None
123
- else min(10, len(attachment_item_map))
124
- )
110
+ max_workers=(max_workers if max_workers is not None else min(10, len(attachment_item_map)))
125
111
  ) as executor:
126
112
  future_to_keys = {
127
113
  executor.submit(
@@ -10,13 +10,14 @@ for each found item and makes the results available as an artifact.
10
10
 
11
11
  import logging
12
12
  from typing import Annotated, Any
13
+
13
14
  from langchain_core.messages import ToolMessage
14
15
  from langchain_core.tools import tool
15
16
  from langchain_core.tools.base import InjectedToolCallId
16
17
  from langgraph.types import Command
17
18
  from pydantic import BaseModel, Field
18
- from .utils.read_helper import ZoteroSearchData
19
19
 
20
+ from .utils.read_helper import ZoteroSearchData
20
21
 
21
22
  # Configure logging
22
23
  logging.basicConfig(level=logging.INFO)
@@ -34,16 +35,12 @@ class ZoteroSearchInput(BaseModel):
34
35
  tool_call_id (str): Internal identifier for this tool invocation.
35
36
  """
36
37
 
37
- query: str = Field(
38
- description="Search query string to find papers in Zotero library."
39
- )
38
+ query: str = Field(description="Search query string to find papers in Zotero library.")
40
39
  only_articles: bool = Field(
41
40
  default=True,
42
41
  description="Whether to only search for journal articles/conference papers.",
43
42
  )
44
- limit: int = Field(
45
- default=2, description="Maximum number of results to return", ge=1, le=100
46
- )
43
+ limit: int = Field(default=2, description="Maximum number of results to return", ge=1, le=100)
47
44
  download_pdfs: bool = Field(
48
45
  default=False,
49
46
  description="Whether to download PDF attachments immediately (default True).",
@@ -5,15 +5,17 @@ This tool implements human-in-the-loop review for Zotero write operations.
5
5
  """
6
6
 
7
7
  import logging
8
- from typing import Annotated, Any, Optional, Literal
8
+ from typing import Annotated, Any, Literal
9
+
10
+ from langchain_core.messages import HumanMessage, ToolMessage
9
11
  from langchain_core.tools import tool
10
- from langchain_core.messages import ToolMessage, HumanMessage
11
12
  from langchain_core.tools.base import InjectedToolCallId
12
13
  from langgraph.prebuilt import InjectedState
13
14
  from langgraph.types import Command, interrupt
14
15
  from pydantic import BaseModel, Field
15
- from .utils.zotero_path import fetch_papers_for_save
16
+
16
17
  from .utils.review_helper import ReviewData
18
+ from .utils.zotero_path import fetch_papers_for_save
17
19
 
18
20
  # Configure logging
19
21
  logging.basicConfig(level=logging.INFO)
@@ -28,7 +30,7 @@ class ZoteroReviewDecision(BaseModel):
28
30
  """
29
31
 
30
32
  decision: Literal["approve", "reject", "custom"]
31
- custom_path: Optional[str] = None
33
+ custom_path: str | None = None
32
34
 
33
35
 
34
36
  class ZoteroReviewInput(BaseModel):
@@ -83,9 +85,7 @@ def zotero_review(
83
85
  raise ValueError("LLM model is not available in the state.")
84
86
  structured_llm = llm_model.with_structured_output(ZoteroReviewDecision)
85
87
  # Convert the raw human response to a message for structured parsing
86
- decision_response = structured_llm.invoke(
87
- [HumanMessage(content=str(human_review))]
88
- )
88
+ decision_response = structured_llm.invoke([HumanMessage(content=str(human_review))])
89
89
 
90
90
  # Process the structured response
91
91
  if decision_response.decision == "approve":
@@ -105,9 +105,7 @@ def zotero_review(
105
105
  }
106
106
  )
107
107
  if decision_response.decision == "custom" and decision_response.custom_path:
108
- logger.info(
109
- "User approved with custom path: %s", decision_response.custom_path
110
- )
108
+ logger.info("User approved with custom path: %s", decision_response.custom_path)
111
109
  return Command(
112
110
  update={
113
111
  "messages": [
@@ -6,14 +6,15 @@ This tool is used to save fetched papers to Zotero library after human approval.
6
6
 
7
7
  import logging
8
8
  from typing import Annotated, Any
9
+
9
10
  from langchain_core.messages import ToolMessage
10
11
  from langchain_core.tools import tool
11
12
  from langchain_core.tools.base import InjectedToolCallId
12
- from langgraph.types import Command
13
13
  from langgraph.prebuilt import InjectedState
14
+ from langgraph.types import Command
14
15
  from pydantic import BaseModel, Field
15
- from .utils.write_helper import ZoteroWriteData
16
16
 
17
+ from .utils.write_helper import ZoteroWriteData
17
18
 
18
19
  # Configure logging
19
20
  logging.basicConfig(level=logging.INFO)