flexllm 0.3.3__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 (39) hide show
  1. flexllm/__init__.py +224 -0
  2. flexllm/__main__.py +1096 -0
  3. flexllm/async_api/__init__.py +9 -0
  4. flexllm/async_api/concurrent_call.py +100 -0
  5. flexllm/async_api/concurrent_executor.py +1036 -0
  6. flexllm/async_api/core.py +373 -0
  7. flexllm/async_api/interface.py +12 -0
  8. flexllm/async_api/progress.py +277 -0
  9. flexllm/base_client.py +988 -0
  10. flexllm/batch_tools/__init__.py +16 -0
  11. flexllm/batch_tools/folder_processor.py +317 -0
  12. flexllm/batch_tools/table_processor.py +363 -0
  13. flexllm/cache/__init__.py +10 -0
  14. flexllm/cache/response_cache.py +293 -0
  15. flexllm/chain_of_thought_client.py +1120 -0
  16. flexllm/claudeclient.py +402 -0
  17. flexllm/client_pool.py +698 -0
  18. flexllm/geminiclient.py +563 -0
  19. flexllm/llm_client.py +523 -0
  20. flexllm/llm_parser.py +60 -0
  21. flexllm/mllm_client.py +559 -0
  22. flexllm/msg_processors/__init__.py +174 -0
  23. flexllm/msg_processors/image_processor.py +729 -0
  24. flexllm/msg_processors/image_processor_helper.py +485 -0
  25. flexllm/msg_processors/messages_processor.py +341 -0
  26. flexllm/msg_processors/unified_processor.py +1404 -0
  27. flexllm/openaiclient.py +256 -0
  28. flexllm/pricing/__init__.py +104 -0
  29. flexllm/pricing/data.json +1201 -0
  30. flexllm/pricing/updater.py +223 -0
  31. flexllm/provider_router.py +213 -0
  32. flexllm/token_counter.py +270 -0
  33. flexllm/utils/__init__.py +1 -0
  34. flexllm/utils/core.py +41 -0
  35. flexllm-0.3.3.dist-info/METADATA +573 -0
  36. flexllm-0.3.3.dist-info/RECORD +39 -0
  37. flexllm-0.3.3.dist-info/WHEEL +4 -0
  38. flexllm-0.3.3.dist-info/entry_points.txt +3 -0
  39. flexllm-0.3.3.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,16 @@
