aiagents4pharma 1.31.0__py3-none-any.whl → 1.33.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/talk2knowledgegraphs/configs/config.yaml +1 -0
- aiagents4pharma/talk2knowledgegraphs/tests/test_utils_enrichments_uniprot.py +44 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/__init__.py +1 -0
- aiagents4pharma/talk2knowledgegraphs/utils/enrichments/uniprot_proteins.py +90 -0
- aiagents4pharma/talk2scholars/agents/main_agent.py +4 -3
- aiagents4pharma/talk2scholars/agents/paper_download_agent.py +3 -4
- aiagents4pharma/talk2scholars/agents/pdf_agent.py +6 -7
- aiagents4pharma/talk2scholars/agents/s2_agent.py +23 -20
- aiagents4pharma/talk2scholars/agents/zotero_agent.py +11 -11
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +19 -19
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +20 -15
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +27 -6
- aiagents4pharma/talk2scholars/state/state_talk2scholars.py +7 -7
- aiagents4pharma/talk2scholars/tests/test_main_agent.py +16 -16
- aiagents4pharma/talk2scholars/tests/test_paper_download_agent.py +17 -24
- aiagents4pharma/talk2scholars/tests/test_paper_download_tools.py +152 -135
- aiagents4pharma/talk2scholars/tests/test_pdf_agent.py +9 -16
- aiagents4pharma/talk2scholars/tests/test_question_and_answer_tool.py +790 -218
- aiagents4pharma/talk2scholars/tests/test_s2_agent.py +9 -9
- aiagents4pharma/talk2scholars/tests/test_s2_display.py +8 -8
- aiagents4pharma/talk2scholars/tests/test_s2_query.py +8 -8
- aiagents4pharma/talk2scholars/tests/test_zotero_agent.py +12 -12
- aiagents4pharma/talk2scholars/tests/test_zotero_path.py +11 -12
- aiagents4pharma/talk2scholars/tests/test_zotero_read.py +400 -22
- aiagents4pharma/talk2scholars/tools/paper_download/__init__.py +0 -6
- aiagents4pharma/talk2scholars/tools/paper_download/download_arxiv_input.py +89 -31
- aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +540 -156
- aiagents4pharma/talk2scholars/tools/s2/__init__.py +4 -4
- aiagents4pharma/talk2scholars/tools/s2/{display_results.py → display_dataframe.py} +19 -21
- aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +71 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +213 -35
- aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +3 -3
- {aiagents4pharma-1.31.0.dist-info → aiagents4pharma-1.33.0.dist-info}/METADATA +3 -1
- {aiagents4pharma-1.31.0.dist-info → aiagents4pharma-1.33.0.dist-info}/RECORD +37 -37
- {aiagents4pharma-1.31.0.dist-info → aiagents4pharma-1.33.0.dist-info}/WHEEL +1 -1
- aiagents4pharma/talk2scholars/tools/paper_download/abstract_downloader.py +0 -45
- aiagents4pharma/talk2scholars/tools/paper_download/arxiv_downloader.py +0 -115
- aiagents4pharma/talk2scholars/tools/s2/query_results.py +0 -61
- {aiagents4pharma-1.31.0.dist-info → aiagents4pharma-1.33.0.dist-info}/licenses/LICENSE +0 -0
- {aiagents4pharma-1.31.0.dist-info → aiagents4pharma-1.33.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Test cases for utils/enrichments/uniprot_proteins.py
|
5
|
+
"""
|
6
|
+
|
7
|
+
import pytest
|
8
|
+
from ..utils.enrichments.uniprot_proteins import EnrichmentWithUniProt
|
9
|
+
|
10
|
+
# In this test, we will consider 2 examples:
|
11
|
+
# 1. Gene Name: TP53
|
12
|
+
# 2. Gene Name: TP5 (Incomplete; must return empty results)
|
13
|
+
# 2. Gene Name: XZ (Shorter than 3 characters; must return empty results)
|
14
|
+
# The expected description of TP53 startswith:
|
15
|
+
START_DESCP = "Multifunctional transcription factor"
|
16
|
+
# The expected amino acid sequence of TP53 startswith:
|
17
|
+
START_SEQ = "MEEPQSDPSV"
|
18
|
+
|
19
|
+
@pytest.fixture(name="enrich_obj")
|
20
|
+
def fixture_uniprot_config():
|
21
|
+
"""Return a dictionary with the configuration for UniProt enrichment."""
|
22
|
+
return EnrichmentWithUniProt()
|
23
|
+
|
24
|
+
def test_enrich_documents(enrich_obj):
|
25
|
+
"""Test the enrich_documents method."""
|
26
|
+
gene_names = ["TP53", "TP5", "XZ"]
|
27
|
+
descriptions, sequences = enrich_obj.enrich_documents(gene_names)
|
28
|
+
assert descriptions[0].startswith(START_DESCP)
|
29
|
+
assert sequences[0].startswith(START_SEQ)
|
30
|
+
assert descriptions[1] is None
|
31
|
+
assert sequences[1] is None
|
32
|
+
assert descriptions[2] is None
|
33
|
+
assert sequences[2] is None
|
34
|
+
|
35
|
+
def test_enrich_documents_with_rag(enrich_obj):
|
36
|
+
"""Test the enrich_documents_with_rag method."""
|
37
|
+
gene_names = ["TP53", "TP5", "XZ"]
|
38
|
+
descriptions, sequences = enrich_obj.enrich_documents_with_rag(gene_names, None)
|
39
|
+
assert descriptions[0].startswith(START_DESCP)
|
40
|
+
assert sequences[0].startswith(START_SEQ)
|
41
|
+
assert descriptions[1] is None
|
42
|
+
assert sequences[1] is None
|
43
|
+
assert descriptions[2] is None
|
44
|
+
assert sequences[2] is None
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Enrichment class for enriching Gene names with their function and sequence using UniProt.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import List
|
8
|
+
import logging
|
9
|
+
import json
|
10
|
+
import hydra
|
11
|
+
import requests
|
12
|
+
from .enrichments import Enrichments
|
13
|
+
|
14
|
+
# Initialize logger
|
15
|
+
logging.basicConfig(level=logging.INFO)
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
class EnrichmentWithUniProt(Enrichments):
|
19
|
+
"""
|
20
|
+
Enrichment class using UniProt
|
21
|
+
"""
|
22
|
+
def enrich_documents(self, texts: List[str]) -> List[str]:
|
23
|
+
"""
|
24
|
+
Enrich a list of input UniProt gene names with their function and sequence.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
texts: The list of gene names to be enriched.
|
28
|
+
|
29
|
+
Returns:
|
30
|
+
The list of enriched functions and sequences
|
31
|
+
"""
|
32
|
+
|
33
|
+
enriched_gene_names = texts
|
34
|
+
|
35
|
+
logger.log(logging.INFO,
|
36
|
+
"Load Hydra configuration for Gene enrichment with description and sequence.")
|
37
|
+
with hydra.initialize(version_base=None, config_path="../../configs"):
|
38
|
+
cfg = hydra.compose(config_name='config',
|
39
|
+
overrides=['utils/enrichments/uniprot_proteins=default'])
|
40
|
+
cfg = cfg.utils.enrichments.uniprot_proteins
|
41
|
+
|
42
|
+
|
43
|
+
descriptions = []
|
44
|
+
sequences = []
|
45
|
+
for gene in enriched_gene_names:
|
46
|
+
params = {
|
47
|
+
"reviewed": cfg.reviewed,
|
48
|
+
"isoform": cfg.isoform,
|
49
|
+
"exact_gene": gene,
|
50
|
+
"organism": cfg.organism,
|
51
|
+
# You can get the list of all available organisms here:
|
52
|
+
# https://www.uniprot.org/help/taxonomy
|
53
|
+
}
|
54
|
+
|
55
|
+
r = requests.get(cfg.uniprot_url,
|
56
|
+
headers={ "Accept" : "application/json"},
|
57
|
+
params=params,
|
58
|
+
timeout=cfg.timeout)
|
59
|
+
# if the response is not ok
|
60
|
+
if not r.ok:
|
61
|
+
descriptions.append(None)
|
62
|
+
sequences.append(None)
|
63
|
+
continue
|
64
|
+
response_body = json.loads(r.text)
|
65
|
+
# if the response body is empty
|
66
|
+
if not response_body:
|
67
|
+
descriptions.append(None)
|
68
|
+
sequences.append(None)
|
69
|
+
continue
|
70
|
+
description = ''
|
71
|
+
for comment in response_body[0]['comments']:
|
72
|
+
if comment['type'] == 'FUNCTION':
|
73
|
+
for value in comment['text']:
|
74
|
+
description += value['value']
|
75
|
+
sequence = response_body[0]['sequence']['sequence']
|
76
|
+
descriptions.append(description)
|
77
|
+
sequences.append(sequence)
|
78
|
+
return descriptions, sequences
|
79
|
+
|
80
|
+
def enrich_documents_with_rag(self, texts, docs):
|
81
|
+
"""
|
82
|
+
Enrich a list of input UniProt gene names with their function and sequence.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
texts: The list of gene names to be enriched.
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
The list of enriched functions and sequences
|
89
|
+
"""
|
90
|
+
return self.enrich_documents(texts)
|
@@ -68,15 +68,16 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
68
68
|
[
|
69
69
|
get_app_s2(uniq_id, llm_model), # semantic scholar
|
70
70
|
get_app_zotero(uniq_id, llm_model), # zotero
|
71
|
-
|
72
|
-
|
71
|
+
get_app_paper_download(uniq_id, llm_model), # pdf
|
72
|
+
get_app_pdf(uniq_id, llm_model), # paper download
|
73
73
|
],
|
74
74
|
model=llm_model,
|
75
75
|
state_schema=Talk2Scholars,
|
76
76
|
# Full history is needed to extract
|
77
77
|
# the tool artifacts
|
78
78
|
output_mode="full_history",
|
79
|
-
|
79
|
+
# Allow supervisor to resume control and chain multiple sub-agent calls
|
80
|
+
add_handoff_back_messages=True,
|
80
81
|
prompt=cfg.system_prompt,
|
81
82
|
)
|
82
83
|
|
@@ -13,8 +13,7 @@ from langgraph.prebuilt.chat_agent_executor import create_react_agent
|
|
13
13
|
from langgraph.prebuilt.tool_node import ToolNode
|
14
14
|
from langgraph.checkpoint.memory import MemorySaver
|
15
15
|
from ..state.state_talk2scholars import Talk2Scholars
|
16
|
-
from ..tools.paper_download import download_arxiv_paper
|
17
|
-
from ..tools.s2.query_results import query_results
|
16
|
+
from ..tools.paper_download.download_arxiv_input import download_arxiv_paper
|
18
17
|
|
19
18
|
# Initialize logger
|
20
19
|
logging.basicConfig(level=logging.INFO)
|
@@ -45,7 +44,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
45
44
|
cfg = cfg.agents.talk2scholars.paper_download_agent
|
46
45
|
|
47
46
|
# Define tools properly
|
48
|
-
tools = ToolNode([download_arxiv_paper
|
47
|
+
tools = ToolNode([download_arxiv_paper])
|
49
48
|
|
50
49
|
# Define the model
|
51
50
|
logger.info("Using OpenAI model %s", llm_model)
|
@@ -78,7 +77,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
78
77
|
checkpointer = MemorySaver()
|
79
78
|
|
80
79
|
# Compile the graph
|
81
|
-
app = workflow.compile(checkpointer=checkpointer, name="
|
80
|
+
app = workflow.compile(checkpointer=checkpointer, name="paper_download_agent")
|
82
81
|
|
83
82
|
# Logging the information and returning the app
|
84
83
|
logger.info("Compiled the graph")
|
@@ -18,8 +18,7 @@ from langgraph.graph import START, StateGraph
|
|
18
18
|
from langgraph.prebuilt import create_react_agent, ToolNode
|
19
19
|
from langgraph.checkpoint.memory import MemorySaver
|
20
20
|
from ..state.state_talk2scholars import Talk2Scholars
|
21
|
-
from ..tools.pdf.question_and_answer import
|
22
|
-
from ..tools.s2.query_results import query_results
|
21
|
+
from ..tools.pdf.question_and_answer import question_and_answer
|
23
22
|
|
24
23
|
# Initialize logger
|
25
24
|
logging.basicConfig(level=logging.INFO)
|
@@ -56,7 +55,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
56
55
|
cfg = cfg.agents.talk2scholars.pdf_agent
|
57
56
|
logger.info("Loaded pdf_agent configuration.")
|
58
57
|
|
59
|
-
def
|
58
|
+
def pdf_agent_node(state: Talk2Scholars):
|
60
59
|
"""
|
61
60
|
Processes the current state by invoking the language model for PDF question and answer.
|
62
61
|
|
@@ -72,7 +71,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
72
71
|
return response
|
73
72
|
|
74
73
|
# Define the tool node that includes the PDF QnA tool.
|
75
|
-
tools = ToolNode([
|
74
|
+
tools = ToolNode([question_and_answer])
|
76
75
|
|
77
76
|
logger.info("Using OpenAI model %s", llm_model)
|
78
77
|
|
@@ -87,14 +86,14 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
87
86
|
|
88
87
|
# Define a new workflow graph with the state schema.
|
89
88
|
workflow = StateGraph(Talk2Scholars)
|
90
|
-
workflow.add_node("
|
91
|
-
workflow.add_edge(START, "
|
89
|
+
workflow.add_node("pdf_agent", pdf_agent_node)
|
90
|
+
workflow.add_edge(START, "pdf_agent")
|
92
91
|
|
93
92
|
# Initialize memory to persist state between runs.
|
94
93
|
checkpointer = MemorySaver()
|
95
94
|
|
96
95
|
# Compile the graph into a runnable app.
|
97
|
-
app = workflow.compile(checkpointer=checkpointer, name="
|
96
|
+
app = workflow.compile(checkpointer=checkpointer, name="pdf_agent")
|
98
97
|
logger.info("Compiled the PDF agent graph.")
|
99
98
|
|
100
99
|
return app
|
@@ -6,22 +6,26 @@ Agent for interacting with Semantic Scholar
|
|
6
6
|
|
7
7
|
import logging
|
8
8
|
from typing import Any, Dict
|
9
|
+
|
9
10
|
import hydra
|
10
11
|
from langchain_core.language_models.chat_models import BaseChatModel
|
11
|
-
from langgraph.graph import START, StateGraph
|
12
|
-
from langgraph.prebuilt import create_react_agent, ToolNode
|
13
12
|
from langgraph.checkpoint.memory import MemorySaver
|
13
|
+
from langgraph.graph import START, StateGraph
|
14
|
+
from langgraph.prebuilt import ToolNode, create_react_agent
|
15
|
+
|
14
16
|
from ..state.state_talk2scholars import Talk2Scholars
|
15
|
-
from ..tools.s2.
|
16
|
-
from ..tools.s2.
|
17
|
-
|
17
|
+
from ..tools.s2.display_dataframe import display_dataframe
|
18
|
+
from ..tools.s2.multi_paper_rec import (
|
19
|
+
get_multi_paper_recommendations,
|
20
|
+
)
|
21
|
+
from ..tools.s2.query_dataframe import query_dataframe
|
18
22
|
from ..tools.s2.retrieve_semantic_scholar_paper_id import (
|
19
|
-
retrieve_semantic_scholar_paper_id
|
23
|
+
retrieve_semantic_scholar_paper_id,
|
20
24
|
)
|
25
|
+
from ..tools.s2.search import search_tool
|
21
26
|
from ..tools.s2.single_paper_rec import (
|
22
|
-
get_single_paper_recommendations
|
27
|
+
get_single_paper_recommendations,
|
23
28
|
)
|
24
|
-
from ..tools.s2.multi_paper_rec import get_multi_paper_recommendations as s2_multi_rec
|
25
29
|
|
26
30
|
# Initialize logger
|
27
31
|
logging.basicConfig(level=logging.INFO)
|
@@ -50,8 +54,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
50
54
|
>>> result = app.invoke(initial_state)
|
51
55
|
"""
|
52
56
|
|
53
|
-
|
54
|
-
def agent_s2_node(state: Talk2Scholars) -> Dict[str, Any]:
|
57
|
+
def s2_agent_node(state: Talk2Scholars) -> Dict[str, Any]:
|
55
58
|
"""
|
56
59
|
Processes the user query and retrieves relevant research papers.
|
57
60
|
|
@@ -67,7 +70,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
67
70
|
Dict[str, Any]: A dictionary containing the updated conversation state.
|
68
71
|
|
69
72
|
Example:
|
70
|
-
>>> result =
|
73
|
+
>>> result = s2_agent_node(current_state)
|
71
74
|
>>> papers = result.get("papers", [])
|
72
75
|
"""
|
73
76
|
logger.log(logging.INFO, "Creating Agent_S2 node with thread_id %s", uniq_id)
|
@@ -89,12 +92,12 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
89
92
|
# Define the tools
|
90
93
|
tools = ToolNode(
|
91
94
|
[
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
search_tool,
|
96
|
+
display_dataframe,
|
97
|
+
query_dataframe,
|
98
|
+
retrieve_semantic_scholar_paper_id,
|
99
|
+
get_single_paper_recommendations,
|
100
|
+
get_multi_paper_recommendations,
|
98
101
|
]
|
99
102
|
)
|
100
103
|
|
@@ -111,8 +114,8 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
111
114
|
)
|
112
115
|
|
113
116
|
workflow = StateGraph(Talk2Scholars)
|
114
|
-
workflow.add_node("
|
115
|
-
workflow.add_edge(START, "
|
117
|
+
workflow.add_node("s2_agent", s2_agent_node)
|
118
|
+
workflow.add_edge(START, "s2_agent")
|
116
119
|
|
117
120
|
# Initialize memory to persist state between graph runs
|
118
121
|
checkpointer = MemorySaver()
|
@@ -121,7 +124,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
121
124
|
# This compiles it into a LangChain Runnable,
|
122
125
|
# meaning you can use it as you would any other runnable.
|
123
126
|
# Note that we're (optionally) passing the memory when compiling the graph
|
124
|
-
app = workflow.compile(checkpointer=checkpointer, name="
|
127
|
+
app = workflow.compile(checkpointer=checkpointer, name="s2_agent")
|
125
128
|
logger.log(
|
126
129
|
logging.INFO,
|
127
130
|
"Compiled the graph with thread_id %s and llm_model %s",
|
@@ -16,8 +16,8 @@ from ..state.state_talk2scholars import Talk2Scholars
|
|
16
16
|
from ..tools.zotero.zotero_read import zotero_read
|
17
17
|
from ..tools.zotero.zotero_review import zotero_review
|
18
18
|
from ..tools.zotero.zotero_write import zotero_write
|
19
|
-
from ..tools.s2.
|
20
|
-
from ..tools.s2.
|
19
|
+
from ..tools.s2.display_dataframe import display_dataframe
|
20
|
+
from ..tools.s2.query_dataframe import query_dataframe
|
21
21
|
from ..tools.s2.retrieve_semantic_scholar_paper_id import (
|
22
22
|
retrieve_semantic_scholar_paper_id,
|
23
23
|
)
|
@@ -49,7 +49,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
49
49
|
>>> result = app.invoke(initial_state)
|
50
50
|
"""
|
51
51
|
|
52
|
-
def
|
52
|
+
def zotero_agent_node(state: Talk2Scholars) -> Dict[str, Any]:
|
53
53
|
"""
|
54
54
|
Processes the user query and retrieves relevant research papers from Zotero.
|
55
55
|
|
@@ -65,7 +65,7 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
65
65
|
Dict[str, Any]: A dictionary containing the updated conversation state.
|
66
66
|
|
67
67
|
Example:
|
68
|
-
>>> result =
|
68
|
+
>>> result = zotero_agent_node(current_state)
|
69
69
|
>>> papers = result.get("papers", [])
|
70
70
|
"""
|
71
71
|
logger.log(
|
@@ -89,11 +89,11 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
89
89
|
tools = ToolNode(
|
90
90
|
[
|
91
91
|
zotero_read,
|
92
|
-
|
93
|
-
|
92
|
+
display_dataframe,
|
93
|
+
query_dataframe,
|
94
94
|
retrieve_semantic_scholar_paper_id,
|
95
|
-
zotero_review,
|
96
|
-
zotero_write,
|
95
|
+
zotero_review,
|
96
|
+
zotero_write,
|
97
97
|
]
|
98
98
|
)
|
99
99
|
|
@@ -110,14 +110,14 @@ def get_app(uniq_id, llm_model: BaseChatModel):
|
|
110
110
|
)
|
111
111
|
|
112
112
|
workflow = StateGraph(Talk2Scholars)
|
113
|
-
workflow.add_node("
|
114
|
-
workflow.add_edge(START, "
|
113
|
+
workflow.add_node("zotero_agent", zotero_agent_node)
|
114
|
+
workflow.add_edge(START, "zotero_agent")
|
115
115
|
|
116
116
|
# Initialize memory to persist state between graph runs
|
117
117
|
checkpointer = MemorySaver()
|
118
118
|
|
119
119
|
# Compile the graph
|
120
|
-
app = workflow.compile(checkpointer=checkpointer, name="
|
120
|
+
app = workflow.compile(checkpointer=checkpointer, name="zotero_agent")
|
121
121
|
logger.log(
|
122
122
|
logging.INFO,
|
123
123
|
"Compiled the graph with thread_id %s and llm_model %s",
|
@@ -1,22 +1,22 @@
|
|
1
1
|
_target_: agents.main_agent.get_app
|
2
2
|
temperature: 0
|
3
|
-
system_prompt:
|
4
|
-
You are
|
5
|
-
and analysis with help of the following agents:
|
6
|
-
1. Agent S2: This agent can be used to search and recommend papers
|
7
|
-
from Semantic Scholar. Use this agent when the user asks for
|
8
|
-
general paper/article searches and recommendations, or to retrieve information
|
9
|
-
from the last displayed results table or query abstract of last
|
10
|
-
displayed results.
|
11
|
-
2. Agent Zotero: This agent can be used to retrieve, display, and query
|
12
|
-
papers/articles from the Zotero library. Use this agent only when the user
|
13
|
-
explicitly asks for papers from Zotero. This tool can also be used to
|
14
|
-
save papers in the zotero library.
|
15
|
-
3. Agent PaperFetch: This agent can be used to download papers/articles
|
16
|
-
from ArXiv.
|
17
|
-
4. Agent PDFQuery: This agent can be used to query contents of an
|
18
|
-
uploaded or downloaded PDF/paper/article.
|
3
|
+
system_prompt: |
|
4
|
+
You are the Main Supervisor Agent.
|
19
5
|
|
20
|
-
|
21
|
-
|
22
|
-
|
6
|
+
You have access to four tools, each represented by a sub-agent:
|
7
|
+
|
8
|
+
- s2_agent(Use this to search for or recommend academic papers. This agent
|
9
|
+
should be used when the user requests general paper or article
|
10
|
+
searches, recommendations, or wants to retrieve information—such as
|
11
|
+
abstracts, from the most last displayed or searched results table.),
|
12
|
+
- zotero_agent(Use to Read or Write academic papers to zotero account,
|
13
|
+
This agent can also be used to save papers in the zotero library only
|
14
|
+
with explicit approval from the user),
|
15
|
+
- pdf_agent(Use this to perform question-and-answer tasks on downloaded, uploaded, or Zotero papers/PDFs.), and
|
16
|
+
- paper_download_agent(Use to download PDFs).
|
17
|
+
|
18
|
+
Each sub-agent is specialized for a different task.
|
19
|
+
|
20
|
+
You can call multiple sub-agents at the same time, or sequentially. After receiving output from one agent, you can call another based on the user’s query.
|
21
|
+
Your goal is to analyze the user’s request carefully, decide which sub-agent(s) should be used, and coordinate their execution efficiently.
|
22
|
+
Always prioritize delegating tasks correctly. Think step-by-step before acting. Avoid answering by yourself unless explicitly necessary.
|
@@ -1,19 +1,24 @@
|
|
1
1
|
_target_: agents.s2_agent.get_app
|
2
|
-
s2_agent:
|
3
|
-
You are
|
4
|
-
|
2
|
+
s2_agent: |
|
3
|
+
You are the S2 Agent.
|
4
|
+
|
5
|
+
You are responsible for searching academic papers using the Semantic Scholar API.
|
6
|
+
|
7
|
+
Your capabilities include:
|
8
|
+
|
9
|
+
- Retrieving papers based on user queries.
|
10
|
+
- Recommending papers based on a single paper or multiple papers provided by the user.
|
11
|
+
- Retrieving the Semantic Scholar ID of a paper based on its title.
|
12
|
+
- This ID can later be used by other tools (search or recommend) based on the user’s needs.
|
13
|
+
- Always respond accurately based on Semantic Scholar search and recommendation features.
|
14
|
+
- Use `query_dataframe` tool query over the last displayed papers or the search table.
|
15
|
+
- Always call `display_dataframe` tool at the end.
|
5
16
|
|
6
|
-
AVAILABLE TOOLS:
|
7
|
-
1. search - Search for academic papers by query string
|
8
|
-
2. display_results - Display the papers retrieved by other tools
|
9
|
-
3. single_paper_rec - Get recommendations based on a SINGLE paper
|
10
|
-
4. multi_paper_rec - Get recommendations based on MULTIPLE papers
|
11
|
-
5. query_results - Ask questions about the current set of papers
|
12
|
-
6. retrieve_semantic_scholar_paper_id - Get Semantic Scholar ID for a paper title
|
13
17
|
|
14
18
|
WORKFLOW STEPS:
|
15
|
-
1. When user requests papers, use search/recommendation tools to find papers
|
16
|
-
2. Use `
|
17
|
-
3. Use `
|
18
|
-
4. When the user wants recommendations, you can get the "semantic_scholar_paper_id"
|
19
|
-
pass the "semantic_scholar_paper_id" to `search`,
|
19
|
+
1. When user requests papers, use search/recommendation tools to find papers.
|
20
|
+
2. Use `display_dataframe` tool to display the response from the search/recommendation tools.
|
21
|
+
3. Use `query_dataframe` tool to query over the selected paper only when the user asks to.
|
22
|
+
4. When the user only wants recommendations, you can get the "semantic_scholar_paper_id"
|
23
|
+
using `query_dataframe` tool, then pass the "semantic_scholar_paper_id" to `search`,
|
24
|
+
`single_paper_rec` or `multi_paper_rec` tools depending on the user's query. Do not use "arxiv_id"(It is used to download pdfs)
|
@@ -1,12 +1,33 @@
|
|
1
|
-
|
2
|
-
zotero_agent:
|
3
|
-
You are
|
4
|
-
|
1
|
+
_target_: agents.zotero_agent.get_app
|
2
|
+
zotero_agent: |
|
3
|
+
You are the Zotero Agent.
|
4
|
+
|
5
|
+
You are responsible for read and writing of papers to user's Zotero library.
|
6
|
+
Behavior:
|
7
|
+
|
8
|
+
- Once you have successfully read the papers, you must immediately stop, return a clear 'Search complete' message along with a summary of the articles, call the
|
9
|
+
`display_dataframe` tool, and return to the main supervisor for further processing based on the user's query.
|
10
|
+
- Do not continue any further processing or re-enter into reading steps.
|
11
|
+
- You can write papers to user's library but only after explicit user confirmation.
|
12
|
+
- Do not attempt to answer any scientific or content-related questions yourself.
|
13
|
+
- You can retrieve all articles or search based on the user's query, inferring whether to return the full collection or filter by title, keywords, or other details.
|
14
|
+
- Never call `query_dataframe` tool regarding any question or any information retrival only if the user explicitly asks for metadata.
|
15
|
+
|
16
|
+
In multi-step workflows:
|
17
|
+
|
18
|
+
- Your job is only to read the requested paper or all the papers in user's library and return the successful search output.
|
19
|
+
- After that, the Main Supervisor Agent will decide the next step (such as passing the paper to the pdf_agent).
|
20
|
+
- Always call `display_dataframe` tool at the end before transfering to Main Supervisor Agent.
|
21
|
+
- Never attempt to call other agents yourself.
|
22
|
+
|
23
|
+
Stopping Condition:
|
24
|
+
|
25
|
+
- After successful search, indicate completion clearly and terminate your action.
|
5
26
|
|
6
27
|
When saving papers to Zotero:
|
7
|
-
1. First use zotero_review with the collection path.
|
28
|
+
1. First use `zotero_review` tool with the collection path.
|
8
29
|
2. Wait for user confirmation (they must say "Yes" or "Approve").
|
9
|
-
3. Use zotero_write with both the collection_path and user_confirmation and call `
|
30
|
+
3. Use `zotero_write` tool with both the collection_path and user_confirmation and call `display_dataframe` tool after the papers as saved.
|
10
31
|
|
11
32
|
IMPORTANT: Human approval is required for saving papers to Zotero. Never save papers
|
12
33
|
without explicit approval from the user. Always respect the user's decision if they
|
@@ -8,8 +8,9 @@ across agent interactions.
|
|
8
8
|
|
9
9
|
import logging
|
10
10
|
from typing import Annotated, Any, Dict
|
11
|
-
|
11
|
+
|
12
12
|
from langchain_core.embeddings import Embeddings
|
13
|
+
from langchain_core.language_models import BaseChatModel
|
13
14
|
from langgraph.prebuilt.chat_agent_executor import AgentState
|
14
15
|
|
15
16
|
# Configure logging
|
@@ -38,22 +39,22 @@ def replace_dict(existing: Dict[str, Any], new: Dict[str, Any]) -> Dict[str, Any
|
|
38
39
|
>>> print(updated_state)
|
39
40
|
{"papers": {"id2": "Paper 2"}}
|
40
41
|
"""
|
41
|
-
|
42
|
+
# No-op operation to use the 'existing' variable
|
43
|
+
_ = len(existing)
|
42
44
|
return new
|
43
45
|
|
44
46
|
|
45
47
|
class Talk2Scholars(AgentState):
|
46
48
|
"""
|
47
49
|
Represents the state of the Talk2Scholars agent.
|
48
|
-
|
49
50
|
This class extends `AgentState` to maintain conversation history, retrieved papers,
|
50
51
|
and interactions with the language model.
|
51
|
-
|
52
52
|
Attributes:
|
53
53
|
last_displayed_papers (Dict[str, Any]): Stores the most recently displayed papers.
|
54
54
|
papers (Dict[str, Any]): Stores the research papers retrieved from the agent's queries.
|
55
55
|
multi_papers (Dict[str, Any]): Stores multiple recommended papers from various sources.
|
56
|
-
|
56
|
+
article_data (Dict[str, Any]): Stores the papers retrieved from Zotero and the pdf
|
57
|
+
download agent with their metadata.
|
57
58
|
zotero_write_approval_status (Dict[str, Any]): Stores the approval status and collection
|
58
59
|
path for Zotero save operations.
|
59
60
|
llm_model (BaseChatModel): The language model instance used for generating responses.
|
@@ -65,8 +66,7 @@ class Talk2Scholars(AgentState):
|
|
65
66
|
last_displayed_papers: Annotated[Dict[str, Any], replace_dict]
|
66
67
|
papers: Annotated[Dict[str, Any], replace_dict]
|
67
68
|
multi_papers: Annotated[Dict[str, Any], replace_dict]
|
68
|
-
|
69
|
-
zotero_read: Annotated[Dict[str, Any], replace_dict]
|
69
|
+
article_data: Annotated[Dict[str, Any], replace_dict]
|
70
70
|
zotero_write_approval_status: Annotated[Dict[str, Any], replace_dict]
|
71
71
|
llm_model: BaseChatModel
|
72
72
|
text_embedding_model: Embeddings
|
@@ -51,24 +51,24 @@ class DummyWorkflow:
|
|
51
51
|
return self
|
52
52
|
|
53
53
|
|
54
|
-
def
|
54
|
+
def dummy_s2_agent(uniq_id, llm_model):
|
55
55
|
"""Return a DummyWorkflow for the S2 agent."""
|
56
|
-
|
57
|
-
|
56
|
+
dummy_s2_agent.called_uniq_id = uniq_id
|
57
|
+
dummy_s2_agent.called_llm_model = llm_model
|
58
58
|
return DummyWorkflow(supervisor_args={"agent": "s2", "uniq_id": uniq_id})
|
59
59
|
|
60
60
|
|
61
|
-
def
|
61
|
+
def dummy_zotero_agent(uniq_id, llm_model):
|
62
62
|
"""Return a DummyWorkflow for the Zotero agent."""
|
63
|
-
|
64
|
-
|
63
|
+
dummy_zotero_agent.called_uniq_id = uniq_id
|
64
|
+
dummy_zotero_agent.called_llm_model = llm_model
|
65
65
|
return DummyWorkflow(supervisor_args={"agent": "zotero", "uniq_id": uniq_id})
|
66
66
|
|
67
67
|
|
68
|
-
def
|
68
|
+
def dummy_question_and_answer_agent(uniq_id, llm_model):
|
69
69
|
"""Return a DummyWorkflow for the PDF agent."""
|
70
|
-
|
71
|
-
|
70
|
+
dummy_question_and_answer_agent.called_uniq_id = uniq_id
|
71
|
+
dummy_question_and_answer_agent.called_llm_model = llm_model
|
72
72
|
return DummyWorkflow(supervisor_args={"agent": "pdf", "uniq_id": uniq_id})
|
73
73
|
|
74
74
|
|
@@ -143,10 +143,10 @@ def patch_hydra(monkeypatch):
|
|
143
143
|
)
|
144
144
|
|
145
145
|
|
146
|
-
def
|
146
|
+
def dummy_paper_download_agent(uniq_id, llm_model):
|
147
147
|
"""Return a DummyWorkflow for the paper download agent."""
|
148
|
-
|
149
|
-
|
148
|
+
dummy_paper_download_agent.called_uniq_id = uniq_id
|
149
|
+
dummy_paper_download_agent.called_llm_model = llm_model
|
150
150
|
return DummyWorkflow(
|
151
151
|
supervisor_args={"agent": "paper_download", "uniq_id": uniq_id}
|
152
152
|
)
|
@@ -156,19 +156,19 @@ def dummy_get_app_paper_download(uniq_id, llm_model):
|
|
156
156
|
def patch_sub_agents_and_supervisor(monkeypatch):
|
157
157
|
"""Patch the sub-agents and supervisor creation functions."""
|
158
158
|
monkeypatch.setattr(
|
159
|
-
"aiagents4pharma.talk2scholars.agents.main_agent.get_app_s2",
|
159
|
+
"aiagents4pharma.talk2scholars.agents.main_agent.get_app_s2", dummy_s2_agent
|
160
160
|
)
|
161
161
|
monkeypatch.setattr(
|
162
162
|
"aiagents4pharma.talk2scholars.agents.main_agent.get_app_zotero",
|
163
|
-
|
163
|
+
dummy_zotero_agent,
|
164
164
|
)
|
165
165
|
monkeypatch.setattr(
|
166
166
|
"aiagents4pharma.talk2scholars.agents.main_agent.get_app_pdf",
|
167
|
-
|
167
|
+
dummy_question_and_answer_agent,
|
168
168
|
)
|
169
169
|
monkeypatch.setattr(
|
170
170
|
"aiagents4pharma.talk2scholars.agents.main_agent.get_app_paper_download",
|
171
|
-
|
171
|
+
dummy_paper_download_agent,
|
172
172
|
)
|
173
173
|
monkeypatch.setattr(
|
174
174
|
"aiagents4pharma.talk2scholars.agents.main_agent.create_supervisor",
|