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.
- isage_middleware-0.1.0.dist-info/METADATA +424 -0
- isage_middleware-0.1.0.dist-info/RECORD +191 -0
- isage_middleware-0.1.0.dist-info/WHEEL +5 -0
- isage_middleware-0.1.0.dist-info/top_level.txt +1 -0
- sage/__init__.py +2 -0
- sage/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/__init__.py +83 -0
- sage/middleware/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/api/__init__.py +22 -0
- sage/middleware/api/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/api/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/api/__pycache__/graph_api.cpython-311.opt-2.pyc +0 -0
- sage/middleware/api/__pycache__/graph_api.cpython-311.pyc +0 -0
- sage/middleware/api/__pycache__/kv_api.cpython-311.opt-2.pyc +0 -0
- sage/middleware/api/__pycache__/kv_api.cpython-311.pyc +0 -0
- sage/middleware/api/__pycache__/memory_api.cpython-311.opt-2.pyc +0 -0
- sage/middleware/api/__pycache__/memory_api.cpython-311.pyc +0 -0
- sage/middleware/api/__pycache__/vdb_api.cpython-311.opt-2.pyc +0 -0
- sage/middleware/api/__pycache__/vdb_api.cpython-311.pyc +0 -0
- sage/middleware/api/graph_api.py +74 -0
- sage/middleware/api/kv_api.py +45 -0
- sage/middleware/api/memory_api.py +64 -0
- sage/middleware/api/vdb_api.py +60 -0
- sage/middleware/enterprise/__init__.py +75 -0
- sage/middleware/enterprise/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/enterprise/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/enterprise/sage_db/__init__.py +132 -0
- sage/middleware/enterprise/sage_db/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/enterprise/sage_db/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/enterprise/sage_db/__pycache__/sage_db.cpython-311.opt-2.pyc +0 -0
- sage/middleware/enterprise/sage_db/__pycache__/sage_db.cpython-311.pyc +0 -0
- sage/middleware/enterprise/sage_db/python/__init__.py +7 -0
- sage/middleware/enterprise/sage_db/python/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/enterprise/sage_db/python/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/enterprise/sage_db/python/__pycache__/sage_db.cpython-311.opt-2.pyc +0 -0
- sage/middleware/enterprise/sage_db/python/__pycache__/sage_db.cpython-311.pyc +0 -0
- sage/middleware/enterprise/sage_db/python/sage_db.py +44 -0
- sage/middleware/enterprise/sage_db/sage_db.py +395 -0
- sage/middleware/enterprise/sage_db/tests/__pycache__/test_python.cpython-311.opt-2.pyc +0 -0
- sage/middleware/enterprise/sage_db/tests/__pycache__/test_python.cpython-311.pyc +0 -0
- sage/middleware/enterprise/sage_db/tests/test_python.py +144 -0
- sage/middleware/examples/__pycache__/api_usage_tutorial.cpython-311.opt-2.pyc +0 -0
- sage/middleware/examples/__pycache__/api_usage_tutorial.cpython-311.pyc +0 -0
- sage/middleware/examples/__pycache__/dag_microservices_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/examples/__pycache__/dag_microservices_demo.cpython-311.pyc +0 -0
- sage/middleware/examples/__pycache__/microservices_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/examples/__pycache__/microservices_demo.cpython-311.pyc +0 -0
- sage/middleware/examples/__pycache__/microservices_integration_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/examples/__pycache__/microservices_integration_demo.cpython-311.pyc +0 -0
- sage/middleware/examples/__pycache__/microservices_registration_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/examples/__pycache__/microservices_registration_demo.cpython-311.pyc +0 -0
- sage/middleware/examples/api_usage_tutorial.py +339 -0
- sage/middleware/examples/dag_microservices_demo.py +220 -0
- sage/middleware/examples/microservices_demo.py +0 -0
- sage/middleware/examples/microservices_integration_demo.py +373 -0
- sage/middleware/examples/microservices_registration_demo.py +144 -0
- sage/middleware/py.typed +2 -0
- sage/middleware/services/graph/__init__.py +8 -0
- sage/middleware/services/graph/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/graph/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/graph/__pycache__/graph_index.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/graph/__pycache__/graph_index.cpython-311.pyc +0 -0
- sage/middleware/services/graph/__pycache__/graph_service.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/graph/__pycache__/graph_service.cpython-311.pyc +0 -0
- sage/middleware/services/graph/examples/__pycache__/graph_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/graph/examples/__pycache__/graph_demo.cpython-311.pyc +0 -0
- sage/middleware/services/graph/examples/graph_demo.py +177 -0
- sage/middleware/services/graph/graph_index.py +194 -0
- sage/middleware/services/graph/graph_service.py +541 -0
- sage/middleware/services/graph/search_engine/__init__.py +0 -0
- sage/middleware/services/graph/search_engine/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/graph/search_engine/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/graph/search_engine/__pycache__/base_graph_index.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/graph/search_engine/__pycache__/base_graph_index.cpython-311.pyc +0 -0
- sage/middleware/services/graph/search_engine/base_graph_index.py +0 -0
- sage/middleware/services/kv/__init__.py +8 -0
- sage/middleware/services/kv/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/kv/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/kv/__pycache__/kv_service.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/kv/__pycache__/kv_service.cpython-311.pyc +0 -0
- sage/middleware/services/kv/examples/__pycache__/kv_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/kv/examples/__pycache__/kv_demo.cpython-311.pyc +0 -0
- sage/middleware/services/kv/examples/kv_demo.py +213 -0
- sage/middleware/services/kv/kv_service.py +306 -0
- sage/middleware/services/kv/search_engine/__init__.py +0 -0
- sage/middleware/services/kv/search_engine/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/kv/search_engine/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/kv/search_engine/__pycache__/base_kv_index.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/kv/search_engine/__pycache__/base_kv_index.cpython-311.pyc +0 -0
- sage/middleware/services/kv/search_engine/__pycache__/bm25s_index.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/kv/search_engine/__pycache__/bm25s_index.cpython-311.pyc +0 -0
- sage/middleware/services/kv/search_engine/base_kv_index.py +75 -0
- sage/middleware/services/kv/search_engine/bm25s_index.py +238 -0
- sage/middleware/services/memory/__init__.py +12 -0
- sage/middleware/services/memory/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/memory/__pycache__/memory_service.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/__pycache__/memory_service.cpython-311.pyc +0 -0
- sage/middleware/services/memory/examples/__pycache__/dag_microservices_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/examples/__pycache__/dag_microservices_demo.cpython-311.pyc +0 -0
- sage/middleware/services/memory/examples/__pycache__/memory_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/examples/__pycache__/memory_demo.cpython-311.pyc +0 -0
- sage/middleware/services/memory/examples/dag_microservices_demo.py +220 -0
- sage/middleware/services/memory/examples/memory_demo.py +490 -0
- sage/middleware/services/memory/memory_collection/__pycache__/base_collection.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/memory_collection/__pycache__/base_collection.cpython-311.pyc +0 -0
- sage/middleware/services/memory/memory_collection/__pycache__/graph_collection.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/memory_collection/__pycache__/graph_collection.cpython-311.pyc +0 -0
- sage/middleware/services/memory/memory_collection/__pycache__/kv_collection.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/memory_collection/__pycache__/kv_collection.cpython-311.pyc +0 -0
- sage/middleware/services/memory/memory_collection/__pycache__/vdb_collection.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/memory_collection/__pycache__/vdb_collection.cpython-311.pyc +0 -0
- sage/middleware/services/memory/memory_collection/base_collection.py +0 -0
- sage/middleware/services/memory/memory_collection/graph_collection.py +0 -0
- sage/middleware/services/memory/memory_collection/kv_collection.py +0 -0
- sage/middleware/services/memory/memory_collection/vdb_collection.py +0 -0
- sage/middleware/services/memory/memory_service.py +474 -0
- sage/middleware/services/memory/utils/__init__.py +0 -0
- sage/middleware/services/memory/utils/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/utils/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/memory/utils/__pycache__/path_utils.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/memory/utils/__pycache__/path_utils.cpython-311.pyc +0 -0
- sage/middleware/services/memory/utils/path_utils.py +0 -0
- sage/middleware/services/vdb/__init__.py +8 -0
- sage/middleware/services/vdb/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/vdb/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/vdb/__pycache__/vdb_service.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/vdb/__pycache__/vdb_service.cpython-311.pyc +0 -0
- sage/middleware/services/vdb/examples/__pycache__/vdb_demo.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/vdb/examples/__pycache__/vdb_demo.cpython-311.pyc +0 -0
- sage/middleware/services/vdb/examples/vdb_demo.py +447 -0
- sage/middleware/services/vdb/search_engine/__init__.py +0 -0
- sage/middleware/services/vdb/search_engine/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/vdb/search_engine/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/services/vdb/search_engine/__pycache__/base_vdb_index.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/vdb/search_engine/__pycache__/base_vdb_index.cpython-311.pyc +0 -0
- sage/middleware/services/vdb/search_engine/__pycache__/faiss_index.cpython-311.opt-2.pyc +0 -0
- sage/middleware/services/vdb/search_engine/__pycache__/faiss_index.cpython-311.pyc +0 -0
- sage/middleware/services/vdb/search_engine/base_vdb_index.py +58 -0
- sage/middleware/services/vdb/search_engine/faiss_index.py +461 -0
- sage/middleware/services/vdb/vdb_service.py +433 -0
- sage/middleware/utils/__init__.py +5 -0
- sage/middleware/utils/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__init__.py +35 -0
- sage/middleware/utils/embedding/__pycache__/__init__.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/__init__.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/_cohere.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/_cohere.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/bedrock.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/bedrock.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/embedding_api.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/embedding_api.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/embedding_model.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/embedding_model.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/hf.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/hf.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/instructor.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/instructor.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/jina.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/jina.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/lollms.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/lollms.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/mockembedder.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/mockembedder.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/nvidia_openai.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/nvidia_openai.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/ollama.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/ollama.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/openai.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/openai.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/siliconcloud.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/siliconcloud.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/zhipu.cpython-311.opt-2.pyc +0 -0
- sage/middleware/utils/embedding/__pycache__/zhipu.cpython-311.pyc +0 -0
- sage/middleware/utils/embedding/_cohere.py +68 -0
- sage/middleware/utils/embedding/bedrock.py +174 -0
- sage/middleware/utils/embedding/embedding_api.py +12 -0
- sage/middleware/utils/embedding/embedding_model.py +150 -0
- sage/middleware/utils/embedding/hf.py +90 -0
- sage/middleware/utils/embedding/instructor.py +10 -0
- sage/middleware/utils/embedding/jina.py +115 -0
- sage/middleware/utils/embedding/lollms.py +100 -0
- sage/middleware/utils/embedding/mockembedder.py +46 -0
- sage/middleware/utils/embedding/nvidia_openai.py +97 -0
- sage/middleware/utils/embedding/ollama.py +97 -0
- sage/middleware/utils/embedding/openai.py +112 -0
- sage/middleware/utils/embedding/siliconcloud.py +133 -0
- sage/middleware/utils/embedding/zhipu.py +85 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VDB Service - 向量数据库微服务
|
|
3
|
+
提供向量存储和相似性搜索功能的服务任务,集成到SAGE DAG中
|
|
4
|
+
使用嵌入式FAISS引擎,不依赖外部数据库
|
|
5
|
+
"""
|
|
6
|
+
from typing import Dict, Any, Optional, List, Union, Tuple, TYPE_CHECKING
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
import numpy as np
|
|
9
|
+
import logging
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
|
|
13
|
+
from sage.core.api.service.base_service import BaseService
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from sage.core.factory.service_factory import ServiceFactory
|
|
17
|
+
from sage.kernel import ServiceContext
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class VDBConfig:
|
|
22
|
+
"""VDB服务配置"""
|
|
23
|
+
embedding_dimension: int = 384
|
|
24
|
+
index_type: str = "IndexFlatL2" # FAISS索引类型
|
|
25
|
+
max_vectors: int = 1000000
|
|
26
|
+
similarity_threshold: float = 0.8
|
|
27
|
+
# FAISS索引配置
|
|
28
|
+
faiss_config: Dict[str, Any] = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class VectorDocument:
|
|
33
|
+
"""向量文档"""
|
|
34
|
+
id: str
|
|
35
|
+
vector: List[float]
|
|
36
|
+
metadata: Dict[str, Any]
|
|
37
|
+
text: Optional[str] = None
|
|
38
|
+
created_at: float = None
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
if self.created_at is None:
|
|
42
|
+
self.created_at = time.time()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FaissVDBBackend:
|
|
46
|
+
"""基于FAISS的向量数据库后端"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, embedding_dimension: int = 384, index_type: str = "IndexFlatL2",
|
|
49
|
+
faiss_config: Optional[Dict[str, Any]] = None):
|
|
50
|
+
self.embedding_dimension = embedding_dimension
|
|
51
|
+
self.index_type = index_type
|
|
52
|
+
self.faiss_config = faiss_config or {}
|
|
53
|
+
self.logger = logging.getLogger(__name__)
|
|
54
|
+
|
|
55
|
+
# 导入FaissIndex
|
|
56
|
+
try:
|
|
57
|
+
from sage.middleware.services.vdb.search_engine.faiss_index import FaissIndex
|
|
58
|
+
|
|
59
|
+
# 初始化FAISS索引
|
|
60
|
+
self.faiss_index = FaissIndex(
|
|
61
|
+
name="vdb_index",
|
|
62
|
+
dim=embedding_dimension,
|
|
63
|
+
config={
|
|
64
|
+
"index_type": index_type,
|
|
65
|
+
**self.faiss_config
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# 文档存储 (id -> VectorDocument)
|
|
70
|
+
self.documents: Dict[str, VectorDocument] = {}
|
|
71
|
+
|
|
72
|
+
self.logger.info(f"FaissVDBBackend initialized with {index_type}, dim={embedding_dimension}")
|
|
73
|
+
|
|
74
|
+
except ImportError as e:
|
|
75
|
+
self.logger.error(f"Failed to import FaissIndex: {e}")
|
|
76
|
+
raise ImportError("FAISS dependencies not available. Install with: pip install faiss-cpu")
|
|
77
|
+
|
|
78
|
+
def add_vectors(self, vectors: List[VectorDocument]) -> List[str]:
|
|
79
|
+
"""添加向量文档"""
|
|
80
|
+
try:
|
|
81
|
+
if not vectors:
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
# 准备向量和ID
|
|
85
|
+
vector_arrays = []
|
|
86
|
+
vector_ids = []
|
|
87
|
+
|
|
88
|
+
for doc in vectors:
|
|
89
|
+
# 如果没有ID,生成一个
|
|
90
|
+
if not doc.id:
|
|
91
|
+
doc.id = str(uuid.uuid4())
|
|
92
|
+
|
|
93
|
+
# 验证向量维度
|
|
94
|
+
if len(doc.vector) != self.embedding_dimension:
|
|
95
|
+
self.logger.warning(f"Vector dimension mismatch: expected {self.embedding_dimension}, got {len(doc.vector)}")
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
vector_arrays.append(np.array(doc.vector, dtype=np.float32))
|
|
99
|
+
vector_ids.append(doc.id)
|
|
100
|
+
|
|
101
|
+
# 存储文档
|
|
102
|
+
self.documents[doc.id] = doc
|
|
103
|
+
|
|
104
|
+
if vector_arrays:
|
|
105
|
+
# 添加到FAISS索引
|
|
106
|
+
vectors_np = np.vstack(vector_arrays)
|
|
107
|
+
self.faiss_index.add(vectors_np, vector_ids)
|
|
108
|
+
|
|
109
|
+
self.logger.debug(f"Added {len(vector_ids)} vectors to FAISS index")
|
|
110
|
+
return vector_ids
|
|
111
|
+
else:
|
|
112
|
+
return []
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
self.logger.error(f"Error adding vectors: {e}")
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
def search_vectors(self, query_vector: List[float], top_k: int = 5,
|
|
119
|
+
metadata_filter: Optional[Dict[str, Any]] = None) -> List[Tuple[VectorDocument, float]]:
|
|
120
|
+
"""搜索相似向量"""
|
|
121
|
+
try:
|
|
122
|
+
if len(query_vector) != self.embedding_dimension:
|
|
123
|
+
raise ValueError(f"Query vector dimension mismatch: expected {self.embedding_dimension}, got {len(query_vector)}")
|
|
124
|
+
|
|
125
|
+
# 转换查询向量
|
|
126
|
+
query_np = np.array([query_vector], dtype=np.float32)
|
|
127
|
+
|
|
128
|
+
# 在FAISS中搜索
|
|
129
|
+
distances, indices, ids = self.faiss_index.search(query_np, top_k)
|
|
130
|
+
|
|
131
|
+
results = []
|
|
132
|
+
for i, (distance, doc_id) in enumerate(zip(distances[0], ids[0])):
|
|
133
|
+
if doc_id in self.documents:
|
|
134
|
+
doc = self.documents[doc_id]
|
|
135
|
+
|
|
136
|
+
# 应用元数据过滤
|
|
137
|
+
if metadata_filter and not self._matches_filter(doc.metadata, metadata_filter):
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
results.append((doc, float(distance)))
|
|
141
|
+
else:
|
|
142
|
+
self.logger.warning(f"Document {doc_id} found in index but not in storage")
|
|
143
|
+
|
|
144
|
+
self.logger.debug(f"Search returned {len(results)} results")
|
|
145
|
+
return results
|
|
146
|
+
|
|
147
|
+
except Exception as e:
|
|
148
|
+
self.logger.error(f"Error searching vectors: {e}")
|
|
149
|
+
return []
|
|
150
|
+
|
|
151
|
+
def get_vector(self, doc_id: str) -> Optional[VectorDocument]:
|
|
152
|
+
"""获取向量文档"""
|
|
153
|
+
return self.documents.get(doc_id)
|
|
154
|
+
|
|
155
|
+
def delete_vectors(self, doc_ids: List[str]) -> int:
|
|
156
|
+
"""删除向量文档"""
|
|
157
|
+
try:
|
|
158
|
+
deleted_count = 0
|
|
159
|
+
valid_ids = []
|
|
160
|
+
|
|
161
|
+
for doc_id in doc_ids:
|
|
162
|
+
if doc_id in self.documents:
|
|
163
|
+
del self.documents[doc_id]
|
|
164
|
+
valid_ids.append(doc_id)
|
|
165
|
+
deleted_count += 1
|
|
166
|
+
|
|
167
|
+
# 从FAISS索引中删除
|
|
168
|
+
if valid_ids:
|
|
169
|
+
self.faiss_index.delete(valid_ids)
|
|
170
|
+
|
|
171
|
+
self.logger.debug(f"Deleted {deleted_count} vectors")
|
|
172
|
+
return deleted_count
|
|
173
|
+
|
|
174
|
+
except Exception as e:
|
|
175
|
+
self.logger.error(f"Error deleting vectors: {e}")
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
def list_vectors(self, metadata_filter: Optional[Dict[str, Any]] = None) -> List[VectorDocument]:
|
|
179
|
+
"""列出向量文档"""
|
|
180
|
+
if metadata_filter is None:
|
|
181
|
+
return list(self.documents.values())
|
|
182
|
+
|
|
183
|
+
return [doc for doc in self.documents.values()
|
|
184
|
+
if self._matches_filter(doc.metadata, metadata_filter)]
|
|
185
|
+
|
|
186
|
+
def count(self) -> int:
|
|
187
|
+
"""获取向量数量"""
|
|
188
|
+
return len(self.documents)
|
|
189
|
+
|
|
190
|
+
def clear(self) -> None:
|
|
191
|
+
"""清空所有向量"""
|
|
192
|
+
self.documents.clear()
|
|
193
|
+
# 重新初始化FAISS索引
|
|
194
|
+
try:
|
|
195
|
+
from sage.middleware.services.vdb.search_engine.faiss_index import FaissIndex
|
|
196
|
+
self.faiss_index = FaissIndex(
|
|
197
|
+
name="vdb_index",
|
|
198
|
+
dim=self.embedding_dimension,
|
|
199
|
+
config={
|
|
200
|
+
"index_type": self.index_type,
|
|
201
|
+
**self.faiss_config
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
except Exception as e:
|
|
205
|
+
self.logger.error(f"Error reinitializing FAISS index: {e}")
|
|
206
|
+
|
|
207
|
+
def save_index(self, path: str) -> bool:
|
|
208
|
+
"""保存索引到磁盘"""
|
|
209
|
+
try:
|
|
210
|
+
self.faiss_index.save(path)
|
|
211
|
+
return True
|
|
212
|
+
except Exception as e:
|
|
213
|
+
self.logger.error(f"Error saving index: {e}")
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
def load_index(self, path: str) -> bool:
|
|
217
|
+
"""从磁盘加载索引"""
|
|
218
|
+
try:
|
|
219
|
+
from sage.middleware.services.vdb.search_engine.faiss_index import FaissIndex
|
|
220
|
+
self.faiss_index = FaissIndex(
|
|
221
|
+
name="vdb_index",
|
|
222
|
+
dim=self.embedding_dimension,
|
|
223
|
+
load_path=path
|
|
224
|
+
)
|
|
225
|
+
return True
|
|
226
|
+
except Exception as e:
|
|
227
|
+
self.logger.error(f"Error loading index: {e}")
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
def _matches_filter(self, metadata: Dict[str, Any], filter_dict: Dict[str, Any]) -> bool:
|
|
231
|
+
"""检查元数据是否匹配过滤条件"""
|
|
232
|
+
for key, value in filter_dict.items():
|
|
233
|
+
if key not in metadata:
|
|
234
|
+
return False
|
|
235
|
+
if metadata[key] != value:
|
|
236
|
+
return False
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
240
|
+
"""获取索引统计信息"""
|
|
241
|
+
try:
|
|
242
|
+
return {
|
|
243
|
+
"total_vectors": self.count(),
|
|
244
|
+
"index_type": self.index_type,
|
|
245
|
+
"embedding_dimension": self.embedding_dimension,
|
|
246
|
+
"faiss_index_ntotal": getattr(self.faiss_index.index, 'ntotal', 0) if self.faiss_index.index else 0,
|
|
247
|
+
"max_vectors": getattr(self, 'max_vectors', -1)
|
|
248
|
+
}
|
|
249
|
+
except Exception as e:
|
|
250
|
+
self.logger.error(f"Error getting stats: {e}")
|
|
251
|
+
return {"error": str(e)}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class VDBService(BaseService):
|
|
255
|
+
"""
|
|
256
|
+
VDB服务任务
|
|
257
|
+
|
|
258
|
+
提供向量存储和相似性搜索功能,可以在SAGE DAG中作为服务节点使用
|
|
259
|
+
使用嵌入式FAISS引擎,高性能的本地向量检索
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
def __init__(self, service_factory: 'ServiceFactory', ctx: 'ServiceContext' = None):
|
|
263
|
+
super().__init__(service_factory, ctx)
|
|
264
|
+
|
|
265
|
+
# 从service_factory获取配置
|
|
266
|
+
self.config: VDBConfig = getattr(service_factory, 'config', VDBConfig())
|
|
267
|
+
|
|
268
|
+
# 初始化FAISS后端
|
|
269
|
+
self.backend = FaissVDBBackend(
|
|
270
|
+
embedding_dimension=self.config.embedding_dimension,
|
|
271
|
+
index_type=self.config.index_type,
|
|
272
|
+
faiss_config=self.config.faiss_config or {}
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
self.logger.info(f"VDB Service '{self.service_name}' initialized with FAISS backend")
|
|
276
|
+
self.logger.info(f"Index type: {self.config.index_type}, Dimension: {self.config.embedding_dimension}")
|
|
277
|
+
|
|
278
|
+
def _start_service_instance(self):
|
|
279
|
+
"""启动VDB服务实例"""
|
|
280
|
+
self.logger.info(f"VDB Service '{self.service_name}' started")
|
|
281
|
+
|
|
282
|
+
def _stop_service_instance(self):
|
|
283
|
+
"""停止VDB服务实例"""
|
|
284
|
+
self.logger.info(f"VDB Service '{self.service_name}' stopped")
|
|
285
|
+
|
|
286
|
+
# VDB操作方法 - 这些方法可以通过服务调用机制被调用
|
|
287
|
+
|
|
288
|
+
def add_vectors(self, vectors: List[Dict[str, Any]]) -> List[str]:
|
|
289
|
+
"""添加向量文档"""
|
|
290
|
+
self.logger.debug(f"Adding {len(vectors)} vectors")
|
|
291
|
+
|
|
292
|
+
# 转换输入格式
|
|
293
|
+
docs = []
|
|
294
|
+
for v in vectors:
|
|
295
|
+
doc = VectorDocument(
|
|
296
|
+
id=v.get('id', ''),
|
|
297
|
+
vector=v['vector'],
|
|
298
|
+
metadata=v.get('metadata', {}),
|
|
299
|
+
text=v.get('text')
|
|
300
|
+
)
|
|
301
|
+
docs.append(doc)
|
|
302
|
+
|
|
303
|
+
result = self.backend.add_vectors(docs)
|
|
304
|
+
self.logger.debug(f"Added {len(result)} vectors successfully")
|
|
305
|
+
return result
|
|
306
|
+
|
|
307
|
+
def search_vectors(self, query_vector: List[float], top_k: int = 5,
|
|
308
|
+
metadata_filter: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
|
|
309
|
+
"""搜索相似向量"""
|
|
310
|
+
self.logger.debug(f"Searching vectors with top_k={top_k}")
|
|
311
|
+
|
|
312
|
+
results = self.backend.search_vectors(query_vector, top_k, metadata_filter)
|
|
313
|
+
|
|
314
|
+
# 转换输出格式
|
|
315
|
+
formatted_results = []
|
|
316
|
+
for doc, distance in results:
|
|
317
|
+
formatted_results.append({
|
|
318
|
+
'id': doc.id,
|
|
319
|
+
'vector': doc.vector,
|
|
320
|
+
'metadata': doc.metadata,
|
|
321
|
+
'text': doc.text,
|
|
322
|
+
'distance': distance,
|
|
323
|
+
'created_at': doc.created_at
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
self.logger.debug(f"Search returned {len(formatted_results)} results")
|
|
327
|
+
return formatted_results
|
|
328
|
+
|
|
329
|
+
def get_vector(self, doc_id: str) -> Optional[Dict[str, Any]]:
|
|
330
|
+
"""获取向量文档"""
|
|
331
|
+
self.logger.debug(f"Getting vector: {doc_id}")
|
|
332
|
+
|
|
333
|
+
doc = self.backend.get_vector(doc_id)
|
|
334
|
+
if doc:
|
|
335
|
+
return {
|
|
336
|
+
'id': doc.id,
|
|
337
|
+
'vector': doc.vector,
|
|
338
|
+
'metadata': doc.metadata,
|
|
339
|
+
'text': doc.text,
|
|
340
|
+
'created_at': doc.created_at
|
|
341
|
+
}
|
|
342
|
+
return None
|
|
343
|
+
|
|
344
|
+
def delete_vectors(self, doc_ids: List[str]) -> int:
|
|
345
|
+
"""删除向量文档"""
|
|
346
|
+
self.logger.debug(f"Deleting {len(doc_ids)} vectors")
|
|
347
|
+
result = self.backend.delete_vectors(doc_ids)
|
|
348
|
+
self.logger.debug(f"Deleted {result} vectors successfully")
|
|
349
|
+
return result
|
|
350
|
+
|
|
351
|
+
def list_vectors(self, metadata_filter: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
|
|
352
|
+
"""列出向量文档"""
|
|
353
|
+
self.logger.debug("Listing vectors")
|
|
354
|
+
|
|
355
|
+
docs = self.backend.list_vectors(metadata_filter)
|
|
356
|
+
results = []
|
|
357
|
+
for doc in docs:
|
|
358
|
+
results.append({
|
|
359
|
+
'id': doc.id,
|
|
360
|
+
'vector': doc.vector,
|
|
361
|
+
'metadata': doc.metadata,
|
|
362
|
+
'text': doc.text,
|
|
363
|
+
'created_at': doc.created_at
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
self.logger.debug(f"Listed {len(results)} vectors")
|
|
367
|
+
return results
|
|
368
|
+
|
|
369
|
+
def count(self) -> int:
|
|
370
|
+
"""获取向量数量"""
|
|
371
|
+
result = self.backend.count()
|
|
372
|
+
self.logger.debug(f"Vector count: {result}")
|
|
373
|
+
return result
|
|
374
|
+
|
|
375
|
+
def clear(self) -> None:
|
|
376
|
+
"""清空所有向量"""
|
|
377
|
+
self.logger.debug("Clearing all vectors")
|
|
378
|
+
self.backend.clear()
|
|
379
|
+
|
|
380
|
+
def save_index(self, path: str) -> bool:
|
|
381
|
+
"""保存索引到磁盘"""
|
|
382
|
+
self.logger.debug(f"Saving index to: {path}")
|
|
383
|
+
return self.backend.save_index(path)
|
|
384
|
+
|
|
385
|
+
def load_index(self, path: str) -> bool:
|
|
386
|
+
"""从磁盘加载索引"""
|
|
387
|
+
self.logger.debug(f"Loading index from: {path}")
|
|
388
|
+
return self.backend.load_index(path)
|
|
389
|
+
|
|
390
|
+
def stats(self) -> Dict[str, Any]:
|
|
391
|
+
"""获取服务统计信息"""
|
|
392
|
+
base_stats = self.get_statistics()
|
|
393
|
+
vdb_stats = self.backend.get_stats()
|
|
394
|
+
base_stats.update(vdb_stats)
|
|
395
|
+
return base_stats
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# 工厂函数,用于在DAG中创建VDB服务
|
|
399
|
+
def create_vdb_service_factory(
|
|
400
|
+
service_name: str = "vdb_service",
|
|
401
|
+
embedding_dimension: int = 384,
|
|
402
|
+
index_type: str = "IndexFlatL2",
|
|
403
|
+
max_vectors: int = 1000000,
|
|
404
|
+
similarity_threshold: float = 0.8,
|
|
405
|
+
faiss_config: Optional[Dict[str, Any]] = None
|
|
406
|
+
):
|
|
407
|
+
"""
|
|
408
|
+
创建VDB服务工厂
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
service_name: 服务名称
|
|
412
|
+
embedding_dimension: 向量维度
|
|
413
|
+
index_type: FAISS索引类型 ("IndexFlatL2", "IndexHNSWFlat", "IndexIVFFlat", etc.)
|
|
414
|
+
max_vectors: 最大向量数量
|
|
415
|
+
similarity_threshold: 相似度阈值
|
|
416
|
+
faiss_config: FAISS索引配置
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
ServiceFactory: 可以用于注册到环境的服务工厂
|
|
420
|
+
"""
|
|
421
|
+
from sage.core.factory.service_factory import ServiceFactory
|
|
422
|
+
|
|
423
|
+
config = VDBConfig(
|
|
424
|
+
embedding_dimension=embedding_dimension,
|
|
425
|
+
index_type=index_type,
|
|
426
|
+
max_vectors=max_vectors,
|
|
427
|
+
similarity_threshold=similarity_threshold,
|
|
428
|
+
faiss_config=faiss_config or {}
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
factory = ServiceFactory(service_name, VDBService)
|
|
432
|
+
factory.config = config
|
|
433
|
+
return factory
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Embedding methods and models for SAGE middleware.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .embedding_api import apply_embedding_model
|
|
6
|
+
from .embedding_model import EmbeddingModel
|
|
7
|
+
|
|
8
|
+
# Import specific embedding implementations
|
|
9
|
+
from . import hf
|
|
10
|
+
from . import ollama
|
|
11
|
+
from . import siliconcloud
|
|
12
|
+
from . import openai
|
|
13
|
+
from . import bedrock
|
|
14
|
+
from . import zhipu
|
|
15
|
+
from . import mockembedder
|
|
16
|
+
from . import _cohere
|
|
17
|
+
from . import nvidia_openai
|
|
18
|
+
from . import lollms
|
|
19
|
+
from . import jina
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'apply_embedding_model',
|
|
23
|
+
'EmbeddingModel',
|
|
24
|
+
'hf',
|
|
25
|
+
'ollama',
|
|
26
|
+
'siliconcloud',
|
|
27
|
+
'openai',
|
|
28
|
+
'bedrock',
|
|
29
|
+
'zhipu',
|
|
30
|
+
'mockembedder',
|
|
31
|
+
'_cohere',
|
|
32
|
+
'nvidia_openai',
|
|
33
|
+
'lollms',
|
|
34
|
+
'jina'
|
|
35
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import cohere
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def cohere_embed(
|
|
9
|
+
texts: list[str], api_key: str, model: str = "embed-multilingual-v3.0", input_type: str = "classification",
|
|
10
|
+
embedding_types: list[str] = ["float"]
|
|
11
|
+
) -> list[float]:
|
|
12
|
+
if api_key is None:
|
|
13
|
+
api_key = os.environ.get("COHERE_API_KEY")
|
|
14
|
+
# print(api_key)
|
|
15
|
+
co = cohere.AsyncClient(api_key=api_key)
|
|
16
|
+
|
|
17
|
+
response = await co.embed(
|
|
18
|
+
texts=texts,
|
|
19
|
+
model=model,
|
|
20
|
+
input_type=input_type,
|
|
21
|
+
# embedding_types=embedding_types
|
|
22
|
+
)
|
|
23
|
+
return response.embeddings
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import cohere
|
|
27
|
+
|
|
28
|
+
def cohere_embed_sync(
|
|
29
|
+
texts: list[str],
|
|
30
|
+
api_key: str = None,
|
|
31
|
+
model: str = "embed-multilingual-v3.0",
|
|
32
|
+
input_type: str = "classification",
|
|
33
|
+
embedding_types: list[str] = ["float"]
|
|
34
|
+
) -> list[list[float]]:
|
|
35
|
+
"""
|
|
36
|
+
同步版本:使用 Cohere 同步客户端生成文本 embeddings。
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
texts: 文本列表
|
|
40
|
+
api_key: Cohere API Key
|
|
41
|
+
model: 模型名称
|
|
42
|
+
input_type: 输入类型,如 classification、search_document 等
|
|
43
|
+
embedding_types: 嵌入格式(默认 float)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
list[list[float]]: 每个文本对应的嵌入向量
|
|
47
|
+
"""
|
|
48
|
+
if api_key is None:
|
|
49
|
+
api_key = os.environ.get("COHERE_API_KEY")
|
|
50
|
+
if api_key is None:
|
|
51
|
+
raise ValueError("Cohere API key must be provided.")
|
|
52
|
+
|
|
53
|
+
co = cohere.Client(api_key=api_key)
|
|
54
|
+
|
|
55
|
+
response = co.embed(
|
|
56
|
+
texts=texts,
|
|
57
|
+
model=model,
|
|
58
|
+
input_type=input_type,
|
|
59
|
+
embedding_types=embedding_types,
|
|
60
|
+
)
|
|
61
|
+
return response.embeddings
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|