maque 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. maque/__init__.py +30 -0
  2. maque/__main__.py +926 -0
  3. maque/ai_platform/__init__.py +0 -0
  4. maque/ai_platform/crawl.py +45 -0
  5. maque/ai_platform/metrics.py +258 -0
  6. maque/ai_platform/nlp_preprocess.py +67 -0
  7. maque/ai_platform/webpage_screen_shot.py +195 -0
  8. maque/algorithms/__init__.py +78 -0
  9. maque/algorithms/bezier.py +15 -0
  10. maque/algorithms/bktree.py +117 -0
  11. maque/algorithms/core.py +104 -0
  12. maque/algorithms/hilbert.py +16 -0
  13. maque/algorithms/rate_function.py +92 -0
  14. maque/algorithms/transform.py +27 -0
  15. maque/algorithms/trie.py +272 -0
  16. maque/algorithms/utils.py +63 -0
  17. maque/algorithms/video.py +587 -0
  18. maque/api/__init__.py +1 -0
  19. maque/api/common.py +110 -0
  20. maque/api/fetch.py +26 -0
  21. maque/api/static/icon.png +0 -0
  22. maque/api/static/redoc.standalone.js +1782 -0
  23. maque/api/static/swagger-ui-bundle.js +3 -0
  24. maque/api/static/swagger-ui.css +3 -0
  25. maque/cli/__init__.py +1 -0
  26. maque/cli/clean_invisible_chars.py +324 -0
  27. maque/cli/core.py +34 -0
  28. maque/cli/groups/__init__.py +26 -0
  29. maque/cli/groups/config.py +205 -0
  30. maque/cli/groups/data.py +615 -0
  31. maque/cli/groups/doctor.py +259 -0
  32. maque/cli/groups/embedding.py +222 -0
  33. maque/cli/groups/git.py +29 -0
  34. maque/cli/groups/help.py +410 -0
  35. maque/cli/groups/llm.py +223 -0
  36. maque/cli/groups/mcp.py +241 -0
  37. maque/cli/groups/mllm.py +1795 -0
  38. maque/cli/groups/mllm_simple.py +60 -0
  39. maque/cli/groups/quant.py +210 -0
  40. maque/cli/groups/service.py +490 -0
  41. maque/cli/groups/system.py +570 -0
  42. maque/cli/mllm_run.py +1451 -0
  43. maque/cli/script.py +52 -0
  44. maque/cli/tree.py +49 -0
  45. maque/clustering/__init__.py +52 -0
  46. maque/clustering/analyzer.py +347 -0
  47. maque/clustering/clusterers.py +464 -0
  48. maque/clustering/sampler.py +134 -0
  49. maque/clustering/visualizer.py +205 -0
  50. maque/constant.py +13 -0
  51. maque/core.py +133 -0
  52. maque/cv/__init__.py +1 -0
  53. maque/cv/image.py +219 -0
  54. maque/cv/utils.py +68 -0
  55. maque/cv/video/__init__.py +3 -0
  56. maque/cv/video/keyframe_extractor.py +368 -0
  57. maque/embedding/__init__.py +43 -0
  58. maque/embedding/base.py +56 -0
  59. maque/embedding/multimodal.py +308 -0
  60. maque/embedding/server.py +523 -0
  61. maque/embedding/text.py +311 -0
  62. maque/git/__init__.py +24 -0
  63. maque/git/pure_git.py +912 -0
  64. maque/io/__init__.py +29 -0
  65. maque/io/core.py +38 -0
  66. maque/io/ops.py +194 -0
  67. maque/llm/__init__.py +111 -0
  68. maque/llm/backend.py +416 -0
  69. maque/llm/base.py +411 -0
  70. maque/llm/server.py +366 -0
  71. maque/mcp_server.py +1096 -0
  72. maque/mllm_data_processor_pipeline/__init__.py +17 -0
  73. maque/mllm_data_processor_pipeline/core.py +341 -0
  74. maque/mllm_data_processor_pipeline/example.py +291 -0
  75. maque/mllm_data_processor_pipeline/steps/__init__.py +56 -0
  76. maque/mllm_data_processor_pipeline/steps/data_alignment.py +267 -0
  77. maque/mllm_data_processor_pipeline/steps/data_loader.py +172 -0
  78. maque/mllm_data_processor_pipeline/steps/data_validation.py +304 -0
  79. maque/mllm_data_processor_pipeline/steps/format_conversion.py +411 -0
  80. maque/mllm_data_processor_pipeline/steps/mllm_annotation.py +331 -0
  81. maque/mllm_data_processor_pipeline/steps/mllm_refinement.py +446 -0
  82. maque/mllm_data_processor_pipeline/steps/result_validation.py +501 -0
  83. maque/mllm_data_processor_pipeline/web_app.py +317 -0
  84. maque/nlp/__init__.py +14 -0
  85. maque/nlp/ngram.py +9 -0
  86. maque/nlp/parser.py +63 -0
  87. maque/nlp/risk_matcher.py +543 -0
  88. maque/nlp/sentence_splitter.py +202 -0
  89. maque/nlp/simple_tradition_cvt.py +31 -0
  90. maque/performance/__init__.py +21 -0
  91. maque/performance/_measure_time.py +70 -0
  92. maque/performance/_profiler.py +367 -0
  93. maque/performance/_stat_memory.py +51 -0
  94. maque/pipelines/__init__.py +15 -0
  95. maque/pipelines/clustering.py +252 -0
  96. maque/quantization/__init__.py +42 -0
  97. maque/quantization/auto_round.py +120 -0
  98. maque/quantization/base.py +145 -0
  99. maque/quantization/bitsandbytes.py +127 -0
  100. maque/quantization/llm_compressor.py +102 -0
  101. maque/retriever/__init__.py +35 -0
  102. maque/retriever/chroma.py +654 -0
  103. maque/retriever/document.py +140 -0
  104. maque/retriever/milvus.py +1140 -0
  105. maque/table_ops/__init__.py +1 -0
  106. maque/table_ops/core.py +133 -0
  107. maque/table_viewer/__init__.py +4 -0
  108. maque/table_viewer/download_assets.py +57 -0
  109. maque/table_viewer/server.py +698 -0
  110. maque/table_viewer/static/element-plus-icons.js +5791 -0
  111. maque/table_viewer/static/element-plus.css +1 -0
  112. maque/table_viewer/static/element-plus.js +65236 -0
  113. maque/table_viewer/static/main.css +268 -0
  114. maque/table_viewer/static/main.js +669 -0
  115. maque/table_viewer/static/vue.global.js +18227 -0
  116. maque/table_viewer/templates/index.html +401 -0
  117. maque/utils/__init__.py +56 -0
  118. maque/utils/color.py +68 -0
  119. maque/utils/color_string.py +45 -0
  120. maque/utils/compress.py +66 -0
  121. maque/utils/constant.py +183 -0
  122. maque/utils/core.py +261 -0
  123. maque/utils/cursor.py +143 -0
  124. maque/utils/distance.py +58 -0
  125. maque/utils/docker.py +96 -0
  126. maque/utils/downloads.py +51 -0
  127. maque/utils/excel_helper.py +542 -0
  128. maque/utils/helper_metrics.py +121 -0
  129. maque/utils/helper_parser.py +168 -0
  130. maque/utils/net.py +64 -0
  131. maque/utils/nvidia_stat.py +140 -0
  132. maque/utils/ops.py +53 -0
  133. maque/utils/packages.py +31 -0
  134. maque/utils/path.py +57 -0
  135. maque/utils/tar.py +260 -0
  136. maque/utils/untar.py +129 -0
  137. maque/web/__init__.py +0 -0
  138. maque/web/image_downloader.py +1410 -0
  139. maque-0.2.1.dist-info/METADATA +450 -0
  140. maque-0.2.1.dist-info/RECORD +143 -0
  141. maque-0.2.1.dist-info/WHEEL +4 -0
  142. maque-0.2.1.dist-info/entry_points.txt +3 -0
  143. maque-0.2.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,368 @@
