aiagents4pharma 1.19.1__py3-none-any.whl → 1.20.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aiagents4pharma/talk2biomodels/configs/config.yaml +5 -0
- aiagents4pharma/talk2scholars/agents/main_agent.py +129 -73
- aiagents4pharma/talk2scholars/agents/s2_agent.py +2 -1
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +10 -31
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +16 -60
- aiagents4pharma/talk2scholars/state/state_talk2scholars.py +9 -8
- aiagents4pharma/talk2scholars/tests/test_integration.py +237 -0
- aiagents4pharma/talk2scholars/tests/test_main_agent.py +180 -0
- aiagents4pharma/talk2scholars/tests/test_s2_agent.py +138 -0
- aiagents4pharma/talk2scholars/tests/{test_langgraph.py → test_s2_tools.py} +79 -151
- aiagents4pharma/talk2scholars/tests/test_state.py +14 -0
- aiagents4pharma/talk2scholars/tools/s2/display_results.py +33 -8
- aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +10 -23
- aiagents4pharma/talk2scholars/tools/s2/search.py +10 -29
- aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +4 -29
- {aiagents4pharma-1.19.1.dist-info → aiagents4pharma-1.20.1.dist-info}/METADATA +19 -3
- {aiagents4pharma-1.19.1.dist-info → aiagents4pharma-1.20.1.dist-info}/RECORD +20 -15
- {aiagents4pharma-1.19.1.dist-info → aiagents4pharma-1.20.1.dist-info}/LICENSE +0 -0
- {aiagents4pharma-1.19.1.dist-info → aiagents4pharma-1.20.1.dist-info}/WHEEL +0 -0
- {aiagents4pharma-1.19.1.dist-info → aiagents4pharma-1.20.1.dist-info}/top_level.txt +0 -0
@@ -1,151 +1,207 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
|
3
3
|
"""
|
4
|
-
Main agent for the talk2scholars app.
|
4
|
+
Main agent for the talk2scholars app using ReAct pattern.
|
5
|
+
|
6
|
+
This module implements a hierarchical agent system where a supervisor agent
|
7
|
+
routes queries to specialized sub-agents. It follows the LangGraph patterns
|
8
|
+
for multi-agent systems and implements proper state management.
|
9
|
+
|
10
|
+
The main components are:
|
11
|
+
1. Supervisor node with ReAct pattern for intelligent routing.
|
12
|
+
2. S2 agent node for handling academic paper queries.
|
13
|
+
3. Shared state management via Talk2Scholars.
|
14
|
+
4. Hydra-based configuration system.
|
15
|
+
|
16
|
+
Example:
|
17
|
+
app = get_app("thread_123", "gpt-4o-mini")
|
18
|
+
result = app.invoke({
|
19
|
+
"messages": [("human", "Find papers about AI agents")]
|
20
|
+
})
|
5
21
|
"""
|
6
22
|
|
7
23
|
import logging
|
8
|
-
from typing import Literal,
|
24
|
+
from typing import Literal, Callable
|
9
25
|
import hydra
|
10
26
|
from langchain_core.language_models.chat_models import BaseChatModel
|
11
|
-
from langchain_core.messages import AIMessage
|
12
27
|
from langchain_openai import ChatOpenAI
|
13
28
|
from langgraph.checkpoint.memory import MemorySaver
|
14
29
|
from langgraph.graph import END, START, StateGraph
|
30
|
+
from langgraph.prebuilt import create_react_agent
|
15
31
|
from langgraph.types import Command
|
16
32
|
from ..agents import s2_agent
|
17
33
|
from ..state.state_talk2scholars import Talk2Scholars
|
18
34
|
|
35
|
+
# Configure logging
|
19
36
|
logging.basicConfig(level=logging.INFO)
|
20
37
|
logger = logging.getLogger(__name__)
|
21
38
|
|
22
39
|
|
23
|
-
def
|
40
|
+
def get_hydra_config():
|
24
41
|
"""
|
25
|
-
|
42
|
+
Loads and returns the Hydra configuration for the main agent.
|
43
|
+
|
44
|
+
This function fetches the configuration settings for the Talk2Scholars
|
45
|
+
agent, ensuring that all required parameters are properly initialized.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
Any: The configuration object for the main agent.
|
49
|
+
"""
|
50
|
+
with hydra.initialize(version_base=None, config_path="../configs"):
|
51
|
+
cfg = hydra.compose(
|
52
|
+
config_name="config", overrides=["agents/talk2scholars/main_agent=default"]
|
53
|
+
)
|
54
|
+
return cfg.agents.talk2scholars.main_agent
|
55
|
+
|
56
|
+
|
57
|
+
def make_supervisor_node(llm: BaseChatModel, thread_id: str) -> Callable:
|
58
|
+
"""
|
59
|
+
Creates and returns a supervisor node for intelligent routing using the ReAct pattern.
|
60
|
+
|
61
|
+
This function initializes a supervisor agent that processes user queries and
|
62
|
+
determines the appropriate sub-agent for further processing. It applies structured
|
63
|
+
reasoning to manage conversations and direct queries based on context.
|
26
64
|
|
27
65
|
Args:
|
28
|
-
llm (BaseChatModel): The language model
|
29
|
-
|
66
|
+
llm (BaseChatModel): The language model used by the supervisor agent.
|
67
|
+
thread_id (str): Unique identifier for the conversation session.
|
30
68
|
|
31
69
|
Returns:
|
32
|
-
|
70
|
+
Callable: A function that acts as the supervisor node in the LangGraph workflow.
|
71
|
+
|
72
|
+
Example:
|
73
|
+
supervisor = make_supervisor_node(llm, "thread_123")
|
74
|
+
workflow.add_node("supervisor", supervisor)
|
33
75
|
"""
|
76
|
+
logger.info("Loading Hydra configuration for Talk2Scholars main agent.")
|
77
|
+
cfg = get_hydra_config()
|
78
|
+
logger.info("Hydra configuration loaded with values: %s", cfg)
|
79
|
+
|
80
|
+
# Create the supervisor agent using the main agent's configuration
|
81
|
+
supervisor_agent = create_react_agent(
|
82
|
+
llm,
|
83
|
+
tools=[], # Will add sub-agents later
|
84
|
+
state_modifier=cfg.main_agent,
|
85
|
+
state_schema=Talk2Scholars,
|
86
|
+
checkpointer=MemorySaver(),
|
87
|
+
)
|
88
|
+
|
34
89
|
def supervisor_node(
|
35
90
|
state: Talk2Scholars,
|
36
91
|
) -> Command[Literal["s2_agent", "__end__"]]:
|
37
92
|
"""
|
38
|
-
|
93
|
+
Processes user queries and determines the next step in the conversation flow.
|
94
|
+
|
95
|
+
This function examines the conversation state and decides whether to forward
|
96
|
+
the query to a specialized sub-agent (e.g., S2 agent) or conclude the interaction.
|
39
97
|
|
40
98
|
Args:
|
41
|
-
state (Talk2Scholars): The current state of the conversation
|
99
|
+
state (Talk2Scholars): The current state of the conversation, containing
|
100
|
+
messages, papers, and metadata.
|
42
101
|
|
43
102
|
Returns:
|
44
|
-
Command
|
103
|
+
Command: The next action to be executed, along with updated state data.
|
104
|
+
|
105
|
+
Example:
|
106
|
+
result = supervisor_node(current_state)
|
107
|
+
next_step = result.goto
|
45
108
|
"""
|
46
109
|
logger.info(
|
47
|
-
"Supervisor node called - Messages count: %d
|
110
|
+
"Supervisor node called - Messages count: %d",
|
48
111
|
len(state["messages"]),
|
49
|
-
state.get("current_agent", "None"),
|
50
112
|
)
|
51
113
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
response = llm.invoke(messages)
|
56
|
-
goto = (
|
57
|
-
"FINISH"
|
58
|
-
if not any(
|
59
|
-
kw in state["messages"][-1].content.lower()
|
60
|
-
for kw in ["search", "paper", "find"]
|
61
|
-
)
|
62
|
-
else "s2_agent"
|
114
|
+
# Invoke the supervisor agent with configurable thread_id
|
115
|
+
result = supervisor_agent.invoke(
|
116
|
+
state, {"configurable": {"thread_id": thread_id}}
|
63
117
|
)
|
118
|
+
goto = "s2_agent"
|
119
|
+
logger.info("Supervisor agent completed with result: %s", result)
|
64
120
|
|
65
|
-
|
66
|
-
return Command(
|
67
|
-
goto=END,
|
68
|
-
update={
|
69
|
-
"messages": state["messages"]
|
70
|
-
+ [AIMessage(content=response.content)],
|
71
|
-
"is_last_step": True,
|
72
|
-
"current_agent": None,
|
73
|
-
},
|
74
|
-
)
|
75
|
-
|
76
|
-
return Command(
|
77
|
-
goto="s2_agent",
|
78
|
-
update={
|
79
|
-
"messages": state["messages"],
|
80
|
-
"is_last_step": False,
|
81
|
-
"current_agent": "s2_agent",
|
82
|
-
},
|
83
|
-
)
|
121
|
+
return Command(goto=goto)
|
84
122
|
|
85
123
|
return supervisor_node
|
86
124
|
|
87
125
|
|
88
|
-
def get_app(thread_id: str, llm_model="gpt-4o-mini") -> StateGraph:
|
126
|
+
def get_app(thread_id: str, llm_model: str = "gpt-4o-mini") -> StateGraph:
|
89
127
|
"""
|
90
|
-
|
128
|
+
Initializes and returns the LangGraph application with a hierarchical agent system.
|
129
|
+
|
130
|
+
This function sets up the full agent architecture, including the supervisor
|
131
|
+
and sub-agents, and compiles the LangGraph workflow for handling user queries.
|
91
132
|
|
92
133
|
Args:
|
93
|
-
thread_id (str):
|
134
|
+
thread_id (str): Unique identifier for the conversation session.
|
135
|
+
llm_model (str, optional): The language model to be used. Defaults to "gpt-4o-mini".
|
94
136
|
|
95
137
|
Returns:
|
96
|
-
|
97
|
-
"""
|
138
|
+
StateGraph: A compiled LangGraph application ready for query invocation.
|
98
139
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
)
|
105
|
-
cfg = cfg.agents.talk2scholars.main_agent
|
106
|
-
logger.info("Hydra configuration loaded with values: %s", cfg)
|
140
|
+
Example:
|
141
|
+
app = get_app("thread_123")
|
142
|
+
result = app.invoke(initial_state)
|
143
|
+
"""
|
144
|
+
cfg = get_hydra_config()
|
107
145
|
|
108
|
-
def call_s2_agent(
|
146
|
+
def call_s2_agent(
|
147
|
+
state: Talk2Scholars,
|
148
|
+
) -> Command[Literal["supervisor", "__end__"]]:
|
109
149
|
"""
|
110
|
-
|
150
|
+
Calls the Semantic Scholar (S2) agent to process academic paper queries.
|
151
|
+
|
152
|
+
This function invokes the S2 agent, retrieves relevant research papers,
|
153
|
+
and updates the conversation state accordingly.
|
111
154
|
|
112
155
|
Args:
|
113
|
-
state (Talk2Scholars): The current state
|
156
|
+
state (Talk2Scholars): The current conversation state, including user queries
|
157
|
+
and any previously retrieved papers.
|
114
158
|
|
115
159
|
Returns:
|
116
|
-
Command
|
160
|
+
Command: The next action to execute, along with updated messages and papers.
|
161
|
+
|
162
|
+
Example:
|
163
|
+
result = call_s2_agent(current_state)
|
164
|
+
next_step = result.goto
|
117
165
|
"""
|
118
166
|
logger.info("Calling S2 agent with state: %s", state)
|
119
167
|
app = s2_agent.get_app(thread_id, llm_model)
|
120
|
-
|
168
|
+
|
169
|
+
# Invoke the S2 agent, passing state,
|
170
|
+
# Pass both config_id and thread_id
|
171
|
+
response = app.invoke(
|
172
|
+
state,
|
173
|
+
{
|
174
|
+
"configurable": {
|
175
|
+
"config_id": thread_id,
|
176
|
+
"thread_id": thread_id,
|
177
|
+
}
|
178
|
+
},
|
179
|
+
)
|
121
180
|
logger.info("S2 agent completed with response: %s", response)
|
181
|
+
|
122
182
|
return Command(
|
123
183
|
goto=END,
|
124
184
|
update={
|
125
185
|
"messages": response["messages"],
|
126
|
-
"papers": response.get("papers",
|
127
|
-
"
|
128
|
-
"current_agent": "s2_agent",
|
186
|
+
"papers": response.get("papers", {}),
|
187
|
+
"multi_papers": response.get("multi_papers", {}),
|
129
188
|
},
|
130
189
|
)
|
131
190
|
|
132
|
-
|
133
|
-
|
134
|
-
"Using OpenAI model %s with temperature %s",
|
135
|
-
llm_model,
|
136
|
-
cfg.temperature
|
137
|
-
)
|
191
|
+
# Initialize LLM
|
192
|
+
logger.info("Using OpenAI model %s with temperature %s", llm_model, cfg.temperature)
|
138
193
|
llm = ChatOpenAI(model=llm_model, temperature=cfg.temperature)
|
194
|
+
|
195
|
+
# Build the graph
|
139
196
|
workflow = StateGraph(Talk2Scholars)
|
197
|
+
supervisor = make_supervisor_node(llm, thread_id)
|
140
198
|
|
141
|
-
supervisor = make_supervisor_node(llm, cfg)
|
142
199
|
workflow.add_node("supervisor", supervisor)
|
143
200
|
workflow.add_node("s2_agent", call_s2_agent)
|
144
|
-
|
145
|
-
# Define edges
|
146
201
|
workflow.add_edge(START, "supervisor")
|
147
202
|
workflow.add_edge("s2_agent", END)
|
148
203
|
|
204
|
+
# Compile the graph without initial state
|
149
205
|
app = workflow.compile(checkpointer=MemorySaver())
|
150
206
|
logger.info("Main agent workflow compiled")
|
151
207
|
return app
|
@@ -39,7 +39,7 @@ def get_app(uniq_id, llm_model="gpt-4o-mini"):
|
|
39
39
|
|
40
40
|
# Load hydra configuration
|
41
41
|
logger.log(logging.INFO, "Load Hydra configuration for Talk2Scholars S2 agent.")
|
42
|
-
with hydra.initialize(version_base=None, config_path="
|
42
|
+
with hydra.initialize(version_base=None, config_path="../configs"):
|
43
43
|
cfg = hydra.compose(
|
44
44
|
config_name="config", overrides=["agents/talk2scholars/s2_agent=default"]
|
45
45
|
)
|
@@ -57,6 +57,7 @@ def get_app(uniq_id, llm_model="gpt-4o-mini"):
|
|
57
57
|
llm,
|
58
58
|
tools=tools,
|
59
59
|
state_schema=Talk2Scholars,
|
60
|
+
# prompt=cfg.s2_agent,
|
60
61
|
state_modifier=cfg.s2_agent,
|
61
62
|
checkpointer=MemorySaver(),
|
62
63
|
)
|
@@ -6,34 +6,13 @@ openai_llms:
|
|
6
6
|
- "gpt-3.5-turbo"
|
7
7
|
temperature: 0
|
8
8
|
main_agent: >
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
"ALWAYS route to semantic_scholar_agent for:\n"
|
20
|
-
"- Finding academic papers\n"
|
21
|
-
"- Searching research topics\n"
|
22
|
-
"- Getting paper recommendations\n"
|
23
|
-
"- Finding similar papers\n"
|
24
|
-
"- Any query about academic literature\n\n"
|
25
|
-
"Approach:\n"
|
26
|
-
"1. Identify the core need in the user's query\n"
|
27
|
-
"2. Select the most appropriate tool based on the guidelines above\n"
|
28
|
-
"3. If unclear, ask for clarification\n"
|
29
|
-
"4. For multi-step tasks, focus on the immediate next step\n\n"
|
30
|
-
"Remember:\n"
|
31
|
-
"- Be decisive in your tool selection\n"
|
32
|
-
"- Focus on the immediate task\n"
|
33
|
-
"- Default to semantic_scholar_agent for any paper-finding tasks\n"
|
34
|
-
"- Ask for clarification if the request is ambiguous\n\n"
|
35
|
-
"When presenting paper search results, always use this exact format:\n\n"
|
36
|
-
"Remember to:\n"
|
37
|
-
"- To always add the url\n"
|
38
|
-
"- Put URLs on the title line itself as markdown\n"
|
39
|
-
"- Maintain consistent spacing and formatting"
|
9
|
+
You are an intelligent research assistant coordinating academic paper discovery and analysis.
|
10
|
+
|
11
|
+
AVAILABLE TOOLS AND ROUTING:
|
12
|
+
1. semantic_scholar_agent:
|
13
|
+
Access to tools:
|
14
|
+
- search_tool: For paper discovery
|
15
|
+
- display_results: For showing paper results
|
16
|
+
- get_single_paper_recommendations: For single paper recommendations
|
17
|
+
- get_multi_paper_recommendations: For multi-paper recommendations
|
18
|
+
→ ROUTE TO THIS AGENT FOR: Any query about academic papers, research, or articles
|
@@ -6,63 +6,19 @@ openai_llms:
|
|
6
6
|
- "gpt-3.5-turbo"
|
7
7
|
temperature: 0
|
8
8
|
s2_agent: >
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
"- Enhance search terms with academic language\n"
|
26
|
-
"- Include field-specific terminology\n"
|
27
|
-
'- Add "recent" or "latest" when appropriate\n'
|
28
|
-
"- Keep queries focused and relevant\n\n"
|
29
|
-
"For paper recommendations:\n"
|
30
|
-
"- Identify paper IDs (40-character hexadecimal strings)\n"
|
31
|
-
"- Use single_paper_recommendations for one ID\n"
|
32
|
-
"- Use multi_paper_recommendations for multiple IDs\n\n"
|
33
|
-
"Best practices:\n"
|
34
|
-
"1. Start with a broad search if no paper IDs are provided\n"
|
35
|
-
"2. Look for paper IDs in user input\n"
|
36
|
-
"3. Enhance search terms for better results\n"
|
37
|
-
"4. Consider the academic context\n"
|
38
|
-
"5. Be prepared to refine searches based on feedback\n\n"
|
39
|
-
"Remember:\n"
|
40
|
-
"- Always select the most appropriate tool\n"
|
41
|
-
"- Enhance search queries naturally\n"
|
42
|
-
"- Consider academic context\n"
|
43
|
-
"- Focus on delivering relevant results\n\n"
|
44
|
-
"IMPORTANT GUIDELINES FOR PAPER RECOMMENDATIONS:\n\n"
|
45
|
-
"For Multiple Papers:\n"
|
46
|
-
"- When getting recommendations for multiple papers, always use "
|
47
|
-
"get_multi_paper_recommendations tool\n"
|
48
|
-
"- DO NOT call get_single_paper_recommendations multiple times\n"
|
49
|
-
"- Always pass all paper IDs in a single call to get_multi_paper_recommendations\n"
|
50
|
-
'- Use for queries like "find papers related to both/all papers" or '
|
51
|
-
'"find similar papers to these papers"\n\n'
|
52
|
-
"For Single Paper:\n"
|
53
|
-
"- Use get_single_paper_recommendations when focusing on one specific paper\n"
|
54
|
-
"- Pass only one paper ID at a time\n"
|
55
|
-
'- Use for queries like "find papers similar to this paper" or '
|
56
|
-
'"get recommendations for paper X"\n'
|
57
|
-
"- Do not use for multiple papers\n\n"
|
58
|
-
"Examples:\n"
|
59
|
-
'- For "find related papers for both papers":\n'
|
60
|
-
" ✓ Use get_multi_paper_recommendations with both paper IDs\n"
|
61
|
-
" × Don't make multiple calls to get_single_paper_recommendations\n\n"
|
62
|
-
'- For "find papers related to the first paper":\n'
|
63
|
-
" ✓ Use get_single_paper_recommendations with just that paper's ID\n"
|
64
|
-
" × Don't use get_multi_paper_recommendations\n\n"
|
65
|
-
"Remember:\n"
|
66
|
-
"- Be precise in identifying which paper ID to use for single recommendations\n"
|
67
|
-
"- Don't reuse previous paper IDs unless specifically requested\n"
|
68
|
-
"- For fresh paper recommendations, always use the original paper ID"
|
9
|
+
You are a specialized academic research agent with access to tools for paper discovery and analysis.
|
10
|
+
|
11
|
+
YOUR TOOLS:
|
12
|
+
1. search_tool:
|
13
|
+
- Finds research papers based on user queries.
|
14
|
+
- If no papers are found, it performs a new search.
|
15
|
+
|
16
|
+
2. display_results:
|
17
|
+
- Shows the current research papers.
|
18
|
+
- If no papers are found, it will instruct you to perform a search.
|
19
|
+
|
20
|
+
3. get_single_paper_recommendations:
|
21
|
+
- Provides recommendations based on a single selected paper.
|
22
|
+
|
23
|
+
4. get_multi_paper_recommendations:
|
24
|
+
- Provides recommendations based on multiple selected papers.
|
@@ -3,10 +3,8 @@ This is the state file for the talk2scholars agent.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Annotated, Any, Dict
|
7
|
-
|
6
|
+
from typing import Annotated, Any, Dict
|
8
7
|
from langgraph.prebuilt.chat_agent_executor import AgentState
|
9
|
-
from typing_extensions import NotRequired, Required
|
10
8
|
|
11
9
|
# Configure logging
|
12
10
|
logging.basicConfig(level=logging.INFO)
|
@@ -22,11 +20,14 @@ def replace_dict(existing: Dict[str, Any], new: Dict[str, Any]) -> Dict[str, Any
|
|
22
20
|
class Talk2Scholars(AgentState):
|
23
21
|
"""
|
24
22
|
The state for the talk2scholars agent, inheriting from AgentState.
|
23
|
+
|
24
|
+
Attributes:
|
25
|
+
papers: Dictionary of papers from search results
|
26
|
+
multi_papers: Dictionary of papers from multi-paper recommendations
|
27
|
+
llm_model: Model being used
|
25
28
|
"""
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
current_agent: NotRequired[Optional[str]]
|
31
|
-
is_last_step: Required[bool] # Required field for LangGraph
|
30
|
+
# Agent state fields
|
31
|
+
papers: Annotated[Dict[str, Any], replace_dict]
|
32
|
+
multi_papers: Annotated[Dict[str, Any], replace_dict]
|
32
33
|
llm_model: str
|