isa-model 0.3.9__py3-none-any.whl → 0.4.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.
- isa_model/__init__.py +1 -1
- isa_model/client.py +732 -565
- isa_model/core/cache/redis_cache.py +401 -0
- isa_model/core/config/config_manager.py +53 -10
- isa_model/core/config.py +1 -1
- isa_model/core/database/__init__.py +1 -0
- isa_model/core/database/migrations.py +277 -0
- isa_model/core/database/supabase_client.py +123 -0
- isa_model/core/models/__init__.py +37 -0
- isa_model/core/models/model_billing_tracker.py +60 -88
- isa_model/core/models/model_manager.py +36 -18
- isa_model/core/models/model_repo.py +44 -38
- isa_model/core/models/model_statistics_tracker.py +234 -0
- isa_model/core/models/model_storage.py +0 -1
- isa_model/core/models/model_version_manager.py +959 -0
- isa_model/core/pricing_manager.py +2 -249
- isa_model/core/resilience/circuit_breaker.py +366 -0
- isa_model/core/security/secrets.py +358 -0
- isa_model/core/services/__init__.py +2 -4
- isa_model/core/services/intelligent_model_selector.py +101 -370
- isa_model/core/storage/hf_storage.py +1 -1
- isa_model/core/types.py +7 -0
- isa_model/deployment/cloud/modal/isa_audio_chatTTS_service.py +520 -0
- isa_model/deployment/cloud/modal/isa_audio_fish_service.py +0 -0
- isa_model/deployment/cloud/modal/isa_audio_openvoice_service.py +758 -0
- isa_model/deployment/cloud/modal/isa_audio_service_v2.py +1044 -0
- isa_model/deployment/cloud/modal/isa_embed_rerank_service.py +296 -0
- isa_model/deployment/cloud/modal/isa_video_hunyuan_service.py +423 -0
- isa_model/deployment/cloud/modal/isa_vision_ocr_service.py +519 -0
- isa_model/deployment/cloud/modal/isa_vision_qwen25_service.py +709 -0
- isa_model/deployment/cloud/modal/isa_vision_table_service.py +467 -323
- isa_model/deployment/cloud/modal/isa_vision_ui_service.py +607 -180
- isa_model/deployment/cloud/modal/isa_vision_ui_service_optimized.py +660 -0
- isa_model/deployment/core/deployment_manager.py +6 -4
- isa_model/deployment/services/auto_hf_modal_deployer.py +894 -0
- isa_model/eval/benchmarks/__init__.py +27 -0
- isa_model/eval/benchmarks/multimodal_datasets.py +460 -0
- isa_model/eval/benchmarks.py +244 -12
- isa_model/eval/evaluators/__init__.py +8 -2
- isa_model/eval/evaluators/audio_evaluator.py +727 -0
- isa_model/eval/evaluators/embedding_evaluator.py +742 -0
- isa_model/eval/evaluators/vision_evaluator.py +564 -0
- isa_model/eval/example_evaluation.py +395 -0
- isa_model/eval/factory.py +272 -5
- isa_model/eval/isa_benchmarks.py +700 -0
- isa_model/eval/isa_integration.py +582 -0
- isa_model/eval/metrics.py +159 -6
- isa_model/eval/tests/unit/test_basic.py +396 -0
- isa_model/inference/ai_factory.py +44 -8
- isa_model/inference/services/audio/__init__.py +21 -0
- isa_model/inference/services/audio/base_realtime_service.py +225 -0
- isa_model/inference/services/audio/isa_tts_service.py +0 -0
- isa_model/inference/services/audio/openai_realtime_service.py +320 -124
- isa_model/inference/services/audio/openai_stt_service.py +32 -6
- isa_model/inference/services/base_service.py +17 -1
- isa_model/inference/services/embedding/__init__.py +13 -0
- isa_model/inference/services/embedding/base_embed_service.py +111 -8
- isa_model/inference/services/embedding/isa_embed_service.py +305 -0
- isa_model/inference/services/embedding/openai_embed_service.py +2 -4
- isa_model/inference/services/embedding/tests/test_embedding.py +222 -0
- isa_model/inference/services/img/__init__.py +2 -2
- isa_model/inference/services/img/base_image_gen_service.py +24 -7
- isa_model/inference/services/img/replicate_image_gen_service.py +84 -422
- isa_model/inference/services/img/services/replicate_face_swap.py +193 -0
- isa_model/inference/services/img/services/replicate_flux.py +226 -0
- isa_model/inference/services/img/services/replicate_flux_kontext.py +219 -0
- isa_model/inference/services/img/services/replicate_sticker_maker.py +249 -0
- isa_model/inference/services/img/tests/test_img_client.py +297 -0
- isa_model/inference/services/llm/base_llm_service.py +30 -6
- isa_model/inference/services/llm/helpers/llm_adapter.py +63 -9
- isa_model/inference/services/llm/ollama_llm_service.py +2 -1
- isa_model/inference/services/llm/openai_llm_service.py +652 -55
- isa_model/inference/services/llm/yyds_llm_service.py +2 -1
- isa_model/inference/services/vision/__init__.py +5 -5
- isa_model/inference/services/vision/base_vision_service.py +118 -185
- isa_model/inference/services/vision/helpers/image_utils.py +11 -5
- isa_model/inference/services/vision/isa_vision_service.py +573 -0
- isa_model/inference/services/vision/tests/test_ocr_client.py +284 -0
- isa_model/serving/api/fastapi_server.py +88 -16
- isa_model/serving/api/middleware/auth.py +311 -0
- isa_model/serving/api/middleware/security.py +278 -0
- isa_model/serving/api/routes/analytics.py +486 -0
- isa_model/serving/api/routes/deployments.py +339 -0
- isa_model/serving/api/routes/evaluations.py +579 -0
- isa_model/serving/api/routes/logs.py +430 -0
- isa_model/serving/api/routes/settings.py +582 -0
- isa_model/serving/api/routes/unified.py +324 -165
- isa_model/serving/api/startup.py +304 -0
- isa_model/serving/modal_proxy_server.py +249 -0
- isa_model/training/__init__.py +100 -6
- isa_model/training/core/__init__.py +4 -1
- isa_model/training/examples/intelligent_training_example.py +281 -0
- isa_model/training/intelligent/__init__.py +25 -0
- isa_model/training/intelligent/decision_engine.py +643 -0
- isa_model/training/intelligent/intelligent_factory.py +888 -0
- isa_model/training/intelligent/knowledge_base.py +751 -0
- isa_model/training/intelligent/resource_optimizer.py +839 -0
- isa_model/training/intelligent/task_classifier.py +576 -0
- isa_model/training/storage/__init__.py +24 -0
- isa_model/training/storage/core_integration.py +439 -0
- isa_model/training/storage/training_repository.py +552 -0
- isa_model/training/storage/training_storage.py +628 -0
- {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/METADATA +13 -1
- isa_model-0.4.0.dist-info/RECORD +182 -0
- isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -766
- isa_model/deployment/cloud/modal/register_models.py +0 -321
- isa_model/inference/adapter/unified_api.py +0 -248
- isa_model/inference/services/helpers/stacked_config.py +0 -148
- isa_model/inference/services/img/flux_professional_service.py +0 -603
- isa_model/inference/services/img/helpers/base_stacked_service.py +0 -274
- isa_model/inference/services/others/table_transformer_service.py +0 -61
- isa_model/inference/services/vision/doc_analysis_service.py +0 -640
- isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -274
- isa_model/inference/services/vision/ui_analysis_service.py +0 -823
- isa_model/scripts/inference_tracker.py +0 -283
- isa_model/scripts/mlflow_manager.py +0 -379
- isa_model/scripts/model_registry.py +0 -465
- isa_model/scripts/register_models.py +0 -370
- isa_model/scripts/register_models_with_embeddings.py +0 -510
- isa_model/scripts/start_mlflow.py +0 -95
- isa_model/scripts/training_tracker.py +0 -257
- isa_model-0.3.9.dist-info/RECORD +0 -138
- {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/WHEEL +0 -0
- {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/top_level.txt +0 -0
@@ -252,7 +252,8 @@ class YydsLLMService(BaseLLMService):
|
|
252
252
|
async def chat(
|
253
253
|
self,
|
254
254
|
input_data: Union[str, List[Dict[str, str]], Any],
|
255
|
-
max_tokens: Optional[int] = None
|
255
|
+
max_tokens: Optional[int] = None,
|
256
|
+
show_reasoning: bool = False
|
256
257
|
) -> Dict[str, Any]:
|
257
258
|
"""
|
258
259
|
Chat method that wraps ainvoke for compatibility with base class
|
@@ -11,9 +11,9 @@ from .base_vision_service import BaseVisionService
|
|
11
11
|
from .openai_vision_service import OpenAIVisionService
|
12
12
|
from .replicate_vision_service import ReplicateVisionService
|
13
13
|
|
14
|
-
# Stacked Vision Services
|
15
|
-
from .doc_analysis_service import DocAnalysisStackedService
|
16
|
-
from .ui_analysis_service import UIAnalysisService
|
14
|
+
# Stacked Vision Services (disabled - files don't exist)
|
15
|
+
# from .doc_analysis_service import DocAnalysisStackedService
|
16
|
+
# from .ui_analysis_service import UIAnalysisService
|
17
17
|
|
18
18
|
# ISA Vision service
|
19
19
|
try:
|
@@ -35,8 +35,8 @@ __all__ = [
|
|
35
35
|
"BaseVisionService",
|
36
36
|
"OpenAIVisionService",
|
37
37
|
"ReplicateVisionService",
|
38
|
-
"DocAnalysisStackedService",
|
39
|
-
"UIAnalysisService"
|
38
|
+
# "DocAnalysisStackedService", # Disabled - file doesn't exist
|
39
|
+
# "UIAnalysisService" # Disabled - file doesn't exist
|
40
40
|
]
|
41
41
|
|
42
42
|
if ISA_VISION_AVAILABLE:
|
@@ -21,43 +21,61 @@ class BaseVisionService(BaseService):
|
|
21
21
|
**kwargs
|
22
22
|
) -> Dict[str, Any]:
|
23
23
|
"""
|
24
|
-
统一的任务分发方法 -
|
24
|
+
统一的任务分发方法 - 基于6个核心任务的设计
|
25
25
|
|
26
26
|
Args:
|
27
27
|
image: Path to image file or image data
|
28
28
|
prompt: Optional text prompt/question about the image
|
29
|
-
task:
|
30
|
-
**kwargs: Additional task-specific parameters
|
29
|
+
task: Core task type (analyze, describe, extract, detect, classify, compare)
|
30
|
+
**kwargs: Additional task-specific parameters including:
|
31
|
+
- target: Sub-task specification (e.g., "text"/"table" for extract, "objects"/"ui" for detect)
|
32
|
+
- max_tokens: Maximum tokens for text generation
|
33
|
+
- confidence_threshold: Confidence threshold for detection
|
34
|
+
- categories: Categories for classification
|
35
|
+
- image2: Second image for comparison
|
31
36
|
|
32
37
|
Returns:
|
33
38
|
Dict containing task results
|
34
39
|
"""
|
35
40
|
task = task or "analyze"
|
36
41
|
|
37
|
-
#
|
38
|
-
if task == "analyze":
|
42
|
+
# Core task dispatch with parameterized sub-tasks
|
43
|
+
if task == "analyze" and hasattr(self, 'analyze_image'):
|
39
44
|
return await self.analyze_image(image, prompt, kwargs.get("max_tokens", 1000))
|
40
|
-
|
45
|
+
|
46
|
+
elif task == "describe" and hasattr(self, 'describe_image'):
|
41
47
|
return await self.describe_image(image, kwargs.get("detail_level", "medium"))
|
42
|
-
|
48
|
+
|
49
|
+
elif task == "extract":
|
50
|
+
# Extract with target specification
|
51
|
+
target = kwargs.get("target", "text")
|
52
|
+
if target == "table" and hasattr(self, 'extract_table_data'):
|
53
|
+
return await self.extract_table_data(image, kwargs.get("table_format", "json"))
|
54
|
+
elif hasattr(self, 'extract_text'):
|
55
|
+
return await self.extract_text(image)
|
56
|
+
else:
|
57
|
+
raise NotImplementedError(f"{self.__class__.__name__} does not support extract task")
|
58
|
+
|
59
|
+
elif task == "detect":
|
60
|
+
# Detect with target specification
|
61
|
+
target = kwargs.get("target", "objects")
|
62
|
+
if target == "ui" and hasattr(self, 'detect_ui_elements'):
|
63
|
+
return await self.detect_ui_elements(image,
|
64
|
+
kwargs.get("element_types"),
|
65
|
+
kwargs.get("confidence_threshold", 0.5))
|
66
|
+
elif target == "coordinates" and hasattr(self, 'get_object_coordinates'):
|
67
|
+
return await self.get_object_coordinates(image, kwargs.get("object_name", ""))
|
68
|
+
elif hasattr(self, 'detect_objects'):
|
69
|
+
return await self.detect_objects(image, kwargs.get("confidence_threshold", 0.5))
|
70
|
+
else:
|
71
|
+
raise NotImplementedError(f"{self.__class__.__name__} does not support detect task")
|
72
|
+
|
73
|
+
elif task == "classify" and hasattr(self, 'classify_image'):
|
43
74
|
return await self.classify_image(image, kwargs.get("categories"))
|
44
|
-
|
75
|
+
|
76
|
+
elif task == "compare" and hasattr(self, 'compare_images'):
|
45
77
|
return await self.compare_images(image, kwargs.get("image2"))
|
46
|
-
|
47
|
-
# ==================== 检测抽取类任务 ====================
|
48
|
-
elif task == "extract_text":
|
49
|
-
return await self.extract_text(image)
|
50
|
-
elif task == "detect_objects":
|
51
|
-
return await self.detect_objects(image, kwargs.get("confidence_threshold", 0.5))
|
52
|
-
elif task == "detect_ui_elements":
|
53
|
-
return await self.detect_ui_elements(image, kwargs.get("element_types"), kwargs.get("confidence_threshold", 0.5))
|
54
|
-
elif task == "detect_document_elements":
|
55
|
-
return await self.detect_document_elements(image, kwargs.get("element_types"), kwargs.get("confidence_threshold", 0.5))
|
56
|
-
elif task == "extract_table_data":
|
57
|
-
return await self.extract_table_data(image, kwargs.get("table_format", "json"), kwargs.get("preserve_formatting", True))
|
58
|
-
elif task == "get_coordinates":
|
59
|
-
return await self.get_object_coordinates(image, kwargs.get("object_name", ""))
|
60
|
-
|
78
|
+
|
61
79
|
else:
|
62
80
|
raise NotImplementedError(f"{self.__class__.__name__} does not support task: {task}")
|
63
81
|
|
@@ -83,40 +101,84 @@ class BaseVisionService(BaseService):
|
|
83
101
|
- metadata: Additional metadata about the analysis
|
84
102
|
"""
|
85
103
|
raise NotImplementedError(f"{self.__class__.__name__} does not support analyze_image task")
|
104
|
+
|
86
105
|
|
87
|
-
# ==================== 图像理解类方法 ====================
|
88
106
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
107
|
+
|
108
|
+
async def close(self):
|
109
|
+
"""Cleanup resources - default implementation does nothing"""
|
110
|
+
pass
|
111
|
+
|
112
|
+
def get_supported_tasks(self) -> List[str]:
|
94
113
|
"""
|
95
|
-
|
114
|
+
获取provider支持的核心任务列表
|
115
|
+
|
116
|
+
Returns:
|
117
|
+
List of core task names (analyze, describe, extract, detect, classify, compare)
|
96
118
|
"""
|
97
|
-
|
119
|
+
supported = []
|
120
|
+
|
121
|
+
# Check core task support based on implemented methods
|
122
|
+
task_method_map = {
|
123
|
+
'analyze': 'analyze_image',
|
124
|
+
'describe': 'describe_image',
|
125
|
+
'extract': 'extract_text', # Basic extract support
|
126
|
+
'detect': 'detect_objects', # Basic detect support
|
127
|
+
'classify': 'classify_image',
|
128
|
+
'compare': 'compare_images'
|
129
|
+
}
|
130
|
+
|
131
|
+
for task_name, method_name in task_method_map.items():
|
132
|
+
if hasattr(self, method_name):
|
133
|
+
try:
|
134
|
+
# Check if method is actually implemented (not just raising NotImplementedError)
|
135
|
+
import inspect
|
136
|
+
method = getattr(self, method_name)
|
137
|
+
if callable(method):
|
138
|
+
source = inspect.getsource(method)
|
139
|
+
# If it's not just raising NotImplementedError, consider it supported
|
140
|
+
if not ('raise NotImplementedError' in source and len(source.split('\n')) < 10):
|
141
|
+
supported.append(task_name)
|
142
|
+
except:
|
143
|
+
# If we can't inspect, assume it's supported if the method exists
|
144
|
+
supported.append(task_name)
|
145
|
+
|
146
|
+
return supported
|
98
147
|
|
99
|
-
|
148
|
+
# ==================== COMMON TASK IMPLEMENTATIONS ====================
|
149
|
+
# 为每个provider提供可选的默认实现,provider可以覆盖这些方法
|
150
|
+
|
151
|
+
async def analyze_images(
|
100
152
|
self,
|
101
|
-
|
102
|
-
|
103
|
-
|
153
|
+
images: List[Union[str, BinaryIO]],
|
154
|
+
prompt: Optional[str] = None,
|
155
|
+
max_tokens: int = 1000
|
156
|
+
) -> List[Dict[str, Any]]:
|
104
157
|
"""
|
105
|
-
|
158
|
+
批量图像分析 - Provider可选实现
|
159
|
+
默认实现:如果provider支持analyze_image,则逐个调用
|
106
160
|
"""
|
107
|
-
|
161
|
+
if hasattr(self, 'analyze_image'):
|
162
|
+
results = []
|
163
|
+
for image in images:
|
164
|
+
try:
|
165
|
+
result = await self.analyze_image(image, prompt, max_tokens)
|
166
|
+
results.append(result)
|
167
|
+
except NotImplementedError:
|
168
|
+
raise NotImplementedError(f"{self.__class__.__name__} does not support analyze_images task")
|
169
|
+
return results
|
170
|
+
else:
|
171
|
+
raise NotImplementedError(f"{self.__class__.__name__} does not support analyze_images task")
|
108
172
|
|
109
|
-
async def
|
173
|
+
async def describe_image(
|
110
174
|
self,
|
111
|
-
|
112
|
-
|
175
|
+
image: Union[str, BinaryIO],
|
176
|
+
detail_level: str = "medium"
|
113
177
|
) -> Dict[str, Any]:
|
114
178
|
"""
|
115
|
-
|
179
|
+
图像描述 - Provider可选实现
|
116
180
|
"""
|
117
|
-
raise NotImplementedError(f"{self.__class__.__name__} does not support
|
118
|
-
|
119
|
-
# ==================== 检测抽取类方法 ====================
|
181
|
+
raise NotImplementedError(f"{self.__class__.__name__} does not support describe_image task")
|
120
182
|
|
121
183
|
async def extract_text(self, image: Union[str, BinaryIO]) -> Dict[str, Any]:
|
122
184
|
"""
|
@@ -130,48 +192,10 @@ class BaseVisionService(BaseService):
|
|
130
192
|
confidence_threshold: float = 0.5
|
131
193
|
) -> Dict[str, Any]:
|
132
194
|
"""
|
133
|
-
|
195
|
+
物体检测 - Provider可选实现
|
134
196
|
"""
|
135
197
|
raise NotImplementedError(f"{self.__class__.__name__} does not support detect_objects task")
|
136
198
|
|
137
|
-
async def detect_ui_elements(
|
138
|
-
self,
|
139
|
-
image: Union[str, BinaryIO],
|
140
|
-
element_types: Optional[List[str]] = None,
|
141
|
-
confidence_threshold: float = 0.5
|
142
|
-
) -> Dict[str, Any]:
|
143
|
-
"""
|
144
|
-
UI界面元素检测 - Provider可选实现
|
145
|
-
|
146
|
-
Args:
|
147
|
-
image: 输入图像
|
148
|
-
element_types: 要检测的元素类型 ['button', 'input', 'text', 'image', 'link', etc.]
|
149
|
-
confidence_threshold: 置信度阈值
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
Dict containing detected UI elements with their bounding boxes and types
|
153
|
-
"""
|
154
|
-
raise NotImplementedError(f"{self.__class__.__name__} does not support detect_ui_elements task")
|
155
|
-
|
156
|
-
async def detect_document_elements(
|
157
|
-
self,
|
158
|
-
image: Union[str, BinaryIO],
|
159
|
-
element_types: Optional[List[str]] = None,
|
160
|
-
confidence_threshold: float = 0.5
|
161
|
-
) -> Dict[str, Any]:
|
162
|
-
"""
|
163
|
-
文档结构元素检测 - Provider可选实现
|
164
|
-
|
165
|
-
Args:
|
166
|
-
image: 输入图像
|
167
|
-
element_types: 要检测的元素类型 ['table', 'header', 'paragraph', 'list', etc.]
|
168
|
-
confidence_threshold: 置信度阈值
|
169
|
-
|
170
|
-
Returns:
|
171
|
-
Dict containing detected document elements with their structure and content
|
172
|
-
"""
|
173
|
-
raise NotImplementedError(f"{self.__class__.__name__} does not support detect_document_elements task")
|
174
|
-
|
175
199
|
async def get_object_coordinates(
|
176
200
|
self,
|
177
201
|
image: Union[str, BinaryIO],
|
@@ -182,116 +206,25 @@ class BaseVisionService(BaseService):
|
|
182
206
|
"""
|
183
207
|
raise NotImplementedError(f"{self.__class__.__name__} does not support get_object_coordinates task")
|
184
208
|
|
185
|
-
async def
|
186
|
-
self,
|
209
|
+
async def classify_image(
|
210
|
+
self,
|
187
211
|
image: Union[str, BinaryIO],
|
188
|
-
|
189
|
-
preserve_formatting: bool = True
|
212
|
+
categories: Optional[List[str]] = None
|
190
213
|
) -> Dict[str, Any]:
|
191
214
|
"""
|
192
|
-
|
193
|
-
|
194
|
-
Args:
|
195
|
-
image: 输入图像
|
196
|
-
table_format: 输出格式 ('json', 'csv', 'markdown', 'html')
|
197
|
-
preserve_formatting: 是否保持原始格式(合并单元格、样式等)
|
198
|
-
|
199
|
-
Returns:
|
200
|
-
Dict containing extracted table data in structured format:
|
201
|
-
{
|
202
|
-
"tables": [
|
203
|
-
{
|
204
|
-
"table_id": "table_1",
|
205
|
-
"headers": ["Column1", "Column2", "Column3"],
|
206
|
-
"rows": [
|
207
|
-
["cell1", "cell2", "cell3"],
|
208
|
-
["cell4", "cell5", "cell6"]
|
209
|
-
],
|
210
|
-
"metadata": {
|
211
|
-
"row_count": 2,
|
212
|
-
"column_count": 3,
|
213
|
-
"has_headers": true,
|
214
|
-
"merged_cells": [],
|
215
|
-
"table_caption": "optional_caption"
|
216
|
-
}
|
217
|
-
}
|
218
|
-
],
|
219
|
-
"raw_data": "original_table_text",
|
220
|
-
"format": "json"
|
221
|
-
}
|
222
|
-
"""
|
223
|
-
raise NotImplementedError(f"{self.__class__.__name__} does not support extract_table_data task")
|
224
|
-
|
225
|
-
async def close(self):
|
226
|
-
"""Cleanup resources - default implementation does nothing"""
|
227
|
-
pass
|
228
|
-
|
229
|
-
def get_supported_tasks(self) -> List[str]:
|
230
|
-
"""
|
231
|
-
获取provider支持的任务列表
|
232
|
-
|
233
|
-
Returns:
|
234
|
-
List of supported task names
|
215
|
+
图像分类 - Provider可选实现
|
235
216
|
"""
|
236
|
-
|
237
|
-
|
238
|
-
# 检查哪些方法被实现了
|
239
|
-
if hasattr(self, 'analyze_image') and callable(getattr(self, 'analyze_image')):
|
240
|
-
try:
|
241
|
-
# 尝试调用看是否抛出NotImplementedError
|
242
|
-
import inspect
|
243
|
-
if not 'NotImplementedError' in inspect.getsource(self.analyze_image):
|
244
|
-
supported.append('analyze')
|
245
|
-
except:
|
246
|
-
pass
|
247
|
-
|
248
|
-
# 检查各类任务支持情况
|
249
|
-
method_task_map = {
|
250
|
-
# 图像理解类
|
251
|
-
'describe_image': 'describe',
|
252
|
-
'classify_image': 'classify',
|
253
|
-
'compare_images': 'compare',
|
254
|
-
# 检测抽取类
|
255
|
-
'extract_text': 'extract_text',
|
256
|
-
'detect_objects': 'detect_objects',
|
257
|
-
'detect_ui_elements': 'detect_ui_elements',
|
258
|
-
'detect_document_elements': 'detect_document_elements',
|
259
|
-
'extract_table_data': 'extract_table_data',
|
260
|
-
'get_object_coordinates': 'get_coordinates'
|
261
|
-
}
|
262
|
-
|
263
|
-
for method_name, task_name in method_task_map.items():
|
264
|
-
if hasattr(self, method_name):
|
265
|
-
# 检查是否是默认实现(基于analyze_image)还是provider自己的实现
|
266
|
-
supported.append(task_name)
|
267
|
-
|
268
|
-
return supported
|
269
|
-
|
270
|
-
# ==================== COMMON TASK IMPLEMENTATIONS ====================
|
271
|
-
# 为每个provider提供可选的默认实现,provider可以覆盖这些方法
|
217
|
+
raise NotImplementedError(f"{self.__class__.__name__} does not support classify_image task")
|
272
218
|
|
273
|
-
async def
|
219
|
+
async def compare_images(
|
274
220
|
self,
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
) -> List[Dict[str, Any]]:
|
221
|
+
image1: Union[str, BinaryIO],
|
222
|
+
image2: Union[str, BinaryIO]
|
223
|
+
) -> Dict[str, Any]:
|
279
224
|
"""
|
280
|
-
|
281
|
-
默认实现:如果provider支持analyze_image,则逐个调用
|
225
|
+
图像比较 - Provider可选实现
|
282
226
|
"""
|
283
|
-
|
284
|
-
results = []
|
285
|
-
for image in images:
|
286
|
-
try:
|
287
|
-
result = await self.analyze_image(image, prompt, max_tokens)
|
288
|
-
results.append(result)
|
289
|
-
except NotImplementedError:
|
290
|
-
raise NotImplementedError(f"{self.__class__.__name__} does not support analyze_images task")
|
291
|
-
return results
|
292
|
-
else:
|
293
|
-
raise NotImplementedError(f"{self.__class__.__name__} does not support analyze_images task")
|
294
|
-
|
227
|
+
raise NotImplementedError(f"{self.__class__.__name__} does not support compare_images task")
|
295
228
|
|
296
229
|
def get_supported_formats(self) -> List[str]:
|
297
230
|
"""
|
@@ -27,16 +27,22 @@ def get_image_data(image: Union[str, BinaryIO]) -> bytes:
|
|
27
27
|
return response.content
|
28
28
|
elif image.startswith('data:'):
|
29
29
|
# Data URL格式 (如 data:image/png;base64,...)
|
30
|
-
import base64
|
31
30
|
if 'base64,' in image:
|
32
31
|
base64_data = image.split('base64,')[1]
|
33
32
|
return base64.b64decode(base64_data)
|
34
33
|
else:
|
35
34
|
raise ValueError("Unsupported data URL format")
|
36
|
-
|
37
|
-
#
|
38
|
-
|
39
|
-
return
|
35
|
+
elif len(image) > 100 and not os.path.exists(image):
|
36
|
+
# 纯base64字符串 (没有data URL前缀)
|
37
|
+
try:
|
38
|
+
return base64.b64decode(image)
|
39
|
+
except Exception:
|
40
|
+
# 如果base64解码失败,则当作文件路径处理
|
41
|
+
pass
|
42
|
+
|
43
|
+
# 本地文件路径
|
44
|
+
with open(image, 'rb') as f:
|
45
|
+
return f.read()
|
40
46
|
elif hasattr(image, 'read'):
|
41
47
|
# 文件类对象
|
42
48
|
data = image.read()
|