1
+ import cv2
2
+ import numpy as np
3
+ from skimage.metrics import structural_similarity as ssim
4
+ from typing import List, Tuple, Optional
5
+ from enum import Enum
6
+ import time
7
+ import os
8
+
9
+ class Method(Enum):
10
+ DIFFERENCE = "difference"
11
+ SSIM = "ssim"
12
+ HISTOGRAM = "histogram"
13
+ MOTION_VECTOR = "motion_vector" # 新增运动矢量方法
14
+
15
+ class AdvancedKeyframeExtractor:
16
+ def __init__(self):
17
+ self.reset()
18
+
19
+ def reset(self):
20
+ """重置提取器状态"""
21
+ self.prev_frame = None
22
+ self.prev_hist = None
23
+ self.prev_gray = None # 用于运动矢量计算
24
+ self.keyframes = []
25
+ self.frame_indices = []
26
+
27
+ def downsample_frame(self,
28
+ frame: np.ndarray,
29
+ scale_factor: float = 0.5,
30
+ target_size: Optional[Tuple[int, int]] = None) -> np.ndarray:
31
+ """对单帧进行降采样"""
32
+ if target_size:
33
+ return cv2.resize(frame, target_size, interpolation=cv2.INTER_AREA)
34
+
35
+ width = int(frame.shape[1] * scale_factor)
36
+ height = int(frame.shape[0] * scale_factor)
37
+ return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)
38
+
39
+ def compute_difference(self, current: np.ndarray, previous: np.ndarray) -> float:
40
+ """计算帧差分"""
41
+ diff = cv2.absdiff(current, previous)
42
+ return np.mean(diff)
43
+
44
+ def compute_ssim(self, current: np.ndarray, previous: np.ndarray) -> float:
45
+ """计算SSIM"""
46
+ return ssim(previous, current)
47
+
48
+ def compute_histogram_similarity(self, current: np.ndarray, previous: np.ndarray) -> float:
49
+ """计算直方图相似度"""
50
+ # 计算HSV直方图
51
+ current_hsv = cv2.cvtColor(current, cv2.COLOR_BGR2HSV)
52
+ current_hist = cv2.calcHist([current_hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
53
+ cv2.normalize(current_hist, current_hist, 0, 1, cv2.NORM_MINMAX)
54
+
55
+ if previous is None:
56
+ return 0.0
57
+
58
+ previous_hsv = cv2.cvtColor(previous, cv2.COLOR_BGR2HSV)
59
+ previous_hist = cv2.calcHist([previous_hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
60
+ cv2.normalize(previous_hist, previous_hist, 0, 1, cv2.NORM_MINMAX)
61
+
62
+ return cv2.compareHist(previous_hist, current_hist, cv2.HISTCMP_CORREL)
63
+
64
+ def compute_motion_vectors(self, current_gray: np.ndarray, previous_gray: np.ndarray) -> Tuple[float, float]:
65
+ """
66
+ 计算运动矢量
67
+
68
+ Returns:
69
+ mean_magnitude: 平均运动大小
70
+ motion_ratio: 运动区域比例
71
+ """
72
+ # 计算光流
73
+ flow = cv2.calcOpticalFlowFarneback(
74
+ previous_gray,
75
+ current_gray,
76
+ None,
77
+ pyr_scale=0.5, # 金字塔缩放比例
78
+ levels=3, # 金字塔层数
79
+ winsize=15, # 窗口大小
80
+ iterations=3, # 迭代次数
81
+ poly_n=5, # 多项式展开阶数
82
+ poly_sigma=1.2, # 高斯标准差
83
+ flags=0
84
+ )
85
+
86
+ # 计算运动矢量大小
87
+ magnitude, _ = cv2.cartToPolar(flow[..., 0], flow[..., 1])
88
+
89
+ # 计算平均运动大小
90
+ mean_magnitude = np.mean(magnitude)
91
+
92
+ # 计算显著运动区域比例
93
+ motion_threshold = np.mean(magnitude) + np.std(magnitude)
94
+ motion_ratio = np.sum(magnitude > motion_threshold) / magnitude.size
95
+
96
+ return mean_magnitude, motion_ratio
97
+
98
+ def is_keyframe(self,
99
+ current_frame: np.ndarray,
100
+ method: Method,
101
+ threshold: float) -> bool:
102
+ """
103
+ 判断是否为关键帧
104
+
105
+ Args:
106
+ current_frame: 当前帧
107
+ method: 使用的方法
108
+ threshold: 阈值
109
+
110
+ Returns:
111
+ 是否为关键帧
112
+ """
113
+ if self.prev_frame is None:
114
+ return True
115
+
116
+ if method == Method.DIFFERENCE:
117
+ diff = self.compute_difference(current_frame, self.prev_frame)
118
+ return diff > threshold
119
+
120
+ elif method == Method.SSIM:
121
+ similarity = self.compute_ssim(current_frame, self.prev_frame)
122
+ return similarity < threshold
123
+
124
+ elif method == Method.HISTOGRAM:
125
+ similarity = self.compute_histogram_similarity(current_frame, self.prev_frame)
126
+ return similarity < threshold
127
+
128
+ elif method == Method.MOTION_VECTOR:
129
+ if self.prev_gray is None:
130
+ return True
131
+
132
+ # 转换为灰度图
133
+ if len(current_frame.shape) == 3:
134
+ current_gray = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
135
+ else:
136
+ current_gray = current_frame
137
+
138
+ # 计算运动矢量特征
139
+ mean_magnitude, motion_ratio = self.compute_motion_vectors(current_gray, self.prev_gray)
140
+
141
+ # 更新previous_gray
142
+ self.prev_gray = current_gray.copy()
143
+
144
+ # 基于运动大小和运动区域比例综合判断
145
+ magnitude_threshold = threshold # 例如 2.0
146
+ ratio_threshold = threshold / 10 # 例如 0.2
147
+
148
+ return (mean_magnitude > magnitude_threshold) or (motion_ratio > ratio_threshold)
149
+
150
+ return False
151
+
152
+ def extract_keyframes(self,
153
+ video_path: str,
154
+ method: Method = Method.DIFFERENCE,
155
+ threshold: float = 30.0,
156
+ sampling_rate: int = 5,
157
+ scale_factor: float = 0.5,
158
+ target_size: Optional[Tuple[int, int]] = None,
159
+ max_frames: Optional[int] = None,
160
+ start_time: Optional[float] = None,
161
+ end_time: Optional[float] = None
162
+ ) -> Tuple[List[np.ndarray], List[int]]:
163
+ """
164
+ 提取关键帧
165
+
166
+ Args:
167
+ video_path: 视频文件路径
168
+ method: 使用的方法
169
+ threshold: 阈值:
170
+ - difference: 30.0 (像素差异)
171
+ - ssim: 0.7 (结构相似度)
172
+ - histogram: 0.8 (直方图相关性)
173
+ - motion_vector: 2.0 (运动矢量阈值)
174
+ sampling_rate: 采样率 (每N帧处理一帧)
175
+ scale_factor: 降采样比例
176
+ target_size: 目标大小
177
+ max_frames: 最大处理帧数
178
+ start_time: 开始时间(秒)
179
+ end_time: 结束时间(秒)
180
+
181
+ Returns:
182
+ keyframes: 关键帧列表
183
+ frame_indices: 关键帧对应的原始帧序号
184
+ """
185
+ self.reset()
186
+ cap = cv2.VideoCapture(video_path)
187
+
188
+ # 获取视频信息
189
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
190
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
191
+ duration = total_frames / fps
192
+
193
+ print("\n开始处理视频:")
194
+ print(f"方法: {method.value}")
195
+ print(f"总帧数: {total_frames}")
196
+ print(f"FPS: {fps}")
197
+ print(f"时长: {duration:.2f}秒")
198
+
199
+ # 处理时间范围
200
+ if start_time is not None:
201
+ start_frame = int(start_time * fps)
202
+ cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
203
+ else:
204
+ start_frame = 0
205
+
206
+ if end_time is not None:
207
+ end_frame = int(end_time * fps)
208
+ else:
209
+ end_frame = total_frames
210
+
211
+ if max_frames:
212
+ end_frame = min(end_frame, start_frame + max_frames)
213
+
214
+ frame_count = start_frame
215
+ processed_count = 0
216
+ start_time = time.time()
217
+
218
+ while frame_count < end_frame:
219
+ ret, frame = cap.read()
220
+ if not ret:
221
+ break
222
+
223
+ # 跳帧采样
224
+ if (frame_count - start_frame) % sampling_rate != 0:
225
+ frame_count += 1
226
+ continue
227
+
228
+ # 降采样
229
+ small_frame = self.downsample_frame(frame, scale_factor, target_size)
230
+
231
+ # 判断是否为关键帧
232
+ if method == Method.DIFFERENCE:
233
+ gray = cv2.cvtColor(small_frame, cv2.COLOR_BGR2GRAY)
234
+ is_key = self.is_keyframe(gray, method, threshold)
235
+ if is_key:
236
+ self.prev_frame = gray
237
+ elif method == Method.MOTION_VECTOR:
238
+ gray = cv2.cvtColor(small_frame, cv2.COLOR_BGR2GRAY)
239
+ is_key = self.is_keyframe(gray, method, threshold)
240
+ # prev_gray的更新已经在is_keyframe中处理
241
+ else:
242
+ is_key = self.is_keyframe(small_frame, method, threshold)
243
+ if is_key:
244
+ self.prev_frame = small_frame
245
+
246
+ if is_key:
247
+ self.keyframes.append(frame) # 保存原始分辨率帧
248
+ self.frame_indices.append(frame_count)
249
+
250
+ processed_count += 1
251
+ frame_count += 1
252
+
253
+ # 打印进度
254
+ if processed_count % 100 == 0:
255
+ elapsed_time = time.time() - start_time
256
+ fps = processed_count / elapsed_time
257
+ progress = ((frame_count - start_frame) / (end_frame - start_frame)) * 100
258
+ print(f"处理进度: {progress:.1f}% ({frame_count}/{end_frame}) "
259
+ f"处理速度: {fps:.1f} fps")
260
+
261
+ cap.release()
262
+
263
+ print("\n处理完成:")
264
+ print(f"总耗时: {time.time() - start_time:.2f}秒")
265
+ print(f"提取关键帧数: {len(self.keyframes)}")
266
+ print(f"平均提取比例: {len(self.keyframes)/processed_count*100:.1f}%")
267
+
268
+ return self.keyframes, self.frame_indices
269
+
270
+ def save_keyframes(self,
271
+ output_dir: str,
272
+ prefix: str = "keyframe",
273
+ ext: str = ".jpg",
274
+ jpg_quality: int = 95):
275
+ """保存关键帧到指定目录"""
276
+ os.makedirs(output_dir, exist_ok=True)
277
+
278
+ for i, frame in enumerate(self.keyframes):
279
+ frame_idx = self.frame_indices[i]
280
+ filename = f"{prefix}_{frame_idx:06d}{ext}"
281
+ filepath = os.path.join(output_dir, filename)
282
+
283
+ if ext.lower() == '.jpg':
284
+ cv2.imwrite(filepath, frame, [cv2.IMWRITE_JPEG_QUALITY, jpg_quality])
285
+ else:
286
+ cv2.imwrite(filepath, frame)
287
+
288
+ print(f"保存了 {len(self.keyframes)} 张关键帧到 {output_dir}")
289
+
290
+ def process_video(self,
291
+ video_path: str,
292
+ output_dir: str,
293
+ methods: List[Method],
294
+ **kwargs):
295
+ """
296
+ 使用多种方法处理同一个视频
297
+
298
+ Args:
299
+ video_path: 视频文件路径
300
+ output_dir: 输出目录
301
+ methods: 要使用的方法列表
302
+ **kwargs: 其他参数传递给extract_keyframes
303
+ """
304
+ for method in methods:
305
+ print(f"\n使用 {method.value} 方法处理...")
306
+
307
+ # 为每种方法创建子目录
308
+ method_dir = os.path.join(output_dir, method.value)
309
+
310
+ # 设置合适的阈值
311
+ if method == Method.DIFFERENCE:
312
+ threshold = kwargs.get('threshold', 30.0)
313
+ elif method == Method.SSIM:
314
+ threshold = kwargs.get('threshold', 0.7)
315
+ elif method == Method.MOTION_VECTOR:
316
+ threshold = kwargs.get('threshold', 2.0)
317
+ else: # HISTOGRAM
318
+ threshold = kwargs.get('threshold', 0.8)
319
+
320
+ # 提取关键帧
321
+ self.extract_keyframes(
322
+ video_path=video_path,
323
+ method=method,
324
+ threshold=threshold,
325
+ **kwargs
326
+ )
327
+
328
+ # 保存关键帧
329
+ self.save_keyframes(
330
+ output_dir=method_dir,
331
+ prefix=f"{method.value}_frame"
332
+ )
333
+
334
+
335
+ if __name__ == "__main__":
336
+ extractor = AdvancedKeyframeExtractor()
337
+ video_path = "./test.mp4"
338
+
339
+ # 示例1: 使用单一方法
340
+ keyframes, indices = extractor.extract_keyframes(
341
+ video_path=video_path,
342
+ method=Method.DIFFERENCE,
343
+ threshold=30.0,
344
+ sampling_rate=5,
345
+ scale_factor=0.5
346
+ )
347
+ extractor.save_keyframes("output/difference_keyframes")
348
+
349
+ # 示例2: 使用所有方法处理视频
350
+ extractor.process_video(
351
+ video_path=video_path,
352
+ output_dir="output/comparison",
353
+ methods=[Method.DIFFERENCE, Method.SSIM, Method.HISTOGRAM],
354
+ sampling_rate=3,
355
+ scale_factor=0.5,
356
+ max_frames=5000 # 只处理前5000帧
357
+ )
358
+
359
+ # 示例3: 处理视频片段
360
+ keyframes, indices = extractor.extract_keyframes(
361
+ video_path=video_path,
362
+ method=Method.SSIM,
363
+ threshold=0.7,
364
+ sampling_rate=2,
365
+ target_size=(320, 240),
366
+ start_time=60, # 从第60秒开始
367
+ end_time=120 # 处理到第120秒
368
+ )
@@ -0,0 +1,43 @@
1
+ #! /usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Embedding 模块 - 提供文本和多模态向量化功能
6
+
7
+ 包含:
8
+ - 客户端: TextEmbedding, MultiModalEmbedding
9
+ - 服务端: EmbeddingServer, create_server
10
+ """
11
+
12
+ from .base import BaseEmbedding
13
+ from .text import (
14
+ TextEmbedding,
15
+ EmbeddingClient, # 向后兼容别名
16
+ EmbeddingResult,
17
+ EmbeddingResponse,
18
+ TaskType,
19
+ )
20
+ from .multimodal import MultiModalEmbedding
21
+
22
+ # 服务端 (延迟导入,避免强依赖 fastapi)
23
+ def __getattr__(name):
24
+ if name in ("EmbeddingServer", "create_server"):
25
+ from .server import EmbeddingServer, create_server
26
+ return {"EmbeddingServer": EmbeddingServer, "create_server": create_server}[name]
27
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
28
+
29
+ __all__ = [
30
+ # 基类
31
+ "BaseEmbedding",
32
+ # 文本客户端
33
+ "TextEmbedding",
34
+ "EmbeddingClient", # 向后兼容
35
+ "EmbeddingResult",
36
+ "EmbeddingResponse",
37
+ "TaskType",
38
+ # 多模态客户端
39
+ "MultiModalEmbedding",
40
+ # 服务端
41
+ "EmbeddingServer",
42
+ "create_server",
43
+ ]
@@ -0,0 +1,56 @@
1
+ #! /usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Embedding 抽象基类
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import List, Optional
10
+
11
+
12
+ class BaseEmbedding(ABC):
13
+ """Embedding 抽象基类,定义统一接口"""
14
+
15
+ @abstractmethod
16
+ def embed(
17
+ self,
18
+ inputs: List[str],
19
+ **kwargs,
20
+ ) -> List[List[float]]:
21
+ """
22
+ 向量化接口
23
+
24
+ Args:
25
+ inputs: 输入列表(文本或图片路径/URL)
26
+ **kwargs: 额外参数
27
+
28
+ Returns:
29
+ 向量列表
30
+ """
31
+ pass
32
+
33
+ @abstractmethod
34
+ async def aembed(
35
+ self,
36
+ inputs: List[str],
37
+ **kwargs,
38
+ ) -> List[List[float]]:
39
+ """异步向量化接口"""
40
+ pass
41
+
42
+ @property
43
+ @abstractmethod
44
+ def dimension(self) -> int:
45
+ """向量维度"""
46
+ pass
47
+
48
+ @property
49
+ def supports_image(self) -> bool:
50
+ """是否支持图片模态"""
51
+ return False
52
+
53
+ @property
54
+ def model_name(self) -> str:
55
+ """模型名称"""
56
+ return getattr(self, "model", "unknown")