aiagents4pharma 1.19.1__py3-none-any.whl → 1.20.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ defaults:
2
+ - _self_
3
+ - agents/t2b_agent: default
4
+ - tools/ask_question: default
5
+ - tools/get_annotation: default
@@ -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, Any
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 make_supervisor_node(llm: BaseChatModel, cfg: Any) -> str:
40
+ def get_hydra_config():
24
41
  """
25
- Creates a supervisor node following LangGraph patterns.
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 to use for generating responses.
29
- cfg (Any): The configuration object.
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
- str: The supervisor node function.
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
- Supervisor node that routes to appropriate sub-agents.
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[Literal["s2_agent", "__end__"]]: The command to execute next.
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, Current Agent: %s",
110
+ "Supervisor node called - Messages count: %d",
48
111
  len(state["messages"]),
49
- state.get("current_agent", "None"),
50
112
  )
51
113
 
52
- messages = [{"role": "system", "content": cfg.state_modifier}] + state[
53
- "messages"
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
- if goto == "FINISH":
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
- Returns the langraph app with hierarchical structure.
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): The thread ID for the conversation.
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
- The compiled langraph app.
97
- """
138
+ StateGraph: A compiled LangGraph application ready for query invocation.
98
139
 
99
- # Load hydra configuration
100
- logger.log(logging.INFO, "Load Hydra configuration for Talk2Scholars main agent.")
101
- with hydra.initialize(version_base=None, config_path="../../configs"):
102
- cfg = hydra.compose(
103
- config_name="config", overrides=["agents/talk2scholars/main_agent=default"]
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(state: Talk2Scholars) -> Command[Literal["__end__"]]:
146
+ def call_s2_agent(
147
+ state: Talk2Scholars,
148
+ ) -> Command[Literal["supervisor", "__end__"]]:
109
149
  """
110
- Node for calling the S2 agent.
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 of the conversation.
156
+ state (Talk2Scholars): The current conversation state, including user queries
157
+ and any previously retrieved papers.
114
158
 
115
159
  Returns:
116
- Command[Literal["__end__"]]: The command to execute next.
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
- response = app.invoke(state)
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
- "is_last_step": True,
128
- "current_agent": "s2_agent",
186
+ "papers": response.get("papers", {}),
187
+ "multi_papers": response.get("multi_papers", {}),
129
188
  },
130
189
  )
131
190
 
132
- logger.log(
133
- logging.INFO,
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="../../configs"):
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
- "You are a supervisory AI agent that routes user queries to specialized tools.\n"
10
- "Your task is to select the most appropriate tool based on the user's request.\n\n"
11
- "Available tools and their capabilities:\n\n"
12
- "1. semantic_scholar_agent:\n"
13
- " - Search for academic papers and research\n"
14
- " - Get paper recommendations\n"
15
- " - Find similar papers\n"
16
- " USE FOR: Any queries about finding papers, academic research, "
17
- "or getting paper recommendations\n\n"
18
- "ROUTING GUIDELINES:\n\n"
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
- "You are a specialized academic research assistant with access to the following tools:\n\n"
10
- "1. search_papers:\n"
11
- " USE FOR: General paper searches\n"
12
- " - Enhances search terms automatically\n"
13
- " - Adds relevant academic keywords\n"
14
- " - Focuses on recent research when appropriate\n\n"
15
- "2. get_single_paper_recommendations:\n"
16
- " USE FOR: Finding papers similar to a specific paper\n"
17
- " - Takes a single paper ID\n"
18
- " - Returns related papers\n\n"
19
- "3. get_multi_paper_recommendations:\n"
20
- " USE FOR: Finding papers similar to multiple papers\n"
21
- " - Takes multiple paper IDs\n"
22
- " - Finds papers related to all inputs\n\n"
23
- "GUIDELINES:\n\n"
24
- "For paper searches:\n"
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, Optional
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
- papers: Annotated[Dict[str, Any], replace_dict] # Changed from List to Dict
28
- search_table: NotRequired[str]
29
- next: str # Required for routing in LangGraph
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