aiagents4pharma 1.30.4__py3-none-any.whl → 1.32.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. aiagents4pharma/talk2scholars/agents/main_agent.py +4 -3
  2. aiagents4pharma/talk2scholars/agents/paper_download_agent.py +3 -4
  3. aiagents4pharma/talk2scholars/agents/pdf_agent.py +6 -7
  4. aiagents4pharma/talk2scholars/agents/s2_agent.py +23 -20
  5. aiagents4pharma/talk2scholars/agents/zotero_agent.py +11 -11
  6. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +19 -19
  7. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +20 -15
  8. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +27 -6
  9. aiagents4pharma/talk2scholars/state/state_talk2scholars.py +7 -7
  10. aiagents4pharma/talk2scholars/tests/test_main_agent.py +16 -16
  11. aiagents4pharma/talk2scholars/tests/test_paper_download_agent.py +17 -24
  12. aiagents4pharma/talk2scholars/tests/test_paper_download_tools.py +152 -135
  13. aiagents4pharma/talk2scholars/tests/test_pdf_agent.py +9 -16
  14. aiagents4pharma/talk2scholars/tests/test_question_and_answer_tool.py +790 -218
  15. aiagents4pharma/talk2scholars/tests/test_s2_agent.py +9 -9
  16. aiagents4pharma/talk2scholars/tests/test_s2_display.py +8 -8
  17. aiagents4pharma/talk2scholars/tests/test_s2_query.py +8 -8
  18. aiagents4pharma/talk2scholars/tests/test_zotero_agent.py +12 -12
  19. aiagents4pharma/talk2scholars/tests/test_zotero_path.py +11 -12
  20. aiagents4pharma/talk2scholars/tests/test_zotero_read.py +400 -22
  21. aiagents4pharma/talk2scholars/tools/paper_download/__init__.py +0 -6
  22. aiagents4pharma/talk2scholars/tools/paper_download/download_arxiv_input.py +89 -31
  23. aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +540 -156
  24. aiagents4pharma/talk2scholars/tools/s2/__init__.py +4 -4
  25. aiagents4pharma/talk2scholars/tools/s2/{display_results.py → display_dataframe.py} +19 -21
  26. aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +71 -0
  27. aiagents4pharma/talk2scholars/tools/zotero/utils/read_helper.py +213 -35
  28. aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +3 -3
  29. aiagents4pharma-1.32.0.dist-info/METADATA +364 -0
  30. {aiagents4pharma-1.30.4.dist-info → aiagents4pharma-1.32.0.dist-info}/RECORD +33 -35
  31. {aiagents4pharma-1.30.4.dist-info → aiagents4pharma-1.32.0.dist-info}/WHEEL +1 -1
  32. aiagents4pharma/talk2scholars/tools/paper_download/abstract_downloader.py +0 -45
  33. aiagents4pharma/talk2scholars/tools/paper_download/arxiv_downloader.py +0 -115
  34. aiagents4pharma/talk2scholars/tools/s2/query_results.py +0 -61
  35. aiagents4pharma-1.30.4.dist-info/METADATA +0 -334
  36. {aiagents4pharma-1.30.4.dist-info → aiagents4pharma-1.32.0.dist-info}/licenses/LICENSE +0 -0
  37. {aiagents4pharma-1.30.4.dist-info → aiagents4pharma-1.32.0.dist-info}/top_level.txt +0 -0
@@ -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
- get_app_pdf(uniq_id, llm_model), # pdf
72
- get_app_paper_download(uniq_id, llm_model), # paper download
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
- add_handoff_back_messages=False,
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, query_results])
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="agent_paper_download")
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 question_and_answer_tool
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 agent_pdf_node(state: Talk2Scholars):
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([question_and_answer_tool, query_results])
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("agent_pdf", agent_pdf_node)
91
- workflow.add_edge(START, "agent_pdf")
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="agent_pdf")
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.search import search_tool as s2_search
16
- from ..tools.s2.display_results import display_results as s2_display
17
- from ..tools.s2.query_results import query_results as s2_query_results
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 as s2_retrieve_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 as s2_single_rec,
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
- # def agent_s2_node(state: Talk2Scholars) -> Command[Literal["supervisor"]]:
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 = agent_s2_node(current_state)
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
- s2_search,
93
- s2_display,
94
- s2_query_results,
95
- s2_retrieve_id,
96
- s2_single_rec,
97
- s2_multi_rec,
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("agent_s2", agent_s2_node)
115
- workflow.add_edge(START, "agent_s2")
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="agent_s2")
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.display_results import display_results as s2_display
20
- from ..tools.s2.query_results import query_results as s2_query_results
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 agent_zotero_node(state: Talk2Scholars) -> Dict[str, Any]:
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 = agent_zotero_node(current_state)
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
- s2_display,
93
- s2_query_results,
92
+ display_dataframe,
93
+ query_dataframe,
94
94
  retrieve_semantic_scholar_paper_id,
