isage-middleware 0.2.4.3__cp311-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.
- isage_middleware-0.2.4.3.dist-info/METADATA +266 -0
- isage_middleware-0.2.4.3.dist-info/RECORD +94 -0
- isage_middleware-0.2.4.3.dist-info/WHEEL +5 -0
- isage_middleware-0.2.4.3.dist-info/top_level.txt +1 -0
- sage/middleware/__init__.py +59 -0
- sage/middleware/_version.py +6 -0
- sage/middleware/components/__init__.py +30 -0
- sage/middleware/components/extensions_compat.py +141 -0
- sage/middleware/components/sage_db/__init__.py +116 -0
- sage/middleware/components/sage_db/backend.py +136 -0
- sage/middleware/components/sage_db/service.py +15 -0
- sage/middleware/components/sage_flow/__init__.py +76 -0
- sage/middleware/components/sage_flow/python/__init__.py +14 -0
- sage/middleware/components/sage_flow/python/micro_service/__init__.py +4 -0
- sage/middleware/components/sage_flow/python/micro_service/sage_flow_service.py +88 -0
- sage/middleware/components/sage_flow/python/sage_flow.py +30 -0
- sage/middleware/components/sage_flow/service.py +14 -0
- sage/middleware/components/sage_mem/__init__.py +83 -0
- sage/middleware/components/sage_sias/__init__.py +59 -0
- sage/middleware/components/sage_sias/continual_learner.py +184 -0
- sage/middleware/components/sage_sias/coreset_selector.py +302 -0
- sage/middleware/components/sage_sias/types.py +94 -0
- sage/middleware/components/sage_tsdb/__init__.py +81 -0
- sage/middleware/components/sage_tsdb/python/__init__.py +21 -0
- sage/middleware/components/sage_tsdb/python/_sage_tsdb.pyi +17 -0
- sage/middleware/components/sage_tsdb/python/algorithms/__init__.py +17 -0
- sage/middleware/components/sage_tsdb/python/algorithms/base.py +51 -0
- sage/middleware/components/sage_tsdb/python/algorithms/out_of_order_join.py +248 -0
- sage/middleware/components/sage_tsdb/python/algorithms/window_aggregator.py +296 -0
- sage/middleware/components/sage_tsdb/python/micro_service/__init__.py +7 -0
- sage/middleware/components/sage_tsdb/python/micro_service/sage_tsdb_service.py +365 -0
- sage/middleware/components/sage_tsdb/python/sage_tsdb.py +523 -0
- sage/middleware/components/sage_tsdb/service.py +17 -0
- sage/middleware/components/vector_stores/__init__.py +25 -0
- sage/middleware/components/vector_stores/chroma.py +483 -0
- sage/middleware/components/vector_stores/chroma_adapter.py +185 -0
- sage/middleware/components/vector_stores/milvus.py +677 -0
- sage/middleware/operators/__init__.py +56 -0
- sage/middleware/operators/agent/__init__.py +24 -0
- sage/middleware/operators/agent/planning/__init__.py +5 -0
- sage/middleware/operators/agent/planning/llm_adapter.py +41 -0
- sage/middleware/operators/agent/planning/planner_adapter.py +98 -0
- sage/middleware/operators/agent/planning/router.py +107 -0
- sage/middleware/operators/agent/runtime.py +296 -0
- sage/middleware/operators/agentic/__init__.py +41 -0
- sage/middleware/operators/agentic/config.py +254 -0
- sage/middleware/operators/agentic/planning_operator.py +125 -0
- sage/middleware/operators/agentic/refined_searcher.py +132 -0
- sage/middleware/operators/agentic/runtime.py +241 -0
- sage/middleware/operators/agentic/timing_operator.py +125 -0
- sage/middleware/operators/agentic/tool_selection_operator.py +127 -0
- sage/middleware/operators/context/__init__.py +17 -0
- sage/middleware/operators/context/critic_evaluation.py +16 -0
- sage/middleware/operators/context/model_context.py +565 -0
- sage/middleware/operators/context/quality_label.py +12 -0
- sage/middleware/operators/context/search_query_results.py +61 -0
- sage/middleware/operators/context/search_result.py +42 -0
- sage/middleware/operators/context/search_session.py +79 -0
- sage/middleware/operators/filters/__init__.py +26 -0
- sage/middleware/operators/filters/context_sink.py +387 -0
- sage/middleware/operators/filters/context_source.py +376 -0
- sage/middleware/operators/filters/evaluate_filter.py +83 -0
- sage/middleware/operators/filters/tool_filter.py +74 -0
- sage/middleware/operators/llm/__init__.py +18 -0
- sage/middleware/operators/llm/sagellm_generator.py +432 -0
- sage/middleware/operators/rag/__init__.py +147 -0
- sage/middleware/operators/rag/arxiv.py +331 -0
- sage/middleware/operators/rag/chunk.py +13 -0
- sage/middleware/operators/rag/document_loaders.py +23 -0
- sage/middleware/operators/rag/evaluate.py +658 -0
- sage/middleware/operators/rag/generator.py +340 -0
- sage/middleware/operators/rag/index_builder/__init__.py +48 -0
- sage/middleware/operators/rag/index_builder/builder.py +363 -0
- sage/middleware/operators/rag/index_builder/manifest.py +101 -0
- sage/middleware/operators/rag/index_builder/storage.py +131 -0
- sage/middleware/operators/rag/pipeline.py +46 -0
- sage/middleware/operators/rag/profiler.py +59 -0
- sage/middleware/operators/rag/promptor.py +400 -0
- sage/middleware/operators/rag/refiner.py +231 -0
- sage/middleware/operators/rag/reranker.py +364 -0
- sage/middleware/operators/rag/retriever.py +1308 -0
- sage/middleware/operators/rag/searcher.py +37 -0
- sage/middleware/operators/rag/types.py +28 -0
- sage/middleware/operators/rag/writer.py +80 -0
- sage/middleware/operators/tools/__init__.py +71 -0
- sage/middleware/operators/tools/arxiv_paper_searcher.py +175 -0
- sage/middleware/operators/tools/arxiv_searcher.py +102 -0
- sage/middleware/operators/tools/duckduckgo_searcher.py +105 -0
- sage/middleware/operators/tools/image_captioner.py +104 -0
- sage/middleware/operators/tools/nature_news_fetcher.py +224 -0
- sage/middleware/operators/tools/searcher_tool.py +514 -0
- sage/middleware/operators/tools/text_detector.py +185 -0
- sage/middleware/operators/tools/url_text_extractor.py +104 -0
- sage/middleware/py.typed +2 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from openai import OpenAI
|
|
8
|
+
|
|
9
|
+
from sage.common.config.output_paths import get_states_file
|
|
10
|
+
from sage.common.core.functions import MapFunction as MapOperator
|
|
11
|
+
from sage.libs.integrations.huggingface import HFClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OpenAIGenerator(MapOperator):
|
|
15
|
+
"""
|
|
16
|
+
生成节点:调用 OpenAI-Compatible / SageLLM 等端点。
|
|
17
|
+
|
|
18
|
+
调用方式::
|
|
19
|
+
sub_conf = config["generator"]["sagellm"] # <- 单端点子配置
|
|
20
|
+
gen = OpenAIGenerator(sub_conf)
|
|
21
|
+
|
|
22
|
+
其中 `sub_conf` 结构示例::
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
"method": "openai",
|
|
26
|
+
"model_name": "gpt-4o-mini",
|
|
27
|
+
"base_url": "http://localhost:8000/v1",
|
|
28
|
+
"api_key": "xxx", # pragma: allowlist secret
|
|
29
|
+
"seed": 42
|
|
30
|
+
}
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, config: dict, enable_profile=False, **kwargs):
|
|
34
|
+
super().__init__(**kwargs)
|
|
35
|
+
|
|
36
|
+
# 直接持有子配置
|
|
37
|
+
self.config = config
|
|
38
|
+
self.enable_profile = enable_profile
|
|
39
|
+
|
|
40
|
+
# 实例化模型
|
|
41
|
+
# API key 优先级: 配置文件 > OPENAI_API_KEY
|
|
42
|
+
api_key = self.config.get("api_key") or os.getenv("OPENAI_API_KEY")
|
|
43
|
+
|
|
44
|
+
# 获取必需的配置参数(使用 .get() 提供默认值)
|
|
45
|
+
model_name = self.config.get("model_name") or self.config.get("model", "gpt-3.5-turbo")
|
|
46
|
+
# 展开环境变量(如果 model_name 包含环境变量)
|
|
47
|
+
model_name = os.path.expandvars(model_name)
|
|
48
|
+
base_url = self.config.get("base_url", "https://api.openai.com/v1")
|
|
49
|
+
|
|
50
|
+
# 直接使用 OpenAI 客户端(支持 sagellm 等 OpenAI 兼容 API)
|
|
51
|
+
self.model = OpenAI(
|
|
52
|
+
base_url=base_url,
|
|
53
|
+
api_key=api_key or "EMPTY", # 本地服务可用任意 key
|
|
54
|
+
)
|
|
55
|
+
self.model_name = model_name
|
|
56
|
+
self.num = 1
|
|
57
|
+
|
|
58
|
+
# 只有启用profile时才设置数据存储路径
|
|
59
|
+
if self.enable_profile:
|
|
60
|
+
# Use unified output path system
|
|
61
|
+
self.data_base_path = str(get_states_file("dummy", "generator_data").parent)
|
|
62
|
+
os.makedirs(self.data_base_path, exist_ok=True)
|
|
63
|
+
self.data_records = []
|
|
64
|
+
|
|
65
|
+
def _save_data_record(self, query, prompt, response):
|
|
66
|
+
"""保存生成数据记录"""
|
|
67
|
+
if not self.enable_profile:
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
record = {
|
|
71
|
+
"timestamp": time.time(),
|
|
72
|
+
"query": query,
|
|
73
|
+
"prompt": prompt,
|
|
74
|
+
"response": response,
|
|
75
|
+
"model_name": self.config.get("model_name") or self.config.get("model", "unknown"),
|
|
76
|
+
}
|
|
77
|
+
self.data_records.append(record)
|
|
78
|
+
self._persist_data_records()
|
|
79
|
+
|
|
80
|
+
def _persist_data_records(self):
|
|
81
|
+
"""将数据记录持久化到文件"""
|
|
82
|
+
if not self.enable_profile or not self.data_records:
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
timestamp = int(time.time())
|
|
86
|
+
filename = f"generator_data_{timestamp}.json"
|
|
87
|
+
path = os.path.join(self.data_base_path, filename)
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
91
|
+
json.dump(self.data_records, f, ensure_ascii=False, indent=2)
|
|
92
|
+
self.data_records = []
|
|
93
|
+
except Exception as e:
|
|
94
|
+
self.logger.error(f"Failed to persist data records: {e}")
|
|
95
|
+
|
|
96
|
+
def execute(self, data: list[Any]) -> dict[str, Any]:
|
|
97
|
+
"""
|
|
98
|
+
输入 : [original_data, prompt] *或* [prompt]
|
|
99
|
+
输出 : 完整的数据字典,包含 generated 字段
|
|
100
|
+
|
|
101
|
+
prompt 可以是:
|
|
102
|
+
- str: 普通字符串,将转换为 [{"role": "user", "content": prompt}]
|
|
103
|
+
- list[dict]: 已格式化的消息列表,直接传递给 OpenAI API
|
|
104
|
+
"""
|
|
105
|
+
# 解析输入数据
|
|
106
|
+
if len(data) > 1:
|
|
107
|
+
# 来自QAPromptor: [original_data, prompt]
|
|
108
|
+
original_data = data[0]
|
|
109
|
+
prompt = data[1]
|
|
110
|
+
else:
|
|
111
|
+
# 直接prompt输入: [prompt]
|
|
112
|
+
original_data = {}
|
|
113
|
+
prompt = data[0]
|
|
114
|
+
|
|
115
|
+
# 提取user_query
|
|
116
|
+
if isinstance(original_data, dict):
|
|
117
|
+
user_query = original_data.get("query", original_data.get("question", ""))
|
|
118
|
+
else:
|
|
119
|
+
user_query = None
|
|
120
|
+
|
|
121
|
+
# 如果 prompt 是字符串,转换为标准消息格式
|
|
122
|
+
if isinstance(prompt, str):
|
|
123
|
+
messages = [{"role": "user", "content": prompt}]
|
|
124
|
+
elif isinstance(prompt, list) and all(isinstance(item, dict) for item in prompt):
|
|
125
|
+
# 如果已经是消息列表格式,直接使用
|
|
126
|
+
messages = prompt
|
|
127
|
+
else:
|
|
128
|
+
# 兜底处理:转换为字符串再构造消息
|
|
129
|
+
messages = [{"role": "user", "content": str(prompt)}]
|
|
130
|
+
|
|
131
|
+
# 准备生成参数(从配置中提取)
|
|
132
|
+
generate_kwargs = {}
|
|
133
|
+
|
|
134
|
+
# 支持的参数列表
|
|
135
|
+
supported_params = [
|
|
136
|
+
"max_tokens",
|
|
137
|
+
"temperature",
|
|
138
|
+
"top_p",
|
|
139
|
+
"enable_thinking", # Qwen 特有参数:禁用思考过程输出
|
|
140
|
+
"stream",
|
|
141
|
+
"frequency_penalty",
|
|
142
|
+
"n",
|
|
143
|
+
"logprobs",
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
# 从配置中提取参数并传递给 generate
|
|
147
|
+
for param in supported_params:
|
|
148
|
+
if param in self.config:
|
|
149
|
+
generate_kwargs[param] = self.config[param]
|
|
150
|
+
|
|
151
|
+
# 使用 OpenAI 客户端调用 chat completions API
|
|
152
|
+
completion = self.model.chat.completions.create(
|
|
153
|
+
model=self.model_name,
|
|
154
|
+
messages=messages,
|
|
155
|
+
**generate_kwargs,
|
|
156
|
+
)
|
|
157
|
+
response = completion.choices[0].message.content
|
|
158
|
+
|
|
159
|
+
self.num += 1
|
|
160
|
+
|
|
161
|
+
# 保存数据记录(只有enable_profile=True时才保存)
|
|
162
|
+
if self.enable_profile:
|
|
163
|
+
self._save_data_record(user_query, prompt, response)
|
|
164
|
+
|
|
165
|
+
self.logger.info(f"[{self.__class__.__name__}] Response: {response}")
|
|
166
|
+
|
|
167
|
+
# 构建完整的输出数据,保持上游数据
|
|
168
|
+
if isinstance(original_data, dict):
|
|
169
|
+
# 保持原始数据结构,添加generated字段
|
|
170
|
+
result = dict(original_data)
|
|
171
|
+
result["generated"] = response
|
|
172
|
+
# generate_time 由 MapOperator 自动添加
|
|
173
|
+
result["question"] = result.get(
|
|
174
|
+
"question",
|
|
175
|
+
{"query": user_query, "references": result.get("references", [])},
|
|
176
|
+
)
|
|
177
|
+
return result
|
|
178
|
+
else:
|
|
179
|
+
# 兼容原有tuple格式输出,但符合返回类型
|
|
180
|
+
return {
|
|
181
|
+
"query": user_query if user_query is not None else "",
|
|
182
|
+
"generated": response,
|
|
183
|
+
# generate_time 由 MapOperator 自动添加
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
def __del__(self):
|
|
187
|
+
"""确保在对象销毁时保存所有未保存的记录"""
|
|
188
|
+
if hasattr(self, "enable_profile") and self.enable_profile:
|
|
189
|
+
try:
|
|
190
|
+
self._persist_data_records()
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class HFGenerator(MapOperator):
|
|
196
|
+
"""
|
|
197
|
+
HFGenerator is a generator rag that interfaces with a Hugging Face model
|
|
198
|
+
to generate responses based on input data.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def __init__(self, config, **kwargs):
|
|
202
|
+
"""
|
|
203
|
+
Initializes the HFGenerator instance with configuration parameters.
|
|
204
|
+
|
|
205
|
+
:param config: Dictionary containing configuration for the generator, including
|
|
206
|
+
the method and model name.
|
|
207
|
+
"""
|
|
208
|
+
super().__init__(**kwargs)
|
|
209
|
+
self.config = config
|
|
210
|
+
# Apply the generator model with the provided configuration
|
|
211
|
+
self.model = HFClient(model_name=self.config["model_name"])
|
|
212
|
+
|
|
213
|
+
def execute(self, data: list, **kwargs) -> tuple[str, str]:
|
|
214
|
+
"""
|
|
215
|
+
Executes the response generation using the configured Hugging Face model based on the input data.
|
|
216
|
+
|
|
217
|
+
:param data: Data object containing a list of input data.
|
|
218
|
+
The expected format and the content of the data depend on the model's requirements.
|
|
219
|
+
:param kwargs: Additional parameters for the model generation (e.g., temperature, max_tokens, etc.).
|
|
220
|
+
|
|
221
|
+
:return: A Data object containing the generated response as a string.
|
|
222
|
+
"""
|
|
223
|
+
# Generate the response from the Hugging Face model using the provided data and additional arguments
|
|
224
|
+
user_query = data[0] if len(data) > 1 else None
|
|
225
|
+
|
|
226
|
+
prompt = data[1] if len(data) > 1 else data[0]
|
|
227
|
+
|
|
228
|
+
response = self.model.generate(prompt, **kwargs)
|
|
229
|
+
|
|
230
|
+
print(f"\033[32m[ {self.__class__.__name__}]: Response: {response}\033[0m ")
|
|
231
|
+
|
|
232
|
+
# Return the generated response as a Data object
|
|
233
|
+
self.logger.info(f"\033[32m[ {self.__class__.__name__}]: Response: {response}\033[0m ")
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
user_query if user_query is not None else "",
|
|
237
|
+
response if isinstance(response, str) else str(response),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@dataclass
|
|
242
|
+
class SageLLMRAGGenerator(MapOperator):
|
|
243
|
+
"""
|
|
244
|
+
RAG 生成器 - 使用 SageLLM 引擎
|
|
245
|
+
|
|
246
|
+
通过 engine_type 参数选择底层 LLM 引擎:
|
|
247
|
+
- sagellm (默认): 使用 SageLLMGenerator,支持 auto/mock/cuda/ascend 后端
|
|
248
|
+
|
|
249
|
+
Example:
|
|
250
|
+
```python
|
|
251
|
+
# 使用 sagellm 引擎(推荐)
|
|
252
|
+
generator = SageLLMRAGGenerator(
|
|
253
|
+
engine_type="sagellm",
|
|
254
|
+
backend_type="auto",
|
|
255
|
+
model_path="Qwen/Qwen2.5-7B-Instruct",
|
|
256
|
+
max_tokens=2048,
|
|
257
|
+
)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Attributes:
|
|
261
|
+
engine_type: 引擎类型,支持 "sagellm"(默认)
|
|
262
|
+
backend_type: 后端类型,支持 "auto"/"mock"/"cuda"/"ascend"
|
|
263
|
+
model_path: 模型路径或 HuggingFace 模型 ID
|
|
264
|
+
max_tokens: 最大生成 token 数
|
|
265
|
+
temperature: 采样温度
|
|
266
|
+
top_p: nucleus 采样参数
|
|
267
|
+
timeout: 请求超时时间
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
# 引擎选择
|
|
271
|
+
engine_type: str = "sagellm" # sagellm only
|
|
272
|
+
backend_type: str = "auto" # auto/mock/cuda/ascend
|
|
273
|
+
|
|
274
|
+
# SageLLM 配置
|
|
275
|
+
model_path: str = ""
|
|
276
|
+
device_map: str = "auto"
|
|
277
|
+
dtype: str = "auto"
|
|
278
|
+
|
|
279
|
+
# 生成参数
|
|
280
|
+
max_tokens: int = 2048
|
|
281
|
+
temperature: float = 0.7
|
|
282
|
+
top_p: float = 0.95
|
|
283
|
+
top_k: int = 50
|
|
284
|
+
|
|
285
|
+
# 配置
|
|
286
|
+
timeout: float = 120.0
|
|
287
|
+
default_options: dict[str, Any] = field(default_factory=dict)
|
|
288
|
+
|
|
289
|
+
# 内部状态
|
|
290
|
+
_generator: Any = field(default=None, init=False, repr=False)
|
|
291
|
+
|
|
292
|
+
def __post_init__(self) -> None:
|
|
293
|
+
super().__init__()
|
|
294
|
+
self._init_generator()
|
|
295
|
+
|
|
296
|
+
def _init_generator(self) -> None:
|
|
297
|
+
"""根据 engine_type 初始化底层生成器"""
|
|
298
|
+
if self.engine_type != "sagellm":
|
|
299
|
+
# 只支持 sagellm
|
|
300
|
+
raise ValueError(
|
|
301
|
+
f"Unsupported engine_type='{self.engine_type}'. "
|
|
302
|
+
f"Only 'sagellm' is supported. vLLM support has been removed in v0.3.0."
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# 默认使用 sagellm
|
|
306
|
+
from sage.middleware.operators.llm import SageLLMGenerator
|
|
307
|
+
|
|
308
|
+
self._generator = SageLLMGenerator(
|
|
309
|
+
backend_type=self.backend_type,
|
|
310
|
+
model_path=self.model_path,
|
|
311
|
+
device_map=self.device_map,
|
|
312
|
+
dtype=self.dtype,
|
|
313
|
+
max_tokens=self.max_tokens,
|
|
314
|
+
temperature=self.temperature,
|
|
315
|
+
top_p=self.top_p,
|
|
316
|
+
top_k=self.top_k,
|
|
317
|
+
timeout=self.timeout,
|
|
318
|
+
default_options=self.default_options,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
def execute(self, data: list[Any]) -> dict[str, Any]:
|
|
322
|
+
"""
|
|
323
|
+
执行生成,委托给底层生成器
|
|
324
|
+
|
|
325
|
+
输入 : [original_data, prompt] 或 [prompt]
|
|
326
|
+
输出 : 包含 generated 字段的数据字典
|
|
327
|
+
"""
|
|
328
|
+
result = self._generator.execute(data)
|
|
329
|
+
|
|
330
|
+
# 统一输出格式
|
|
331
|
+
if isinstance(result, dict):
|
|
332
|
+
return result
|
|
333
|
+
elif isinstance(result, tuple) and len(result) >= 2:
|
|
334
|
+
# Generator returns (original, text)
|
|
335
|
+
return {
|
|
336
|
+
"query": result[0] if result[0] else "",
|
|
337
|
+
"generated": result[1],
|
|
338
|
+
}
|
|
339
|
+
else:
|
|
340
|
+
return {"generated": str(result)}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""RAG Index Building Service (L4 Middleware Operator)
|
|
2
|
+
|
|
3
|
+
This module provides index building functionality for RAG systems.
|
|
4
|
+
It orchestrates document processing, embedding, and vector storage.
|
|
5
|
+
|
|
6
|
+
Layer: L4 (sage-middleware/operators/rag)
|
|
7
|
+
Dependencies:
|
|
8
|
+
- sage.libs.rag (L3) - chunk, document_loaders
|
|
9
|
+
- sage.middleware.components.sage_db (L4) - SageDB backend
|
|
10
|
+
- sage.common (L1) - embedding models
|
|
11
|
+
|
|
12
|
+
Components:
|
|
13
|
+
- VectorStore: Protocol defining vector storage interface
|
|
14
|
+
- IndexManifest: Metadata describing a built index
|
|
15
|
+
- IndexBuilder: Service for building vector indices
|
|
16
|
+
|
|
17
|
+
Architecture Pattern:
|
|
18
|
+
- L4 defines IndexBuilder (orchestration)
|
|
19
|
+
- L4 provides SageDB backend implementation
|
|
20
|
+
- L3 provides ChromaDB backend via integrations
|
|
21
|
+
- L5 (sage-cli) uses IndexBuilder
|
|
22
|
+
|
|
23
|
+
Example Usage:
|
|
24
|
+
>>> from sage.middleware.operators.rag.index_builder import IndexBuilder
|
|
25
|
+
>>> from sage.middleware.components.sage_db import SageVDBBackend
|
|
26
|
+
>>>
|
|
27
|
+
>>> # Create backend factory
|
|
28
|
+
>>> def factory(path, dim):
|
|
29
|
+
... return SageVDBBackend(path, dim)
|
|
30
|
+
>>>
|
|
31
|
+
>>> # Build index
|
|
32
|
+
>>> builder = IndexBuilder(backend_factory=factory)
|
|
33
|
+
>>> manifest = builder.build_from_docs(
|
|
34
|
+
... source_dir=Path("docs"),
|
|
35
|
+
... persist_path=Path(".sage/index"),
|
|
36
|
+
... embedding_model=embedder,
|
|
37
|
+
... )
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from sage.middleware.operators.rag.index_builder.builder import IndexBuilder
|
|
41
|
+
from sage.middleware.operators.rag.index_builder.manifest import IndexManifest
|
|
42
|
+
from sage.middleware.operators.rag.index_builder.storage import VectorStore
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"IndexBuilder",
|
|
46
|
+
"IndexManifest",
|
|
47
|
+
"VectorStore",
|
|
48
|
+
]
|