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.
Files changed (94) hide show
  1. isage_middleware-0.2.4.3.dist-info/METADATA +266 -0
  2. isage_middleware-0.2.4.3.dist-info/RECORD +94 -0
  3. isage_middleware-0.2.4.3.dist-info/WHEEL +5 -0
  4. isage_middleware-0.2.4.3.dist-info/top_level.txt +1 -0
  5. sage/middleware/__init__.py +59 -0
  6. sage/middleware/_version.py +6 -0
  7. sage/middleware/components/__init__.py +30 -0
  8. sage/middleware/components/extensions_compat.py +141 -0
  9. sage/middleware/components/sage_db/__init__.py +116 -0
  10. sage/middleware/components/sage_db/backend.py +136 -0
  11. sage/middleware/components/sage_db/service.py +15 -0
  12. sage/middleware/components/sage_flow/__init__.py +76 -0
  13. sage/middleware/components/sage_flow/python/__init__.py +14 -0
  14. sage/middleware/components/sage_flow/python/micro_service/__init__.py +4 -0
  15. sage/middleware/components/sage_flow/python/micro_service/sage_flow_service.py +88 -0
  16. sage/middleware/components/sage_flow/python/sage_flow.py +30 -0
  17. sage/middleware/components/sage_flow/service.py +14 -0
  18. sage/middleware/components/sage_mem/__init__.py +83 -0
  19. sage/middleware/components/sage_sias/__init__.py +59 -0
  20. sage/middleware/components/sage_sias/continual_learner.py +184 -0
  21. sage/middleware/components/sage_sias/coreset_selector.py +302 -0
  22. sage/middleware/components/sage_sias/types.py +94 -0
  23. sage/middleware/components/sage_tsdb/__init__.py +81 -0
  24. sage/middleware/components/sage_tsdb/python/__init__.py +21 -0
  25. sage/middleware/components/sage_tsdb/python/_sage_tsdb.pyi +17 -0
  26. sage/middleware/components/sage_tsdb/python/algorithms/__init__.py +17 -0
  27. sage/middleware/components/sage_tsdb/python/algorithms/base.py +51 -0
  28. sage/middleware/components/sage_tsdb/python/algorithms/out_of_order_join.py +248 -0
  29. sage/middleware/components/sage_tsdb/python/algorithms/window_aggregator.py +296 -0
  30. sage/middleware/components/sage_tsdb/python/micro_service/__init__.py +7 -0
  31. sage/middleware/components/sage_tsdb/python/micro_service/sage_tsdb_service.py +365 -0
  32. sage/middleware/components/sage_tsdb/python/sage_tsdb.py +523 -0
  33. sage/middleware/components/sage_tsdb/service.py +17 -0
  34. sage/middleware/components/vector_stores/__init__.py +25 -0
  35. sage/middleware/components/vector_stores/chroma.py +483 -0
  36. sage/middleware/components/vector_stores/chroma_adapter.py +185 -0
  37. sage/middleware/components/vector_stores/milvus.py +677 -0
  38. sage/middleware/operators/__init__.py +56 -0
  39. sage/middleware/operators/agent/__init__.py +24 -0
  40. sage/middleware/operators/agent/planning/__init__.py +5 -0
  41. sage/middleware/operators/agent/planning/llm_adapter.py +41 -0
  42. sage/middleware/operators/agent/planning/planner_adapter.py +98 -0
  43. sage/middleware/operators/agent/planning/router.py +107 -0
  44. sage/middleware/operators/agent/runtime.py +296 -0
  45. sage/middleware/operators/agentic/__init__.py +41 -0
  46. sage/middleware/operators/agentic/config.py +254 -0
  47. sage/middleware/operators/agentic/planning_operator.py +125 -0
  48. sage/middleware/operators/agentic/refined_searcher.py +132 -0
  49. sage/middleware/operators/agentic/runtime.py +241 -0
  50. sage/middleware/operators/agentic/timing_operator.py +125 -0
  51. sage/middleware/operators/agentic/tool_selection_operator.py +127 -0
  52. sage/middleware/operators/context/__init__.py +17 -0
  53. sage/middleware/operators/context/critic_evaluation.py +16 -0
  54. sage/middleware/operators/context/model_context.py +565 -0
  55. sage/middleware/operators/context/quality_label.py +12 -0
  56. sage/middleware/operators/context/search_query_results.py +61 -0
  57. sage/middleware/operators/context/search_result.py +42 -0
  58. sage/middleware/operators/context/search_session.py +79 -0
  59. sage/middleware/operators/filters/__init__.py +26 -0
  60. sage/middleware/operators/filters/context_sink.py +387 -0
  61. sage/middleware/operators/filters/context_source.py +376 -0
  62. sage/middleware/operators/filters/evaluate_filter.py +83 -0
  63. sage/middleware/operators/filters/tool_filter.py +74 -0
  64. sage/middleware/operators/llm/__init__.py +18 -0
  65. sage/middleware/operators/llm/sagellm_generator.py +432 -0
  66. sage/middleware/operators/rag/__init__.py +147 -0
  67. sage/middleware/operators/rag/arxiv.py +331 -0
  68. sage/middleware/operators/rag/chunk.py +13 -0
  69. sage/middleware/operators/rag/document_loaders.py +23 -0
  70. sage/middleware/operators/rag/evaluate.py +658 -0
  71. sage/middleware/operators/rag/generator.py +340 -0
  72. sage/middleware/operators/rag/index_builder/__init__.py +48 -0
  73. sage/middleware/operators/rag/index_builder/builder.py +363 -0
  74. sage/middleware/operators/rag/index_builder/manifest.py +101 -0
  75. sage/middleware/operators/rag/index_builder/storage.py +131 -0
  76. sage/middleware/operators/rag/pipeline.py +46 -0
  77. sage/middleware/operators/rag/profiler.py +59 -0
  78. sage/middleware/operators/rag/promptor.py +400 -0
  79. sage/middleware/operators/rag/refiner.py +231 -0
  80. sage/middleware/operators/rag/reranker.py +364 -0
  81. sage/middleware/operators/rag/retriever.py +1308 -0
  82. sage/middleware/operators/rag/searcher.py +37 -0
  83. sage/middleware/operators/rag/types.py +28 -0
  84. sage/middleware/operators/rag/writer.py +80 -0
  85. sage/middleware/operators/tools/__init__.py +71 -0
  86. sage/middleware/operators/tools/arxiv_paper_searcher.py +175 -0
  87. sage/middleware/operators/tools/arxiv_searcher.py +102 -0
  88. sage/middleware/operators/tools/duckduckgo_searcher.py +105 -0
  89. sage/middleware/operators/tools/image_captioner.py +104 -0
  90. sage/middleware/operators/tools/nature_news_fetcher.py +224 -0
  91. sage/middleware/operators/tools/searcher_tool.py +514 -0
  92. sage/middleware/operators/tools/text_detector.py +185 -0
  93. sage/middleware/operators/tools/url_text_extractor.py +104 -0
  94. 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
+ ]