95
- zotero_review, # First review
96
- zotero_write, # Then save with user confirmation
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("agent_zotero", agent_zotero_node)
114
- workflow.add_edge(START, "agent_zotero")
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="agent_zotero")
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 Talk2Scholars agent coordinating academic paper discovery
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
- Your final response should be a one sentence summary of the information
21
- retrieved from the agents above. Do not repeat the information already
22
- displayed to the user in the response of the agents.
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 an academic research assistant with access to the
4
- Semantic Scholar API for paper discovery and analysis.
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 `display_results` tool to display the response from the search/recommendation tools
17
- 3. Use `query_results` tool to query over the selected paper only when the user asks to
18
- 4. When the user wants recommendations, you can get the "semantic_scholar_paper_id" using `query_results` tool in the "last_displayed_results" key, then
19
- pass the "semantic_scholar_paper_id" to `search`, `single_paper_rec` or `multi_paper_rec` tools depending on the user's query. Do not use "arxiv_id"
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
- target: agents.zotero_agent.get_app
2
- zotero_agent: >
3
- You are a knowledgeable research assistant with expertise in academic literature.
4
- Your goal is to help users find, manage, and save relevant research papers and display them.
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 `s2_display` tool after the papers as saved.
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
- from langchain_core.language_models import BaseChatModel
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
- logger.info("Updating existing state %s with the state dict: %s", existing, new)
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
- zotero_read (Dict[str, Any]): Stores the papers retrieved from Zotero.
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
- pdf_data: Annotated[Dict[str, Any], replace_dict]
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 dummy_get_app_s2(uniq_id, llm_model):
54
+ def dummy_s2_agent(uniq_id, llm_model):
55
55
  """Return a DummyWorkflow for the S2 agent."""
56
- dummy_get_app_s2.called_uniq_id = uniq_id
57
- dummy_get_app_s2.called_llm_model = llm_model
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 dummy_get_app_zotero(uniq_id, llm_model):
61
+ def dummy_zotero_agent(uniq_id, llm_model):
62
62
  """Return a DummyWorkflow for the Zotero agent."""
63
- dummy_get_app_zotero.called_uniq_id = uniq_id
64
- dummy_get_app_zotero.called_llm_model = llm_model
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 dummy_get_app_pdf(uniq_id, llm_model):
68
+ def dummy_question_and_answer_agent(uniq_id, llm_model):
69
69
  """Return a DummyWorkflow for the PDF agent."""
70
- dummy_get_app_pdf.called_uniq_id = uniq_id
71
- dummy_get_app_pdf.called_llm_model = llm_model
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 dummy_get_app_paper_download(uniq_id, llm_model):
146
+ def dummy_paper_download_agent(uniq_id, llm_model):
147
147
  """Return a DummyWorkflow for the paper download agent."""
148
- dummy_get_app_paper_download.called_uniq_id = uniq_id
149
- dummy_get_app_paper_download.called_llm_model = llm_model
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", dummy_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
- dummy_get_app_zotero,
163
+ dummy_zotero_agent,
164
164
  )
165
165
  monkeypatch.setattr(
166
166
  "aiagents4pharma.talk2scholars.agents.main_agent.get_app_pdf",
167
- dummy_get_app_pdf,
167
+ dummy_question_and_answer_agent,
168
168
  )
169
169
  monkeypatch.setattr(
170
170
  "aiagents4pharma.talk2scholars.agents.main_agent.get_app_paper_download",
171
- dummy_get_app_paper_download,
171
+ dummy_paper_download_agent,
172
172
  )
