aiagents4pharma 1.39.0__py3-none-any.whl → 1.39.2__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/talk2scholars/agents/main_agent.py +7 -7
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +88 -12
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/paper_download_agent/default.yaml +5 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +5 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +1 -20
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +1 -26
- aiagents4pharma/talk2scholars/configs/tools/download_arxiv_paper/default.yaml +4 -0
- aiagents4pharma/talk2scholars/configs/tools/download_biorxiv_paper/default.yaml +2 -0
- aiagents4pharma/talk2scholars/configs/tools/download_medrxiv_paper/default.yaml +2 -0
- aiagents4pharma/talk2scholars/configs/tools/question_and_answer/default.yaml +22 -0
- aiagents4pharma/talk2scholars/tests/test_main_agent.py +20 -2
- aiagents4pharma/talk2scholars/tests/test_nvidia_nim_reranker_utils.py +28 -0
- aiagents4pharma/talk2scholars/tests/test_paper_download_tools.py +107 -29
- aiagents4pharma/talk2scholars/tests/test_pdf_agent.py +2 -3
- aiagents4pharma/talk2scholars/tests/test_question_and_answer_tool.py +194 -543
- aiagents4pharma/talk2scholars/tests/test_s2_agent.py +2 -2
- aiagents4pharma/talk2scholars/tests/{test_s2_display.py → test_s2_display_dataframe.py} +2 -3
- aiagents4pharma/talk2scholars/tests/test_s2_query_dataframe.py +201 -0
- aiagents4pharma/talk2scholars/tests/test_s2_retrieve.py +7 -6
- aiagents4pharma/talk2scholars/tests/test_s2_utils_ext_ids.py +413 -0
- aiagents4pharma/talk2scholars/tests/test_tool_helper_utils.py +140 -0
- aiagents4pharma/talk2scholars/tests/test_zotero_agent.py +0 -1
- aiagents4pharma/talk2scholars/tests/test_zotero_read.py +16 -18
- aiagents4pharma/talk2scholars/tools/paper_download/download_arxiv_input.py +92 -37
- aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +73 -575
- aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +10 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +97 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +77 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +83 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +125 -0
- aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +162 -0
- aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +33 -10
- aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +39 -16
- aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +124 -10
- aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +49 -17
- aiagents4pharma/talk2scholars/tools/s2/search.py +39 -16
- aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +34 -16
- aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +49 -16
- aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +51 -16
- aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +50 -17
- {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/METADATA +58 -105
- {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/RECORD +45 -32
- aiagents4pharma/talk2scholars/tests/test_llm_main_integration.py +0 -89
- aiagents4pharma/talk2scholars/tests/test_routing_logic.py +0 -74
- aiagents4pharma/talk2scholars/tests/test_s2_query.py +0 -95
- {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/WHEEL +0 -0
- {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/licenses/LICENSE +0 -0
- {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/top_level.txt +0 -0
@@ -1,89 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Integration tests for talk2scholars system with OpenAI.
|
3
|
-
This test triggers all sub-agents by sending a conversation that covers:
|
4
|
-
- Searching Semantic Scholar (S2 agent)
|
5
|
-
- Retrieving Zotero results (Zotero agent)
|
6
|
-
- Querying PDF content (PDF agent)
|
7
|
-
- Downloading paper details from arXiv (Paper Download agent)
|
8
|
-
"""
|
9
|
-
|
10
|
-
# This will be covered in the next pr.
|
11
|
-
|
12
|
-
#
|
13
|
-
# import os
|
14
|
-
# import pytest
|
15
|
-
# import hydra
|
16
|
-
# from langchain_openai import ChatOpenAI
|
17
|
-
# from langchain_core.messages import HumanMessage, AIMessage
|
18
|
-
# from ..agents.main_agent import get_app
|
19
|
-
# from ..state.state_talk2scholars import Talk2Scholars
|
20
|
-
#
|
21
|
-
# # pylint: disable=redefined-outer-name,too-few-public-methods
|
22
|
-
#
|
23
|
-
#
|
24
|
-
# @pytest.mark.skipif(
|
25
|
-
# not os.getenv("OPENAI_API_KEY"), reason="Requires OpenAI API key to run"
|
26
|
-
# )
|
27
|
-
# def test_main_agent_real_llm():
|
28
|
-
# """
|
29
|
-
# Integration test for the Talk2Scholars system using a real OpenAI LLM.
|
30
|
-
# This test verifies that the supervisor correctly routes to all sub-agents by
|
31
|
-
# providing a conversation with queries intended to trigger each agent.
|
32
|
-
# """
|
33
|
-
# # Load Hydra configuration EXACTLY like in main_agent.py
|
34
|
-
# with hydra.initialize(version_base=None, config_path="../configs"):
|
35
|
-
# cfg = hydra.compose(
|
36
|
-
# config_name="config", overrides=["agents/talk2scholars/main_agent=default"]
|
37
|
-
# )
|
38
|
-
# hydra_cfg = cfg.agents.talk2scholars.main_agent
|
39
|
-
# assert hydra_cfg is not None, "Hydra config failed to load"
|
40
|
-
#
|
41
|
-
# # Use the real OpenAI API (ensure OPENAI_API_KEY is set in environment)
|
42
|
-
# llm = ChatOpenAI(model="gpt-4o-mini", temperature=hydra_cfg.temperature)
|
43
|
-
#
|
44
|
-
# # Initialize the main agent workflow (with real Hydra config)
|
45
|
-
# thread_id = "test_thread"
|
46
|
-
# app = get_app(thread_id, llm)
|
47
|
-
#
|
48
|
-
# # Provide a multi-turn conversation intended to trigger all sub-agents:
|
49
|
-
# # - S2 agent: "Search Semantic Scholar for AI papers on transformers."
|
50
|
-
# # - Zotero agent: "Retrieve Zotero results for these papers."
|
51
|
-
# # - PDF agent: "Analyze the attached PDF and summarize its key findings."
|
52
|
-
# # - Paper Download agent: "Download the paper details from arXiv."
|
53
|
-
# initial_state = Talk2Scholars(
|
54
|
-
# messages=[
|
55
|
-
# HumanMessage(
|
56
|
-
# content="Search Semantic Scholar for AI papers on transformers."
|
57
|
-
# ),
|
58
|
-
# HumanMessage(content="Also, retrieve Zotero results for these papers."),
|
59
|
-
# HumanMessage(
|
60
|
-
# content="I have attached a PDF; analyze it and tell me the key findings."
|
61
|
-
# ),
|
62
|
-
# HumanMessage(content="Finally, download the paper from arXiv."),
|
63
|
-
# ]
|
64
|
-
# )
|
65
|
-
#
|
66
|
-
# # Invoke the agent (which routes to the appropriate sub-agents)
|
67
|
-
# result = app.invoke(
|
68
|
-
# initial_state,
|
69
|
-
# {"configurable": {"config_id": thread_id, "thread_id": thread_id}},
|
70
|
-
# )
|
71
|
-
#
|
72
|
-
# # Assert that the result contains messages and that the final message is valid.
|
73
|
-
# assert "messages" in result, "Expected 'messages' in the response"
|
74
|
-
# last_message = result["messages"][-1]
|
75
|
-
# assert isinstance(
|
76
|
-
# last_message, (HumanMessage, AIMessage, str)
|
77
|
-
# ), "Last message should be a valid response type"
|
78
|
-
#
|
79
|
-
# # Concatenate message texts (if available) to perform keyword checks.
|
80
|
-
# output_text = " ".join(
|
81
|
-
# msg.content if hasattr(msg, "content") else str(msg)
|
82
|
-
# for msg in result["messages"]
|
83
|
-
# ).lower()
|
84
|
-
#
|
85
|
-
# # Check for keywords that suggest each sub-agent was invoked.
|
86
|
-
# for keyword in ["semantic scholar", "zotero", "pdf", "arxiv"]:
|
87
|
-
# assert (
|
88
|
-
# keyword in output_text
|
89
|
-
# ), f"Expected keyword '{keyword}' in the output response"
|
@@ -1,74 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Routing logic for zotero_agent through the main_agent
|
3
|
-
"""
|
4
|
-
|
5
|
-
import pytest
|
6
|
-
from langgraph.types import Command
|
7
|
-
from langgraph.graph import END
|
8
|
-
from langchain_core.messages import HumanMessage
|
9
|
-
from aiagents4pharma.talk2scholars.state.state_talk2scholars import Talk2Scholars
|
10
|
-
|
11
|
-
# pylint: disable=redefined-outer-name
|
12
|
-
|
13
|
-
|
14
|
-
@pytest.fixture
|
15
|
-
def mock_state():
|
16
|
-
"""Creates a mock state to simulate an ongoing conversation."""
|
17
|
-
return Talk2Scholars(messages=[])
|
18
|
-
|
19
|
-
|
20
|
-
@pytest.fixture
|
21
|
-
def mock_router():
|
22
|
-
"""Creates a mock supervisor router that routes based on keyword matching."""
|
23
|
-
|
24
|
-
def mock_supervisor_node(state):
|
25
|
-
"""Mock supervisor node that routes based on keyword matching."""
|
26
|
-
query = state["messages"][-1].content.lower()
|
27
|
-
# Define keywords for each sub-agent.
|
28
|
-
s2_keywords = [
|
29
|
-
"paper",
|
30
|
-
"research",
|
31
|
-
"citations",
|
32
|
-
"journal",
|
33
|
-
"articles",
|
34
|
-
"references",
|
35
|
-
]
|
36
|
-
zotero_keywords = ["zotero", "library", "saved papers", "academic library"]
|
37
|
-
pdf_keywords = ["pdf", "document", "read pdf"]
|
38
|
-
paper_download_keywords = ["download", "arxiv", "fetch paper", "paper download"]
|
39
|
-
|
40
|
-
# Priority ordering: Zotero, then paper download, then PDF, then S2.
|
41
|
-
if any(keyword in query for keyword in zotero_keywords):
|
42
|
-
return Command(goto="zotero_agent")
|
43
|
-
if any(keyword in query for keyword in paper_download_keywords):
|
44
|
-
return Command(goto="paper_download_agent")
|
45
|
-
if any(keyword in query for keyword in pdf_keywords):
|
46
|
-
return Command(goto="pdf_agent")
|
47
|
-
if any(keyword in query for keyword in s2_keywords):
|
48
|
-
return Command(goto="s2_agent")
|
49
|
-
# Default to end if no keyword matches.
|
50
|
-
return Command(goto=END)
|
51
|
-
|
52
|
-
return mock_supervisor_node
|
53
|
-
|
54
|
-
|
55
|
-
@pytest.mark.parametrize(
|
56
|
-
"user_query,expected_agent",
|
57
|
-
[
|
58
|
-
("Find papers on deep learning.", "s2_agent"),
|
59
|
-
("Show me my saved references in Zotero.", "zotero_agent"),
|
60
|
-
("I need some research articles.", "s2_agent"),
|
61
|
-
("Fetch my academic library.", "zotero_agent"),
|
62
|
-
("Retrieve citations.", "s2_agent"),
|
63
|
-
("Can you get journal articles?", "s2_agent"),
|
64
|
-
("I want to read the PDF document.", "pdf_agent"),
|
65
|
-
("Download the paper from arxiv.", "paper_download_agent"),
|
66
|
-
("Completely unrelated query.", "__end__"),
|
67
|
-
],
|
68
|
-
)
|
69
|
-
def test_routing_logic(mock_state, mock_router, user_query, expected_agent):
|
70
|
-
"""Tests that the routing logic correctly assigns the right agent or ends conversation."""
|
71
|
-
mock_state["messages"].append(HumanMessage(content=user_query))
|
72
|
-
result = mock_router(mock_state)
|
73
|
-
|
74
|
-
assert result.goto == expected_agent, f"Failed for query: {user_query}"
|
@@ -1,95 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Unit tests for S2 tools functionality.
|
3
|
-
"""
|
4
|
-
|
5
|
-
# pylint: disable=redefined-outer-name
|
6
|
-
from unittest.mock import patch
|
7
|
-
from unittest.mock import MagicMock
|
8
|
-
import pytest
|
9
|
-
from ..tools.s2.query_dataframe import query_dataframe, NoPapersFoundError
|
10
|
-
|
11
|
-
|
12
|
-
@pytest.fixture
|
13
|
-
def initial_state():
|
14
|
-
"""Provides an empty initial state for tests."""
|
15
|
-
return {"papers": {}, "multi_papers": {}}
|
16
|
-
|
17
|
-
|
18
|
-
# Fixed test data for deterministic results
|
19
|
-
MOCK_SEARCH_RESPONSE = {
|
20
|
-
"data": [
|
21
|
-
{
|
22
|
-
"paperId": "123",
|
23
|
-
"title": "Machine Learning Basics",
|
24
|
-
"abstract": "An introduction to ML",
|
25
|
-
"year": 2023,
|
26
|
-
"citationCount": 100,
|
27
|
-
"url": "https://example.com/paper1",
|
28
|
-
"authors": [{"name": "Test Author"}],
|
29
|
-
}
|
30
|
-
]
|
31
|
-
}
|
32
|
-
|
33
|
-
MOCK_STATE_PAPER = {
|
34
|
-
"123": {
|
35
|
-
"Title": "Machine Learning Basics",
|
36
|
-
"Abstract": "An introduction to ML",
|
37
|
-
"Year": 2023,
|
38
|
-
"Citation Count": 100,
|
39
|
-
"URL": "https://example.com/paper1",
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
|
44
|
-
class TestS2Tools:
|
45
|
-
"""Unit tests for individual S2 tools"""
|
46
|
-
|
47
|
-
def test_query_dataframe_empty_state(self, initial_state):
|
48
|
-
"""Tests query_dataframe tool behavior when no papers are found."""
|
49
|
-
with pytest.raises(
|
50
|
-
NoPapersFoundError,
|
51
|
-
match="No papers found. A search needs to be performed first.",
|
52
|
-
):
|
53
|
-
query_dataframe.invoke(
|
54
|
-
{"question": "List all papers", "state": initial_state}
|
55
|
-
)
|
56
|
-
|
57
|
-
@patch(
|
58
|
-
"aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent"
|
59
|
-
)
|
60
|
-
def test_query_dataframe_with_papers(self, mock_create_agent, initial_state):
|
61
|
-
"""Tests querying papers when data is available."""
|
62
|
-
state = initial_state.copy()
|
63
|
-
state["last_displayed_papers"] = "papers"
|
64
|
-
state["papers"] = MOCK_STATE_PAPER
|
65
|
-
|
66
|
-
# Mock the dataframe agent instead of the LLM
|
67
|
-
mock_agent = MagicMock()
|
68
|
-
mock_agent.invoke.return_value = {"output": "Mocked response"}
|
69
|
-
|
70
|
-
mock_create_agent.return_value = (
|
71
|
-
mock_agent # Mock the function returning the agent
|
72
|
-
)
|
73
|
-
|
74
|
-
# Ensure that the output of query_dataframe is correctly structured
|
75
|
-
result = query_dataframe.invoke({"question": "List all papers", "state": state})
|
76
|
-
|
77
|
-
assert isinstance(result, str) # Ensure output is a string
|
78
|
-
assert result == "Mocked response" # Validate the expected response
|
79
|
-
|
80
|
-
@patch(
|
81
|
-
"aiagents4pharma.talk2scholars.tools.s2.query_dataframe.create_pandas_dataframe_agent"
|
82
|
-
)
|
83
|
-
def test_query_dataframe_direct_mapping(self, mock_create_agent, initial_state):
|
84
|
-
"""Tests query_dataframe when last_displayed_papers is a direct dict mapping."""
|
85
|
-
# Prepare state with direct mapping
|
86
|
-
state = initial_state.copy()
|
87
|
-
state["last_displayed_papers"] = MOCK_STATE_PAPER
|
88
|
-
# Mock the dataframe agent
|
89
|
-
mock_agent = MagicMock()
|
90
|
-
mock_agent.invoke.return_value = {"output": "Direct mapping response"}
|
91
|
-
mock_create_agent.return_value = mock_agent
|
92
|
-
# Invoke tool
|
93
|
-
result = query_dataframe.invoke({"question": "Filter papers", "state": state})
|
94
|
-
assert isinstance(result, str)
|
95
|
-
assert result == "Direct mapping response"
|
File without changes
|
File without changes
|
File without changes
|