isage-middleware 0.1.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.

Potentially problematic release.


This version of isage-middleware might be problematic. Click here for more details.

Files changed (191) hide show
  1. isage_middleware-0.1.0.dist-info/METADATA +424 -0
  2. isage_middleware-0.1.0.dist-info/RECORD +191 -0
  3. isage_middleware-0.1.0.dist-info/WHEEL +5 -0
  4. isage_middleware-0.1.0.dist-info/top_level.txt +1 -0
  5. sage/__init__.py +2 -0
  6. sage/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  7. sage/__pycache__/__init__.cpython-311.pyc +0 -0
  8. sage/middleware/__init__.py +83 -0
  9. sage/middleware/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  10. sage/middleware/__pycache__/__init__.cpython-311.pyc +0 -0
  11. sage/middleware/api/__init__.py +22 -0
  12. sage/middleware/api/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  13. sage/middleware/api/__pycache__/__init__.cpython-311.pyc +0 -0
  14. sage/middleware/api/__pycache__/graph_api.cpython-311.opt-2.pyc +0 -0
  15. sage/middleware/api/__pycache__/graph_api.cpython-311.pyc +0 -0
  16. sage/middleware/api/__pycache__/kv_api.cpython-311.opt-2.pyc +0 -0
  17. sage/middleware/api/__pycache__/kv_api.cpython-311.pyc +0 -0
  18. sage/middleware/api/__pycache__/memory_api.cpython-311.opt-2.pyc +0 -0
  19. sage/middleware/api/__pycache__/memory_api.cpython-311.pyc +0 -0
  20. sage/middleware/api/__pycache__/vdb_api.cpython-311.opt-2.pyc +0 -0
  21. sage/middleware/api/__pycache__/vdb_api.cpython-311.pyc +0 -0
  22. sage/middleware/api/graph_api.py +74 -0
  23. sage/middleware/api/kv_api.py +45 -0
  24. sage/middleware/api/memory_api.py +64 -0
  25. sage/middleware/api/vdb_api.py +60 -0
  26. sage/middleware/enterprise/__init__.py +75 -0
  27. sage/middleware/enterprise/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  28. sage/middleware/enterprise/__pycache__/__init__.cpython-311.pyc +0 -0
  29. sage/middleware/enterprise/sage_db/__init__.py +132 -0
  30. sage/middleware/enterprise/sage_db/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  31. sage/middleware/enterprise/sage_db/__pycache__/__init__.cpython-311.pyc +0 -0
  32. sage/middleware/enterprise/sage_db/__pycache__/sage_db.cpython-311.opt-2.pyc +0 -0
  33. sage/middleware/enterprise/sage_db/__pycache__/sage_db.cpython-311.pyc +0 -0
  34. sage/middleware/enterprise/sage_db/python/__init__.py +7 -0
  35. sage/middleware/enterprise/sage_db/python/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  36. sage/middleware/enterprise/sage_db/python/__pycache__/__init__.cpython-311.pyc +0 -0
  37. sage/middleware/enterprise/sage_db/python/__pycache__/sage_db.cpython-311.opt-2.pyc +0 -0
  38. sage/middleware/enterprise/sage_db/python/__pycache__/sage_db.cpython-311.pyc +0 -0
  39. sage/middleware/enterprise/sage_db/python/sage_db.py +44 -0
  40. sage/middleware/enterprise/sage_db/sage_db.py +395 -0
  41. sage/middleware/enterprise/sage_db/tests/__pycache__/test_python.cpython-311.opt-2.pyc +0 -0
  42. sage/middleware/enterprise/sage_db/tests/__pycache__/test_python.cpython-311.pyc +0 -0
  43. sage/middleware/enterprise/sage_db/tests/test_python.py +144 -0
  44. sage/middleware/examples/__pycache__/api_usage_tutorial.cpython-311.opt-2.pyc +0 -0
  45. sage/middleware/examples/__pycache__/api_usage_tutorial.cpython-311.pyc +0 -0
  46. sage/middleware/examples/__pycache__/dag_microservices_demo.cpython-311.opt-2.pyc +0 -0
  47. sage/middleware/examples/__pycache__/dag_microservices_demo.cpython-311.pyc +0 -0
  48. sage/middleware/examples/__pycache__/microservices_demo.cpython-311.opt-2.pyc +0 -0
  49. sage/middleware/examples/__pycache__/microservices_demo.cpython-311.pyc +0 -0
  50. sage/middleware/examples/__pycache__/microservices_integration_demo.cpython-311.opt-2.pyc +0 -0
  51. sage/middleware/examples/__pycache__/microservices_integration_demo.cpython-311.pyc +0 -0
  52. sage/middleware/examples/__pycache__/microservices_registration_demo.cpython-311.opt-2.pyc +0 -0
  53. sage/middleware/examples/__pycache__/microservices_registration_demo.cpython-311.pyc +0 -0
  54. sage/middleware/examples/api_usage_tutorial.py +339 -0
  55. sage/middleware/examples/dag_microservices_demo.py +220 -0
  56. sage/middleware/examples/microservices_demo.py +0 -0
  57. sage/middleware/examples/microservices_integration_demo.py +373 -0
  58. sage/middleware/examples/microservices_registration_demo.py +144 -0
  59. sage/middleware/py.typed +2 -0
  60. sage/middleware/services/graph/__init__.py +8 -0
  61. sage/middleware/services/graph/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  62. sage/middleware/services/graph/__pycache__/__init__.cpython-311.pyc +0 -0
  63. sage/middleware/services/graph/__pycache__/graph_index.cpython-311.opt-2.pyc +0 -0
  64. sage/middleware/services/graph/__pycache__/graph_index.cpython-311.pyc +0 -0
  65. sage/middleware/services/graph/__pycache__/graph_service.cpython-311.opt-2.pyc +0 -0
  66. sage/middleware/services/graph/__pycache__/graph_service.cpython-311.pyc +0 -0
  67. sage/middleware/services/graph/examples/__pycache__/graph_demo.cpython-311.opt-2.pyc +0 -0
  68. sage/middleware/services/graph/examples/__pycache__/graph_demo.cpython-311.pyc +0 -0
  69. sage/middleware/services/graph/examples/graph_demo.py +177 -0
  70. sage/middleware/services/graph/graph_index.py +194 -0
  71. sage/middleware/services/graph/graph_service.py +541 -0
  72. sage/middleware/services/graph/search_engine/__init__.py +0 -0
  73. sage/middleware/services/graph/search_engine/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  74. sage/middleware/services/graph/search_engine/__pycache__/__init__.cpython-311.pyc +0 -0
  75. sage/middleware/services/graph/search_engine/__pycache__/base_graph_index.cpython-311.opt-2.pyc +0 -0
  76. sage/middleware/services/graph/search_engine/__pycache__/base_graph_index.cpython-311.pyc +0 -0
  77. sage/middleware/services/graph/search_engine/base_graph_index.py +0 -0
  78. sage/middleware/services/kv/__init__.py +8 -0
  79. sage/middleware/services/kv/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  80. sage/middleware/services/kv/__pycache__/__init__.cpython-311.pyc +0 -0
  81. sage/middleware/services/kv/__pycache__/kv_service.cpython-311.opt-2.pyc +0 -0
  82. sage/middleware/services/kv/__pycache__/kv_service.cpython-311.pyc +0 -0
  83. sage/middleware/services/kv/examples/__pycache__/kv_demo.cpython-311.opt-2.pyc +0 -0
  84. sage/middleware/services/kv/examples/__pycache__/kv_demo.cpython-311.pyc +0 -0
  85. sage/middleware/services/kv/examples/kv_demo.py +213 -0
  86. sage/middleware/services/kv/kv_service.py +306 -0
  87. sage/middleware/services/kv/search_engine/__init__.py +0 -0
  88. sage/middleware/services/kv/search_engine/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  89. sage/middleware/services/kv/search_engine/__pycache__/__init__.cpython-311.pyc +0 -0
  90. sage/middleware/services/kv/search_engine/__pycache__/base_kv_index.cpython-311.opt-2.pyc +0 -0
  91. sage/middleware/services/kv/search_engine/__pycache__/base_kv_index.cpython-311.pyc +0 -0
  92. sage/middleware/services/kv/search_engine/__pycache__/bm25s_index.cpython-311.opt-2.pyc +0 -0
  93. sage/middleware/services/kv/search_engine/__pycache__/bm25s_index.cpython-311.pyc +0 -0
  94. sage/middleware/services/kv/search_engine/base_kv_index.py +75 -0
  95. sage/middleware/services/kv/search_engine/bm25s_index.py +238 -0
  96. sage/middleware/services/memory/__init__.py +12 -0
  97. sage/middleware/services/memory/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  98. sage/middleware/services/memory/__pycache__/__init__.cpython-311.pyc +0 -0
  99. sage/middleware/services/memory/__pycache__/memory_service.cpython-311.opt-2.pyc +0 -0
  100. sage/middleware/services/memory/__pycache__/memory_service.cpython-311.pyc +0 -0
  101. sage/middleware/services/memory/examples/__pycache__/dag_microservices_demo.cpython-311.opt-2.pyc +0 -0
  102. sage/middleware/services/memory/examples/__pycache__/dag_microservices_demo.cpython-311.pyc +0 -0
  103. sage/middleware/services/memory/examples/__pycache__/memory_demo.cpython-311.opt-2.pyc +0 -0
  104. sage/middleware/services/memory/examples/__pycache__/memory_demo.cpython-311.pyc +0 -0
  105. sage/middleware/services/memory/examples/dag_microservices_demo.py +220 -0
  106. sage/middleware/services/memory/examples/memory_demo.py +490 -0
  107. sage/middleware/services/memory/memory_collection/__pycache__/base_collection.cpython-311.opt-2.pyc +0 -0
  108. sage/middleware/services/memory/memory_collection/__pycache__/base_collection.cpython-311.pyc +0 -0
  109. sage/middleware/services/memory/memory_collection/__pycache__/graph_collection.cpython-311.opt-2.pyc +0 -0
  110. sage/middleware/services/memory/memory_collection/__pycache__/graph_collection.cpython-311.pyc +0 -0
  111. sage/middleware/services/memory/memory_collection/__pycache__/kv_collection.cpython-311.opt-2.pyc +0 -0
  112. sage/middleware/services/memory/memory_collection/__pycache__/kv_collection.cpython-311.pyc +0 -0
  113. sage/middleware/services/memory/memory_collection/__pycache__/vdb_collection.cpython-311.opt-2.pyc +0 -0
  114. sage/middleware/services/memory/memory_collection/__pycache__/vdb_collection.cpython-311.pyc +0 -0
  115. sage/middleware/services/memory/memory_collection/base_collection.py +0 -0
  116. sage/middleware/services/memory/memory_collection/graph_collection.py +0 -0
  117. sage/middleware/services/memory/memory_collection/kv_collection.py +0 -0
  118. sage/middleware/services/memory/memory_collection/vdb_collection.py +0 -0
  119. sage/middleware/services/memory/memory_service.py +474 -0
  120. sage/middleware/services/memory/utils/__init__.py +0 -0
  121. sage/middleware/services/memory/utils/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  122. sage/middleware/services/memory/utils/__pycache__/__init__.cpython-311.pyc +0 -0
  123. sage/middleware/services/memory/utils/__pycache__/path_utils.cpython-311.opt-2.pyc +0 -0
  124. sage/middleware/services/memory/utils/__pycache__/path_utils.cpython-311.pyc +0 -0
  125. sage/middleware/services/memory/utils/path_utils.py +0 -0
  126. sage/middleware/services/vdb/__init__.py +8 -0
  127. sage/middleware/services/vdb/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  128. sage/middleware/services/vdb/__pycache__/__init__.cpython-311.pyc +0 -0
  129. sage/middleware/services/vdb/__pycache__/vdb_service.cpython-311.opt-2.pyc +0 -0
  130. sage/middleware/services/vdb/__pycache__/vdb_service.cpython-311.pyc +0 -0
  131. sage/middleware/services/vdb/examples/__pycache__/vdb_demo.cpython-311.opt-2.pyc +0 -0
  132. sage/middleware/services/vdb/examples/__pycache__/vdb_demo.cpython-311.pyc +0 -0
  133. sage/middleware/services/vdb/examples/vdb_demo.py +447 -0
  134. sage/middleware/services/vdb/search_engine/__init__.py +0 -0
  135. sage/middleware/services/vdb/search_engine/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  136. sage/middleware/services/vdb/search_engine/__pycache__/__init__.cpython-311.pyc +0 -0
  137. sage/middleware/services/vdb/search_engine/__pycache__/base_vdb_index.cpython-311.opt-2.pyc +0 -0
  138. sage/middleware/services/vdb/search_engine/__pycache__/base_vdb_index.cpython-311.pyc +0 -0
  139. sage/middleware/services/vdb/search_engine/__pycache__/faiss_index.cpython-311.opt-2.pyc +0 -0
  140. sage/middleware/services/vdb/search_engine/__pycache__/faiss_index.cpython-311.pyc +0 -0
  141. sage/middleware/services/vdb/search_engine/base_vdb_index.py +58 -0
  142. sage/middleware/services/vdb/search_engine/faiss_index.py +461 -0
  143. sage/middleware/services/vdb/vdb_service.py +433 -0
  144. sage/middleware/utils/__init__.py +5 -0
  145. sage/middleware/utils/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  146. sage/middleware/utils/__pycache__/__init__.cpython-311.pyc +0 -0
  147. sage/middleware/utils/embedding/__init__.py +35 -0
  148. sage/middleware/utils/embedding/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
  149. sage/middleware/utils/embedding/__pycache__/__init__.cpython-311.pyc +0 -0
  150. sage/middleware/utils/embedding/__pycache__/_cohere.cpython-311.opt-2.pyc +0 -0
  151. sage/middleware/utils/embedding/__pycache__/_cohere.cpython-311.pyc +0 -0
  152. sage/middleware/utils/embedding/__pycache__/bedrock.cpython-311.opt-2.pyc +0 -0
  153. sage/middleware/utils/embedding/__pycache__/bedrock.cpython-311.pyc +0 -0
  154. sage/middleware/utils/embedding/__pycache__/embedding_api.cpython-311.opt-2.pyc +0 -0
  155. sage/middleware/utils/embedding/__pycache__/embedding_api.cpython-311.pyc +0 -0
  156. sage/middleware/utils/embedding/__pycache__/embedding_model.cpython-311.opt-2.pyc +0 -0
  157. sage/middleware/utils/embedding/__pycache__/embedding_model.cpython-311.pyc +0 -0
  158. sage/middleware/utils/embedding/__pycache__/hf.cpython-311.opt-2.pyc +0 -0
  159. sage/middleware/utils/embedding/__pycache__/hf.cpython-311.pyc +0 -0
  160. sage/middleware/utils/embedding/__pycache__/instructor.cpython-311.opt-2.pyc +0 -0
  161. sage/middleware/utils/embedding/__pycache__/instructor.cpython-311.pyc +0 -0
  162. sage/middleware/utils/embedding/__pycache__/jina.cpython-311.opt-2.pyc +0 -0
  163. sage/middleware/utils/embedding/__pycache__/jina.cpython-311.pyc +0 -0
  164. sage/middleware/utils/embedding/__pycache__/lollms.cpython-311.opt-2.pyc +0 -0
  165. sage/middleware/utils/embedding/__pycache__/lollms.cpython-311.pyc +0 -0
  166. sage/middleware/utils/embedding/__pycache__/mockembedder.cpython-311.opt-2.pyc +0 -0
  167. sage/middleware/utils/embedding/__pycache__/mockembedder.cpython-311.pyc +0 -0
  168. sage/middleware/utils/embedding/__pycache__/nvidia_openai.cpython-311.opt-2.pyc +0 -0
  169. sage/middleware/utils/embedding/__pycache__/nvidia_openai.cpython-311.pyc +0 -0
  170. sage/middleware/utils/embedding/__pycache__/ollama.cpython-311.opt-2.pyc +0 -0
  171. sage/middleware/utils/embedding/__pycache__/ollama.cpython-311.pyc +0 -0
  172. sage/middleware/utils/embedding/__pycache__/openai.cpython-311.opt-2.pyc +0 -0
  173. sage/middleware/utils/embedding/__pycache__/openai.cpython-311.pyc +0 -0
  174. sage/middleware/utils/embedding/__pycache__/siliconcloud.cpython-311.opt-2.pyc +0 -0
  175. sage/middleware/utils/embedding/__pycache__/siliconcloud.cpython-311.pyc +0 -0
  176. sage/middleware/utils/embedding/__pycache__/zhipu.cpython-311.opt-2.pyc +0 -0
  177. sage/middleware/utils/embedding/__pycache__/zhipu.cpython-311.pyc +0 -0
  178. sage/middleware/utils/embedding/_cohere.py +68 -0
  179. sage/middleware/utils/embedding/bedrock.py +174 -0
  180. sage/middleware/utils/embedding/embedding_api.py +12 -0
  181. sage/middleware/utils/embedding/embedding_model.py +150 -0
  182. sage/middleware/utils/embedding/hf.py +90 -0
  183. sage/middleware/utils/embedding/instructor.py +10 -0
  184. sage/middleware/utils/embedding/jina.py +115 -0
  185. sage/middleware/utils/embedding/lollms.py +100 -0
  186. sage/middleware/utils/embedding/mockembedder.py +46 -0
  187. sage/middleware/utils/embedding/nvidia_openai.py +97 -0
  188. sage/middleware/utils/embedding/ollama.py +97 -0
  189. sage/middleware/utils/embedding/openai.py +112 -0
  190. sage/middleware/utils/embedding/siliconcloud.py +133 -0
  191. sage/middleware/utils/embedding/zhipu.py +85 -0