173
173
  monkeypatch.setattr(
174
174
  "aiagents4pharma.talk2scholars.agents.main_agent.create_supervisor",
@@ -22,22 +22,15 @@ def mock_hydra_fixture():
22
22
  @pytest.fixture
23
23
  def mock_tools_fixture():
24
24
  """Mocks paper download tools to prevent real HTTP calls."""
25
- with (
26
- mock.patch(
27
- "aiagents4pharma.talk2scholars.tools.paper_download."
28
- "download_arxiv_input.download_arxiv_paper"
29
- ) as mock_download_arxiv_paper,
30
- mock.patch(
31
- "aiagents4pharma.talk2scholars.tools.s2.query_results.query_results"
32
- ) as mock_query_results,
33
- ):
25
+ with mock.patch(
26
+ "aiagents4pharma.talk2scholars.tools.paper_download."
27
+ "download_arxiv_input.download_arxiv_paper"
28
+ ) as mock_download_arxiv_paper:
34
29
  mock_download_arxiv_paper.return_value = {
35
- "pdf_data": {"dummy_key": "dummy_value"}
30
+ "article_data": {"dummy_key": "dummy_value"}
36
31
  }
37
- mock_query_results.return_value = {
38
- "result": "Mocked Query Result"
39
- }
40
- yield [mock_download_arxiv_paper, mock_query_results]
32
+ yield [mock_download_arxiv_paper]
33
+
41
34
 
42
35
  @pytest.mark.usefixtures("mock_hydra_fixture")
43
36
  def test_paper_download_agent_initialization():
@@ -54,6 +47,7 @@ def test_paper_download_agent_initialization():
54
47
  assert app is not None, "The agent app should be successfully created."
55
48
  assert mock_create_agent.called
56
49
 
50
+
57
51
  def test_paper_download_agent_invocation():
58
52
  """Verifies agent processes queries and updates state correctly."""
59
53
  _ = mock_tools_fixture # Prevents unused-argument warning
@@ -70,10 +64,9 @@ def test_paper_download_agent_invocation():
70
64
  mock_create_agent.return_value = mock_agent
71
65
  mock_agent.invoke.return_value = {
72
66
  "messages": [AIMessage(content="Here is the paper")],
73
- "pdf_data": {"file_bytes": b"FAKE_PDF_CONTENTS"},
67
+ "article_data": {"file_bytes": b"FAKE_PDF_CONTENTS"},
74
68
  }
75
69
 
76
-
77
70
  app = get_app(thread_id, llm_mock)
78
71
  result = app.invoke(
79
72
  mock_state,
@@ -87,11 +80,11 @@ def test_paper_download_agent_invocation():
87
80
  )
88
81
 
89
82
  assert "messages" in result
90
- assert "pdf_data" in result
83
+ assert "article_data" in result
91
84
 
92
85
 
93
86
  def test_paper_download_agent_tools_assignment(request): # Keep fixture name
94
- """Checks correct tool assignment (download_arxiv_paper, query_results)."""
87
+ """Checks correct tool assignment (download_arxiv_paper, query_dataframe)."""
95
88
  thread_id = "test_thread_paper_dl"
96
89
  mock_tools = request.getfixturevalue("mock_tools_fixture")
97
90
  llm_mock = mock.Mock(spec=BaseChatModel)
@@ -107,12 +100,12 @@ def test_paper_download_agent_tools_assignment(request): # Keep fixture name
107
100
  mock_agent = mock.Mock()
108
101
  mock_create_agent.return_value = mock_agent
109
102
  mock_tool_instance = mock.Mock()
110
- mock_tool_instance.tools = mock_tools
111
- mock_toolnode.return_value= mock_tool_instance
103
+ mock_tool_instance.tools = mock_tools if mock_tools else []
104
+ mock_toolnode.return_value = mock_tool_instance
112
105
 
113
106
  get_app(thread_id, llm_mock)
114
107
  assert mock_toolnode.called
115
- assert len(mock_tool_instance.tools) == 2
108
+ assert len(mock_tool_instance.tools) == 1
116
109
 
117
110
 
118
111
  def test_paper_download_agent_hydra_failure():
@@ -137,6 +130,6 @@ def test_paper_download_agent_model_failure():
137
130
  ):
138
131
  with pytest.raises(Exception) as exc_info:
139
132
  get_app(thread_id, llm_mock)
140
- assert "Mock model failure" in str(exc_info.value), (
141
- "Model initialization failure should raise an exception."
142
- )
133
+ assert "Mock model failure" in str(
134
+ exc_info.value
135
+ ), "Model initialization failure should raise an exception."