aiagents4pharma 1.27.2__py3-none-any.whl → 1.29.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.
- aiagents4pharma/talk2scholars/agents/__init__.py +1 -0
- aiagents4pharma/talk2scholars/agents/main_agent.py +35 -209
- aiagents4pharma/talk2scholars/agents/pdf_agent.py +106 -0
- aiagents4pharma/talk2scholars/agents/s2_agent.py +10 -6
- aiagents4pharma/talk2scholars/agents/zotero_agent.py +12 -6
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py +1 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +2 -48
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +5 -28
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +5 -21
- aiagents4pharma/talk2scholars/configs/config.yaml +3 -0
- aiagents4pharma/talk2scholars/configs/tools/__init__.py +2 -0
- aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/default.yaml +1 -1
- aiagents4pharma/talk2scholars/configs/tools/question_and_answer/__init__.py +3 -0
- aiagents4pharma/talk2scholars/configs/tools/search/default.yaml +1 -1
- aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/default.yaml +1 -1
- aiagents4pharma/talk2scholars/configs/tools/zotero_read/default.yaml +42 -1
- aiagents4pharma/talk2scholars/configs/tools/zotero_write/__inti__.py +3 -0
- aiagents4pharma/talk2scholars/state/state_talk2scholars.py +1 -0
- aiagents4pharma/talk2scholars/tests/test_main_agent.py +186 -111
- aiagents4pharma/talk2scholars/tests/test_pdf_agent.py +126 -0
- aiagents4pharma/talk2scholars/tests/test_question_and_answer_tool.py +186 -0
- aiagents4pharma/talk2scholars/tests/test_s2_display.py +74 -0
- aiagents4pharma/talk2scholars/tests/test_s2_multi.py +282 -0
- aiagents4pharma/talk2scholars/tests/test_s2_query.py +78 -0
- aiagents4pharma/talk2scholars/tests/test_s2_retrieve.py +65 -0
- aiagents4pharma/talk2scholars/tests/test_s2_search.py +266 -0
- aiagents4pharma/talk2scholars/tests/test_s2_single.py +274 -0
- aiagents4pharma/talk2scholars/tests/test_zotero_path.py +57 -0
- aiagents4pharma/talk2scholars/tests/test_zotero_read.py +412 -0
- aiagents4pharma/talk2scholars/tests/test_zotero_write.py +626 -0
- aiagents4pharma/talk2scholars/tools/__init__.py +1 -0
- aiagents4pharma/talk2scholars/tools/pdf/__init__.py +5 -0
- aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py +170 -0
- aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py +50 -34
- aiagents4pharma/talk2scholars/tools/s2/query_results.py +1 -1
- aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +8 -8
- aiagents4pharma/talk2scholars/tools/s2/search.py +36 -23
- aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py +44 -38
- aiagents4pharma/talk2scholars/tools/zotero/__init__.py +2 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py +5 -0
- aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py +63 -0
- aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py +64 -19
- aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py +247 -0
- {aiagents4pharma-1.27.2.dist-info → aiagents4pharma-1.29.0.dist-info}/METADATA +6 -5
- {aiagents4pharma-1.27.2.dist-info → aiagents4pharma-1.29.0.dist-info}/RECORD +49 -33
- aiagents4pharma/talk2scholars/tests/test_call_s2.py +0 -100
- aiagents4pharma/talk2scholars/tests/test_call_zotero.py +0 -94
- aiagents4pharma/talk2scholars/tests/test_s2_tools.py +0 -355
- aiagents4pharma/talk2scholars/tests/test_zotero_tool.py +0 -171
- {aiagents4pharma-1.27.2.dist-info → aiagents4pharma-1.29.0.dist-info}/LICENSE +0 -0
- {aiagents4pharma-1.27.2.dist-info → aiagents4pharma-1.29.0.dist-info}/WHEEL +0 -0
- {aiagents4pharma-1.27.2.dist-info → aiagents4pharma-1.29.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Utility functions for Zotero tools.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
|
9
|
+
# Configure logging
|
10
|
+
logging.basicConfig(level=logging.INFO)
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
def get_item_collections(zot):
|
15
|
+
"""
|
16
|
+
Fetch all Zotero collections and map item keys to their full collection paths.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
zot (Zotero): An initialized Zotero client.
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
dict: A dictionary mapping item keys to a list of full collection paths.
|
23
|
+
"""
|
24
|
+
logger.info("Fetching Zotero collections...")
|
25
|
+
|
26
|
+
# Fetch all collections
|
27
|
+
collections = zot.collections()
|
28
|
+
|
29
|
+
# Create mappings: collection key → name and collection key → parent key
|
30
|
+
collection_map = {col["key"]: col["data"]["name"] for col in collections}
|
31
|
+
parent_map = {
|
32
|
+
col["key"]: col["data"].get("parentCollection") for col in collections
|
33
|
+
}
|
34
|
+
|
35
|
+
# Build full paths for collections
|
36
|
+
def build_collection_path(col_key):
|
37
|
+
path = []
|
38
|
+
while col_key:
|
39
|
+
path.insert(0, collection_map.get(col_key, "Unknown"))
|
40
|
+
col_key = parent_map.get(col_key)
|
41
|
+
return "/" + "/".join(path) # Convert to "/path/to/collection"
|
42
|
+
|
43
|
+
collection_paths = {key: build_collection_path(key) for key in collection_map}
|
44
|
+
|
45
|
+
# Manually create an item-to-collection mapping with full paths
|
46
|
+
item_to_collections = {}
|
47
|
+
|
48
|
+
for collection in collections:
|
49
|
+
collection_key = collection["key"]
|
50
|
+
collection_items = zot.collection_items(
|
51
|
+
collection_key
|
52
|
+
) # Fetch items in the collection
|
53
|
+
|
54
|
+
for item in collection_items:
|
55
|
+
item_key = item["data"]["key"]
|
56
|
+
if item_key in item_to_collections:
|
57
|
+
item_to_collections[item_key].append(collection_paths[collection_key])
|
58
|
+
else:
|
59
|
+
item_to_collections[item_key] = [collection_paths[collection_key]]
|
60
|
+
|
61
|
+
logger.info("Successfully mapped items to collection paths.")
|
62
|
+
|
63
|
+
return item_to_collections
|
@@ -13,6 +13,11 @@ from langchain_core.tools import tool
|
|
13
13
|
from langchain_core.tools.base import InjectedToolCallId
|
14
14
|
from langgraph.types import Command
|
15
15
|
from pydantic import BaseModel, Field
|
16
|
+
from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path import (
|
17
|
+
get_item_collections,
|
18
|
+
)
|
19
|
+
|
20
|
+
# pylint: disable=R0914,R0912,R0915
|
16
21
|
|
17
22
|
# Configure logging
|
18
23
|
logging.basicConfig(level=logging.INFO)
|
@@ -27,7 +32,7 @@ class ZoteroSearchInput(BaseModel):
|
|
27
32
|
)
|
28
33
|
only_articles: bool = Field(
|
29
34
|
default=True,
|
30
|
-
description="Whether to only search for journal articles/
|
35
|
+
description="Whether to only search for journal articles/conference papers.",
|
31
36
|
)
|
32
37
|
limit: int = Field(
|
33
38
|
default=2, description="Maximum number of results to return", ge=1, le=100
|
@@ -35,12 +40,6 @@ class ZoteroSearchInput(BaseModel):
|
|
35
40
|
tool_call_id: Annotated[str, InjectedToolCallId]
|
36
41
|
|
37
42
|
|
38
|
-
# Load hydra configuration
|
39
|
-
with hydra.initialize(version_base=None, config_path="../../configs"):
|
40
|
-
cfg = hydra.compose(config_name="config", overrides=["tools/zotero_read=default"])
|
41
|
-
cfg = cfg.tools.zotero_read
|
42
|
-
|
43
|
-
|
44
43
|
@tool(args_schema=ZoteroSearchInput, parse_docstring=True)
|
45
44
|
def zotero_search_tool(
|
46
45
|
query: str,
|
@@ -59,22 +58,55 @@ def zotero_search_tool(
|
|
59
58
|
Returns:
|
60
59
|
Dict[str, Any]: The search results and related information.
|
61
60
|
"""
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
# Load hydra configuration
|
62
|
+
with hydra.initialize(version_base=None, config_path="../../configs"):
|
63
|
+
cfg = hydra.compose(
|
64
|
+
config_name="config", overrides=["tools/zotero_read=default"]
|
65
|
+
)
|
66
|
+
logger.info("Loaded configuration for Zotero search tool")
|
67
|
+
cfg = cfg.tools.zotero_read
|
68
|
+
logger.info(
|
69
|
+
"Searching Zotero for query: '%s' (only_articles: %s, limit: %d)",
|
70
|
+
query,
|
71
|
+
only_articles,
|
72
|
+
limit,
|
73
|
+
)
|
68
74
|
|
69
75
|
# Initialize Zotero client
|
70
76
|
zot = zotero.Zotero(cfg.user_id, cfg.library_type, cfg.api_key)
|
71
77
|
|
72
|
-
#
|
73
|
-
|
78
|
+
# Fetch collection mapping once
|
79
|
+
item_to_collections = get_item_collections(zot)
|
80
|
+
|
81
|
+
# If the query is empty, fetch all items (up to max_limit), otherwise use the query
|
82
|
+
try:
|
83
|
+
if query.strip() == "":
|
84
|
+
logger.info(
|
85
|
+
"Empty query provided, fetching all items up to max_limit: %d",
|
86
|
+
cfg.zotero.max_limit,
|
87
|
+
)
|
88
|
+
items = zot.items(limit=cfg.zotero.max_limit)
|
89
|
+
else:
|
90
|
+
items = zot.items(q=query, limit=min(limit, cfg.zotero.max_limit))
|
91
|
+
except Exception as e:
|
92
|
+
logger.error("Failed to fetch items from Zotero: %s", e)
|
93
|
+
raise RuntimeError(
|
94
|
+
"Failed to fetch items from Zotero. Please retry the same query."
|
95
|
+
) from e
|
96
|
+
|
74
97
|
logger.info("Received %d items from Zotero", len(items))
|
75
98
|
|
99
|
+
if not items:
|
100
|
+
logger.error("No items returned from Zotero for query: '%s'", query)
|
101
|
+
raise RuntimeError(
|
102
|
+
"No items returned from Zotero. Please retry the same query."
|
103
|
+
)
|
104
|
+
|
76
105
|
# Define filter criteria
|
77
106
|
filter_item_types = cfg.zotero.filter_item_types if only_articles else []
|
107
|
+
filter_excluded_types = (
|
108
|
+
cfg.zotero.filter_excluded_types
|
109
|
+
) # Exclude non-research items
|
78
110
|
|
79
111
|
# Filter and format papers
|
80
112
|
filtered_papers = {}
|
@@ -88,10 +120,16 @@ def zotero_search_tool(
|
|
88
120
|
continue
|
89
121
|
|
90
122
|
item_type = data.get("itemType")
|
91
|
-
|
123
|
+
logger.debug("Item type: %s", item_type)
|
124
|
+
|
125
|
+
# Exclude attachments, notes, and other unwanted types
|
126
|
+
if (
|
92
127
|
not item_type
|
93
128
|
or not isinstance(item_type, str)
|
94
|
-
or item_type
|
129
|
+
or item_type in filter_excluded_types # Skip attachments & notes
|
130
|
+
or (
|
131
|
+
only_articles and item_type not in filter_item_types
|
132
|
+
) # Skip non-research types
|
95
133
|
):
|
96
134
|
continue
|
97
135
|
|
@@ -99,20 +137,27 @@ def zotero_search_tool(
|
|
99
137
|
if not key:
|
100
138
|
continue
|
101
139
|
|
140
|
+
# Use the imported utility function's mapping to get collection paths
|
141
|
+
collection_paths = item_to_collections.get(key, ["/Unknown"])
|
142
|
+
|
102
143
|
filtered_papers[key] = {
|
103
144
|
"Title": data.get("title", "N/A"),
|
104
145
|
"Abstract": data.get("abstractNote", "N/A"),
|
105
146
|
"Date": data.get("date", "N/A"),
|
106
147
|
"URL": data.get("url", "N/A"),
|
107
148
|
"Type": item_type if isinstance(item_type, str) else "N/A",
|
149
|
+
"Collections": collection_paths, # Now displays full paths
|
108
150
|
}
|
109
151
|
|
110
152
|
if not filtered_papers:
|
111
|
-
logger.
|
153
|
+
logger.error("No matching papers returned from Zotero for query: '%s'", query)
|
154
|
+
raise RuntimeError(
|
155
|
+
"No matching papers returned from Zotero. Please retry the same query."
|
156
|
+
)
|
112
157
|
|
113
158
|
logger.info("Filtered %d items", len(filtered_papers))
|
114
159
|
|
115
|
-
# Prepare content with top
|
160
|
+
# Prepare content with top 2 paper titles and types
|
116
161
|
top_papers = list(filtered_papers.values())[:2]
|
117
162
|
top_papers_info = "\n".join(
|
118
163
|
[
|
@@ -0,0 +1,247 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
This tool is used to save fetched papers to Zotero library.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from typing import Annotated, Any
|
9
|
+
import hydra
|
10
|
+
from pyzotero import zotero
|
11
|
+
from langchain_core.messages import ToolMessage
|
12
|
+
from langchain_core.tools import tool
|
13
|
+
from langchain_core.tools.base import InjectedToolCallId
|
14
|
+
from langgraph.types import Command
|
15
|
+
from langgraph.prebuilt import InjectedState
|
16
|
+
from pydantic import BaseModel, Field
|
17
|
+
from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path import (
|
18
|
+
get_item_collections,
|
19
|
+
)
|
20
|
+
|
21
|
+
# pylint: disable=R0914,R0912,R0915
|
22
|
+
|
23
|
+
# Configure logging
|
24
|
+
logging.basicConfig(level=logging.INFO)
|
25
|
+
logger = logging.getLogger(__name__)
|
26
|
+
|
27
|
+
|
28
|
+
class ZoteroSaveInput(BaseModel):
|
29
|
+
"""Input schema for the Zotero save tool."""
|
30
|
+
|
31
|
+
tool_call_id: Annotated[str, InjectedToolCallId]
|
32
|
+
collection_path: str = Field(
|
33
|
+
default=None,
|
34
|
+
description=(
|
35
|
+
"The path where the paper should be saved in the Zotero library."
|
36
|
+
"Example: '/machine/cern/mobile'"
|
37
|
+
),
|
38
|
+
)
|
39
|
+
state: Annotated[dict, InjectedState]
|
40
|
+
|
41
|
+
|
42
|
+
@tool(args_schema=ZoteroSaveInput, parse_docstring=True)
|
43
|
+
def zotero_save_tool(
|
44
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
45
|
+
collection_path: str,
|
46
|
+
state: Annotated[dict, InjectedState],
|
47
|
+
) -> Command[Any]:
|
48
|
+
"""
|
49
|
+
Use this tool to save previously fetched papers from Semantic Scholar
|
50
|
+
to a specified Zotero collection.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
tool_call_id (Annotated[str, InjectedToolCallId]): The tool call ID.
|
54
|
+
collection_path (str): The Zotero collection path where papers should be saved.
|
55
|
+
state (Annotated[dict, InjectedState]): The state containing previously fetched papers.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
Command[Any]: The save results and related information.
|
59
|
+
"""
|
60
|
+
# Load hydra configuration
|
61
|
+
with hydra.initialize(version_base=None, config_path="../../configs"):
|
62
|
+
cfg = hydra.compose(
|
63
|
+
config_name="config", overrides=["tools/zotero_write=default"]
|
64
|
+
)
|
65
|
+
cfg = cfg.tools.zotero_write
|
66
|
+
logger.info("Loaded configuration for Zotero write tool")
|
67
|
+
logger.info(
|
68
|
+
"Saving fetched papers to Zotero under collection path: %s", collection_path
|
69
|
+
)
|
70
|
+
|
71
|
+
# Initialize Zotero client
|
72
|
+
zot = zotero.Zotero(cfg.user_id, cfg.library_type, cfg.api_key)
|
73
|
+
|
74
|
+
# Retrieve last displayed papers from the agent state
|
75
|
+
last_displayed_key = state.get("last_displayed_papers", {})
|
76
|
+
if isinstance(last_displayed_key, str):
|
77
|
+
# If it's a string (key to another state object), get that object
|
78
|
+
fetched_papers = state.get(last_displayed_key, {})
|
79
|
+
logger.info("Using papers from '%s' state key", last_displayed_key)
|
80
|
+
else:
|
81
|
+
# If it's already the papers object
|
82
|
+
fetched_papers = last_displayed_key
|
83
|
+
logger.info("Using papers directly from last_displayed_papers")
|
84
|
+
|
85
|
+
if not fetched_papers:
|
86
|
+
logger.warning("No fetched papers found to save.")
|
87
|
+
raise RuntimeError(
|
88
|
+
"No fetched papers were found to save. Please retry the same query."
|
89
|
+
)
|
90
|
+
|
91
|
+
# First, check if zotero_read exists in state and has collection data
|
92
|
+
zotero_read_data = state.get("zotero_read", {})
|
93
|
+
logger.info("Retrieved zotero_read from state: %d items", len(zotero_read_data))
|
94
|
+
|
95
|
+
# If zotero_read is empty, use get_item_collections as fallback
|
96
|
+
if not zotero_read_data:
|
97
|
+
logger.info(
|
98
|
+
"zotero_read is empty, fetching paths dynamically using get_item_collections"
|
99
|
+
)
|
100
|
+
try:
|
101
|
+
zotero_read_data = get_item_collections(zot)
|
102
|
+
logger.info(
|
103
|
+
"Successfully generated %d path mappings", len(zotero_read_data)
|
104
|
+
)
|
105
|
+
except Exception as e:
|
106
|
+
logger.error("Error generating path mappings: %s", str(e))
|
107
|
+
raise RuntimeError(
|
108
|
+
"Failed to generate collection path mappings. Please retry the same query."
|
109
|
+
) from e
|
110
|
+
|
111
|
+
# Get all collections to find the correct one
|
112
|
+
collections = zot.collections()
|
113
|
+
logger.info("Found %d collections", len(collections))
|
114
|
+
|
115
|
+
# Normalize the requested collection path (remove trailing slash, lowercase for comparison)
|
116
|
+
normalized_path = collection_path.rstrip("/").lower()
|
117
|
+
|
118
|
+
# Find matching collection
|
119
|
+
matched_collection_key = None
|
120
|
+
|
121
|
+
# First, try to directly find the collection key in zotero_read data
|
122
|
+
for key, paths in zotero_read_data.items():
|
123
|
+
if isinstance(paths, list):
|
124
|
+
for path in paths:
|
125
|
+
if path.lower() == normalized_path:
|
126
|
+
matched_collection_key = key
|
127
|
+
logger.info(
|
128
|
+
"Found direct match in zotero_read: %s -> %s", path, key
|
129
|
+
)
|
130
|
+
break
|
131
|
+
elif isinstance(paths, str) and paths.lower() == normalized_path:
|
132
|
+
matched_collection_key = key
|
133
|
+
logger.info("Found direct match in zotero_read: %s -> %s", paths, key)
|
134
|
+
break
|
135
|
+
|
136
|
+
# If not found in zotero_read, try matching by collection name
|
137
|
+
if not matched_collection_key:
|
138
|
+
for col in collections:
|
139
|
+
col_name = col["data"]["name"]
|
140
|
+
if f"/{col_name}".lower() == normalized_path:
|
141
|
+
matched_collection_key = col["key"]
|
142
|
+
logger.info(
|
143
|
+
"Found direct match by collection name: %s (key: %s)",
|
144
|
+
col_name,
|
145
|
+
col["key"],
|
146
|
+
)
|
147
|
+
break
|
148
|
+
|
149
|
+
# If still not found, try part-matching
|
150
|
+
if not matched_collection_key:
|
151
|
+
name_to_key = {col["data"]["name"].lower(): col["key"] for col in collections}
|
152
|
+
collection_name = normalized_path.lstrip("/")
|
153
|
+
if collection_name in name_to_key:
|
154
|
+
matched_collection_key = name_to_key[collection_name]
|
155
|
+
logger.info(
|
156
|
+
"Found match by collection name: %s -> %s",
|
157
|
+
collection_name,
|
158
|
+
matched_collection_key,
|
159
|
+
)
|
160
|
+
else:
|
161
|
+
path_parts = normalized_path.strip("/").split("/")
|
162
|
+
for part in path_parts:
|
163
|
+
if part in name_to_key:
|
164
|
+
matched_collection_key = name_to_key[part]
|
165
|
+
logger.info(
|
166
|
+
"Found match by path component: %s -> %s",
|
167
|
+
part,
|
168
|
+
matched_collection_key,
|
169
|
+
)
|
170
|
+
break
|
171
|
+
|
172
|
+
# Do not fall back to a default collection: raise error if no match found
|
173
|
+
if not matched_collection_key:
|
174
|
+
logger.error(
|
175
|
+
"Invalid collection path: %s. No matching collection found in Zotero.",
|
176
|
+
collection_path,
|
177
|
+
)
|
178
|
+
|
179
|
+
available_paths = ", ".join(["/" + col["data"]["name"] for col in collections])
|
180
|
+
raise RuntimeError(
|
181
|
+
f"Error: The collection path '{collection_path}' does not exist in Zotero. "
|
182
|
+
f"Available collections are: {available_paths}"
|
183
|
+
)
|
184
|
+
|
185
|
+
# Format papers for Zotero and assign to the specified collection
|
186
|
+
zotero_items = []
|
187
|
+
for paper_id, paper in fetched_papers.items():
|
188
|
+
title = paper.get("Title", paper.get("title", "N/A"))
|
189
|
+
abstract = paper.get("Abstract", paper.get("abstractNote", "N/A"))
|
190
|
+
date = paper.get("Date", paper.get("date", "N/A"))
|
191
|
+
url = paper.get("URL", paper.get("url", paper.get("URL", "N/A")))
|
192
|
+
citations = paper.get("Citations", "N/A")
|
193
|
+
|
194
|
+
zotero_items.append(
|
195
|
+
{
|
196
|
+
"itemType": "journalArticle",
|
197
|
+
"title": title,
|
198
|
+
"abstractNote": abstract,
|
199
|
+
"date": date,
|
200
|
+
"url": url,
|
201
|
+
"extra": f"Paper ID: {paper_id}\nCitations: {citations}",
|
202
|
+
"collections": [matched_collection_key],
|
203
|
+
}
|
204
|
+
)
|
205
|
+
|
206
|
+
# Save items to Zotero
|
207
|
+
try:
|
208
|
+
response = zot.create_items(zotero_items)
|
209
|
+
logger.info("Papers successfully saved to Zotero: %s", response)
|
210
|
+
except Exception as e:
|
211
|
+
logger.error("Error saving to Zotero: %s", str(e))
|
212
|
+
raise RuntimeError(f"Error saving papers to Zotero: {str(e)}") from e
|
213
|
+
|
214
|
+
# Get the collection name for better feedback
|
215
|
+
collection_name = ""
|
216
|
+
for col in collections:
|
217
|
+
if col["key"] == matched_collection_key:
|
218
|
+
collection_name = col["data"]["name"]
|
219
|
+
break
|
220
|
+
|
221
|
+
content = (
|
222
|
+
f"Save was successful. Papers have been saved to Zotero collection '{collection_name}' "
|
223
|
+
f"with the requested path '{collection_path}'.\n"
|
224
|
+
)
|
225
|
+
content += "Summary of saved papers:\n"
|
226
|
+
content += f"Number of articles saved: {len(fetched_papers)}\n"
|
227
|
+
content += f"Query: {state.get('query', 'N/A')}\n"
|
228
|
+
top_papers = list(fetched_papers.values())[:2]
|
229
|
+
top_papers_info = "\n".join(
|
230
|
+
[
|
231
|
+
f"{i+1}. {paper.get('Title', 'N/A')} ({paper.get('URL', 'N/A')})"
|
232
|
+
for i, paper in enumerate(top_papers)
|
233
|
+
]
|
234
|
+
)
|
235
|
+
content += "Here are the top articles:\n" + top_papers_info
|
236
|
+
|
237
|
+
return Command(
|
238
|
+
update={
|
239
|
+
"messages": [
|
240
|
+
ToolMessage(
|
241
|
+
content=content,
|
242
|
+
tool_call_id=tool_call_id,
|
243
|
+
artifact=fetched_papers,
|
244
|
+
)
|
245
|
+
],
|
246
|
+
}
|
247
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: aiagents4pharma
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.29.0
|
4
4
|
Summary: AI Agents for drug discovery, drug development, and other pharmaceutical R&D.
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -91,6 +91,7 @@ Our toolkit currently consists of the following agents:
|
|
91
91
|

|
92
92
|
|
93
93
|
### Installation
|
94
|
+
|
94
95
|
_Please use version 1.26.2 or later for better support with NVIDIA NIM models._
|
95
96
|
|
96
97
|
#### Option 1: PyPI
|
@@ -135,10 +136,10 @@ cd AIAgents4Pharma/aiagents4pharma/talk2knowledgegraphs
|
|
135
136
|
LANGCHAIN_TRACING_V2=true
|
136
137
|
LANGCHAIN_API_KEY=your_langchain_api_key_here
|
137
138
|
# Notes:
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
139
|
+
# The API endpoint for Ollama is already set in env.example.
|
140
|
+
# Both API keys (OPENAI_API_KEY and NVIDIA_API_KEY) are required for Talk2AIAgents4Pharma.
|
141
|
+
# If using Talk2KnowledgeGraphs separately, only the OPENAI_API_KEY is needed.
|
142
|
+
# Langsmith API for tracing is optional for both, set it in env.example if required.
|
142
143
|
```
|
143
144
|
|
144
145
|
4. Save the file.
|
@@ -135,59 +135,75 @@ aiagents4pharma/talk2knowledgegraphs/utils/enrichments/pubchem_strings.py,sha256
|
|
135
135
|
aiagents4pharma/talk2knowledgegraphs/utils/extractions/__init__.py,sha256=7gwwtfzKhB8GuOBD47XRi0NprwEXkOzwNl5eeu-hDTI,86
|
136
136
|
aiagents4pharma/talk2knowledgegraphs/utils/extractions/pcst.py,sha256=m5p0yoJb7I19ua5yeQfXPf7c4r6S1XPwttsrM7Qoy94,9336
|
137
137
|
aiagents4pharma/talk2scholars/__init__.py,sha256=gphERyVKZHvOnMQsml7TIHlaIshHJ75R1J3FKExkfuY,120
|
138
|
-
aiagents4pharma/talk2scholars/agents/__init__.py,sha256=
|
139
|
-
aiagents4pharma/talk2scholars/agents/main_agent.py,sha256=
|
140
|
-
aiagents4pharma/talk2scholars/agents/
|
141
|
-
aiagents4pharma/talk2scholars/agents/
|
138
|
+
aiagents4pharma/talk2scholars/agents/__init__.py,sha256=ZwFiHOlDGJk1601J5xEZDy0btPzqiOk2UCocKxohde8,168
|
139
|
+
aiagents4pharma/talk2scholars/agents/main_agent.py,sha256=TABzGSOg7I0_fJ0qybBVqZDdrU8YCjyG_m-kasO4WgE,2854
|
140
|
+
aiagents4pharma/talk2scholars/agents/pdf_agent.py,sha256=c9-_z5qp5Zkgh6piEIlgI4uo4OMXD3janZNmfYwnFCg,3729
|
141
|
+
aiagents4pharma/talk2scholars/agents/s2_agent.py,sha256=ua1bjKE2HBKZuLnDn8me5fuV1lSvdZbwAlo3Yp27TT4,4659
|
142
|
+
aiagents4pharma/talk2scholars/agents/zotero_agent.py,sha256=5jfIJiLsRdlCJjkF7BQMkP5PsEY_Gr7SfztWKozbUGo,4223
|
142
143
|
aiagents4pharma/talk2scholars/configs/__init__.py,sha256=tf2gz8n7M4ko6xLdX_C925ELVIxoP6SgkPcbeh59ad4,151
|
143
|
-
aiagents4pharma/talk2scholars/configs/config.yaml,sha256=
|
144
|
+
aiagents4pharma/talk2scholars/configs/config.yaml,sha256=dQIMg3jLGYAudkc1Zz85qqvFf-HdVXPfewUfAfPNNzU,501
|
144
145
|
aiagents4pharma/talk2scholars/configs/agents/__init__.py,sha256=yyh7PB2oY_JulnpSQCWS4wwCH_uzIdt47O2Ay48x_oU,75
|
145
|
-
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py,sha256=
|
146
|
+
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/__init__.py,sha256=64GEWAoKOd_YHLi27eSOcOC5eSLK0IG_FNra3ZBt02Y,146
|
146
147
|
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
147
|
-
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml,sha256=
|
148
|
+
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml,sha256=rZfZ_dJArjlznHzusjxCnOjhptLTyejFiB0euV5R13c,662
|
149
|
+
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
148
150
|
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
149
|
-
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml,sha256=
|
151
|
+
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml,sha256=sn6vX6r-P0CR7UWS63ZqCmMKKn4As8pZoITRWx8sdoo,1151
|
150
152
|
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
151
|
-
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml,sha256=
|
153
|
+
aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml,sha256=lWBreotqsu1jlHi1uZ9vY60zi-MiiG2VuHxo5IoAvkE,1112
|
152
154
|
aiagents4pharma/talk2scholars/configs/app/__init__.py,sha256=JoSZV6N669kGMv5zLDszwf0ZjcRHx9TJfIqGhIIdPXE,70
|
153
155
|
aiagents4pharma/talk2scholars/configs/app/frontend/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
154
156
|
aiagents4pharma/talk2scholars/configs/app/frontend/default.yaml,sha256=wsELBdRLv6UqZ9QZfwpS7K4xfMj5s-a99-aXqIs6WEI,868
|
155
|
-
aiagents4pharma/talk2scholars/configs/tools/__init__.py,sha256=
|
157
|
+
aiagents4pharma/talk2scholars/configs/tools/__init__.py,sha256=GwpgnRrfjyZDVsangewSVTG3H3GBYM6s_YaQd9-zI10,238
|
156
158
|
aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
157
|
-
aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/default.yaml,sha256=
|
159
|
+
aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/default.yaml,sha256=QV7HrG7NdjBEjTMszh27MbGBYMbf_78V3sCGftdTtvo,442
|
160
|
+
aiagents4pharma/talk2scholars/configs/tools/question_and_answer/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
158
161
|
aiagents4pharma/talk2scholars/configs/tools/retrieve_semantic_scholar_paper_id/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
159
162
|
aiagents4pharma/talk2scholars/configs/tools/retrieve_semantic_scholar_paper_id/default.yaml,sha256=HG-N8yRjlX9zFwbIBvaDI9ndKjfL-gqPTCCPMLgdUpw,271
|
160
163
|
aiagents4pharma/talk2scholars/configs/tools/search/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
161
|
-
aiagents4pharma/talk2scholars/configs/tools/search/default.yaml,sha256=
|
164
|
+
aiagents4pharma/talk2scholars/configs/tools/search/default.yaml,sha256=153R4NmtG2bGKpxwo73tR15IetGKdrD4QgZRlz8zS18,504
|
162
165
|
aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
163
|
-
aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/default.yaml,sha256=
|
166
|
+
aiagents4pharma/talk2scholars/configs/tools/single_paper_recommendation/default.yaml,sha256=1QtFWqnGftIipftlALnG_IdCAOwzJTyOpUSUfWqQ7cA,596
|
164
167
|
aiagents4pharma/talk2scholars/configs/tools/zotero_read/__init__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
165
|
-
aiagents4pharma/talk2scholars/configs/tools/zotero_read/default.yaml,sha256=
|
168
|
+
aiagents4pharma/talk2scholars/configs/tools/zotero_read/default.yaml,sha256=6ZvZdCsnudPeVjnatv78Z0QfMwsHZuliE2RCIRCW05Y,1221
|
169
|
+
aiagents4pharma/talk2scholars/configs/tools/zotero_write/__inti__.py,sha256=fqQQ-GlRcbzru2KmEk3oMma0R6_SzGM8dOXzYeU4oVA,46
|
166
170
|
aiagents4pharma/talk2scholars/state/__init__.py,sha256=S6SxlszIMZSIMJehjevPF9sKyR-PAwWb5TEdo6xWXE8,103
|
167
|
-
aiagents4pharma/talk2scholars/state/state_talk2scholars.py,sha256=
|
171
|
+
aiagents4pharma/talk2scholars/state/state_talk2scholars.py,sha256=E0epqlBurzNcMzN4WV6nh--YkVAesbxQTuBBlJhESVA,2436
|
168
172
|
aiagents4pharma/talk2scholars/tests/__init__.py,sha256=U3PsTiUZaUBD1IZanFGkDIOdFieDVJtGKQ5-woYUo8c,45
|
169
|
-
aiagents4pharma/talk2scholars/tests/test_call_s2.py,sha256=ZL5HmnYNVyaBJgPGQi9JnbD1d1rtWnWusVxVRVW3aHc,3375
|
170
|
-
aiagents4pharma/talk2scholars/tests/test_call_zotero.py,sha256=N4g6Pt2vuaxIhHQbIqlMaDUF4O7vIvRqa7pPIkpL8FI,3314
|
171
173
|
aiagents4pharma/talk2scholars/tests/test_llm_main_integration.py,sha256=SAMG-Kb2S9sei8Us5vUWCUJikTKXPZVKQ6aJJPEhJsc,1880
|
172
|
-
aiagents4pharma/talk2scholars/tests/test_main_agent.py,sha256=
|
174
|
+
aiagents4pharma/talk2scholars/tests/test_main_agent.py,sha256=5QnOPKNrQCd5GdYU-vVF3bUrmitOsUcazZA7BsXeomo,5947
|
175
|
+
aiagents4pharma/talk2scholars/tests/test_pdf_agent.py,sha256=TN4Sq5-SCxv-9VfFyq7sOlBlxbekmnWuB7-qh4MrhkA,4656
|
176
|
+
aiagents4pharma/talk2scholars/tests/test_question_and_answer_tool.py,sha256=TpCDiGfsC2y6bOkm0ZTXjT1Vp8D-Po25wiEH5aDT_DA,6491
|
173
177
|
aiagents4pharma/talk2scholars/tests/test_routing_logic.py,sha256=AZrvaEBDk51KL6edrZY3GpQ_N6VbrlADqXFeg_jxDoQ,2284
|
174
178
|
aiagents4pharma/talk2scholars/tests/test_s2_agent.py,sha256=BhW1wGc-wUPS4fwNBQRtBXJaJ_i7L6t_G9Bq57fK7rI,7784
|
175
|
-
aiagents4pharma/talk2scholars/tests/
|
179
|
+
aiagents4pharma/talk2scholars/tests/test_s2_display.py,sha256=w1TqgEdl9WpW_A2Ud1slfI5fkRFkKtKadAlkEfSLOZk,2247
|
180
|
+
aiagents4pharma/talk2scholars/tests/test_s2_multi.py,sha256=fkTQ268WqOYvJEtTteVJ7eav3QuMAahhYR6LOnx1Huk,10161
|
181
|
+
aiagents4pharma/talk2scholars/tests/test_s2_query.py,sha256=hEcBt142nn_bKV9lor__Yk4LusgE1tN5dA-qpT606Bc,2443
|
182
|
+
aiagents4pharma/talk2scholars/tests/test_s2_retrieve.py,sha256=YtA2nbPRtoSR7mPqEjqLF5ERGVzTfeULztsNoCI48X8,2003
|
183
|
+
aiagents4pharma/talk2scholars/tests/test_s2_search.py,sha256=ZnfBO0b9xMwMvT1oaw1yIFxLToSej1_KSyEzHr6HbOQ,9068
|
184
|
+
aiagents4pharma/talk2scholars/tests/test_s2_single.py,sha256=J4r_J4gPeIIWAIUlahClINnCu7bEiW5AcphRNChv2Eo,9317
|
176
185
|
aiagents4pharma/talk2scholars/tests/test_state.py,sha256=_iHXvoZnU_eruf8l1sQKBSCIVnxNkH_9VzkVtZZA6bY,384
|
177
186
|
aiagents4pharma/talk2scholars/tests/test_zotero_agent.py,sha256=3TKz6yjNfYulaQv-MBv1zXCmR9xh9g3ju4Ge5HDdt1o,6136
|
178
|
-
aiagents4pharma/talk2scholars/tests/
|
179
|
-
aiagents4pharma/talk2scholars/
|
187
|
+
aiagents4pharma/talk2scholars/tests/test_zotero_path.py,sha256=XeXYqTlSkJgZ02tCz84VNDHGYnmrxrGFLxlLq_Bargs,2356
|
188
|
+
aiagents4pharma/talk2scholars/tests/test_zotero_read.py,sha256=vLAPAFeL8MjDju_HlsLnio-9HxzN1RqOApr9jyemYBk,14951
|
189
|
+
aiagents4pharma/talk2scholars/tests/test_zotero_write.py,sha256=76V7ezb6Xw-BEEwdJQvJs78JPGRYpAsijHIi3bTGsW8,23206
|
190
|
+
aiagents4pharma/talk2scholars/tools/__init__.py,sha256=UtGutYNNaRcr2nOmT_XqbTiaJpgVYKo3KVGVPFVrX2Y,107
|
191
|
+
aiagents4pharma/talk2scholars/tools/pdf/__init__.py,sha256=WOm-o-fFzyjFZBaHg658Gjzdiu1Kt-h9xvzvw0hR7aE,103
|
192
|
+
aiagents4pharma/talk2scholars/tools/pdf/question_and_answer.py,sha256=22JvT7F0rY11TF40pBfe9Cn2Y-6Tx73NfWDt4NJv700,6639
|
180
193
|
aiagents4pharma/talk2scholars/tools/s2/__init__.py,sha256=wytqCmGm8Fbl8y5qLdIkxhhG8VHLYMifCGjbH_LK2Fc,258
|
181
194
|
aiagents4pharma/talk2scholars/tools/s2/display_results.py,sha256=UR0PtEHGDpOhPH0Di5HT8-Fip2RkEMTJgzROsChb1gc,2959
|
182
|
-
aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py,sha256=
|
183
|
-
aiagents4pharma/talk2scholars/tools/s2/query_results.py,sha256=
|
184
|
-
aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py,sha256=
|
185
|
-
aiagents4pharma/talk2scholars/tools/s2/search.py,sha256=
|
186
|
-
aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py,sha256=
|
187
|
-
aiagents4pharma/talk2scholars/tools/zotero/__init__.py,sha256=
|
188
|
-
aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py,sha256=
|
189
|
-
aiagents4pharma
|
190
|
-
aiagents4pharma
|
191
|
-
aiagents4pharma
|
192
|
-
aiagents4pharma-1.
|
193
|
-
aiagents4pharma-1.
|
195
|
+
aiagents4pharma/talk2scholars/tools/s2/multi_paper_rec.py,sha256=Y-nIjtPSYvL7kLaN9_cueQM-VZF1SPZZ1_FB8KhS0XY,6352
|
196
|
+
aiagents4pharma/talk2scholars/tools/s2/query_results.py,sha256=S4yBNtg1loDu4ckLPrW4H8GAswriPaRU4U08cOuw2HE,2028
|
197
|
+
aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py,sha256=llzMMnEQKeYVamJbF4_DTMx-BgVe79vwDcUIFGLrmUY,2615
|
198
|
+
aiagents4pharma/talk2scholars/tools/s2/search.py,sha256=496sv4aAfqB65zgjNxU2AGnhclcRRNF0VuG4fguN3gw,5319
|
199
|
+
aiagents4pharma/talk2scholars/tools/s2/single_paper_rec.py,sha256=YJ1P-BAn5d2vGKIg3OTsYH1g8as5LeqaRuraL0buqJo,6095
|
200
|
+
aiagents4pharma/talk2scholars/tools/zotero/__init__.py,sha256=HF47ta_r94Y4gP3fK3WG_ix8kg1zUQw8yWjLJksnTfc,100
|
201
|
+
aiagents4pharma/talk2scholars/tools/zotero/zotero_read.py,sha256=eRqdQCyWws8q6iC_w4OIBR6w9Ha5x5UT5S8jifNxcqw,6142
|
202
|
+
aiagents4pharma/talk2scholars/tools/zotero/zotero_write.py,sha256=dqYc5HWMK3vz77psHYUosMLE63NYg9Nk6xbWy8TOrU4,9246
|
203
|
+
aiagents4pharma/talk2scholars/tools/zotero/utils/__init__.py,sha256=Ll8YQZj9sYJpXmoGxj_0ZcuEHDj06_CUqdDlTlevGL4,53
|
204
|
+
aiagents4pharma/talk2scholars/tools/zotero/utils/zotero_path.py,sha256=nHmYe3kcrygNOslHki4YeMztfnmRDPul4gZvXl_XsV0,1954
|
205
|
+
aiagents4pharma-1.29.0.dist-info/LICENSE,sha256=IcIbyB1Hyk5ZDah03VNQvJkbNk2hkBCDqQ8qtnCvB4Q,1077
|
206
|
+
aiagents4pharma-1.29.0.dist-info/METADATA,sha256=qYpzGvw6Raduy-RwlrnMNTElHqb4HP9n3LslreZaNl0,13245
|
207
|
+
aiagents4pharma-1.29.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
208
|
+
aiagents4pharma-1.29.0.dist-info/top_level.txt,sha256=-AH8rMmrSnJtq7HaAObS78UU-cTCwvX660dSxeM7a0A,16
|
209
|
+
aiagents4pharma-1.29.0.dist-info/RECORD,,
|