aiagents4pharma 1.28.0__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/main_agent.py +35 -209
- aiagents4pharma/talk2scholars/agents/s2_agent.py +10 -6
- aiagents4pharma/talk2scholars/agents/zotero_agent.py +12 -6
- aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +2 -48
- 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 +1 -0
- aiagents4pharma/talk2scholars/configs/tools/__init__.py +1 -0
- aiagents4pharma/talk2scholars/configs/tools/multi_paper_recommendation/default.yaml +1 -1
- 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/tests/test_main_agent.py +186 -111
- 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/s2/multi_paper_rec.py +50 -34
- 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.28.0.dist-info → aiagents4pharma-1.29.0.dist-info}/METADATA +6 -5
- {aiagents4pharma-1.28.0.dist-info → aiagents4pharma-1.29.0.dist-info}/RECORD +37 -28
- 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.28.0.dist-info → aiagents4pharma-1.29.0.dist-info}/LICENSE +0 -0
- {aiagents4pharma-1.28.0.dist-info → aiagents4pharma-1.29.0.dist-info}/WHEEL +0 -0
- {aiagents4pharma-1.28.0.dist-info → aiagents4pharma-1.29.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
"""
|
2
|
+
Unit tests for Zotero path utility in zotero_path.py.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import unittest
|
6
|
+
from unittest.mock import MagicMock
|
7
|
+
from aiagents4pharma.talk2scholars.tools.zotero.utils.zotero_path import (
|
8
|
+
get_item_collections,
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
class TestGetItemCollections(unittest.TestCase):
|
13
|
+
"""Unit tests for the get_item_collections function."""
|
14
|
+
|
15
|
+
def test_basic_collections_mapping(self):
|
16
|
+
"""test_basic_collections_mapping"""
|
17
|
+
# Define fake collections with one parent-child relationship and one independent collection.
|
18
|
+
fake_collections = [
|
19
|
+
{"key": "A", "data": {"name": "Parent", "parentCollection": None}},
|
20
|
+
{"key": "B", "data": {"name": "Child", "parentCollection": "A"}},
|
21
|
+
{"key": "C", "data": {"name": "Independent", "parentCollection": None}},
|
22
|
+
]
|
23
|
+
# Define fake collection items for each collection:
|
24
|
+
# - Collection A returns one item with key "item1"
|
25
|
+
# - Collection B returns one item with key "item2"
|
26
|
+
# - Collection C returns two items: one duplicate ("item1") and one new ("item3")
|
27
|
+
fake_collection_items = {
|
28
|
+
"A": [{"data": {"key": "item1"}}],
|
29
|
+
"B": [{"data": {"key": "item2"}}],
|
30
|
+
"C": [{"data": {"key": "item1"}}, {"data": {"key": "item3"}}],
|
31
|
+
}
|
32
|
+
fake_zot = MagicMock()
|
33
|
+
fake_zot.collections.return_value = fake_collections
|
34
|
+
|
35
|
+
# When collection_items is called, return the appropriate list based on collection key.
|
36
|
+
def fake_collection_items_func(collection_key):
|
37
|
+
return fake_collection_items.get(collection_key, [])
|
38
|
+
|
39
|
+
fake_zot.collection_items.side_effect = fake_collection_items_func
|
40
|
+
|
41
|
+
# Expected full collection paths:
|
42
|
+
# - Collection A: "/Parent"
|
43
|
+
# - Collection B: "/Parent/Child" (child of A)
|
44
|
+
# - Collection C: "/Independent"
|
45
|
+
#
|
46
|
+
# Expected mapping for items:
|
47
|
+
# - "item1" appears in collections A and C → ["/Parent", "/Independent"]
|
48
|
+
# - "item2" appears in B → ["/Parent/Child"]
|
49
|
+
# - "item3" appears in C → ["/Independent"]
|
50
|
+
expected_mapping = {
|
51
|
+
"item1": ["/Parent", "/Independent"],
|
52
|
+
"item2": ["/Parent/Child"],
|
53
|
+
"item3": ["/Independent"],
|
54
|
+
}
|
55
|
+
|
56
|
+
result = get_item_collections(fake_zot)
|
57
|
+
self.assertEqual(result, expected_mapping)
|
@@ -0,0 +1,412 @@
|
|
1
|
+
"""
|
2
|
+
Unit tests for Zotero search tool in zotero_read.py.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from types import SimpleNamespace
|
6
|
+
import unittest
|
7
|
+
from unittest.mock import patch, MagicMock
|
8
|
+
from langgraph.types import Command
|
9
|
+
from aiagents4pharma.talk2scholars.tools.zotero.zotero_read import (
|
10
|
+
zotero_search_tool,
|
11
|
+
)
|
12
|
+
|
13
|
+
|
14
|
+
# Dummy Hydra configuration to be used in tests
|
15
|
+
dummy_zotero_read_config = SimpleNamespace(
|
16
|
+
user_id="dummy_user",
|
17
|
+
library_type="user",
|
18
|
+
api_key="dummy_api_key",
|
19
|
+
zotero=SimpleNamespace(
|
20
|
+
max_limit=5,
|
21
|
+
filter_item_types=["journalArticle", "conferencePaper"],
|
22
|
+
filter_excluded_types=["attachment", "note"],
|
23
|
+
),
|
24
|
+
)
|
25
|
+
dummy_cfg = SimpleNamespace(tools=SimpleNamespace(zotero_read=dummy_zotero_read_config))
|
26
|
+
|
27
|
+
|
28
|
+
class TestZoteroSearchTool(unittest.TestCase):
|
29
|
+
"""test for Zotero search tool"""
|
30
|
+
|
31
|
+
@patch(
|
32
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
33
|
+
)
|
34
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
35
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
36
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
37
|
+
def test_valid_query(
|
38
|
+
self,
|
39
|
+
mock_hydra_init,
|
40
|
+
mock_hydra_compose,
|
41
|
+
mock_zotero_class,
|
42
|
+
mock_get_item_collections,
|
43
|
+
):
|
44
|
+
"""test valid query"""
|
45
|
+
# Setup Hydra mocks
|
46
|
+
mock_hydra_compose.return_value = dummy_cfg
|
47
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
48
|
+
|
49
|
+
# Create a fake Zotero client that returns two valid items
|
50
|
+
fake_zot = MagicMock()
|
51
|
+
fake_items = [
|
52
|
+
{
|
53
|
+
"data": {
|
54
|
+
"key": "paper1",
|
55
|
+
"title": "Paper 1",
|
56
|
+
"abstractNote": "Abstract 1",
|
57
|
+
"date": "2021",
|
58
|
+
"url": "http://example.com",
|
59
|
+
"itemType": "journalArticle",
|
60
|
+
}
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"data": {
|
64
|
+
"key": "paper2",
|
65
|
+
"title": "Paper 2",
|
66
|
+
"abstractNote": "Abstract 2",
|
67
|
+
"date": "2022",
|
68
|
+
"url": "http://example2.com",
|
69
|
+
"itemType": "conferencePaper",
|
70
|
+
}
|
71
|
+
},
|
72
|
+
]
|
73
|
+
fake_zot.items.return_value = fake_items
|
74
|
+
mock_zotero_class.return_value = fake_zot
|
75
|
+
|
76
|
+
# Fake mapping for collection paths
|
77
|
+
mock_get_item_collections.return_value = {
|
78
|
+
"paper1": ["/Test Collection"],
|
79
|
+
"paper2": ["/Test Collection"],
|
80
|
+
}
|
81
|
+
|
82
|
+
# Call the tool with a valid query using .run() with a dictionary input
|
83
|
+
tool_call_id = "test_id_1"
|
84
|
+
tool_input = {
|
85
|
+
"query": "test",
|
86
|
+
"only_articles": True,
|
87
|
+
"tool_call_id": tool_call_id,
|
88
|
+
"limit": 2,
|
89
|
+
}
|
90
|
+
result = zotero_search_tool.run(tool_input)
|
91
|
+
|
92
|
+
# Verify the Command update structure and contents
|
93
|
+
self.assertIsInstance(result, Command)
|
94
|
+
update = result.update
|
95
|
+
self.assertIn("zotero_read", update)
|
96
|
+
self.assertIn("last_displayed_papers", update)
|
97
|
+
self.assertIn("messages", update)
|
98
|
+
|
99
|
+
filtered_papers = update["zotero_read"]
|
100
|
+
self.assertIn("paper1", filtered_papers)
|
101
|
+
self.assertIn("paper2", filtered_papers)
|
102
|
+
message_content = update["messages"][0].content
|
103
|
+
self.assertIn("Query: test", message_content)
|
104
|
+
self.assertIn("Number of papers found: 2", message_content)
|
105
|
+
|
106
|
+
@patch(
|
107
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
108
|
+
)
|
109
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
110
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
111
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
112
|
+
def test_empty_query_fetch_all_items(
|
113
|
+
self,
|
114
|
+
mock_hydra_init,
|
115
|
+
mock_hydra_compose,
|
116
|
+
mock_zotero_class,
|
117
|
+
mock_get_item_collections,
|
118
|
+
):
|
119
|
+
"""test empty query fetches all items"""
|
120
|
+
mock_hydra_compose.return_value = dummy_cfg
|
121
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
122
|
+
|
123
|
+
fake_zot = MagicMock()
|
124
|
+
fake_items = [
|
125
|
+
{
|
126
|
+
"data": {
|
127
|
+
"key": "paper1",
|
128
|
+
"title": "Paper 1",
|
129
|
+
"abstractNote": "Abstract 1",
|
130
|
+
"date": "2021",
|
131
|
+
"url": "http://example.com",
|
132
|
+
"itemType": "journalArticle",
|
133
|
+
}
|
134
|
+
},
|
135
|
+
]
|
136
|
+
fake_zot.items.return_value = fake_items
|
137
|
+
mock_zotero_class.return_value = fake_zot
|
138
|
+
mock_get_item_collections.return_value = {"paper1": ["/Test Collection"]}
|
139
|
+
|
140
|
+
tool_call_id = "test_id_2"
|
141
|
+
tool_input = {
|
142
|
+
"query": " ",
|
143
|
+
"only_articles": True,
|
144
|
+
"tool_call_id": tool_call_id,
|
145
|
+
"limit": 2,
|
146
|
+
}
|
147
|
+
result = zotero_search_tool.run(tool_input)
|
148
|
+
|
149
|
+
update = result.update
|
150
|
+
filtered_papers = update["zotero_read"]
|
151
|
+
self.assertIn("paper1", filtered_papers)
|
152
|
+
fake_zot.items.assert_called_with(
|
153
|
+
limit=dummy_cfg.tools.zotero_read.zotero.max_limit
|
154
|
+
)
|
155
|
+
|
156
|
+
@patch(
|
157
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
158
|
+
)
|
159
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
160
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
161
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
162
|
+
def test_no_items_returned(
|
163
|
+
self,
|
164
|
+
mock_hydra_init,
|
165
|
+
mock_hydra_compose,
|
166
|
+
mock_zotero_class,
|
167
|
+
mock_get_item_collections,
|
168
|
+
):
|
169
|
+
"""test no items returned from Zotero"""
|
170
|
+
mock_hydra_compose.return_value = dummy_cfg
|
171
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
172
|
+
|
173
|
+
fake_zot = MagicMock()
|
174
|
+
fake_zot.items.return_value = []
|
175
|
+
mock_zotero_class.return_value = fake_zot
|
176
|
+
mock_get_item_collections.return_value = {}
|
177
|
+
|
178
|
+
tool_call_id = "test_id_3"
|
179
|
+
tool_input = {
|
180
|
+
"query": "nonexistent",
|
181
|
+
"only_articles": True,
|
182
|
+
"tool_call_id": tool_call_id,
|
183
|
+
"limit": 2,
|
184
|
+
}
|
185
|
+
with self.assertRaises(RuntimeError) as context:
|
186
|
+
zotero_search_tool.run(tool_input)
|
187
|
+
self.assertIn("No items returned from Zotero", str(context.exception))
|
188
|
+
|
189
|
+
@patch(
|
190
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
191
|
+
)
|
192
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
193
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
194
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
195
|
+
def test_filtering_no_matching_papers(
|
196
|
+
self,
|
197
|
+
mock_hydra_init,
|
198
|
+
mock_hydra_compose,
|
199
|
+
mock_zotero_class,
|
200
|
+
mock_get_item_collections,
|
201
|
+
):
|
202
|
+
"""test no matching papers returned from Zotero"""
|
203
|
+
mock_hydra_compose.return_value = dummy_cfg
|
204
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
205
|
+
|
206
|
+
fake_zot = MagicMock()
|
207
|
+
fake_items = [
|
208
|
+
{
|
209
|
+
"data": {
|
210
|
+
"key": "paper1",
|
211
|
+
"title": "Paper 1",
|
212
|
+
"abstractNote": "Abstract 1",
|
213
|
+
"date": "2021",
|
214
|
+
"url": "http://example.com",
|
215
|
+
"itemType": "attachment",
|
216
|
+
}
|
217
|
+
},
|
218
|
+
{
|
219
|
+
"data": {
|
220
|
+
"key": "paper2",
|
221
|
+
"title": "Paper 2",
|
222
|
+
"abstractNote": "Abstract 2",
|
223
|
+
"date": "2022",
|
224
|
+
"url": "http://example2.com",
|
225
|
+
"itemType": "note",
|
226
|
+
}
|
227
|
+
},
|
228
|
+
]
|
229
|
+
fake_zot.items.return_value = fake_items
|
230
|
+
mock_zotero_class.return_value = fake_zot
|
231
|
+
mock_get_item_collections.return_value = {
|
232
|
+
"paper1": ["/Test Collection"],
|
233
|
+
"paper2": ["/Test Collection"],
|
234
|
+
}
|
235
|
+
|
236
|
+
tool_call_id = "test_id_4"
|
237
|
+
tool_input = {
|
238
|
+
"query": "test",
|
239
|
+
"only_articles": True,
|
240
|
+
"tool_call_id": tool_call_id,
|
241
|
+
"limit": 2,
|
242
|
+
}
|
243
|
+
with self.assertRaises(RuntimeError) as context:
|
244
|
+
zotero_search_tool.run(tool_input)
|
245
|
+
self.assertIn("No matching papers returned from Zotero", str(context.exception))
|
246
|
+
|
247
|
+
@patch(
|
248
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
249
|
+
)
|
250
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
251
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
252
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
253
|
+
def test_items_api_exception(
|
254
|
+
self,
|
255
|
+
mock_hydra_init,
|
256
|
+
mock_hydra_compose,
|
257
|
+
mock_zotero_class,
|
258
|
+
mock_get_item_collections,
|
259
|
+
):
|
260
|
+
"""test items API exception"""
|
261
|
+
mock_hydra_compose.return_value = dummy_cfg
|
262
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
263
|
+
mock_get_item_collections.return_value = {}
|
264
|
+
|
265
|
+
fake_zot = MagicMock()
|
266
|
+
fake_zot.items.side_effect = Exception("API error")
|
267
|
+
mock_zotero_class.return_value = fake_zot
|
268
|
+
|
269
|
+
tool_call_id = "test_id_5"
|
270
|
+
tool_input = {
|
271
|
+
"query": "test",
|
272
|
+
"only_articles": True,
|
273
|
+
"tool_call_id": tool_call_id,
|
274
|
+
"limit": 2,
|
275
|
+
}
|
276
|
+
with self.assertRaises(RuntimeError) as context:
|
277
|
+
zotero_search_tool.run(tool_input)
|
278
|
+
self.assertIn("Failed to fetch items from Zotero", str(context.exception))
|
279
|
+
|
280
|
+
@patch(
|
281
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
282
|
+
)
|
283
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
284
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
285
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
286
|
+
def test_missing_key_in_item(
|
287
|
+
self,
|
288
|
+
mock_hydra_init,
|
289
|
+
mock_hydra_compose,
|
290
|
+
mock_zotero_class,
|
291
|
+
mock_get_item_collections,
|
292
|
+
):
|
293
|
+
"""
|
294
|
+
Test that an item with a valid 'data' structure but missing the 'key' field is skipped.
|
295
|
+
"""
|
296
|
+
mock_hydra_compose.return_value = dummy_cfg
|
297
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
298
|
+
|
299
|
+
fake_zot = MagicMock()
|
300
|
+
fake_items = [
|
301
|
+
{
|
302
|
+
"data": {
|
303
|
+
"title": "No Key Paper",
|
304
|
+
"abstractNote": "Abstract",
|
305
|
+
"date": "2021",
|
306
|
+
"url": "http://example.com",
|
307
|
+
"itemType": "journalArticle",
|
308
|
+
}
|
309
|
+
}, # missing key triggers line 136
|
310
|
+
{
|
311
|
+
"data": {
|
312
|
+
"key": "paper_valid",
|
313
|
+
"title": "Valid Paper",
|
314
|
+
"abstractNote": "Valid Abstract",
|
315
|
+
"date": "2021",
|
316
|
+
"url": "http://example.com",
|
317
|
+
"itemType": "journalArticle",
|
318
|
+
}
|
319
|
+
},
|
320
|
+
]
|
321
|
+
fake_zot.items.return_value = fake_items
|
322
|
+
mock_zotero_class.return_value = fake_zot
|
323
|
+
mock_get_item_collections.return_value = {"paper_valid": ["/Test Collection"]}
|
324
|
+
|
325
|
+
tool_call_id = "test_id_6"
|
326
|
+
tool_input = {
|
327
|
+
"query": "dummy",
|
328
|
+
"only_articles": True,
|
329
|
+
"tool_call_id": tool_call_id,
|
330
|
+
"limit": 2,
|
331
|
+
}
|
332
|
+
result = zotero_search_tool.run(tool_input)
|
333
|
+
|
334
|
+
update = result.update
|
335
|
+
filtered_papers = update["zotero_read"]
|
336
|
+
self.assertIn("paper_valid", filtered_papers)
|
337
|
+
self.assertEqual(len(filtered_papers), 1)
|
338
|
+
|
339
|
+
@patch(
|
340
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
341
|
+
)
|
342
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
343
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
344
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
345
|
+
def test_item_not_dict(
|
346
|
+
self,
|
347
|
+
mock_hydra_init,
|
348
|
+
mock_hydra_compose,
|
349
|
+
mock_zotero_class,
|
350
|
+
mock_get_item_collections,
|
351
|
+
):
|
352
|
+
"""
|
353
|
+
Test that if the items list contains an element that is not a dict, it is skipped.
|
354
|
+
"""
|
355
|
+
mock_hydra_compose.return_value = dummy_cfg
|
356
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
357
|
+
|
358
|
+
fake_zot = MagicMock()
|
359
|
+
# Supply one item that is not a dict.
|
360
|
+
fake_items = ["this is not a dict"]
|
361
|
+
fake_zot.items.return_value = fake_items
|
362
|
+
mock_zotero_class.return_value = fake_zot
|
363
|
+
# Mapping doesn't matter here.
|
364
|
+
mock_get_item_collections.return_value = {}
|
365
|
+
|
366
|
+
tool_call_id = "test_id_7"
|
367
|
+
tool_input = {
|
368
|
+
"query": "dummy",
|
369
|
+
"only_articles": True,
|
370
|
+
"tool_call_id": tool_call_id,
|
371
|
+
"limit": 2,
|
372
|
+
}
|
373
|
+
with self.assertRaises(RuntimeError) as context:
|
374
|
+
zotero_search_tool.run(tool_input)
|
375
|
+
self.assertIn("No matching papers returned from Zotero", str(context.exception))
|
376
|
+
|
377
|
+
@patch(
|
378
|
+
"aiagents4pharma.talk2scholars.tools.zotero.zotero_read.get_item_collections"
|
379
|
+
)
|
380
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.zotero.Zotero")
|
381
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.compose")
|
382
|
+
@patch("aiagents4pharma.talk2scholars.tools.zotero.zotero_read.hydra.initialize")
|
383
|
+
def test_data_not_dict(
|
384
|
+
self,
|
385
|
+
mock_hydra_init,
|
386
|
+
mock_hydra_compose,
|
387
|
+
mock_zotero_class,
|
388
|
+
mock_get_item_collections,
|
389
|
+
):
|
390
|
+
"""
|
391
|
+
Test that if an item has a 'data' field that is not a dict, it is skipped.
|
392
|
+
"""
|
393
|
+
mock_hydra_compose.return_value = dummy_cfg
|
394
|
+
mock_hydra_init.return_value.__enter__.return_value = None
|
395
|
+
|
396
|
+
fake_zot = MagicMock()
|
397
|
+
# Supply one item whose "data" field is not a dict.
|
398
|
+
fake_items = [{"data": "this is not a dict"}]
|
399
|
+
fake_zot.items.return_value = fake_items
|
400
|
+
mock_zotero_class.return_value = fake_zot
|
401
|
+
mock_get_item_collections.return_value = {}
|
402
|
+
|
403
|
+
tool_call_id = "test_id_8"
|
404
|
+
tool_input = {
|
405
|
+
"query": "dummy",
|
406
|
+
"only_articles": True,
|
407
|
+
"tool_call_id": tool_call_id,
|
408
|
+
"limit": 2,
|
409
|
+
}
|
410
|
+
with self.assertRaises(RuntimeError) as context:
|
411
|
+
zotero_search_tool.run(tool_input)
|
412
|
+
self.assertIn("No matching papers returned from Zotero", str(context.exception))
|