1
+ """
2
+ 批处理工具模块
3
+
4
+ - MllmFolderProcessor: 文件夹批量处理
5
+ - MllmTableProcessor: 表格数据批量处理
6
+ """
7
+
8
+ from .folder_processor import MllmFolderProcessor
9
+
10
+ # MllmTableProcessor 需要 pandas(可选依赖)
11
+ try:
12
+ from .table_processor import MllmTableProcessor
13
+ except ImportError:
14
+ MllmTableProcessor = None
15
+
16
+ __all__ = ["MllmFolderProcessor", "MllmTableProcessor"]
@@ -0,0 +1,317 @@
1
+ """
2
+ Folder processor for MLLM client
3
+ 专门处理文件夹图像数据的处理器类
4
+ """
5
+
6
+ import os
7
+ from pathlib import Path
8
+ from typing import List, Callable, Optional, Any, Union, TYPE_CHECKING
9
+
10
+ # 使用TYPE_CHECKING避免运行时循环引用
11
+ if TYPE_CHECKING:
12
+ from ..mllm_client import MllmClientBase, MllmClient
13
+
14
+
15
+ class MllmFolderProcessor:
16
+ """
17
+ 文件夹处理器类
18
+ 专门处理文件夹内图像文件与MLLM客户端的交互
19
+ """
20
+
21
+ # 支持的图像格式
22
+ SUPPORTED_IMAGE_EXTENSIONS = {
23
+ '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp',
24
+ '.tiff', '.tif', '.svg', '.ico'
25
+ }
26
+
27
+ def __init__(self, mllm_client: "MllmClient"):
28
+ """
29
+ 初始化文件夹处理器
30
+
31
+ Args:
32
+ mllm_client: MLLM客户端实例
33
+ """
34
+ self.mllm_client = mllm_client
35
+
36
+ # 属性委托:委托给mllm_client的核心方法
37
+ @property
38
+ def call_llm(self):
39
+ """委托给mllm_client的call_llm方法"""
40
+ return self.mllm_client.call_llm
41
+
42
+ @property
43
+ def call_llm_with_selection(self):
44
+ """委托给mllm_client的call_llm_with_selection方法"""
45
+ return self.mllm_client.call_llm_with_selection
46
+
47
+ @property
48
+ def call_llm_sync(self):
49
+ """委托"""
50
+ return self.mllm_client.call_llm_sync
51
+
52
+ # 数据预处理工具方法(独立于mllm_client)
53
+ def process_image(self, image_path: str) -> str:
54
+ """
55
+ 预处理图像路径
56
+
57
+ Args:
58
+ image_path: 图片路径
59
+
60
+ Returns:
61
+ str: 处理后的图片路径
62
+ """
63
+ # 转换为绝对路径
64
+ return os.path.abspath(image_path)
65
+
66
+ def scan_folder_images(
67
+ self,
68
+ folder_path: str,
69
+ recursive: bool = True,
70
+ max_num: Optional[int] = None,
71
+ extensions: Optional[set] = None
72
+ ) -> List[str]:
73
+ """
74
+ 扫描文件夹中的图像文件
75
+
76
+ Args:
77
+ folder_path: 文件夹路径
78
+ recursive: 是否递归扫描子文件夹,默认为True
79
+ max_num: 最大文件数量限制
80
+ extensions: 支持的文件扩展名集合,默认使用SUPPORTED_IMAGE_EXTENSIONS
81
+
82
+ Returns:
83
+ List[str]: 图像文件路径列表
84
+
85
+ Raises:
86
+ ValueError: 当输入参数无效时
87
+ FileNotFoundError: 当文件夹不存在时
88
+ """
89
+ # 验证输入参数
90
+ if not folder_path:
91
+ raise ValueError("文件夹路径不能为空")
92
+
93
+ folder_path = Path(folder_path)
94
+ if not folder_path.exists():
95
+ raise FileNotFoundError(f"文件夹不存在: {folder_path}")
96
+
97
+ if not folder_path.is_dir():
98
+ raise ValueError(f"路径不是文件夹: {folder_path}")
99
+
100
+ # 使用默认扩展名或用户指定的扩展名
101
+ if extensions is None:
102
+ extensions = self.SUPPORTED_IMAGE_EXTENSIONS
103
+
104
+ # 转换为小写用于比较
105
+ extensions = {ext.lower() for ext in extensions}
106
+
107
+ image_files = []
108
+
109
+ # 扫描文件
110
+ if recursive:
111
+ # 递归扫描所有子文件夹
112
+ for root, dirs, files in os.walk(folder_path):
113
+ for file in files:
114
+ file_path = os.path.join(root, file)
115
+ if Path(file_path).suffix.lower() in extensions:
116
+ image_files.append(file_path)
117
+
118
+ # 检查数量限制
119
+ if max_num and len(image_files) >= max_num:
120
+ break
121
+ if max_num and len(image_files) >= max_num:
122
+ break
123
+ else:
124
+ # 只扫描当前文件夹
125
+ for file_path in folder_path.iterdir():
126
+ if file_path.is_file() and file_path.suffix.lower() in extensions:
127
+ image_files.append(str(file_path))
128
+
129
+ # 检查数量限制
130
+ if max_num and len(image_files) >= max_num:
131
+ break
132
+
133
+ # 按文件名排序,确保结果的一致性
134
+ image_files.sort()
135
+
136
+ print(f"扫描完成: 发现 {len(image_files)} 个图像文件")
137
+ if image_files:
138
+ print(f"示例文件: {image_files[0]}")
139
+
140
+ return image_files
141
+
142
+ def _build_image_messages_from_files(
143
+ self,
144
+ image_files: List[str],
145
+ system_prompt: str = "",
146
+ text_prompt: str = "请描述这幅图片",
147
+ ) -> List[List[dict]]:
148
+ """
149
+ 从图像文件列表构建消息列表
150
+
151
+ Args:
152
+ image_files: 图像文件路径列表
153
+ system_prompt: 系统提示词
154
+ text_prompt: 文本提示词
155
+
156
+ Returns:
157
+ messages_list: 消息列表
158
+ """
159
+ messages_list = []
160
+
161
+ for image_path in image_files:
162
+ messages = []
163
+
164
+ # 添加系统提示词(如果提供)
165
+ if system_prompt:
166
+ messages.append({
167
+ "role": "system",
168
+ "content": system_prompt
169
+ })
170
+
171
+ # 处理图像路径
172
+ processed_image_path = self.process_image(image_path)
173
+
174
+ # 添加用户消息(包含文本提示和图像)
175
+ messages.append({
176
+ "role": "user",
177
+ "content": [
178
+ {"type": "text", "text": f"{text_prompt}\n文件路径: {processed_image_path}"},
179
+ {"type": "image_url", "image_url": {"url": f"file://{processed_image_path}"}},
180
+ ],
181
+ })
182
+
183
+ messages_list.append(messages)
184
+
185
+ return messages_list
186
+
187
+ async def call_folder_images(
188
+ self,
189
+ folder_path: str,
190
+ system_prompt: str = "",
191
+ text_prompt: str = "请描述这幅图片",
192
+ recursive: bool = True,
193
+ max_num: Optional[int] = None,
194
+ extensions: Optional[set] = None,
195
+ use_selection: bool = False,
196
+ n_predictions: int = 1,
197
+ selector_fn: Optional[Callable[[List[Any]], Any]] = None,
198
+ return_image_files: bool = False,
199
+ **kwargs,
200
+ ):
201
+ """
202
+ 对文件夹中的图像进行批量请求大模型
203
+
204
+ Args:
205
+ folder_path: 文件夹路径
206
+ system_prompt: 系统提示词,默认为空
207
+ text_prompt: 文本提示词,默认为"请描述这幅图片"
208
+ recursive: 是否递归扫描子文件夹,默认为True
209
+ max_num: 最大处理图像数量限制
210
+ extensions: 支持的文件扩展名集合,默认使用SUPPORTED_IMAGE_EXTENSIONS
211
+ use_selection: 是否使用选择模式
212
+ n_predictions: 每条消息预测次数(仅在use_selection=True时有效)
213
+ selector_fn: 选择函数(仅在use_selection=True时有效)
214
+ return_image_files: 是否在返回结果中包含图像文件列表,默认为False
215
+ **kwargs: 其他传递给MLLM的参数
216
+
217
+ Returns:
218
+ 如果return_image_files=False: response_list
219
+ 如果return_image_files=True: (response_list, image_files)
220
+
221
+ Raises:
222
+ ValueError: 当输入参数无效时
223
+ FileNotFoundError: 当文件夹不存在时
224
+ """
225
+ # 扫描文件夹获取图像文件
226
+ image_files = self.scan_folder_images(
227
+ folder_path=folder_path,
228
+ recursive=recursive,
229
+ max_num=max_num,
230
+ extensions=extensions
231
+ )
232
+
233
+ if not image_files:
234
+ print("警告: 未找到任何图像文件")
235
+ if return_image_files:
236
+ return [], []
237
+ else:
238
+ return []
239
+
240
+ # 构建消息列表
241
+ messages_list = self._build_image_messages_from_files(
242
+ image_files, system_prompt, text_prompt
243
+ )
244
+
245
+ # 调用MLLM
246
+ if use_selection:
247
+ response_list = await self.call_llm_with_selection(
248
+ messages_list,
249
+ n_predictions=n_predictions,
250
+ selector_fn=selector_fn,
251
+ **kwargs
252
+ )
253
+ else:
254
+ response_list = await self.call_llm(messages_list, **kwargs)
255
+
256
+ # 根据参数决定返回格式
257
+ if return_image_files:
258
+ return response_list, image_files
259
+ else:
260
+ return response_list
261
+
262
+ async def call_image_files(
263
+ self,
264
+ image_files: List[str],
265
+ system_prompt: str = "",
266
+ text_prompt: str = "请描述这幅图片",
267
+ use_selection: bool = False,
268
+ n_predictions: int = 1,
269
+ selector_fn: Optional[Callable[[List[Any]], Any]] = None,
270
+ **kwargs,
271
+ ):
272
+ """
273
+ 对指定的图像文件列表进行批量请求大模型
274
+
275
+ Args:
276
+ image_files: 图像文件路径列表
277
+ system_prompt: 系统提示词,默认为空
278
+ text_prompt: 文本提示词,默认为"请描述这幅图片"
279
+ use_selection: 是否使用选择模式
280
+ n_predictions: 每条消息预测次数(仅在use_selection=True时有效)
281
+ selector_fn: 选择函数(仅在use_selection=True时有效)
282
+ **kwargs: 其他传递给MLLM的参数
283
+
284
+ Returns:
285
+ response_list: 响应列表
286
+ """
287
+ if not image_files:
288
+ print("警告: 图像文件列表为空")
289
+ return []
290
+
291
+ # 验证文件存在性
292
+ valid_files = []
293
+ for file_path in image_files:
294
+ if os.path.exists(file_path):
295
+ valid_files.append(file_path)
296
+ else:
297
+ print(f"警告: 文件不存在,跳过: {file_path}")
298
+
299
+ if not valid_files:
300
+ print("警告: 没有有效的图像文件")
301
+ return []
302
+
303
+ # 构建消息列表
304
+ messages_list = self._build_image_messages_from_files(
305
+ valid_files, system_prompt, text_prompt
306
+ )
307
+
308
+ # 调用MLLM
309
+ if use_selection:
310
+ return await self.call_llm_with_selection(
311
+ messages_list,
312
+ n_predictions=n_predictions,
313
+ selector_fn=selector_fn,
314
+ **kwargs
315
+ )
316
+ else:
317
+ return await self.call_llm(messages_list, **kwargs)
@@ -0,0 +1,363 @@
1
+ """
2
+ Table processor for MLLM client
3
+ 专门处理表格数据的处理器类
4
+ """
5
+
6
+ import pandas as pd
7
+ from typing import List, Callable, Optional, Any, Union, TYPE_CHECKING
8
+
9
+ # 使用TYPE_CHECKING避免运行时循环引用
10
+ if TYPE_CHECKING:
11
+ from ..mllm_client import MllmClient
12
+
13
+
14
+ class MllmTableProcessor:
15
+ """
16
+ 表格处理器类
17
+ 专门处理表格数据与MLLM客户端的交互
18
+ """
19
+
20
+ def __init__(self, mllm_client: "MllmClient"):
21
+ """
22
+ 初始化表格处理器
23
+
24
+ Args:
25
+ mllm_client: MLLM客户端实例
26
+ """
27
+ self.mllm_client = mllm_client
28
+
29
+ # 属性委托:委托给mllm_client的核心方法
30
+ @property
31
+ def call_llm(self):
32
+ """委托给mllm_client的call_llm方法"""
33
+ return self.mllm_client.call_llm
34
+
35
+ @property
36
+ def call_llm_with_selection(self):
37
+ """委托给mllm_client的call_llm_with_selection方法"""
38
+ return self.mllm_client.call_llm_with_selection
39
+
40
+ @property
41
+ def call_llm_sync(self):
42
+ """委托给mllm_client的call_llm_sync方法"""
43
+ return self.mllm_client.call_llm_sync
44
+
45
+ # 数据预处理工具方法(独立于mllm_client)
46
+ def process_text(self, text: str) -> str:
47
+ """
48
+ 预处理文本
49
+
50
+ Args:
51
+ text: 输入文本
52
+
53
+ Returns:
54
+ str: 处理后的文本
55
+ """
56
+ return text
57
+
58
+ def process_image(self, image: str) -> str:
59
+ """
60
+ 预处理图像
61
+
62
+ Args:
63
+ image: 图片路径或URL
64
+
65
+ Returns:
66
+ str: 处理后的图片路径或URL
67
+ """
68
+ return image
69
+
70
+ def load_dataframe(
71
+ self,
72
+ table_path: str,
73
+ sheet_name: str = 0,
74
+ max_num: int = None,
75
+ ) -> pd.DataFrame:
76
+ """
77
+ 加载并过滤Excel数据
78
+
79
+ Args:
80
+ table_path: 表格文件路径
81
+ sheet_name: 要读取的sheet名称
82
+ max_num: 最大处理数量限制
83
+
84
+ Returns:
85
+ 处理后的DataFrame
86
+
87
+ Raises:
88
+ ValueError: 当输入参数无效时
89
+ FileNotFoundError: 当文件不存在时
90
+ """
91
+ # 验证输入参数
92
+ if not table_path:
93
+ raise ValueError("表格文件路径不能为空")
94
+
95
+ # 读取数据
96
+ try:
97
+ if table_path.endswith(".xlsx"):
98
+ df = pd.read_excel(table_path, sheet_name=sheet_name)
99
+ elif table_path.endswith(".csv"):
100
+ df = pd.read_csv(table_path)
101
+ else:
102
+ raise ValueError(f"不支持的文件格式: {table_path}")
103
+ except Exception as e:
104
+ raise ValueError(f"读取文件失败: {str(e)}")
105
+
106
+ if df.empty:
107
+ print(f"警告: 过滤后数据为空")
108
+ return df
109
+
110
+ # 应用数量限制
111
+ if max_num is not None:
112
+ df = df.head(max_num)
113
+
114
+ print(f"加载数据完成: {len(df)} 行")
115
+ df = df.astype(str)
116
+ print(f"{df.head(2)=}")
117
+
118
+ return df
119
+
120
+ def preprocess_dataframe(
121
+ self,
122
+ df: pd.DataFrame,
123
+ image_col: Optional[str],
124
+ text_col: str,
125
+ ):
126
+ """
127
+ 预处理df
128
+ """
129
+ df[text_col] = df[text_col].apply(self.process_text)
130
+ # 只有当图像列存在时才处理图像列
131
+ if image_col and image_col in df.columns:
132
+ df[image_col] = df[image_col].apply(self.process_image)
133
+ return df
134
+
135
+ def _build_messages_from_dataframe(
136
+ self,
137
+ df: pd.DataFrame,
138
+ image_col: Optional[str],
139
+ text_col: str,
140
+ ) -> List[List[dict]]:
141
+ """
142
+ 从dataframe构建消息列表
143
+
144
+ Args:
145
+ df: 数据框
146
+ image_col: 图片列名,如果为None或列不存在则只使用文本
147
+ text_col: 文本列名
148
+
149
+ Returns:
150
+ messages_list: 消息列表
151
+ """
152
+ messages_list = []
153
+ # 检查是否有图像列
154
+ has_image_col = image_col and image_col in df.columns
155
+
156
+ for index, row in df.iterrows():
157
+ if has_image_col:
158
+ # 有图像列时,包含图像和文本
159
+ messages_list.append(
160
+ [
161
+ {
162
+ "role": "user",
163
+ "content": [
164
+ {"type": "text", "text": f"{row[text_col]}"},
165
+ {"type": "image_url", "image_url": {"url": f"{str(row[image_col])}"}},
166
+ ],
167
+ }
168
+ ]
169
+ )
170
+ else:
171
+ # 没有图像列时,只包含文本
172
+ messages_list.append(
173
+ [
174
+ {
175
+ "role": "user",
176
+ "content": f"{row[text_col]}"
177
+ }
178
+ ]
179
+ )
180
+ return messages_list
181
+
182
+ def _build_image_messages_from_dataframe(
183
+ self,
184
+ df: pd.DataFrame,
185
+ image_col: str,
186
+ system_prompt: str = "",
187
+ text_prompt: str = "请描述这幅图片",
188
+ ) -> List[List[dict]]:
189
+ """
190
+ 从dataframe构建图像处理消息列表
191
+
192
+ Args:
193
+ df: 数据框
194
+ image_col: 图片列名
195
+ system_prompt: 系统提示词
196
+ text_prompt: 文本提示词
197
+
198
+ Returns:
199
+ messages_list: 消息列表
200
+ """
201
+ messages_list = []
202
+ for index, row in df.iterrows():
203
+ messages = []
204
+
205
+ # 添加系统提示词(如果提供)
206
+ if system_prompt:
207
+ messages.append({
208
+ "role": "system",
209
+ "content": system_prompt
210
+ })
211
+
212
+ # 添加用户消息(包含文本提示和图像)
213
+ messages.append({
214
+ "role": "user",
215
+ "content": [
216
+ {"type": "text", "text": text_prompt},
217
+ {"type": "image_url", "image_url": {"url": f"{str(row[image_col])}"}},
218
+ ],
219
+ })
220
+
221
+ messages_list.append(messages)
222
+ return messages_list
223
+
224
+ async def call_dataframe(
225
+ self,
226
+ df: pd.DataFrame,
227
+ text_col: str,
228
+ image_col: Optional[str] = None,
229
+ use_selection: bool = False,
230
+ n_predictions: int = 3,
231
+ selector_fn: Optional[Callable[[List[Any]], Any]] = None,
232
+ **kwargs,
233
+ ):
234
+ """
235
+ 调用dataframe
236
+
237
+ Args:
238
+ df: 数据框
239
+ image_col: 图片列名,如果为None或列不存在则只使用文本
240
+ text_col: 文本列名
241
+ use_selection: 是否使用选择模式
242
+ n_predictions: 每条消息预测次数(仅在use_selection=True时有效)
243
+ selector_fn: 选择函数(仅在use_selection=True时有效)
244
+ **kwargs: 模型请求参数
245
+
246
+ Returns:
247
+ response_list: 响应列表
248
+
249
+ Examples:
250
+ # 纯文本模式(推荐的默认方式)
251
+ await processor.call_dataframe(df, image_col=None, text_col="text")
252
+
253
+ # 图像+文本模式(需要显式指定图像列)
254
+ await processor.call_dataframe(df, image_col="image", text_col="text")
255
+ """
256
+ df = self.preprocess_dataframe(df, image_col, text_col)
257
+ messages_list = self._build_messages_from_dataframe(df, image_col, text_col)
258
+
259
+ if use_selection:
260
+ return await self.call_llm_with_selection(
261
+ messages_list,
262
+ n_predictions=n_predictions,
263
+ selector_fn=selector_fn,
264
+ **kwargs
265
+ )
266
+ else:
267
+ return await self.call_llm(messages_list, **kwargs)
268
+
269
+
270
+ async def call_table(
271
+ self,
272
+ table_path: str,
273
+ text_col: str = "text",
274
+ image_col: Optional[str] = None,
275
+ sheet_name: str = 0,
276
+ max_num: int = None,
277
+ use_selection: bool = False,
278
+ n_predictions: int = 1,
279
+ selector_fn: Optional[Callable[[List[Any]], Any]] = None,
280
+ **kwargs,
281
+ ):
282
+ """
283
+ 调用table
284
+
285
+ Args:
286
+ table_path: 表格文件路径
287
+ image_col: 图片列名,默认为None(纯文本模式),指定列名则启用图像+文本模式
288
+ text_col: 文本列名,默认为"text"
289
+ sheet_name: sheet名称,默认为0
290
+ max_num: 最大处理数量限制
291
+ use_selection: 是否使用选择模式
292
+ n_predictions: 每条消息预测次数(仅在use_selection=True时有效)
293
+ selector_fn: 选择函数(仅在use_selection=True时有效)
294
+ **kwargs: 其他参数
295
+
296
+ Returns:
297
+ response_list: 响应列表
298
+ """
299
+ df = self.load_dataframe(table_path, sheet_name, max_num)
300
+ return await self.call_dataframe(
301
+ df=df,
302
+ text_col=text_col,
303
+ image_col=image_col,
304
+ use_selection=use_selection,
305
+ n_predictions=n_predictions,
306
+ selector_fn=selector_fn,
307
+ **kwargs
308
+ )
309
+
310
+
311
+ async def call_table_images(
312
+ self,
313
+ table_path: str,
314
+ image_col: str = "image",
315
+ system_prompt: str = "",
316
+ text_prompt: str = "请描述这幅图片",
317
+ sheet_name: str = 0,
318
+ max_num: int = None,
319
+ use_selection: bool = False,
320
+ n_predictions: int = 1,
321
+ selector_fn: Optional[Callable[[List[Any]], Any]] = None,
322
+ **kwargs,
323
+ ):
324
+ """
325
+ 对表格中的图像列进行批量请求大模型
326
+
327
+ Args:
328
+ table_path: 表格文件路径
329
+ image_col: 图片列名,默认为"image"(此方法专门处理图像,必须指定有效的图像列)
330
+ system_prompt: 系统提示词,默认为空
331
+ text_prompt: 文本提示词,默认为"请描述这幅图片"
332
+ sheet_name: sheet名称,默认为0
333
+ max_num: 最大处理数量限制
334
+ use_selection: 是否使用选择模式
335
+ n_predictions: 每条消息预测次数(仅在use_selection=True时有效)
336
+ selector_fn: 选择函数(仅在use_selection=True时有效)
337
+ **kwargs: 其他参数
338
+
339
+ Returns:
340
+ response_list: 响应列表
341
+ """
342
+ df = self.load_dataframe(table_path, sheet_name, max_num)
343
+
344
+ # 检查图像列是否存在
345
+ if not image_col or image_col not in df.columns:
346
+ raise ValueError(f"图像列 '{image_col}' 不存在于表格中")
347
+
348
+ # 预处理图像列
349
+ df[image_col] = df[image_col].apply(self.process_image)
350
+
351
+ messages_list = self._build_image_messages_from_dataframe(
352
+ df, image_col, system_prompt, text_prompt
353
+ )
354
+
355
+ if use_selection:
356
+ return await self.call_llm_with_selection(
357
+ messages_list,
358
+ n_predictions=n_predictions,
359
+ selector_fn=selector_fn,
360
+ **kwargs
361
+ )
362
+ else:
363
+ return await self.call_llm(messages_list, **kwargs)
@@ -0,0 +1,10 @@
1
+ """
2
+ 缓存模块
3
+
4
+ 支持多种缓存后端:
5
+ - ResponseCache: 基于 flaxkv2 的响应缓存(需要 flaxkv2>=0.1.5)
6
+ """
7
+
8
+ from .response_cache import ResponseCache, ResponseCacheConfig
9
+
10
+ __all__ = ["ResponseCache", "ResponseCacheConfig"]