aiagents4pharma 1.42.0__py3-none-any.whl → 1.44.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.
Files changed (47) hide show
  1. aiagents4pharma/talk2knowledgegraphs/configs/tools/multimodal_subgraph_extraction/default.yaml +17 -2
  2. aiagents4pharma/talk2knowledgegraphs/tests/test_tools_milvus_multimodal_subgraph_extraction.py +618 -413
  3. aiagents4pharma/talk2knowledgegraphs/tests/test_utils_extractions_milvus_multimodal_pcst.py +362 -25
  4. aiagents4pharma/talk2knowledgegraphs/tools/milvus_multimodal_subgraph_extraction.py +146 -109
  5. aiagents4pharma/talk2knowledgegraphs/utils/extractions/milvus_multimodal_pcst.py +240 -83
  6. aiagents4pharma/talk2scholars/agents/paper_download_agent.py +7 -4
  7. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/main_agent/default.yaml +49 -95
  8. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/paper_download_agent/default.yaml +15 -1
  9. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/pdf_agent/default.yaml +16 -2
  10. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/s2_agent/default.yaml +40 -5
  11. aiagents4pharma/talk2scholars/configs/agents/talk2scholars/zotero_agent/default.yaml +15 -5
  12. aiagents4pharma/talk2scholars/configs/config.yaml +1 -3
  13. aiagents4pharma/talk2scholars/configs/tools/paper_download/default.yaml +124 -0
  14. aiagents4pharma/talk2scholars/tests/test_arxiv_downloader.py +478 -0
  15. aiagents4pharma/talk2scholars/tests/test_base_paper_downloader.py +620 -0
  16. aiagents4pharma/talk2scholars/tests/test_biorxiv_downloader.py +697 -0
  17. aiagents4pharma/talk2scholars/tests/test_medrxiv_downloader.py +534 -0
  18. aiagents4pharma/talk2scholars/tests/test_paper_download_agent.py +22 -12
  19. aiagents4pharma/talk2scholars/tests/test_paper_downloader.py +545 -0
  20. aiagents4pharma/talk2scholars/tests/test_pubmed_downloader.py +1067 -0
  21. aiagents4pharma/talk2scholars/tools/paper_download/__init__.py +2 -4
  22. aiagents4pharma/talk2scholars/tools/paper_download/paper_downloader.py +457 -0
  23. aiagents4pharma/talk2scholars/tools/paper_download/utils/__init__.py +20 -0
  24. aiagents4pharma/talk2scholars/tools/paper_download/utils/arxiv_downloader.py +209 -0
  25. aiagents4pharma/talk2scholars/tools/paper_download/utils/base_paper_downloader.py +343 -0
  26. aiagents4pharma/talk2scholars/tools/paper_download/utils/biorxiv_downloader.py +321 -0
  27. aiagents4pharma/talk2scholars/tools/paper_download/utils/medrxiv_downloader.py +198 -0
  28. aiagents4pharma/talk2scholars/tools/paper_download/utils/pubmed_downloader.py +337 -0
  29. aiagents4pharma/talk2scholars/tools/s2/query_dataframe.py +97 -45
  30. aiagents4pharma/talk2scholars/tools/s2/retrieve_semantic_scholar_paper_id.py +47 -29
  31. {aiagents4pharma-1.42.0.dist-info → aiagents4pharma-1.44.0.dist-info}/METADATA +3 -1
  32. {aiagents4pharma-1.42.0.dist-info → aiagents4pharma-1.44.0.dist-info}/RECORD +36 -33
  33. aiagents4pharma/talk2scholars/configs/tools/download_arxiv_paper/default.yaml +0 -4
  34. aiagents4pharma/talk2scholars/configs/tools/download_biorxiv_paper/__init__.py +0 -3
  35. aiagents4pharma/talk2scholars/configs/tools/download_biorxiv_paper/default.yaml +0 -2
  36. aiagents4pharma/talk2scholars/configs/tools/download_medrxiv_paper/__init__.py +0 -3
  37. aiagents4pharma/talk2scholars/configs/tools/download_medrxiv_paper/default.yaml +0 -2
  38. aiagents4pharma/talk2scholars/tests/test_paper_download_biorxiv.py +0 -151
  39. aiagents4pharma/talk2scholars/tests/test_paper_download_medrxiv.py +0 -151
  40. aiagents4pharma/talk2scholars/tests/test_paper_download_tools.py +0 -249
  41. aiagents4pharma/talk2scholars/tools/paper_download/download_arxiv_input.py +0 -177
  42. aiagents4pharma/talk2scholars/tools/paper_download/download_biorxiv_input.py +0 -114
  43. aiagents4pharma/talk2scholars/tools/paper_download/download_medrxiv_input.py +0 -114
  44. /aiagents4pharma/talk2scholars/configs/tools/{download_arxiv_paper → paper_download}/__init__.py +0 -0
  45. {aiagents4pharma-1.42.0.dist-info → aiagents4pharma-1.44.0.dist-info}/WHEEL +0 -0
  46. {aiagents4pharma-1.42.0.dist-info → aiagents4pharma-1.44.0.dist-info}/licenses/LICENSE +0 -0
  47. {aiagents4pharma-1.42.0.dist-info → aiagents4pharma-1.44.0.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,9 @@ import unittest
8
8
  from unittest.mock import patch, MagicMock, mock_open
9
9
  import numpy as np
10
10
  import pandas as pd
11
- from ..utils.extractions.milvus_multimodal_pcst import MultimodalPCSTPruning
11
+ from ..utils.extractions.milvus_multimodal_pcst import (
12
+ MultimodalPCSTPruning, SystemDetector, DynamicLibraryLoader
13
+ )
12
14
 
13
15
  class TestMultimodalPCSTPruning(unittest.TestCase):
14
16
  """
@@ -21,29 +23,29 @@ class TestMultimodalPCSTPruning(unittest.TestCase):
21
23
  self.addCleanup(patcher_cupy.stop)
22
24
 
23
25
  # Patch pcst_fast
24
- self.pcst_fast_patcher = patch("aiagents4pharma.talk2knowledgegraphs.utils."
25
- "extractions.milvus_multimodal_pcst.pcst_fast")
26
- self.mock_pcst_fast = self.pcst_fast_patcher.start()
27
- self.addCleanup(self.pcst_fast_patcher.stop)
28
- self.mock_pcst_fast.pcst_fast.return_value = ([0, 1], [0])
26
+ pcst_fast_patcher = patch("aiagents4pharma.talk2knowledgegraphs.utils."
27
+ "extractions.milvus_multimodal_pcst.pcst_fast")
28
+ mock_pcst_fast = pcst_fast_patcher.start()
29
+ self.addCleanup(pcst_fast_patcher.stop)
30
+ mock_pcst_fast.pcst_fast.return_value = ([0, 1], [0])
29
31
 
30
32
  # Patch Collection
31
- self.collection_patcher = patch("aiagents4pharma.talk2knowledgegraphs.utils."
32
- "extractions.milvus_multimodal_pcst.Collection")
33
- self.mock_collection = self.collection_patcher.start()
34
- self.addCleanup(self.collection_patcher.stop)
33
+ collection_patcher = patch("aiagents4pharma.talk2knowledgegraphs.utils."
34
+ "extractions.milvus_multimodal_pcst.Collection")
35
+ self.mock_collection = collection_patcher.start()
36
+ self.addCleanup(collection_patcher.stop)
35
37
 
36
38
  # Patch open for cache_edge_index_path
37
- self.open_patcher = patch('builtins.open', mock_open(read_data='[[0,1],[1,2]]'))
38
- self.mock_open = self.open_patcher.start()
39
- self.addCleanup(self.open_patcher.stop)
39
+ open_patcher = patch('builtins.open', mock_open(read_data='[[0,1],[1,2]]'))
40
+ open_patcher.start()
41
+ self.addCleanup(open_patcher.stop)
40
42
 
41
43
  # Patch pickle.load to return a numpy array for edge_index
42
- self.pickle_patcher = patch("aiagents4pharma.talk2knowledgegraphs.utils."
43
- "extractions.milvus_multimodal_pcst.pickle")
44
- self.mock_pickle = self.pickle_patcher.start()
45
- self.addCleanup(self.pickle_patcher.stop)
46
- self.mock_pickle.load.return_value = np.array([[0, 1], [1, 2]])
44
+ pickle_patcher = patch("aiagents4pharma.talk2knowledgegraphs.utils."
45
+ "extractions.milvus_multimodal_pcst.pickle")
46
+ mock_pickle = pickle_patcher.start()
47
+ self.addCleanup(pickle_patcher.stop)
48
+ mock_pickle.load.return_value = np.array([[0, 1], [1, 2]])
47
49
 
48
50
  # Setup config mock
49
51
  self.cfg = MagicMock()
@@ -59,6 +61,12 @@ class TestMultimodalPCSTPruning(unittest.TestCase):
59
61
  edge_coll.search.return_value = [[MagicMock(id=0, score=1.0), MagicMock(id=1, score=0.5)]]
60
62
  self.mock_collection.side_effect = lambda name: node_coll if "nodes" in name else edge_coll
61
63
 
64
+ # Setup mock loader
65
+ self.mock_loader = MagicMock()
66
+ self.mock_loader.py = np # Use numpy for array operations
67
+ self.mock_loader.df = pd # Use pandas for dataframes
68
+ self.mock_loader.to_list = lambda x: x.tolist() if hasattr(x, 'tolist') else list(x)
69
+
62
70
  def test_extract_subgraph_use_description_true(self):
63
71
  """
64
72
  Test the extract_subgraph method of MultimodalPCSTPruning with use_description=True.
@@ -66,7 +74,8 @@ class TestMultimodalPCSTPruning(unittest.TestCase):
66
74
  # Create instance
67
75
  pcst = MultimodalPCSTPruning(
68
76
  topk=3, topk_e=3, cost_e=0.5, c_const=0.01, root=-1,
69
- num_clusters=1, pruning="gw", verbosity_level=0, use_description=True, metric_type="IP"
77
+ num_clusters=1, pruning="gw", verbosity_level=0, use_description=True, metric_type="IP",
78
+ loader=self.mock_loader
70
79
  )
71
80
  # Dummy embeddings
72
81
  text_emb = [0.1, 0.2, 0.3]
@@ -89,7 +98,9 @@ class TestMultimodalPCSTPruning(unittest.TestCase):
89
98
  # Create instance
90
99
  pcst = MultimodalPCSTPruning(
91
100
  topk=3, topk_e=3, cost_e=0.5, c_const=0.01, root=-1,
92
- num_clusters=1, pruning="gw", verbosity_level=0, use_description=False, metric_type="IP"
101
+ num_clusters=1, pruning="gw", verbosity_level=0, use_description=False,
102
+ metric_type="IP",
103
+ loader=self.mock_loader
93
104
  )
94
105
  # Dummy embeddings
95
106
  text_emb = [0.1, 0.2, 0.3]
@@ -111,7 +122,8 @@ class TestMultimodalPCSTPruning(unittest.TestCase):
111
122
  """
112
123
  pcst = MultimodalPCSTPruning(
113
124
  topk=3, topk_e=3, cost_e=0.5, c_const=0.01, root=-1,
114
- num_clusters=1, pruning="gw", verbosity_level=0, use_description=True, metric_type="IP"
125
+ num_clusters=1, pruning="gw", verbosity_level=0, use_description=True, metric_type="IP",
126
+ loader=self.mock_loader
115
127
  )
116
128
  # Simulate num_nodes = 2, vertices contains [0, 1, 2, 3] (2 and 3 are virtual)
117
129
  num_nodes = 2
@@ -150,15 +162,20 @@ class TestMultimodalPCSTPruning(unittest.TestCase):
150
162
  with patch.dict("sys.modules", {"cupy": np, "cudf": pd}):
151
163
  # Reload the module to trigger the GPU branch
152
164
  mod = importlib.reload(sys.modules[module_name])
153
- # Patch Collection, pcst_fast, and pickle after reload
165
+ # Create local mocks for this test
166
+ mock_pcst_fast = MagicMock()
167
+ mock_pcst_fast.pcst_fast.return_value = ([0, 1], [0])
168
+ mock_pickle = MagicMock()
169
+ mock_pickle.load.return_value = np.array([[0, 1], [1, 2]])
170
+ # Patch Collection, pcst_fast, and pickle after reload
154
171
  with patch(f"{module_name}.Collection", self.mock_collection), \
155
- patch(f"{module_name}.pcst_fast", self.mock_pcst_fast), \
156
- patch(f"{module_name}.pickle", self.mock_pickle):
172
+ patch(f"{module_name}.pcst_fast", mock_pcst_fast), \
173
+ patch(f"{module_name}.pickle", mock_pickle):
157
174
  pcst_pruning_cls = getattr(mod, "MultimodalPCSTPruning")
158
175
  pcst = pcst_pruning_cls(
159
176
  topk=3, topk_e=3, cost_e=0.5, c_const=0.01, root=-1,
160
177
  num_clusters=1, pruning="gw", verbosity_level=0, use_description=True,
161
- metric_type="IP"
178
+ metric_type="IP", loader=self.mock_loader
162
179
  )
163
180
  # Dummy embeddings
164
181
  text_emb = [0.1, 0.2, 0.3]
@@ -173,3 +190,323 @@ class TestMultimodalPCSTPruning(unittest.TestCase):
173
190
  self.assertIn("edges", result)
174
191
  self.assertGreaterEqual(len(result["nodes"]), 0)
175
192
  self.assertGreaterEqual(len(result["edges"]), 0)
193
+
194
+
195
+ class TestSystemDetector(unittest.TestCase):
196
+ """Test cases for SystemDetector class."""
197
+
198
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
199
+ 'milvus_multimodal_pcst.platform')
200
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
201
+ 'milvus_multimodal_pcst.subprocess')
202
+ def test_system_detector_gpu_detected(self, mock_subprocess, mock_platform):
203
+ """Test SystemDetector when GPU is detected."""
204
+ # Mock platform calls
205
+ mock_platform.system.return_value = 'Linux'
206
+ mock_platform.machine.return_value = 'x86_64'
207
+
208
+ # Mock successful nvidia-smi call
209
+ mock_result = MagicMock()
210
+ mock_result.returncode = 0
211
+ mock_subprocess.run.return_value = mock_result
212
+
213
+ detector = SystemDetector()
214
+
215
+ # Assertions
216
+ self.assertEqual(detector.os_type, 'linux')
217
+ self.assertEqual(detector.architecture, 'x86_64')
218
+ self.assertTrue(detector.has_nvidia_gpu)
219
+ self.assertTrue(detector.use_gpu)
220
+
221
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
222
+ 'milvus_multimodal_pcst.platform')
223
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
224
+ 'milvus_multimodal_pcst.subprocess')
225
+ def test_system_detector_no_gpu(self, mock_subprocess, mock_platform):
226
+ """Test SystemDetector when no GPU is detected."""
227
+ # Mock platform calls
228
+ mock_platform.system.return_value = 'Linux'
229
+ mock_platform.machine.return_value = 'x86_64'
230
+
231
+ # Mock failed nvidia-smi call
232
+ mock_result = MagicMock()
233
+ mock_result.returncode = 1
234
+ mock_subprocess.run.return_value = mock_result
235
+
236
+ detector = SystemDetector()
237
+
238
+ # Assertions
239
+ self.assertEqual(detector.os_type, 'linux')
240
+ self.assertEqual(detector.architecture, 'x86_64')
241
+ self.assertFalse(detector.has_nvidia_gpu)
242
+ self.assertFalse(detector.use_gpu)
243
+
244
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
245
+ 'milvus_multimodal_pcst.platform')
246
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
247
+ 'milvus_multimodal_pcst.subprocess')
248
+ def test_system_detector_macos_no_gpu(self, mock_subprocess, mock_platform):
249
+ """Test SystemDetector on macOS (no GPU support)."""
250
+ # Mock platform calls
251
+ mock_platform.system.return_value = 'Darwin'
252
+ mock_platform.machine.return_value = 'arm64'
253
+
254
+ # Mock successful nvidia-smi call (but macOS should still disable GPU)
255
+ mock_result = MagicMock()
256
+ mock_result.returncode = 0
257
+ mock_subprocess.run.return_value = mock_result
258
+
259
+ detector = SystemDetector()
260
+
261
+ # Assertions
262
+ self.assertEqual(detector.os_type, 'darwin')
263
+ self.assertEqual(detector.architecture, 'arm64')
264
+ self.assertTrue(detector.has_nvidia_gpu) # GPU detected
265
+ self.assertFalse(detector.use_gpu) # But not used on macOS
266
+
267
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
268
+ 'milvus_multimodal_pcst.platform')
269
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
270
+ 'milvus_multimodal_pcst.subprocess')
271
+ def test_system_detector_subprocess_exception(self, mock_subprocess, mock_platform):
272
+ """Test SystemDetector when subprocess raises exception."""
273
+ # Mock platform calls
274
+ mock_platform.system.return_value = 'Linux'
275
+ mock_platform.machine.return_value = 'x86_64'
276
+
277
+ # Mock subprocess to raise FileNotFoundError
278
+ mock_subprocess.run.side_effect = FileNotFoundError("nvidia-smi not found")
279
+ mock_subprocess.TimeoutExpired = Exception
280
+ mock_subprocess.SubprocessError = Exception
281
+
282
+ detector = SystemDetector()
283
+
284
+ # Assertions
285
+ self.assertEqual(detector.os_type, 'linux')
286
+ self.assertEqual(detector.architecture, 'x86_64')
287
+ self.assertFalse(detector.has_nvidia_gpu)
288
+ self.assertFalse(detector.use_gpu)
289
+
290
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
291
+ 'milvus_multimodal_pcst.platform')
292
+ @patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
293
+ 'milvus_multimodal_pcst.subprocess')
294
+ def test_system_detector_timeout(self, mock_subprocess, mock_platform):
295
+ """Test SystemDetector when subprocess times out."""
296
+ # Mock platform calls
297
+ mock_platform.system.return_value = 'Linux'
298
+ mock_platform.machine.return_value = 'x86_64'
299
+
300
+ # Mock subprocess to raise TimeoutExpired
301
+ mock_subprocess.TimeoutExpired = Exception
302
+ mock_subprocess.SubprocessError = Exception
303
+ mock_subprocess.run.side_effect = mock_subprocess.TimeoutExpired("nvidia-smi", 10)
304
+
305
+ detector = SystemDetector()
306
+
307
+ # Assertions
308
+ self.assertEqual(detector.os_type, 'linux')
309
+ self.assertEqual(detector.architecture, 'x86_64')
310
+ self.assertFalse(detector.has_nvidia_gpu)
311
+ self.assertFalse(detector.use_gpu)
312
+
313
+ def test_get_system_info(self):
314
+ """Test get_system_info method."""
315
+ with patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
316
+ 'milvus_multimodal_pcst.platform') as mock_platform, \
317
+ patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
318
+ 'milvus_multimodal_pcst.subprocess') as mock_subprocess:
319
+
320
+ mock_platform.system.return_value = 'Linux'
321
+ mock_platform.machine.return_value = 'x86_64'
322
+
323
+ mock_result = MagicMock()
324
+ mock_result.returncode = 0
325
+ mock_subprocess.run.return_value = mock_result
326
+
327
+ detector = SystemDetector()
328
+ info = detector.get_system_info()
329
+
330
+ expected_info = {
331
+ "os_type": "linux",
332
+ "architecture": "x86_64",
333
+ "has_nvidia_gpu": True,
334
+ "use_gpu": True,
335
+ }
336
+ self.assertEqual(info, expected_info)
337
+
338
+ def test_is_gpu_compatible(self):
339
+ """Test is_gpu_compatible method."""
340
+ with patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
341
+ 'milvus_multimodal_pcst.platform') as mock_platform, \
342
+ patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
343
+ 'milvus_multimodal_pcst.subprocess') as mock_subprocess:
344
+
345
+ mock_platform.system.return_value = 'Linux'
346
+ mock_platform.machine.return_value = 'x86_64'
347
+
348
+ mock_result = MagicMock()
349
+ mock_result.returncode = 0
350
+ mock_subprocess.run.return_value = mock_result
351
+
352
+ detector = SystemDetector()
353
+
354
+ # Should be compatible (has GPU + not macOS)
355
+ self.assertTrue(detector.is_gpu_compatible())
356
+
357
+
358
+ class TestDynamicLibraryLoader(unittest.TestCase):
359
+ """Test cases for DynamicLibraryLoader class."""
360
+
361
+ def setUp(self):
362
+ """Set up test fixtures."""
363
+ self.mock_detector = MagicMock()
364
+
365
+ def test_dynamic_library_loader_cpu_mode(self):
366
+ """Test DynamicLibraryLoader in CPU mode."""
367
+ self.mock_detector.use_gpu = False
368
+
369
+ loader = DynamicLibraryLoader(self.mock_detector)
370
+
371
+ # Assertions
372
+ self.assertFalse(loader.use_gpu)
373
+ self.assertEqual(loader.py, np)
374
+ self.assertEqual(loader.df, pd)
375
+ self.assertFalse(loader.normalize_vectors)
376
+ self.assertEqual(loader.metric_type, "COSINE")
377
+
378
+ @patch.dict('sys.modules', {'cupy': MagicMock(), 'cudf': MagicMock()})
379
+ def test_dynamic_library_loader_gpu_mode_success(self):
380
+ """Test DynamicLibraryLoader in GPU mode with successful imports."""
381
+ self.mock_detector.use_gpu = True
382
+
383
+ # Mock the CUDF_AVAILABLE flag
384
+ with patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
385
+ 'milvus_multimodal_pcst.CUDF_AVAILABLE', True):
386
+ loader = DynamicLibraryLoader(self.mock_detector)
387
+
388
+ # Assertions
389
+ self.assertTrue(loader.use_gpu)
390
+ self.assertTrue(loader.normalize_vectors)
391
+ self.assertEqual(loader.metric_type, "IP")
392
+
393
+ def test_dynamic_library_loader_gpu_mode_import_failure(self):
394
+ """Test DynamicLibraryLoader when GPU libraries are not available."""
395
+ self.mock_detector.use_gpu = True
396
+
397
+ # Mock CUDF_AVAILABLE as False to simulate import failure
398
+ with patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
399
+ 'milvus_multimodal_pcst.CUDF_AVAILABLE', False):
400
+ loader = DynamicLibraryLoader(self.mock_detector)
401
+
402
+ # Should fallback to CPU mode
403
+ self.assertFalse(loader.use_gpu)
404
+ self.assertEqual(loader.py, np)
405
+ self.assertEqual(loader.df, pd)
406
+ self.assertFalse(loader.normalize_vectors)
407
+ self.assertEqual(loader.metric_type, "COSINE")
408
+
409
+ def test_normalize_matrix_cpu_mode(self):
410
+ """Test normalize_matrix in CPU mode."""
411
+ self.mock_detector.use_gpu = False
412
+ loader = DynamicLibraryLoader(self.mock_detector)
413
+
414
+ matrix = np.array([[1, 2], [3, 4]])
415
+ result = loader.normalize_matrix(matrix)
416
+
417
+ # In CPU mode, should return matrix unchanged
418
+ np.testing.assert_array_equal(result, matrix)
419
+
420
+ def test_normalize_matrix_cpu_mode_normalize_false(self):
421
+ """Test normalize_matrix in CPU mode with normalize_vectors=False explicitly."""
422
+ self.mock_detector.use_gpu = False
423
+ loader = DynamicLibraryLoader(self.mock_detector)
424
+
425
+ # Explicitly set normalize_vectors to False to test the return path
426
+ loader.normalize_vectors = False
427
+
428
+ matrix = np.array([[1, 2], [3, 4]])
429
+ result = loader.normalize_matrix(matrix)
430
+
431
+ # Should return matrix unchanged when normalize_vectors is False
432
+ np.testing.assert_array_equal(result, matrix)
433
+
434
+ def test_normalize_matrix_cpu_mode_normalize_true(self):
435
+ """Test normalize_matrix in CPU mode with normalize_vectors=True (edge case)."""
436
+ self.mock_detector.use_gpu = False
437
+ loader = DynamicLibraryLoader(self.mock_detector)
438
+
439
+ # Force normalize_vectors to True to test the else branch at line 144
440
+ loader.normalize_vectors = True
441
+ loader.use_gpu = False # Ensure GPU is disabled
442
+
443
+ matrix = np.array([[1, 2], [3, 4]])
444
+ result = loader.normalize_matrix(matrix)
445
+
446
+ # Should return matrix unchanged in CPU mode even when normalize_vectors is True
447
+ np.testing.assert_array_equal(result, matrix)
448
+
449
+ @patch.dict('sys.modules', {'cupy': MagicMock(), 'cudf': MagicMock()})
450
+ def test_normalize_matrix_gpu_mode(self):
451
+ """Test normalize_matrix in GPU mode."""
452
+ self.mock_detector.use_gpu = True
453
+
454
+ with patch('aiagents4pharma.talk2knowledgegraphs.utils.extractions.'
455
+ 'milvus_multimodal_pcst.CUDF_AVAILABLE', True):
456
+ loader = DynamicLibraryLoader(self.mock_detector)
457
+
458
+ # Mock cupy operations
459
+ mock_cp = MagicMock()
460
+ mock_array = MagicMock()
461
+ mock_norms = MagicMock()
462
+
463
+ mock_cp.asarray.return_value = mock_array
464
+ mock_cp.linalg.norm.return_value = mock_norms
465
+ mock_cp.float32 = np.float32
466
+
467
+ loader.cp = mock_cp
468
+ loader.py = mock_cp
469
+
470
+ matrix = [[1, 2], [3, 4]]
471
+ loader.normalize_matrix(matrix)
472
+
473
+ # Verify cupy operations were called
474
+ mock_cp.asarray.assert_called_once()
475
+ mock_cp.linalg.norm.assert_called_once()
476
+
477
+ def test_to_list_with_tolist(self):
478
+ """Test to_list with data that has tolist method."""
479
+ self.mock_detector.use_gpu = False
480
+ loader = DynamicLibraryLoader(self.mock_detector)
481
+
482
+ data = np.array([1, 2, 3])
483
+ result = loader.to_list(data)
484
+
485
+ self.assertEqual(result, [1, 2, 3])
486
+
487
+ def test_to_list_with_to_arrow(self):
488
+ """Test to_list with data that has to_arrow method."""
489
+ self.mock_detector.use_gpu = False
490
+ loader = DynamicLibraryLoader(self.mock_detector)
491
+
492
+ # Mock data with to_arrow method but no tolist method
493
+ mock_data = MagicMock()
494
+ mock_arrow = MagicMock()
495
+ mock_arrow.to_pylist.return_value = [1, 2, 3]
496
+ mock_data.to_arrow.return_value = mock_arrow
497
+ # Remove tolist method to test the to_arrow path
498
+ del mock_data.tolist
499
+
500
+ result = loader.to_list(mock_data)
501
+
502
+ self.assertEqual(result, [1, 2, 3])
503
+
504
+ def test_to_list_fallback(self):
505
+ """Test to_list fallback to list()."""
506
+ self.mock_detector.use_gpu = False
507
+ loader = DynamicLibraryLoader(self.mock_detector)
508
+
509
+ data = (1, 2, 3) # tuple without tolist or to_arrow
510
+ result = loader.to_list(data)
511
+
512
+ self.assertEqual(result, [1, 2, 3])