@@ -0,0 +1,395 @@
1
+ """
2
+ SAGE DB - ้ซ˜ๆ€ง่ƒฝๅ‘้‡ๆ•ฐๆฎๅบ“
3
+
4
+ ็›ดๆŽฅๅฏผๅ‡บ C++ ๆ‰ฉๅฑ•ไธญ็š„็ฑปๅ’Œๅ‡ฝๆ•ฐ
5
+ """
6
+
7
+ # ็›ดๆŽฅไปŽ C++ ๆ‰ฉๅฑ•ๅฏผๅ…ฅๆ‰€ๆœ‰ๅฟ…่ฆ็š„็ฑป
8
+ try:
9
+ from ._sage_db import (
10
+ SageDB,
11
+ DatabaseConfig,
12
+ IndexType,
13
+ DistanceMetric,
14
+ QueryResult,
15
+ SearchParams,
16
+ SageDBException,
17
+ create_database,
18
+ index_type_to_string,
19
+ string_to_index_type,
20
+ distance_metric_to_string,
21
+ string_to_distance_metric
22
+ )
23
+ print("โœ“ SAGE DB C++ extension loaded successfully")
24
+ except ImportError as e:
25
+ raise ImportError(f"Failed to import SAGE DB C++ extension: {e}")
26
+
27
+ # ๅฏผๅ‡บๆ‰€ๆœ‰API
28
+ __all__ = [
29
+ 'SageDB',
30
+ 'DatabaseConfig',
31
+ 'IndexType',
32
+ 'DistanceMetric',
33
+ 'QueryResult',
34
+ 'SearchParams',
35
+ 'SageDBException',
36
+ 'create_database',
37
+ 'index_type_to_string',
38
+ 'string_to_index_type',
39
+ 'distance_metric_to_string',
40
+ 'string_to_distance_metric'
41
+ ]
42
+
43
+ import numpy as np
44
+ from typing import List, Dict, Any, Optional, Callable, Tuple, Union
45
+
46
+ # ๅฐ่ฏ•ๅฏผๅ…ฅC++ๆ‰ฉๅฑ•
47
+ try:
48
+ from . import _sage_db
49
+ _cpp_available = True
50
+ except ImportError:
51
+ try:
52
+ import _sage_db
53
+ _cpp_available = True
54
+ except ImportError:
55
+ _cpp_available = False
56
+ raise ImportError("SAGE DB C++ extension not available")
57
+
58
+ # ้‡ๆ–ฐๅฏผๅ‡บC++็ฑปๅ’Œๆžšไธพ
59
+ IndexType = _sage_db.IndexType
60
+ DistanceMetric = _sage_db.DistanceMetric
61
+ QueryResult = _sage_db.QueryResult
62
+ SearchParams = _sage_db.SearchParams
63
+ DatabaseConfig = _sage_db.DatabaseConfig
64
+ SageDBException = _sage_db.SageDBException
65
+
66
+ class SageDB:
67
+ """
68
+ High-performance vector database with FAISS backend.
69
+
70
+ Supports efficient similarity search, metadata filtering, and hybrid search.
71
+ """
72
+
73
+ def __init__(self, dimension: int,
74
+ index_type: IndexType = IndexType.AUTO,
75
+ metric: DistanceMetric = DistanceMetric.L2):
76
+ """
77
+ Initialize a new SageDB instance.
78
+
79
+ Args:
80
+ dimension: Vector dimension
81
+ index_type: Index type for similarity search
82
+ metric: Distance metric
83
+ """
84
+ self._db = _sage_db.create_database(dimension, index_type, metric)
85
+
86
+ @classmethod
87
+ def from_config(cls, config: DatabaseConfig):
88
+ """Create SageDB from configuration object."""
89
+ instance = cls.__new__(cls)
90
+ instance._db = _sage_db.create_database(config)
91
+ return instance
92
+
93
+ def add(self, vector: Union[List[float], np.ndarray],
94
+ metadata: Optional[Dict[str, str]] = None) -> int:
95
+ """Add a single vector with optional metadata."""
96
+ if isinstance(vector, np.ndarray):
97
+ vector = vector.tolist()
98
+ return self._db.add(vector, metadata or {})
99
+
100
+ def add_batch(self, vectors: Union[List[List[float]], np.ndarray],
101
+ metadata: Optional[List[Dict[str, str]]] = None) -> List[int]:
102
+ """Add multiple vectors with optional metadata."""
103
+ if isinstance(vectors, np.ndarray):
104
+ if len(vectors.shape) != 2:
105
+ raise ValueError("Vectors array must be 2-dimensional")
106
+ return _sage_db.add_numpy(self._db, vectors, metadata or [])
107
+ else:
108
+ return self._db.add_batch(vectors, metadata or [])
109
+
110
+ def search(self, query: Union[List[float], np.ndarray],
111
+ k: int = 10, include_metadata: bool = True) -> List[QueryResult]:
112
+ """Search for similar vectors."""
113
+ if isinstance(query, np.ndarray):
114
+ return _sage_db.search_numpy(self._db, query, SearchParams(k))
115
+ return self._db.search(query, k, include_metadata)
116
+
117
+ def search_with_params(self, query: Union[List[float], np.ndarray],
118
+ params: SearchParams) -> List[QueryResult]:
119
+ """Search with custom parameters."""
120
+ if isinstance(query, np.ndarray):
121
+ return _sage_db.search_numpy(self._db, query, params)
122
+ return self._db.search(query, params)
123
+
124
+ def filtered_search(self, query: Union[List[float], np.ndarray],
125
+ params: SearchParams,
126
+ filter_fn: Callable[[Dict[str, str]], bool]) -> List[QueryResult]:
127
+ """Search with metadata filtering."""
128
+ if isinstance(query, np.ndarray):
129
+ query = query.tolist()
130
+ return self._db.filtered_search(query, params, filter_fn)
131
+
132
+ def remove(self, vector_id: int) -> bool:
133
+ """Remove a vector by ID."""
134
+ return self._db.remove(vector_id)
135
+
136
+ def update(self, vector_id: int, vector: Union[List[float], np.ndarray],
137
+ metadata: Optional[Dict[str, str]] = None) -> bool:
138
+ """Update a vector and its metadata."""
139
+ if isinstance(vector, np.ndarray):
140
+ vector = vector.tolist()
141
+ return self._db.update(vector_id, vector, metadata or {})
142
+
143
+ def build_index(self):
144
+ """Build the search index."""
145
+ self._db.build_index()
146
+
147
+ def train_index(self, training_data: Optional[List[List[float]]] = None):
148
+ """Train the index with training data."""
149
+ self._db.train_index(training_data or [])
150
+
151
+ def is_trained(self) -> bool:
152
+ """Check if the index is trained."""
153
+ return self._db.is_trained()
154
+
155
+ def set_metadata(self, vector_id: int, metadata: Dict[str, str]) -> bool:
156
+ """Set metadata for a vector."""
157
+ return self._db.set_metadata(vector_id, metadata)
158
+
159
+ def get_metadata(self, vector_id: int) -> Optional[Dict[str, str]]:
160
+ """Get metadata for a vector."""
161
+ result = self._db.get_metadata(vector_id)
162
+ return result if result is not None else None
163
+
164
+ def find_by_metadata(self, key: str, value: str) -> List[int]:
165
+ """Find vectors by metadata key-value pair."""
166
+ return self._db.find_by_metadata(key, value)
167
+
168
+ def save(self, filepath: str):
169
+ """Save database to file."""
170
+ self._db.save(filepath)
171
+
172
+ def load(self, filepath: str):
173
+ """Load database from file."""
174
+ self._db.load(filepath)
175
+
176
+ def size(self) -> int:
177
+ """Get number of vectors in database."""
178
+ return self._db.size()
179
+
180
+ def dimension(self) -> int:
181
+ """Get vector dimension."""
182
+ return self._db.dimension()
183
+
184
+ def index_type(self) -> IndexType:
185
+ """Get index type."""
186
+ return self._db.index_type()
187
+
188
+ def config(self) -> DatabaseConfig:
189
+ """Get database configuration."""
190
+ return self._db.config()
191
+
192
+ # ้ซ˜็บงๅŠŸ่ƒฝ่ฎฟ้—ฎ
193
+ def query_engine(self):
194
+ """Get query engine for advanced search operations."""
195
+ return self._db.query_engine()
196
+
197
+ def vector_store(self):
198
+ """Get vector store for low-level operations."""
199
+ return self._db.vector_store()
200
+
201
+ def metadata_store(self):
202
+ """Get metadata store for metadata operations."""
203
+ return self._db.metadata_store()
204
+
205
+ # ๅฏผๅ‡บ็š„API
206
+ __all__ = [
207
+ 'SageDB',
208
+ 'IndexType',
209
+ 'DistanceMetric',
210
+ 'QueryResult',
211
+ 'SearchParams',
212
+ 'DatabaseConfig',
213
+ 'SageDBException'
214
+ ]
215
+
216
+ import numpy as np
217
+ from typing import List, Dict, Any, Optional, Callable, Tuple, Union
218
+ try:
219
+ from . import _sage_db
220
+ except ImportError:
221
+ import _sage_db
222
+
223
+ # Re-export C++ classes and enums
224
+ IndexType = _sage_db.IndexType
225
+ DistanceMetric = _sage_db.DistanceMetric
226
+ QueryResult = _sage_db.QueryResult
227
+ SearchParams = _sage_db.SearchParams
228
+ DatabaseConfig = _sage_db.DatabaseConfig
229
+ SageDBException = _sage_db.SageDBException
230
+
231
+ class SageDB:
232
+ """
233
+ High-performance vector database with FAISS backend.
234
+
235
+ Supports efficient similarity search, metadata filtering, and hybrid search.
236
+ """
237
+
238
+ def __init__(self, dimension: int,
239
+ index_type: IndexType = IndexType.AUTO,
240
+ metric: DistanceMetric = DistanceMetric.L2):
241
+ """
242
+ Initialize a new SageDB instance.
243
+
244
+ Args:
245
+ dimension: Vector dimension
246
+ index_type: Index type for similarity search
247
+ metric: Distance metric
248
+ """
249
+ self._db = _sage_db.create_database(dimension, index_type, metric)
250
+
251
+ @classmethod
252
+ def from_config(cls, config: DatabaseConfig):
253
+ """Create SageDB from configuration object."""
254
+ instance = cls.__new__(cls)
255
+ instance._db = _sage_db.create_database(config)
256
+ return instance
257
+
258
+ def add(self, vector: Union[List[float], np.ndarray],
259
+ metadata: Optional[Dict[str, str]] = None) -> int:
260
+ """Add a single vector with optional metadata."""
261
+ if isinstance(vector, np.ndarray):
262
+ vector = vector.tolist()
263
+ return self._db.add(vector, metadata or {})
264
+
265
+ def add_batch(self, vectors: Union[List[List[float]], np.ndarray],
266
+ metadata: Optional[List[Dict[str, str]]] = None) -> List[int]:
267
+ """Add multiple vectors with optional metadata."""
268
+ if isinstance(vectors, np.ndarray):
269
+ if len(vectors.shape) != 2:
270
+ raise ValueError("Vectors array must be 2-dimensional")
271
+ return _sage_db.add_numpy(self._db, vectors, metadata or [])
272
+ else:
273
+ return self._db.add_batch(vectors, metadata or [])
274
+
275
+ def search(self, query: Union[List[float], np.ndarray],
276
+ k: int = 10, include_metadata: bool = True) -> List[QueryResult]:
277
+ """Search for similar vectors."""
278
+ if isinstance(query, np.ndarray):
279
+ return _sage_db.search_numpy(self._db, query, SearchParams(k))
280
+ return self._db.search(query, k, include_metadata)
281
+
282
+ def search_with_params(self, query: Union[List[float], np.ndarray],
283
+ params: SearchParams) -> List[QueryResult]:
284
+ """Search with custom parameters."""
285
+ if isinstance(query, np.ndarray):
286
+ return _sage_db.search_numpy(self._db, query, params)
287
+ return self._db.search(query, params)
288
+
289
+ def filtered_search(self, query: Union[List[float], np.ndarray],
290
+ params: SearchParams,
291
+ filter_fn: Callable[[Dict[str, str]], bool]) -> List[QueryResult]:
292
+ """Search with metadata filtering."""
293
+ if isinstance(query, np.ndarray):
294
+ query = query.tolist()
295
+ return self._db.filtered_search(query, params, filter_fn)
296
+
297
+ def search_by_metadata(self, query: Union[List[float], np.ndarray],
298
+ params: SearchParams,
299
+ metadata_key: str,
300
+ metadata_value: str) -> List[QueryResult]:
301
+ """Search with specific metadata constraint."""
302
+ if isinstance(query, np.ndarray):
303
+ query = query.tolist()
304
+ return self._db.query_engine().search_with_metadata(query, params, metadata_key, metadata_value)
305
+
306
+ def hybrid_search(self, query: Union[List[float], np.ndarray],
307
+ params: SearchParams,
308
+ text_query: str = "",
309
+ vector_weight: float = 0.7,
310
+ text_weight: float = 0.3) -> List[QueryResult]:
311
+ """Hybrid vector and text search."""
312
+ if isinstance(query, np.ndarray):
313
+ query = query.tolist()
314
+ return self._db.query_engine().hybrid_search(query, params, text_query, vector_weight, text_weight)
315
+
316
+ def build_index(self):
317
+ """Build/train the search index."""
318
+ self._db.build_index()
319
+
320
+ def train_index(self, training_vectors: Optional[Union[List[List[float]], np.ndarray]] = None):
321
+ """Train the index with training data."""
322
+ if training_vectors is None:
323
+ self._db.train_index()
324
+ elif isinstance(training_vectors, np.ndarray):
325
+ training_list = [training_vectors[i].tolist() for i in range(training_vectors.shape[0])]
326
+ self._db.train_index(training_list)
327
+ else:
328
+ self._db.train_index(training_vectors)
329
+
330
+ def is_trained(self) -> bool:
331
+ """Check if the index is trained."""
332
+ return self._db.is_trained()
333
+
334
+ def set_metadata(self, vector_id: int, metadata: Dict[str, str]) -> bool:
335
+ """Set metadata for a vector."""
336
+ return self._db.set_metadata(vector_id, metadata)
337
+
338
+ def get_metadata(self, vector_id: int) -> Optional[Dict[str, str]]:
339
+ """Get metadata for a vector."""
340
+ return self._db.get_metadata(vector_id)
341
+
342
+ def find_by_metadata(self, key: str, value: str) -> List[int]:
343
+ """Find vectors by metadata key-value."""
344
+ return self._db.find_by_metadata(key, value)
345
+
346
+ def save(self, filepath: str):
347
+ """Save database to disk."""
348
+ self._db.save(filepath)
349
+
350
+ def load(self, filepath: str):
351
+ """Load database from disk."""
352
+ self._db.load(filepath)
353
+
354
+ @property
355
+ def size(self) -> int:
356
+ """Number of vectors in the database."""
357
+ return self._db.size()
358
+
359
+ @property
360
+ def dimension(self) -> int:
361
+ """Vector dimension."""
362
+ return self._db.dimension()
363
+
364
+ @property
365
+ def index_type(self) -> IndexType:
366
+ """Index type."""
367
+ return self._db.index_type()
368
+
369
+ def get_search_stats(self) -> Dict[str, Any]:
370
+ """Get last search statistics."""
371
+ stats = self._db.query_engine().get_last_search_stats()
372
+ return {
373
+ 'total_candidates': stats.total_candidates,
374
+ 'filtered_candidates': stats.filtered_candidates,
375
+ 'final_results': stats.final_results,
376
+ 'search_time_ms': stats.search_time_ms,
377
+ 'filter_time_ms': stats.filter_time_ms,
378
+ 'total_time_ms': stats.total_time_ms
379
+ }
380
+
381
+ # Convenience functions
382
+ def create_database(dimension: int,
383
+ index_type: IndexType = IndexType.AUTO,
384
+ metric: DistanceMetric = DistanceMetric.L2) -> SageDB:
385
+ """Create a new SageDB instance."""
386
+ return SageDB(dimension, index_type, metric)
387
+
388
+ def create_database_from_config(config: DatabaseConfig) -> SageDB:
389
+ """Create SageDB from configuration."""
390
+ return SageDB.from_config(config)
391
+
392
+ __all__ = [
393
+ 'SageDB', 'IndexType', 'DistanceMetric', 'QueryResult', 'SearchParams',
394
+ 'DatabaseConfig', 'SageDBException', 'create_database', 'create_database_from_config'
395
+ ]
@@ -0,0 +1,144 @@
1
+ import sys
2
+ import os
3
+ import numpy as np
4
+
5
+ # Add the build directory to the path
6
+ sys.path.append('/home/shuhao/SAGE/sage_ext/sage_db/build')
7
+
8
+ try:
9
+ import sage_db_py
10
+ from sage_db import SageDatabase
11
+ print("โœ… Import successful!")
12
+ except ImportError as e:
13
+ print(f"โŒ Import failed: {e}")
14
+ print("Make sure to build the module first with ./build.sh")
15
+ sys.exit(1)
16
+
17
+ def test_python_api():
18
+ """Test the Python API wrapper"""
19
+ print("\n๐Ÿ Testing Python API...")
20
+
21
+ # Create database
22
+ db = SageDatabase(dimension=64)
23
+
24
+ # Add single vector
25
+ vector = np.random.random(64).astype(np.float32)
26
+ metadata = {"text": "test vector", "category": "example"}
27
+
28
+ vector_id = db.add_vector(vector, metadata)
29
+ print(f"Added vector with ID: {vector_id}")
30
+
31
+ # Add batch
32
+ batch_vectors = np.random.random((10, 64)).astype(np.float32)
33
+ batch_metadata = [{"text": f"vector_{i}", "batch": "test"} for i in range(10)]
34
+
35
+ batch_ids = db.add_batch(batch_vectors, batch_metadata)
36
+ print(f"Added batch of {len(batch_ids)} vectors")
37
+
38
+ # Search
39
+ query = np.random.random(64).astype(np.float32)
40
+ results = db.search(query, k=3)
41
+
42
+ print(f"Search returned {len(results)} results:")
43
+ for i, result in enumerate(results):
44
+ print(f" {i+1}. ID: {result['id']}, Distance: {result['distance']:.4f}")
45
+ print(f" Metadata: {result['metadata']}")
46
+
47
+ # Metadata search
48
+ category_results = db.find_by_metadata("category", "example")
49
+ print(f"Found {len(category_results)} vectors with category='example'")
50
+
51
+ print("โœ… Python API test passed!")
52
+
53
+ def test_pybind11_interface():
54
+ """Test the direct pybind11 interface"""
55
+ print("\n๐Ÿ”ง Testing pybind11 interface...")
56
+
57
+ # Test vector store
58
+ vector_store = sage_db_py.VectorStore(32)
59
+
60
+ # Add vectors
61
+ vec1 = np.array([1.0] * 32, dtype=np.float32)
62
+ vec2 = np.array([2.0] * 32, dtype=np.float32)
63
+
64
+ id1 = vector_store.add(vec1)
65
+ id2 = vector_store.add(vec2)
66
+
67
+ print(f"Added vectors with IDs: {id1}, {id2}")
68
+ print(f"Vector store size: {vector_store.size()}")
69
+
70
+ # Search
71
+ query = np.array([1.1] * 32, dtype=np.float32)
72
+ results = vector_store.search(query, 2)
73
+
74
+ print(f"Search results: {len(results)} items")
75
+ for result in results:
76
+ print(f" ID: {result['id']}, Distance: {result['distance']:.4f}")
77
+
78
+ # Test metadata store
79
+ metadata_store = sage_db_py.MetadataStore()
80
+
81
+ metadata_store.set(id1, {"name": "first", "type": "test"})
82
+ metadata_store.set(id2, {"name": "second", "type": "test"})
83
+
84
+ meta1 = metadata_store.get(id1)
85
+ print(f"Metadata for ID {id1}: {meta1}")
86
+
87
+ type_matches = metadata_store.find_by_key_value("type", "test")
88
+ print(f"Found {len(type_matches)} vectors with type='test'")
89
+
90
+ print("โœ… pybind11 interface test passed!")
91
+
92
+ def benchmark_python():
93
+ """Simple performance test"""
94
+ print("\n๐Ÿ“Š Python performance benchmark...")
95
+
96
+ import time
97
+
98
+ dimension = 128
99
+ num_vectors = 1000
100
+
101
+ db = SageDatabase(dimension=dimension)
102
+
103
+ # Generate random data
104
+ vectors = np.random.random((num_vectors, dimension)).astype(np.float32)
105
+ metadata = [{"id": str(i), "batch": "benchmark"} for i in range(num_vectors)]
106
+
107
+ # Time batch addition
108
+ start_time = time.time()
109
+ ids = db.add_batch(vectors, metadata)
110
+ add_time = time.time() - start_time
111
+
112
+ print(f"Added {num_vectors} vectors in {add_time:.3f} seconds")
113
+ print(f"Rate: {num_vectors / add_time:.1f} vectors/second")
114
+
115
+ # Time searches
116
+ num_queries = 100
117
+ query_vectors = np.random.random((num_queries, dimension)).astype(np.float32)
118
+
119
+ start_time = time.time()
120
+ for query in query_vectors:
121
+ results = db.search(query, k=10)
122
+ search_time = time.time() - start_time
123
+
124
+ print(f"Performed {num_queries} searches in {search_time:.3f} seconds")
125
+ print(f"Rate: {num_queries / search_time:.1f} searches/second")
126
+
127
+ print("โœ… Performance benchmark completed!")
128
+
129
+ if __name__ == "__main__":
130
+ print("๐Ÿงช SAGE DB Python Test Suite")
131
+ print("============================")
132
+
133
+ try:
134
+ test_python_api()
135
+ test_pybind11_interface()
136
+ benchmark_python()
137
+
138
+ print("\n๐ŸŽ‰ All Python tests passed!")
139
+
140
+ except Exception as e:
141
+ print(f"\nโŒ Test failed: {e}")
142
+ import traceback
143
+ traceback.print_exc()
144
+ sys.exit(1)