aiagents4pharma 1.43.0__py3-none-any.whl → 1.45.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.
- aiagents4pharma/__init__.py +2 -2
- aiagents4pharma/talk2aiagents4pharma/.dockerignore +13 -0
- aiagents4pharma/talk2aiagents4pharma/Dockerfile +105 -0
- aiagents4pharma/talk2aiagents4pharma/README.md +1 -0
- aiagents4pharma/talk2aiagents4pharma/__init__.py +4 -5
- aiagents4pharma/talk2aiagents4pharma/agents/__init__.py +3 -2
- aiagents4pharma/talk2aiagents4pharma/agents/main_agent.py +24 -23
- aiagents4pharma/talk2aiagents4pharma/configs/__init__.py +2 -2
- aiagents4pharma/talk2aiagents4pharma/configs/agents/__init__.py +2 -2
- aiagents4pharma/talk2aiagents4pharma/configs/agents/main_agent/default.yaml +2 -2
- aiagents4pharma/talk2aiagents4pharma/configs/config.yaml +1 -1
- aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/.env.example +23 -0
- aiagents4pharma/talk2aiagents4pharma/docker-compose/cpu/docker-compose.yml +93 -0
- aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/.env.example +23 -0
- aiagents4pharma/talk2aiagents4pharma/docker-compose/gpu/docker-compose.yml +108 -0
- aiagents4pharma/talk2aiagents4pharma/install.md +127 -0
- aiagents4pharma/talk2aiagents4pharma/states/__init__.py +3 -2
- aiagents4pharma/talk2aiagents4pharma/states/state_talk2aiagents4pharma.py +5 -3
- aiagents4pharma/talk2aiagents4pharma/tests/__init__.py +2 -2
- aiagents4pharma/talk2aiagents4pharma/tests/test_main_agent.py +72 -50
- aiagents4pharma/talk2biomodels/.dockerignore +13 -0
- aiagents4pharma/talk2biomodels/Dockerfile +104 -0
- aiagents4pharma/talk2biomodels/README.md +1 -0
- aiagents4pharma/talk2biomodels/__init__.py +4 -8
- aiagents4pharma/talk2biomodels/agents/__init__.py +3 -2
- aiagents4pharma/talk2biomodels/agents/t2b_agent.py +47 -42
- aiagents4pharma/talk2biomodels/api/__init__.py +4 -5
- aiagents4pharma/talk2biomodels/api/kegg.py +14 -10
- aiagents4pharma/talk2biomodels/api/ols.py +13 -10
- aiagents4pharma/talk2biomodels/api/uniprot.py +7 -6
- aiagents4pharma/talk2biomodels/configs/__init__.py +3 -4
- aiagents4pharma/talk2biomodels/configs/agents/__init__.py +2 -2
- aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/__init__.py +2 -2
- aiagents4pharma/talk2biomodels/configs/agents/t2b_agent/default.yaml +1 -1
- aiagents4pharma/talk2biomodels/configs/config.yaml +1 -1
- aiagents4pharma/talk2biomodels/configs/tools/__init__.py +4 -5
- aiagents4pharma/talk2biomodels/configs/tools/ask_question/__init__.py +2 -2
- aiagents4pharma/talk2biomodels/configs/tools/ask_question/default.yaml +1 -2
- aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/__init__.py +2 -2
- aiagents4pharma/talk2biomodels/configs/tools/custom_plotter/default.yaml +1 -1
- aiagents4pharma/talk2biomodels/configs/tools/get_annotation/__init__.py +2 -2
- aiagents4pharma/talk2biomodels/configs/tools/get_annotation/default.yaml +1 -1
- aiagents4pharma/talk2biomodels/install.md +63 -0
- aiagents4pharma/talk2biomodels/models/__init__.py +4 -4
- aiagents4pharma/talk2biomodels/models/basico_model.py +36 -28
- aiagents4pharma/talk2biomodels/models/sys_bio_model.py +13 -10
- aiagents4pharma/talk2biomodels/states/__init__.py +3 -2
- aiagents4pharma/talk2biomodels/states/state_talk2biomodels.py +12 -8
- aiagents4pharma/talk2biomodels/tests/BIOMD0000000449_url.xml +1585 -0
- aiagents4pharma/talk2biomodels/tests/__init__.py +2 -2
- aiagents4pharma/talk2biomodels/tests/article_on_model_537.pdf +0 -0
- aiagents4pharma/talk2biomodels/tests/test_api.py +18 -14
- aiagents4pharma/talk2biomodels/tests/test_ask_question.py +8 -9
- aiagents4pharma/talk2biomodels/tests/test_basico_model.py +15 -9
- aiagents4pharma/talk2biomodels/tests/test_get_annotation.py +54 -55
- aiagents4pharma/talk2biomodels/tests/test_getmodelinfo.py +28 -27
- aiagents4pharma/talk2biomodels/tests/test_integration.py +21 -33
- aiagents4pharma/talk2biomodels/tests/test_load_biomodel.py +14 -11
- aiagents4pharma/talk2biomodels/tests/test_param_scan.py +21 -20
- aiagents4pharma/talk2biomodels/tests/test_query_article.py +129 -29
- aiagents4pharma/talk2biomodels/tests/test_search_models.py +9 -13
- aiagents4pharma/talk2biomodels/tests/test_simulate_model.py +16 -15
- aiagents4pharma/talk2biomodels/tests/test_steady_state.py +12 -22
- aiagents4pharma/talk2biomodels/tests/test_sys_bio_model.py +33 -29
- aiagents4pharma/talk2biomodels/tools/__init__.py +15 -12
- aiagents4pharma/talk2biomodels/tools/ask_question.py +42 -32
- aiagents4pharma/talk2biomodels/tools/custom_plotter.py +51 -43
- aiagents4pharma/talk2biomodels/tools/get_annotation.py +99 -75
- aiagents4pharma/talk2biomodels/tools/get_modelinfo.py +57 -51
- aiagents4pharma/talk2biomodels/tools/load_arguments.py +52 -32
- aiagents4pharma/talk2biomodels/tools/load_biomodel.py +8 -2
- aiagents4pharma/talk2biomodels/tools/parameter_scan.py +107 -90
- aiagents4pharma/talk2biomodels/tools/query_article.py +14 -13
- aiagents4pharma/talk2biomodels/tools/search_models.py +37 -26
- aiagents4pharma/talk2biomodels/tools/simulate_model.py +47 -37
- aiagents4pharma/talk2biomodels/tools/steady_state.py +76 -58
- aiagents4pharma/talk2biomodels/tools/utils.py +4 -3
- aiagents4pharma/talk2cells/README.md +1 -0
- aiagents4pharma/talk2cells/__init__.py +4 -5
- aiagents4pharma/talk2cells/agents/__init__.py +3 -2
- aiagents4pharma/talk2cells/agents/scp_agent.py +21 -19
- aiagents4pharma/talk2cells/states/__init__.py +3 -2
- aiagents4pharma/talk2cells/states/state_talk2cells.py +4 -2
- aiagents4pharma/talk2cells/tests/scp_agent/test_scp_agent.py +8 -9
- aiagents4pharma/talk2cells/tools/__init__.py +3 -2
- aiagents4pharma/talk2cells/tools/scp_agent/__init__.py +4 -4
- aiagents4pharma/talk2cells/tools/scp_agent/display_studies.py +5 -3
- aiagents4pharma/talk2cells/tools/scp_agent/search_studies.py +21 -22
- aiagents4pharma/talk2knowledgegraphs/.dockerignore +13 -0
- aiagents4pharma/talk2knowledgegraphs/Dockerfile +103 -0
- aiagents4pharma/talk2knowledgegraphs/README.md +1 -0
- aiagents4pharma/talk2knowledgegraphs/__init__.py +4 -7
- aiagents4pharma/talk2knowledgegraphs/agents/__init__.py +3 -2
- aiagents4pharma/talk2knowledgegraphs/agents/t2kg_agent.py +40 -30
- aiagents4pharma/talk2knowledgegraphs/configs/__init__.py +3 -6
- aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/__init__.py +2 -2
- aiagents4pharma/talk2knowledgegraphs/configs/agents/t2kg_agent/default.yaml +8 -8
- aiagents4pharma/talk2knowledgegraphs/configs/app/__init__.py +3 -2
- aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/__init__.py +2 -2
- aiagents4pharma/talk2knowledgegraphs/configs/app/frontend/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/config.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/tools/__init__.py +4 -5
- aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/__init__.py +2 -2
- aiagents4pharma/talk2knowledgegraphs/configs/tools/graphrag_reasoning/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/tools/multimodal_subgraph_extraction/default.yaml +17 -2
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/__init__.py +2 -2
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_extraction/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/__init__.py +2 -2
- aiagents4pharma/talk2knowledgegraphs/configs/tools/subgraph_summarization/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/ols_terms/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/reactome_pathways/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/utils/enrichments/uniprot_proteins/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/configs/utils/pubchem_utils/default.yaml +1 -1
- aiagents4pharma/talk2knowledgegraphs/datasets/__init__.py +4 -6
- aiagents4pharma/talk2knowledgegraphs/datasets/biobridge_primekg.py +115 -67
- aiagents4pharma/talk2knowledgegraphs/datasets/dataset.py +2 -0
- aiagents4pharma/talk2knowledgegraphs/datasets/primekg.py +35 -24
- aiagents4pharma/talk2knowledgegraphs/datasets/starkqa_primekg.py +29 -21
- aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/.env.example +23 -0
- aiagents4pharma/talk2knowledgegraphs/docker-compose/cpu/docker-compose.yml +93 -0
- aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/.env.example +23 -0
- aiagents4pharma/talk2knowledgegraphs/docker-compose/gpu/docker-compose.yml +108 -0
- aiagents4pharma/talk2knowledgegraphs/entrypoint.sh +190 -0
- aiagents4pharma/talk2knowledgegraphs/install.md +140 -0
- aiagents4pharma/talk2knowledgegraphs/milvus_data_dump.py +31 -65
- aiagents4pharma/talk2knowledgegraphs/states/__init__.py +3 -2
- aiagents4pharma/talk2knowledgegraphs/states/state_talk2knowledgegraphs.py +1 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_agents_t2kg_agent.py +65 -40
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_biobridge_primekg.py +54 -48
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_dataset.py +4 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_primekg.py +17 -4
- aiagents4pharma/talk2knowledgegraphs/tests/test_datasets_starkqa_primekg.py +33 -24
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_graphrag_reasoning.py +116 -69
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_milvus_multimodal_subgraph_extraction.py +736 -413
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_multimodal_subgraph_extraction.py +22 -15
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_extraction.py +19 -12
- aiagents4pharma/talk2knowledgegraphs/tests/test_tools_subgraph_summarization.py +95 -48
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_embeddings.py +4 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_huggingface.py +5 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_nim_molmim.py +13 -18
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_embeddings_ollama.py +10 -3
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_enrichments.py +4 -3
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ollama.py +3 -2
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_ols.py +1 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_pubchem.py +9 -4
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_reactome.py +6 -6
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_uniprot.py +4 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_extractions_milvus_multimodal_pcst.py +442 -42
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_kg_utils.py +3 -4
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_pubchem_utils.py +10 -6
- aiagents4pharma/talk2knowledgegraphs/tools/__init__.py +10 -7
- aiagents4pharma/talk2knowledgegraphs/tools/graphrag_reasoning.py +15 -20
- aiagents4pharma/talk2knowledgegraphs/tools/milvus_multimodal_subgraph_extraction.py +245 -205
- aiagents4pharma/talk2knowledgegraphs/tools/multimodal_subgraph_extraction.py +92 -90
- aiagents4pharma/talk2knowledgegraphs/tools/subgraph_extraction.py +25 -37
- aiagents4pharma/talk2knowledgegraphs/tools/subgraph_summarization.py +10 -13
- aiagents4pharma/talk2knowledgegraphs/utils/__init__.py +4 -7
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/__init__.py +4 -7
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/embeddings.py +4 -0
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/huggingface.py +11 -14
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/nim_molmim.py +7 -7
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/ollama.py +12 -6
- aiagents4pharma/talk2knowledgegraphs/utils/embeddings/sentence_transformer.py +8 -6
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +9 -6
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/enrichments.py +1 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ollama.py +15 -9
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/ols_terms.py +23 -20
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/pubchem_strings.py +12 -10
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/reactome_pathways.py +16 -10
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/uniprot_proteins.py +26 -18
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/__init__.py +4 -5
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/milvus_multimodal_pcst.py +218 -81
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/multimodal_pcst.py +53 -47
- aiagents4pharma/talk2knowledgegraphs/utils/extractions/pcst.py +18 -14
- aiagents4pharma/talk2knowledgegraphs/utils/kg_utils.py +22 -23
- aiagents4pharma/talk2knowledgegraphs/utils/pubchem_utils.py +11 -10
- aiagents4pharma/talk2scholars/.dockerignore +13 -0
- aiagents4pharma/talk2scholars/Dockerfile +104 -0
- aiagents4pharma/talk2scholars/README.md +1 -0
- aiagents4pharma/talk2scholars/agents/__init__.py +1 -5
- aiagents4pharma/talk2scholars/agents/main_agent.py +6 -4
- aiagents4pharma/talk2scholars/agents/paper_download_agent.py +5 -4
- aiagents4pharma/talk2scholars/agents/pdf_agent.py +4 -2
- aiagents4pharma/talk2scholars/agents/s2_agent.py +2 -2
- aiagents4pharma/talk2scholars/agents/zotero_agent.py +10 -11
- aiagents4pharma/talk2scholars/configs/__init__.py +1 -3
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py +1 -4
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +1 -1
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +1 -1
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +8 -8
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +7 -7
- aiagents4pharma/talk2scholars/configs/tools/__init__.py +8 -6
- aiagents4pharma/talk2scholars/docker-compose/cpu/.env.example +21 -0
- aiagents4pharma/talk2scholars/docker-compose/cpu/docker-compose.yml +90 -0
- aiagents4pharma/talk2scholars/docker-compose/gpu/.env.example +21 -0
- aiagents4pharma/talk2scholars/docker-compose/gpu/docker-compose.yml +105 -0
- aiagents4pharma/talk2scholars/install.md +122 -0
- aiagents4pharma/talk2scholars/state/state_talk2scholars.py +8 -8
- aiagents4pharma/talk2scholars/tests/{test_main_agent.py → test_agents_main_agent.py} +41 -23
- aiagents4pharma/talk2scholars/tests/{test_paper_download_agent.py → test_agents_paper_agents_download_agent.py} +10 -16
- aiagents4pharma/talk2scholars/tests/{test_pdf_agent.py → test_agents_pdf_agent.py} +6 -10
- aiagents4pharma/talk2scholars/tests/{test_s2_agent.py → test_agents_s2_agent.py} +8 -16
- aiagents4pharma/talk2scholars/tests/{test_zotero_agent.py → test_agents_zotero_agent.py} +5 -7
- aiagents4pharma/talk2scholars/tests/{test_s2_display_dataframe.py → test_s2_tools_display_dataframe.py} +6 -7
- aiagents4pharma/talk2scholars/tests/{test_s2_query_dataframe.py → test_s2_tools_query_dataframe.py} +5 -15
- aiagents4pharma/talk2scholars/tests/{test_paper_downloader.py → test_tools_paper_downloader.py} +25 -63
- aiagents4pharma/talk2scholars/tests/{test_question_and_answer_tool.py → test_tools_question_and_answer_tool.py} +2 -6
- aiagents4pharma/talk2scholars/tests/{test_s2_multi.py → test_tools_s2_multi.py} +5 -5
- aiagents4pharma/talk2scholars/tests/{test_s2_retrieve.py → test_tools_s2_retrieve.py} +2 -1
- aiagents4pharma/talk2scholars/tests/{test_s2_search.py → test_tools_s2_search.py} +5 -5
- aiagents4pharma/talk2scholars/tests/{test_s2_single.py → test_tools_s2_single.py} +5 -5
- aiagents4pharma/talk2scholars/tests/{test_arxiv_downloader.py → test_utils_arxiv_downloader.py} +16 -25
- aiagents4pharma/talk2scholars/tests/{test_base_paper_downloader.py → test_utils_base_paper_downloader.py} +25 -47
- aiagents4pharma/talk2scholars/tests/{test_biorxiv_downloader.py → test_utils_biorxiv_downloader.py} +14 -42
- aiagents4pharma/talk2scholars/tests/{test_medrxiv_downloader.py → test_utils_medrxiv_downloader.py} +15 -49
- aiagents4pharma/talk2scholars/tests/{test_nvidia_nim_reranker.py → test_utils_nvidia_nim_reranker.py} +6 -16
- aiagents4pharma/talk2scholars/tests/{test_pdf_answer_formatter.py → test_utils_pdf_answer_formatter.py} +1 -0
- aiagents4pharma/talk2scholars/tests/{test_pdf_batch_processor.py → test_utils_pdf_batch_processor.py} +6 -15
- aiagents4pharma/talk2scholars/tests/{test_pdf_collection_manager.py → test_utils_pdf_collection_manager.py} +34 -11
- aiagents4pharma/talk2scholars/tests/{test_pdf_document_processor.py → test_utils_pdf_document_processor.py} +2 -3
- aiagents4pharma/talk2scholars/tests/{test_pdf_generate_answer.py → test_utils_pdf_generate_answer.py} +3 -6
- aiagents4pharma/talk2scholars/tests/{test_pdf_gpu_detection.py → test_utils_pdf_gpu_detection.py} +5 -16
- aiagents4pharma/talk2scholars/tests/{test_pdf_rag_pipeline.py → test_utils_pdf_rag_pipeline.py} +7 -17
- aiagents4pharma/talk2scholars/tests/{test_pdf_retrieve_chunks.py → test_utils_pdf_retrieve_chunks.py} +4 -11
- aiagents4pharma/talk2scholars/tests/{test_pdf_singleton_manager.py → test_utils_pdf_singleton_manager.py} +26 -23
- aiagents4pharma/talk2scholars/tests/{test_pdf_vector_normalization.py → test_utils_pdf_vector_normalization.py} +1 -1
- aiagents4pharma/talk2scholars/tests/{test_pdf_vector_store.py → test_utils_pdf_vector_store.py} +27 -55
- aiagents4pharma/talk2scholars/tests/{test_pubmed_downloader.py → test_utils_pubmed_downloader.py} +31 -91
- aiagents4pharma/talk2scholars/tests/{test_read_helper_utils.py → test_utils_read_helper_utils.py} +2 -6
- aiagents4pharma/talk2scholars/tests/{test_s2_utils_ext_ids.py → test_utils_s2_utils_ext_ids.py} +5 -15
- aiagents4pharma/talk2scholars/tests/{test_zotero_human_in_the_loop.py → test_utils_zotero_human_in_the_loop.py} +6 -13
- aiagents4pharma/talk2scholars/tests/{test_zotero_path.py → test_utils_zotero_path.py} +53 -45
- aiagents4pharma/talk2scholars/tests/{test_zotero_read.py → test_utils_zotero_read.py} +30 -91
- aiagents4pharma/talk2scholars/tests/{test_zotero_write.py → test_utils_zotero_write.py} +6 -16
- aiagents4pharma/talk2scholars/tools/__init__.py +1 -4
- aiagents4pharma/talk2scholars/tools/paper_download/paper_downloader.py +20 -35
- aiagents4pharma/talk2scholars/tools/paper_download/utils/__init__.py +7 -5
- aiagents4pharma/talk2scholars/tools/paper_download/utils/arxiv_downloader.py +9 -11
- aiagents4pharma/talk2scholars/tools/paper_download/utils/base_paper_downloader.py +14 -21
- aiagents4pharma/talk2scholars/tools/paper_download/utils/biorxiv_downloader.py +14 -22
- aiagents4pharma/talk2scholars/tools/paper_download/utils/medrxiv_downloader.py +11 -13
- aiagents4pharma/talk2scholars/tools/paper_download/utils/pubmed_downloader.py +14 -28
- aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +4 -8
- aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +16 -14
- aiagents4pharma/talk2scholars/tools/pdf/utils/answer_formatter.py +4 -4
- aiagents4pharma/talk2scholars/tools/pdf/utils/batch_processor.py +15 -17
- aiagents4pharma/talk2scholars/tools/pdf/utils/collection_manager.py +2 -2
- aiagents4pharma/talk2scholars/tools/pdf/utils/document_processor.py +5 -5
- aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +4 -4
- aiagents4pharma/talk2scholars/tools/pdf/utils/get_vectorstore.py +2 -6
- aiagents4pharma/talk2scholars/tools/pdf/utils/gpu_detection.py +5 -9
- aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +4 -4
- aiagents4pharma/talk2scholars/tools/pdf/utils/paper_loader.py +2 -2
- aiagents4pharma/talk2scholars/tools/pdf/utils/rag_pipeline.py +6 -15
- aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +7 -15
- aiagents4pharma/talk2scholars/tools/pdf/utils/singleton_manager.py +2 -2
- aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +3 -4
- aiagents4pharma/talk2scholars/tools/pdf/utils/vector_normalization.py +8 -17
- aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +17 -33
- aiagents4pharma/talk2scholars/tools/s2/__init__.py +8 -6
- aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +3 -7
- aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +7 -6
- aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +5 -12
- aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +2 -4
- aiagents4pharma/talk2scholars/tools/s2/search.py +6 -6
- aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +5 -3
- aiagents4pharma/talk2scholars/tools/s2/utils/__init__.py +1 -3
- aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +12 -18
- aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +11 -18
- aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +11 -16
- aiagents4pharma/talk2scholars/tools/zotero/__init__.py +1 -4
- aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py +1 -4
- aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +21 -39
- aiagents4pharma/talk2scholars/tools/zotero/utils/review_helper.py +2 -6
- aiagents4pharma/talk2scholars/tools/zotero/utils/write_helper.py +8 -11
- aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py +4 -12
- aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_pdf_downloader.py +13 -27
- aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +4 -7
- aiagents4pharma/talk2scholars/tools/zotero/zotero_review.py +8 -10
- aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py +3 -2
- {aiagents4pharma-1.43.0.dist-info → aiagents4pharma-1.45.0.dist-info}/METADATA +115 -50
- aiagents4pharma-1.45.0.dist-info/RECORD +324 -0
- {aiagents4pharma-1.43.0.dist-info → aiagents4pharma-1.45.0.dist-info}/WHEEL +1 -2
- aiagents4pharma-1.43.0.dist-info/RECORD +0 -293
- aiagents4pharma-1.43.0.dist-info/top_level.txt +0 -1
- /aiagents4pharma/talk2scholars/tests/{test_state.py → test_states_state.py} +0 -0
- /aiagents4pharma/talk2scholars/tests/{test_pdf_paper_loader.py → test_utils_pdf_paper_loader.py} +0 -0
- /aiagents4pharma/talk2scholars/tests/{test_tool_helper_utils.py → test_utils_tool_helper_utils.py} +0 -0
- /aiagents4pharma/talk2scholars/tests/{test_zotero_pdf_downloader_utils.py → test_utils_zotero_pdf_downloader_utils.py} +0 -0
- {aiagents4pharma-1.43.0.dist-info → aiagents4pharma-1.45.0.dist-info}/licenses/LICENSE +0 -0
@@ -26,9 +26,8 @@ Notes
|
|
26
26
|
first identifier is returned; otherwise a list is returned from all rows that have values.
|
27
27
|
"""
|
28
28
|
|
29
|
-
|
30
29
|
import logging
|
31
|
-
from typing import Annotated,
|
30
|
+
from typing import Annotated, Any
|
32
31
|
|
33
32
|
import pandas as pd
|
34
33
|
from langchain_core.messages import ToolMessage
|
@@ -99,7 +98,7 @@ class QueryDataFrameInput(BaseModel):
|
|
99
98
|
"extract when extract_ids=True."
|
100
99
|
),
|
101
100
|
)
|
102
|
-
row_number:
|
101
|
+
row_number: int | None = Field(
|
103
102
|
default=None,
|
104
103
|
description=(
|
105
104
|
"1-based index of the ID to extract from the list; if provided, returns only"
|
@@ -180,9 +179,7 @@ def query_dataframe(
|
|
180
179
|
context_val = state.get("last_displayed_papers")
|
181
180
|
if not context_val:
|
182
181
|
logger.info("No papers displayed so far, raising NoPapersFoundError")
|
183
|
-
raise NoPapersFoundError(
|
184
|
-
"No papers found. A search needs to be performed first."
|
185
|
-
)
|
182
|
+
raise NoPapersFoundError("No papers found. A search needs to be performed first.")
|
186
183
|
|
187
184
|
# Resolve the paper dictionary
|
188
185
|
if isinstance(context_val, dict):
|
@@ -205,14 +202,10 @@ def query_dataframe(
|
|
205
202
|
if not id_column:
|
206
203
|
raise ValueError("Must specify 'id_column' when extract_ids=True.")
|
207
204
|
if row_number is not None:
|
208
|
-
question_to_agent = (
|
209
|
-
f"df['{id_column}'].dropna().str[0].tolist()[{row_number-1}]"
|
210
|
-
)
|
205
|
+
question_to_agent = f"df['{id_column}'].dropna().str[0].tolist()[{row_number - 1}]"
|
211
206
|
else:
|
212
207
|
question_to_agent = f"df['{id_column}'].dropna().str[0].tolist()"
|
213
|
-
logger.info(
|
214
|
-
"extract_ids enabled: asking agent to run expression: %s", question_to_agent
|
215
|
-
)
|
208
|
+
logger.info("extract_ids enabled: asking agent to run expression: %s", question_to_agent)
|
216
209
|
|
217
210
|
df_agent = create_pandas_dataframe_agent(
|
218
211
|
llm_model,
|
@@ -10,6 +10,7 @@ Configuration is loaded via Hydra and the top ranked result is returned.
|
|
10
10
|
|
11
11
|
import logging
|
12
12
|
from typing import Annotated, Any
|
13
|
+
|
13
14
|
import hydra
|
14
15
|
import requests
|
15
16
|
from langchain_core.messages import ToolMessage
|
@@ -18,7 +19,6 @@ from langchain_core.tools.base import InjectedToolCallId
|
|
18
19
|
from langgraph.types import Command
|
19
20
|
from pydantic import BaseModel, Field
|
20
21
|
|
21
|
-
|
22
22
|
# Configure logging
|
23
23
|
logging.basicConfig(level=logging.INFO)
|
24
24
|
logger = logging.getLogger(__name__)
|
@@ -36,9 +36,7 @@ class RetrieveSemanticScholarPaperIdInput(BaseModel):
|
|
36
36
|
Runtime-injected identifier for tracing the tool invocation.
|
37
37
|
"""
|
38
38
|
|
39
|
-
paper_title: str = Field(
|
40
|
-
..., description="The paper title to search for on Semantic Scholar."
|
41
|
-
)
|
39
|
+
paper_title: str = Field(..., description="The paper title to search for on Semantic Scholar.")
|
42
40
|
tool_call_id: Annotated[str, InjectedToolCallId]
|
43
41
|
|
44
42
|
|
@@ -8,12 +8,14 @@ optionally filtered by publication year.
|
|
8
8
|
"""
|
9
9
|
|
10
10
|
import logging
|
11
|
-
from typing import Annotated, Any
|
11
|
+
from typing import Annotated, Any
|
12
|
+
|
12
13
|
from langchain_core.messages import ToolMessage
|
13
14
|
from langchain_core.tools import tool
|
14
15
|
from langchain_core.tools.base import InjectedToolCallId
|
15
16
|
from langgraph.types import Command
|
16
17
|
from pydantic import BaseModel, Field
|
18
|
+
|
17
19
|
from .utils.search_helper import SearchData
|
18
20
|
|
19
21
|
# Configure logging
|
@@ -32,16 +34,14 @@ class SearchInput(BaseModel):
|
|
32
34
|
tool_call_id: Internal tool call identifier injected by the system.
|
33
35
|
"""
|
34
36
|
|
35
|
-
query: str = Field(
|
36
|
-
description="Full or partial paper title or keywords to search for"
|
37
|
-
)
|
37
|
+
query: str = Field(description="Full or partial paper title or keywords to search for")
|
38
38
|
limit: int = Field(
|
39
39
|
default=10,
|
40
40
|
description="Maximum number of search results to return (1-100)",
|
41
41
|
ge=1,
|
42
42
|
le=100,
|
43
43
|
)
|
44
|
-
year:
|
44
|
+
year: str | None = Field(
|
45
45
|
default=None,
|
46
46
|
description="Publication year filter; supports formats:"
|
47
47
|
"'YYYY', 'YYYY-', '-YYYY', 'YYYY:YYYY'",
|
@@ -58,7 +58,7 @@ def search_tool(
|
|
58
58
|
query: str,
|
59
59
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
60
60
|
limit: int = 10,
|
61
|
-
year:
|
61
|
+
year: str | None = None,
|
62
62
|
) -> Command[Any]:
|
63
63
|
"""
|
64
64
|
Return academic papers from Semantic Scholar matching a title or keyword query.
|
@@ -8,12 +8,14 @@ Given a Semantic Scholar paper ID, this tool retrieves related works
|
|
8
8
|
"""
|
9
9
|
|
10
10
|
import logging
|
11
|
-
from typing import Annotated, Any
|
11
|
+
from typing import Annotated, Any
|
12
|
+
|
12
13
|
from langchain_core.messages import ToolMessage
|
13
14
|
from langchain_core.tools import tool
|
14
15
|
from langchain_core.tools.base import InjectedToolCallId
|
15
16
|
from langgraph.types import Command
|
16
17
|
from pydantic import BaseModel, Field
|
18
|
+
|
17
19
|
from .utils.single_helper import SinglePaperRecData
|
18
20
|
|
19
21
|
# Configure logging
|
@@ -40,7 +42,7 @@ class SinglePaperRecInput(BaseModel):
|
|
40
42
|
ge=1,
|
41
43
|
le=500,
|
42
44
|
)
|
43
|
-
year:
|
45
|
+
year: str | None = Field(
|
44
46
|
default=None,
|
45
47
|
description="Publication year filter; supports formats::"
|
46
48
|
"'YYYY', 'YYYY-', '-YYYY', 'YYYY:YYYY'",
|
@@ -57,7 +59,7 @@ def get_single_paper_recommendations(
|
|
57
59
|
paper_id: str,
|
58
60
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
59
61
|
limit: int = 10,
|
60
|
-
year:
|
62
|
+
year: str | None = None,
|
61
63
|
) -> Command[Any]:
|
62
64
|
"""
|
63
65
|
Recommend related research papers using the Semantic Scholar API for a single paper ID.
|
@@ -1,7 +1,5 @@
|
|
1
1
|
"""This module contains utility functions for the Semantic Scholar search tool."""
|
2
2
|
|
3
|
-
from . import search_helper
|
4
|
-
from . import single_helper
|
5
|
-
from . import multi_helper
|
3
|
+
from . import multi_helper, search_helper, single_helper
|
6
4
|
|
7
5
|
__all__ = ["search_helper", "single_helper", "multi_helper"]
|
@@ -6,11 +6,11 @@ Utility for fetching recommendations based on multiple papers.
|
|
6
6
|
|
7
7
|
import json
|
8
8
|
import logging
|
9
|
-
from typing import Any
|
9
|
+
from typing import Any
|
10
|
+
|
10
11
|
import hydra
|
11
12
|
import requests
|
12
13
|
|
13
|
-
|
14
14
|
# Configure logging
|
15
15
|
logging.basicConfig(level=logging.INFO)
|
16
16
|
logger = logging.getLogger(__name__)
|
@@ -21,9 +21,9 @@ class MultiPaperRecData:
|
|
21
21
|
|
22
22
|
def __init__(
|
23
23
|
self,
|
24
|
-
paper_ids:
|
24
|
+
paper_ids: list[str],
|
25
25
|
limit: int,
|
26
|
-
year:
|
26
|
+
year: str | None,
|
27
27
|
tool_call_id: str,
|
28
28
|
):
|
29
29
|
self.paper_ids = paper_ids
|
@@ -51,7 +51,7 @@ class MultiPaperRecData:
|
|
51
51
|
logger.info("Loaded configuration for multi-paper recommendation tool")
|
52
52
|
return cfg.tools.multi_paper_recommendation
|
53
53
|
|
54
|
-
def _create_params(self) ->
|
54
|
+
def _create_params(self) -> dict[str, Any]:
|
55
55
|
"""Create parameters for the API request."""
|
56
56
|
params = {
|
57
57
|
"limit": min(self.limit, 500),
|
@@ -94,9 +94,7 @@ class MultiPaperRecData:
|
|
94
94
|
) from e
|
95
95
|
|
96
96
|
if self.response is None:
|
97
|
-
raise RuntimeError(
|
98
|
-
"Failed to obtain a response from the Semantic Scholar API."
|
99
|
-
)
|
97
|
+
raise RuntimeError("Failed to obtain a response from the Semantic Scholar API.")
|
100
98
|
|
101
99
|
logger.info(
|
102
100
|
"API Response Status for multi-paper recommendations: %s",
|
@@ -117,9 +115,7 @@ class MultiPaperRecData:
|
|
117
115
|
|
118
116
|
self.recommendations = self.data.get("recommendedPapers", [])
|
119
117
|
if not self.recommendations:
|
120
|
-
logger.error(
|
121
|
-
"No recommendations returned from API for paper IDs: %s", self.paper_ids
|
122
|
-
)
|
118
|
+
logger.error("No recommendations returned from API for paper IDs: %s", self.paper_ids)
|
123
119
|
raise RuntimeError(
|
124
120
|
"No recommendations were found for your query. Consider refining your search "
|
125
121
|
"by using more specific keywords or different terms."
|
@@ -128,12 +124,12 @@ class MultiPaperRecData:
|
|
128
124
|
def _filter_papers(self) -> None:
|
129
125
|
"""Filter and format papers."""
|
130
126
|
# Build filtered recommendations with unified paper_ids
|
131
|
-
filtered:
|
127
|
+
filtered: dict[str, Any] = {}
|
132
128
|
for paper in self.recommendations:
|
133
129
|
if not paper.get("title") or not paper.get("authors"):
|
134
130
|
continue
|
135
131
|
ext = paper.get("externalIds", {}) or {}
|
136
|
-
ids:
|
132
|
+
ids: list[str] = []
|
137
133
|
arxiv = ext.get("ArXiv")
|
138
134
|
if arxiv:
|
139
135
|
ids.append(f"arxiv:{arxiv}")
|
@@ -191,7 +187,7 @@ class MultiPaperRecData:
|
|
191
187
|
title = paper.get("Title", "N/A")
|
192
188
|
year = paper.get("Year", "N/A")
|
193
189
|
snippet = self._get_snippet(paper.get("Abstract", ""))
|
194
|
-
entry = f"{i+1}. {title} ({year})"
|
190
|
+
entry = f"{i + 1}. {title} ({year})"
|
195
191
|
if snippet:
|
196
192
|
entry += f"\n Abstract snippet: {snippet}"
|
197
193
|
entries.append(entry)
|
@@ -202,14 +198,12 @@ class MultiPaperRecData:
|
|
202
198
|
"Papers are attached as an artifact."
|
203
199
|
)
|
204
200
|
self.content += " Here is a summary of the recommendations:\n"
|
205
|
-
self.content += (
|
206
|
-
f"Number of recommended papers found: {self.get_paper_count()}\n"
|
207
|
-
)
|
201
|
+
self.content += f"Number of recommended papers found: {self.get_paper_count()}\n"
|
208
202
|
self.content += f"Query Paper IDs: {', '.join(self.paper_ids)}\n"
|
209
203
|
self.content += f"Year: {self.year}\n" if self.year else ""
|
210
204
|
self.content += "Here are a few of these papers:\n" + top_papers_info
|
211
205
|
|
212
|
-
def process_recommendations(self) ->
|
206
|
+
def process_recommendations(self) -> dict[str, Any]:
|
213
207
|
"""Process the recommendations request and return results."""
|
214
208
|
self._fetch_recommendations()
|
215
209
|
self._filter_papers()
|
@@ -5,7 +5,8 @@ Utility for fetching recommendations based on a single paper.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import logging
|
8
|
-
from typing import Any
|
8
|
+
from typing import Any
|
9
|
+
|
9
10
|
import hydra
|
10
11
|
import requests
|
11
12
|
|
@@ -21,7 +22,7 @@ class SearchData:
|
|
21
22
|
self,
|
22
23
|
query: str,
|
23
24
|
limit: int,
|
24
|
-
year:
|
25
|
+
year: str | None,
|
25
26
|
tool_call_id: str,
|
26
27
|
):
|
27
28
|
self.query = query
|
@@ -40,13 +41,11 @@ class SearchData:
|
|
40
41
|
def _load_config(self) -> Any:
|
41
42
|
"""Load hydra configuration."""
|
42
43
|
with hydra.initialize(version_base=None, config_path="../../../configs"):
|
43
|
-
cfg = hydra.compose(
|
44
|
-
config_name="config", overrides=["tools/search=default"]
|
45
|
-
)
|
44
|
+
cfg = hydra.compose(config_name="config", overrides=["tools/search=default"])
|
46
45
|
logger.info("Loaded configuration for search tool")
|
47
46
|
return cfg.tools.search
|
48
47
|
|
49
|
-
def _create_params(self) ->
|
48
|
+
def _create_params(self) -> dict[str, Any]:
|
50
49
|
"""Create parameters for the API request."""
|
51
50
|
params = {
|
52
51
|
"query": self.query,
|
@@ -64,9 +63,7 @@ class SearchData:
|
|
64
63
|
# Wrap API call in try/except to catch connectivity issues
|
65
64
|
for attempt in range(10):
|
66
65
|
try:
|
67
|
-
self.response = requests.get(
|
68
|
-
self.endpoint, params=self.params, timeout=10
|
69
|
-
)
|
66
|
+
self.response = requests.get(self.endpoint, params=self.params, timeout=10)
|
70
67
|
self.response.raise_for_status() # Raises HTTPError for bad responses
|
71
68
|
break # Exit loop if request is successful
|
72
69
|
except requests.exceptions.RequestException as e:
|
@@ -82,9 +79,7 @@ class SearchData:
|
|
82
79
|
) from e
|
83
80
|
|
84
81
|
if self.response is None:
|
85
|
-
raise RuntimeError(
|
86
|
-
"Failed to obtain a response from the Semantic Scholar API."
|
87
|
-
)
|
82
|
+
raise RuntimeError("Failed to obtain a response from the Semantic Scholar API.")
|
88
83
|
|
89
84
|
self.data = self.response.json()
|
90
85
|
|
@@ -99,9 +94,7 @@ class SearchData:
|
|
99
94
|
|
100
95
|
self.papers = self.data.get("data", [])
|
101
96
|
if not self.papers:
|
102
|
-
logger.error(
|
103
|
-
"No papers returned from Semantic Scholar API for query: %s", self.query
|
104
|
-
)
|
97
|
+
logger.error("No papers returned from Semantic Scholar API for query: %s", self.query)
|
105
98
|
raise RuntimeError(
|
106
99
|
"No papers were found for your query. Consider refining your search "
|
107
100
|
"by using more specific keywords or different terms."
|
@@ -110,7 +103,7 @@ class SearchData:
|
|
110
103
|
def _filter_papers(self) -> None:
|
111
104
|
"""Filter and format papers."""
|
112
105
|
# Build filtered papers mapping with unified paper_ids list
|
113
|
-
filtered:
|
106
|
+
filtered: dict[str, Any] = {}
|
114
107
|
for paper in self.papers:
|
115
108
|
if not paper.get("title") or not paper.get("authors"):
|
116
109
|
continue
|
@@ -175,7 +168,7 @@ class SearchData:
|
|
175
168
|
title = paper.get("Title", "N/A")
|
176
169
|
year = paper.get("Year", "N/A")
|
177
170
|
snippet = self._get_snippet(paper.get("Abstract", ""))
|
178
|
-
entry = f"{i+1}. {title} ({year})"
|
171
|
+
entry = f"{i + 1}. {title} ({year})"
|
179
172
|
if snippet:
|
180
173
|
entry += f"\n Abstract snippet: {snippet}"
|
181
174
|
entries.append(entry)
|
@@ -192,7 +185,7 @@ class SearchData:
|
|
192
185
|
self.content += f"Year: {self.year}\n" if self.year else ""
|
193
186
|
self.content += "Top 3 papers:\n" + top_papers_info
|
194
187
|
|
195
|
-
def process_search(self) ->
|
188
|
+
def process_search(self) -> dict[str, Any]:
|
196
189
|
"""Process the search request and return results."""
|
197
190
|
self._fetch_papers()
|
198
191
|
self._filter_papers()
|
@@ -5,7 +5,8 @@ Utility for fetching recommendations based on a single paper.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import logging
|
8
|
-
from typing import Any
|
8
|
+
from typing import Any
|
9
|
+
|
9
10
|
import hydra
|
10
11
|
import requests
|
11
12
|
|
@@ -21,7 +22,7 @@ class SinglePaperRecData:
|
|
21
22
|
self,
|
22
23
|
paper_id: str,
|
23
24
|
limit: int,
|
24
|
-
year:
|
25
|
+
year: str | None,
|
25
26
|
tool_call_id: str,
|
26
27
|
):
|
27
28
|
self.paper_id = paper_id
|
@@ -47,7 +48,7 @@ class SinglePaperRecData:
|
|
47
48
|
logger.info("Loaded configuration for single paper recommendation tool")
|
48
49
|
return cfg.tools.single_paper_recommendation
|
49
50
|
|
50
|
-
def _create_params(self) ->
|
51
|
+
def _create_params(self) -> dict[str, Any]:
|
51
52
|
"""Create parameters for the API request."""
|
52
53
|
params = {
|
53
54
|
"limit": min(self.limit, 500), # Max 500 per API docs
|
@@ -86,9 +87,7 @@ class SinglePaperRecData:
|
|
86
87
|
) from e
|
87
88
|
|
88
89
|
if self.response is None:
|
89
|
-
raise RuntimeError(
|
90
|
-
"Failed to obtain a response from the Semantic Scholar API."
|
91
|
-
)
|
90
|
+
raise RuntimeError("Failed to obtain a response from the Semantic Scholar API.")
|
92
91
|
|
93
92
|
logger.info(
|
94
93
|
"API Response Status for recommendations of paper %s: %s",
|
@@ -110,9 +109,7 @@ class SinglePaperRecData:
|
|
110
109
|
|
111
110
|
self.recommendations = self.data.get("recommendedPapers", [])
|
112
111
|
if not self.recommendations:
|
113
|
-
logger.error(
|
114
|
-
"No recommendations returned from API for paper: %s", self.paper_id
|
115
|
-
)
|
112
|
+
logger.error("No recommendations returned from API for paper: %s", self.paper_id)
|
116
113
|
raise RuntimeError(
|
117
114
|
"No recommendations were found for your query. Consider refining your search "
|
118
115
|
"by using more specific keywords or different terms."
|
@@ -121,12 +118,12 @@ class SinglePaperRecData:
|
|
121
118
|
def _filter_papers(self) -> None:
|
122
119
|
"""Filter and format papers."""
|
123
120
|
# Build filtered recommendations with unified paper_ids
|
124
|
-
filtered:
|
121
|
+
filtered: dict[str, Any] = {}
|
125
122
|
for paper in self.recommendations:
|
126
123
|
if not paper.get("title") or not paper.get("authors"):
|
127
124
|
continue
|
128
125
|
ext = paper.get("externalIds", {}) or {}
|
129
|
-
ids:
|
126
|
+
ids: list[str] = []
|
130
127
|
arxiv = ext.get("ArXiv")
|
131
128
|
if arxiv:
|
132
129
|
ids.append(f"arxiv:{arxiv}")
|
@@ -184,7 +181,7 @@ class SinglePaperRecData:
|
|
184
181
|
title = paper.get("Title", "N/A")
|
185
182
|
year = paper.get("Year", "N/A")
|
186
183
|
snippet = self._get_snippet(paper.get("Abstract", ""))
|
187
|
-
entry = f"{i+1}. {title} ({year})"
|
184
|
+
entry = f"{i + 1}. {title} ({year})"
|
188
185
|
if snippet:
|
189
186
|
entry += f"\n Abstract snippet: {snippet}"
|
190
187
|
entries.append(entry)
|
@@ -195,13 +192,11 @@ class SinglePaperRecData:
|
|
195
192
|
"Papers are attached as an artifact. "
|
196
193
|
"Here is a summary of the recommendations:\n"
|
197
194
|
)
|
198
|
-
self.content += (
|
199
|
-
f"Number of recommended papers found: {self.get_paper_count()}\n"
|
200
|
-
)
|
195
|
+
self.content += f"Number of recommended papers found: {self.get_paper_count()}\n"
|
201
196
|
self.content += f"Query Paper ID: {self.paper_id}\n"
|
202
197
|
self.content += "Here are a few of these papers:\n" + top_papers_info
|
203
198
|
|
204
|
-
def process_recommendations(self) ->
|
199
|
+
def process_recommendations(self) -> dict[str, Any]:
|
205
200
|
"""Process the recommendations request and return results."""
|
206
201
|
self._fetch_recommendations()
|
207
202
|
self._filter_papers()
|
@@ -2,9 +2,6 @@
|
|
2
2
|
Import statements
|
3
3
|
"""
|
4
4
|
|
5
|
-
from . import zotero_read
|
6
|
-
from . import zotero_write
|
7
|
-
from . import utils
|
8
|
-
from . import zotero_review
|
5
|
+
from . import utils, zotero_read, zotero_review, zotero_write
|
9
6
|
|
10
7
|
__all__ = ["zotero_read", "zotero_write", "utils", "zotero_review"]
|
@@ -2,9 +2,6 @@
|
|
2
2
|
Import statements
|
3
3
|
"""
|
4
4
|
|
5
|
-
from . import zotero_path
|
6
|
-
from . import read_helper
|
7
|
-
from . import write_helper
|
8
|
-
from . import review_helper
|
5
|
+
from . import read_helper, review_helper, write_helper, zotero_path
|
9
6
|
|
10
7
|
__all__ = ["zotero_path", "read_helper", "write_helper", "review_helper"]
|
@@ -5,7 +5,7 @@ Utility for zotero read tool.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import logging
|
8
|
-
from typing import Any
|
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) ->
|
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) ->
|
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) ->
|
105
|
+
def _collect_item_attachments(self) -> dict[str, str]:
|
110
106
|
"""Collect PDF attachment keys for non-orphan items."""
|
111
|
-
item_attachments:
|
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:
|
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
|
-
|
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:
|
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:
|
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:
|
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) ->
|
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]:
|