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.
Files changed (48) hide show
  1. aiagents4pharma/talk2scholars/agents/main_agent.py +7 -7
  2. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +88 -12
  3. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/paper_download_agent/default.yaml +5 -0
  4. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +5 -0
  5. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +1 -20
  6. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +1 -26
  7. aiagents4pharma/talk2scholars/configs/tools/download_arxiv_paper/default.yaml +4 -0
  8. aiagents4pharma/talk2scholars/configs/tools/download_biorxiv_paper/default.yaml +2 -0
  9. aiagents4pharma/talk2scholars/configs/tools/download_medrxiv_paper/default.yaml +2 -0
  10. aiagents4pharma/talk2scholars/configs/tools/question_and_answer/default.yaml +22 -0
  11. aiagents4pharma/talk2scholars/tests/test_main_agent.py +20 -2
  12. aiagents4pharma/talk2scholars/tests/test_nvidia_nim_reranker_utils.py +28 -0
  13. aiagents4pharma/talk2scholars/tests/test_paper_download_tools.py +107 -29
  14. aiagents4pharma/talk2scholars/tests/test_pdf_agent.py +2 -3
  15. aiagents4pharma/talk2scholars/tests/test_question_and_answer_tool.py +194 -543
  16. aiagents4pharma/talk2scholars/tests/test_s2_agent.py +2 -2
  17. aiagents4pharma/talk2scholars/tests/{test_s2_display.py → test_s2_display_dataframe.py} +2 -3
  18. aiagents4pharma/talk2scholars/tests/test_s2_query_dataframe.py +201 -0
  19. aiagents4pharma/talk2scholars/tests/test_s2_retrieve.py +7 -6
  20. aiagents4pharma/talk2scholars/tests/test_s2_utils_ext_ids.py +413 -0
  21. aiagents4pharma/talk2scholars/tests/test_tool_helper_utils.py +140 -0
  22. aiagents4pharma/talk2scholars/tests/test_zotero_agent.py +0 -1
  23. aiagents4pharma/talk2scholars/tests/test_zotero_read.py +16 -18
  24. aiagents4pharma/talk2scholars/tools/paper_download/download_arxiv_input.py +92 -37
  25. aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +73 -575
  26. aiagents4pharma/talk2scholars/tools/pdf/utils/__init__.py +10 -0
  27. aiagents4pharma/talk2scholars/tools/pdf/utils/generate_answer.py +97 -0
  28. aiagents4pharma/talk2scholars/tools/pdf/utils/nvidia_nim_reranker.py +77 -0
  29. aiagents4pharma/talk2scholars/tools/pdf/utils/retrieve_chunks.py +83 -0
  30. aiagents4pharma/talk2scholars/tools/pdf/utils/tool_helper.py +125 -0
  31. aiagents4pharma/talk2scholars/tools/pdf/utils/vector_store.py +162 -0
  32. aiagents4pharma/talk2scholars/tools/s2/display_dataframe.py +33 -10
  33. aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +39 -16
  34. aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +124 -10
  35. aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +49 -17
  36. aiagents4pharma/talk2scholars/tools/s2/search.py +39 -16
  37. aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +34 -16
  38. aiagents4pharma/talk2scholars/tools/s2/utils/multi_helper.py +49 -16
  39. aiagents4pharma/talk2scholars/tools/s2/utils/search_helper.py +51 -16
  40. aiagents4pharma/talk2scholars/tools/s2/utils/single_helper.py +50 -17
  41. {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/METADATA +58 -105
  42. {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/RECORD +45 -32
  43. aiagents4pharma/talk2scholars/tests/test_llm_main_integration.py +0 -89
  44. aiagents4pharma/talk2scholars/tests/test_routing_logic.py +0 -74
  45. aiagents4pharma/talk2scholars/tests/test_s2_query.py +0 -95
  46. {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/WHEEL +0 -0
  47. {aiagents4pharma-1.39.0.dist-info → aiagents4pharma-1.39.2.dist-info}/licenses/LICENSE +0 -0
  48. {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"