isa-model 0.3.5__py3-none-any.whl → 0.3.6__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 +30 -1
- isa_model/client.py +770 -0
- isa_model/core/config/__init__.py +16 -0
- isa_model/core/config/config_manager.py +514 -0
- isa_model/core/config.py +426 -0
- isa_model/core/models/model_billing_tracker.py +476 -0
- isa_model/core/models/model_manager.py +399 -0
- isa_model/core/{storage/supabase_storage.py → models/model_repo.py} +72 -73
- isa_model/core/pricing_manager.py +426 -0
- isa_model/core/services/__init__.py +19 -0
- isa_model/core/services/intelligent_model_selector.py +547 -0
- isa_model/core/types.py +291 -0
- isa_model/deployment/__init__.py +2 -0
- isa_model/deployment/cloud/modal/isa_vision_doc_service.py +157 -3
- isa_model/deployment/cloud/modal/isa_vision_table_service.py +532 -0
- isa_model/deployment/cloud/modal/isa_vision_ui_service.py +104 -3
- isa_model/deployment/cloud/modal/register_models.py +321 -0
- isa_model/deployment/runtime/deployed_service.py +338 -0
- isa_model/deployment/services/__init__.py +9 -0
- isa_model/deployment/services/auto_deploy_vision_service.py +537 -0
- isa_model/deployment/services/model_service.py +332 -0
- isa_model/deployment/services/service_monitor.py +356 -0
- isa_model/deployment/services/service_registry.py +527 -0
- isa_model/eval/__init__.py +80 -44
- isa_model/eval/config/__init__.py +10 -0
- isa_model/eval/config/evaluation_config.py +108 -0
- isa_model/eval/evaluators/__init__.py +18 -0
- isa_model/eval/evaluators/base_evaluator.py +503 -0
- isa_model/eval/evaluators/llm_evaluator.py +472 -0
- isa_model/eval/factory.py +417 -709
- isa_model/eval/infrastructure/__init__.py +24 -0
- isa_model/eval/infrastructure/experiment_tracker.py +466 -0
- isa_model/eval/metrics.py +191 -21
- isa_model/inference/ai_factory.py +181 -605
- isa_model/inference/services/audio/base_stt_service.py +65 -1
- isa_model/inference/services/audio/base_tts_service.py +75 -1
- isa_model/inference/services/audio/openai_stt_service.py +189 -151
- isa_model/inference/services/audio/openai_tts_service.py +12 -10
- isa_model/inference/services/audio/replicate_tts_service.py +61 -56
- isa_model/inference/services/base_service.py +55 -17
- isa_model/inference/services/embedding/base_embed_service.py +65 -1
- isa_model/inference/services/embedding/ollama_embed_service.py +103 -43
- isa_model/inference/services/embedding/openai_embed_service.py +8 -10
- isa_model/inference/services/helpers/stacked_config.py +148 -0
- isa_model/inference/services/img/__init__.py +18 -0
- isa_model/inference/services/{vision → img}/base_image_gen_service.py +80 -1
- isa_model/inference/services/{stacked → img}/flux_professional_service.py +25 -1
- isa_model/inference/services/{stacked → img/helpers}/base_stacked_service.py +40 -35
- isa_model/inference/services/{vision → img}/replicate_image_gen_service.py +44 -31
- isa_model/inference/services/llm/__init__.py +3 -3
- isa_model/inference/services/llm/base_llm_service.py +492 -40
- isa_model/inference/services/llm/helpers/llm_prompts.py +258 -0
- isa_model/inference/services/llm/helpers/llm_utils.py +280 -0
- isa_model/inference/services/llm/ollama_llm_service.py +51 -17
- isa_model/inference/services/llm/openai_llm_service.py +70 -19
- isa_model/inference/services/llm/yyds_llm_service.py +24 -23
- isa_model/inference/services/vision/__init__.py +38 -4
- isa_model/inference/services/vision/base_vision_service.py +218 -117
- isa_model/inference/services/vision/{isA_vision_service.py → disabled/isA_vision_service.py} +98 -0
- isa_model/inference/services/{stacked → vision}/doc_analysis_service.py +1 -1
- isa_model/inference/services/vision/helpers/base_stacked_service.py +274 -0
- isa_model/inference/services/vision/helpers/image_utils.py +272 -3
- isa_model/inference/services/vision/helpers/vision_prompts.py +297 -0
- isa_model/inference/services/vision/openai_vision_service.py +104 -307
- isa_model/inference/services/vision/replicate_vision_service.py +140 -325
- isa_model/inference/services/{stacked → vision}/ui_analysis_service.py +2 -498
- isa_model/scripts/register_models.py +370 -0
- isa_model/scripts/register_models_with_embeddings.py +510 -0
- isa_model/serving/api/fastapi_server.py +6 -1
- isa_model/serving/api/routes/unified.py +202 -0
- {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/METADATA +4 -1
- {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/RECORD +77 -53
- isa_model/config/__init__.py +0 -9
- isa_model/config/config_manager.py +0 -213
- isa_model/core/model_manager.py +0 -213
- isa_model/core/model_registry.py +0 -375
- isa_model/core/vision_models_init.py +0 -116
- isa_model/inference/billing_tracker.py +0 -406
- isa_model/inference/services/llm/triton_llm_service.py +0 -481
- isa_model/inference/services/stacked/__init__.py +0 -26
- isa_model/inference/services/stacked/config.py +0 -426
- isa_model/inference/services/vision/ollama_vision_service.py +0 -194
- /isa_model/core/{model_storage.py → models/model_storage.py} +0 -0
- /isa_model/inference/services/{vision → embedding}/helpers/text_splitter.py +0 -0
- /isa_model/inference/services/llm/{llm_adapter.py → helpers/llm_adapter.py} +0 -0
- {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/WHEEL +0 -0
- {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,370 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
模型注册脚本
|
4
|
+
|
5
|
+
从YAML配置文件中读取模型定义,并注册到ModelRegistry中。
|
6
|
+
支持批量注册、更新和验证。
|
7
|
+
|
8
|
+
Usage:
|
9
|
+
python register_models.py --all # 注册所有providers的模型
|
10
|
+
python register_models.py --provider openai # 只注册openai的模型
|
11
|
+
python register_models.py --dry-run # 仅验证配置,不实际注册
|
12
|
+
python register_models.py --update # 更新已存在的模型
|
13
|
+
"""
|
14
|
+
|
15
|
+
import argparse
|
16
|
+
import asyncio
|
17
|
+
import logging
|
18
|
+
import sys
|
19
|
+
from pathlib import Path
|
20
|
+
from typing import Dict, List, Any
|
21
|
+
import yaml
|
22
|
+
|
23
|
+
# 添加项目根目录到Python路径
|
24
|
+
project_root = Path(__file__).parent.parent
|
25
|
+
sys.path.insert(0, str(project_root))
|
26
|
+
|
27
|
+
from isa_model.core.models.model_manager import ModelManager
|
28
|
+
from isa_model.core.models.model_repo import ModelType, ModelCapability
|
29
|
+
|
30
|
+
# 配置日志
|
31
|
+
logging.basicConfig(
|
32
|
+
level=logging.INFO,
|
33
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
34
|
+
)
|
35
|
+
logger = logging.getLogger(__name__)
|
36
|
+
|
37
|
+
|
38
|
+
class ModelRegistrationScript:
|
39
|
+
"""模型注册脚本类"""
|
40
|
+
|
41
|
+
def __init__(self):
|
42
|
+
self.model_manager = ModelManager()
|
43
|
+
self.config_dir = project_root / "isa_model" / "config" / "models"
|
44
|
+
|
45
|
+
# 能力映射
|
46
|
+
self.capability_mapping = {
|
47
|
+
"text_generation": ModelCapability.TEXT_GENERATION,
|
48
|
+
"chat": ModelCapability.CHAT,
|
49
|
+
"embedding": ModelCapability.EMBEDDING,
|
50
|
+
"reranking": ModelCapability.RERANKING,
|
51
|
+
"reasoning": ModelCapability.REASONING,
|
52
|
+
"image_generation": ModelCapability.IMAGE_GENERATION,
|
53
|
+
"image_analysis": ModelCapability.IMAGE_ANALYSIS,
|
54
|
+
"audio_transcription": ModelCapability.AUDIO_TRANSCRIPTION,
|
55
|
+
"image_understanding": ModelCapability.IMAGE_UNDERSTANDING,
|
56
|
+
"ui_detection": ModelCapability.UI_DETECTION,
|
57
|
+
"ocr": ModelCapability.OCR,
|
58
|
+
"table_detection": ModelCapability.TABLE_DETECTION,
|
59
|
+
"table_structure_recognition": ModelCapability.TABLE_STRUCTURE_RECOGNITION,
|
60
|
+
}
|
61
|
+
|
62
|
+
# 模型类型映射
|
63
|
+
self.type_mapping = {
|
64
|
+
"llm": ModelType.LLM,
|
65
|
+
"embedding": ModelType.EMBEDDING,
|
66
|
+
"rerank": ModelType.RERANK,
|
67
|
+
"image": ModelType.IMAGE,
|
68
|
+
"audio": ModelType.AUDIO,
|
69
|
+
"video": ModelType.VIDEO,
|
70
|
+
"vision": ModelType.VISION,
|
71
|
+
}
|
72
|
+
|
73
|
+
def load_provider_config(self, provider: str) -> Dict[str, Any]:
|
74
|
+
"""加载provider的YAML配置"""
|
75
|
+
config_file = self.config_dir / f"{provider}_models.yaml"
|
76
|
+
|
77
|
+
if not config_file.exists():
|
78
|
+
raise FileNotFoundError(f"Configuration file not found: {config_file}")
|
79
|
+
|
80
|
+
try:
|
81
|
+
with open(config_file, 'r', encoding='utf-8') as f:
|
82
|
+
config = yaml.safe_load(f)
|
83
|
+
|
84
|
+
logger.info(f"Loaded configuration for provider '{provider}' with {len(config.get('models', []))} models")
|
85
|
+
return config
|
86
|
+
|
87
|
+
except yaml.YAMLError as e:
|
88
|
+
raise ValueError(f"Invalid YAML in {config_file}: {e}")
|
89
|
+
|
90
|
+
def validate_model_config(self, model_config: Dict[str, Any], provider: str) -> bool:
|
91
|
+
"""验证单个模型配置"""
|
92
|
+
required_fields = ["model_id", "model_type", "capabilities", "metadata"]
|
93
|
+
|
94
|
+
for field in required_fields:
|
95
|
+
if field not in model_config:
|
96
|
+
logger.error(f"Missing required field '{field}' in model config: {model_config}")
|
97
|
+
return False
|
98
|
+
|
99
|
+
# 验证model_type
|
100
|
+
model_type = model_config["model_type"]
|
101
|
+
if model_type not in self.type_mapping:
|
102
|
+
logger.error(f"Invalid model_type '{model_type}' for model {model_config['model_id']}")
|
103
|
+
return False
|
104
|
+
|
105
|
+
# 验证capabilities
|
106
|
+
capabilities = model_config["capabilities"]
|
107
|
+
if not isinstance(capabilities, list) or not capabilities:
|
108
|
+
logger.error(f"Capabilities must be a non-empty list for model {model_config['model_id']}")
|
109
|
+
return False
|
110
|
+
|
111
|
+
for cap in capabilities:
|
112
|
+
if cap not in self.capability_mapping:
|
113
|
+
logger.error(f"Invalid capability '{cap}' for model {model_config['model_id']}")
|
114
|
+
return False
|
115
|
+
|
116
|
+
# 验证metadata
|
117
|
+
metadata = model_config["metadata"]
|
118
|
+
required_metadata = ["description", "performance_tier", "provider_model_name"]
|
119
|
+
|
120
|
+
for field in required_metadata:
|
121
|
+
if field not in metadata:
|
122
|
+
logger.error(f"Missing required metadata field '{field}' for model {model_config['model_id']}")
|
123
|
+
return False
|
124
|
+
|
125
|
+
return True
|
126
|
+
|
127
|
+
def validate_provider_config(self, config: Dict[str, Any], provider: str) -> bool:
|
128
|
+
"""验证provider的完整配置"""
|
129
|
+
if "provider" not in config:
|
130
|
+
logger.error(f"Missing 'provider' field in config")
|
131
|
+
return False
|
132
|
+
|
133
|
+
if config["provider"] != provider:
|
134
|
+
logger.error(f"Provider mismatch: expected '{provider}', got '{config['provider']}'")
|
135
|
+
return False
|
136
|
+
|
137
|
+
if "models" not in config:
|
138
|
+
logger.error(f"Missing 'models' field in config")
|
139
|
+
return False
|
140
|
+
|
141
|
+
models = config["models"]
|
142
|
+
if not isinstance(models, list) or not models:
|
143
|
+
logger.error(f"Models must be a non-empty list")
|
144
|
+
return False
|
145
|
+
|
146
|
+
# 验证每个模型
|
147
|
+
valid_count = 0
|
148
|
+
for i, model_config in enumerate(models):
|
149
|
+
if self.validate_model_config(model_config, provider):
|
150
|
+
valid_count += 1
|
151
|
+
else:
|
152
|
+
logger.error(f"Invalid configuration for model #{i+1}")
|
153
|
+
|
154
|
+
if valid_count != len(models):
|
155
|
+
logger.error(f"Only {valid_count}/{len(models)} models have valid configuration")
|
156
|
+
return False
|
157
|
+
|
158
|
+
logger.info(f"All {len(models)} models in {provider} configuration are valid")
|
159
|
+
return True
|
160
|
+
|
161
|
+
async def register_model(self, model_config: Dict[str, Any], provider: str, update: bool = False) -> bool:
|
162
|
+
"""注册单个模型"""
|
163
|
+
try:
|
164
|
+
model_id = model_config["model_id"]
|
165
|
+
model_type = self.type_mapping[model_config["model_type"]]
|
166
|
+
|
167
|
+
# 转换capabilities
|
168
|
+
capabilities = [
|
169
|
+
self.capability_mapping[cap] for cap in model_config["capabilities"]
|
170
|
+
]
|
171
|
+
|
172
|
+
# 准备metadata
|
173
|
+
metadata = model_config["metadata"].copy()
|
174
|
+
metadata["provider"] = provider
|
175
|
+
|
176
|
+
# 检查模型是否已存在
|
177
|
+
existing_model = await self.model_manager.get_model_info(model_id)
|
178
|
+
|
179
|
+
if existing_model and not update:
|
180
|
+
logger.warning(f"Model {model_id} already exists. Use --update to overwrite.")
|
181
|
+
return False
|
182
|
+
|
183
|
+
if existing_model and update:
|
184
|
+
logger.info(f"Updating existing model {model_id}")
|
185
|
+
else:
|
186
|
+
logger.info(f"Registering new model {model_id}")
|
187
|
+
|
188
|
+
# 注册模型
|
189
|
+
success = await self.model_manager.register_model_for_lifecycle(
|
190
|
+
model_id=model_id,
|
191
|
+
model_type=model_type,
|
192
|
+
capabilities=capabilities,
|
193
|
+
provider=provider,
|
194
|
+
provider_model_name=metadata["provider_model_name"],
|
195
|
+
metadata=metadata
|
196
|
+
)
|
197
|
+
|
198
|
+
if success:
|
199
|
+
logger.info(f"✅ Successfully registered {model_id}")
|
200
|
+
else:
|
201
|
+
logger.error(f"❌ Failed to register {model_id}")
|
202
|
+
|
203
|
+
return success
|
204
|
+
|
205
|
+
except Exception as e:
|
206
|
+
logger.error(f"❌ Error registering model {model_config.get('model_id', 'unknown')}: {e}")
|
207
|
+
return False
|
208
|
+
|
209
|
+
async def register_provider_models(self, provider: str, dry_run: bool = False, update: bool = False) -> bool:
|
210
|
+
"""注册provider的所有模型"""
|
211
|
+
try:
|
212
|
+
# 加载配置
|
213
|
+
config = self.load_provider_config(provider)
|
214
|
+
|
215
|
+
# 验证配置
|
216
|
+
if not self.validate_provider_config(config, provider):
|
217
|
+
logger.error(f"❌ Invalid configuration for provider {provider}")
|
218
|
+
return False
|
219
|
+
|
220
|
+
if dry_run:
|
221
|
+
logger.info(f"✅ Dry run successful for provider {provider} - configuration is valid")
|
222
|
+
return True
|
223
|
+
|
224
|
+
# 注册所有模型
|
225
|
+
models = config["models"]
|
226
|
+
success_count = 0
|
227
|
+
|
228
|
+
logger.info(f"🚀 Starting registration of {len(models)} models for provider {provider}")
|
229
|
+
|
230
|
+
for model_config in models:
|
231
|
+
if await self.register_model(model_config, provider, update):
|
232
|
+
success_count += 1
|
233
|
+
|
234
|
+
# 汇总结果
|
235
|
+
if success_count == len(models):
|
236
|
+
logger.info(f"🎉 All {len(models)} models registered successfully for provider {provider}")
|
237
|
+
else:
|
238
|
+
logger.warning(f"⚠️ Only {success_count}/{len(models)} models registered successfully for provider {provider}")
|
239
|
+
|
240
|
+
return success_count == len(models)
|
241
|
+
|
242
|
+
except Exception as e:
|
243
|
+
logger.error(f"❌ Failed to register models for provider {provider}: {e}")
|
244
|
+
return False
|
245
|
+
|
246
|
+
async def register_all_providers(self, dry_run: bool = False, update: bool = False) -> bool:
|
247
|
+
"""注册所有providers的模型"""
|
248
|
+
providers = ["openai", "replicate", "yyds", "ollama"]
|
249
|
+
|
250
|
+
logger.info(f"🚀 Starting registration for all providers: {', '.join(providers)}")
|
251
|
+
|
252
|
+
overall_success = True
|
253
|
+
results = {}
|
254
|
+
|
255
|
+
for provider in providers:
|
256
|
+
logger.info(f"\n📁 Processing provider: {provider}")
|
257
|
+
try:
|
258
|
+
success = await self.register_provider_models(provider, dry_run, update)
|
259
|
+
results[provider] = success
|
260
|
+
if not success:
|
261
|
+
overall_success = False
|
262
|
+
except FileNotFoundError:
|
263
|
+
logger.warning(f"⚠️ Configuration file not found for provider {provider}, skipping")
|
264
|
+
results[provider] = None
|
265
|
+
except Exception as e:
|
266
|
+
logger.error(f"❌ Unexpected error with provider {provider}: {e}")
|
267
|
+
results[provider] = False
|
268
|
+
overall_success = False
|
269
|
+
|
270
|
+
# 打印汇总报告
|
271
|
+
logger.info("\n" + "="*60)
|
272
|
+
logger.info("📊 REGISTRATION SUMMARY")
|
273
|
+
logger.info("="*60)
|
274
|
+
|
275
|
+
for provider, success in results.items():
|
276
|
+
if success is True:
|
277
|
+
logger.info(f"✅ {provider}: SUCCESS")
|
278
|
+
elif success is False:
|
279
|
+
logger.error(f"❌ {provider}: FAILED")
|
280
|
+
else:
|
281
|
+
logger.warning(f"⚠️ {provider}: SKIPPED (no config)")
|
282
|
+
|
283
|
+
if overall_success:
|
284
|
+
logger.info("\n🎉 All model registration completed successfully!")
|
285
|
+
else:
|
286
|
+
logger.error("\n❌ Some models failed to register. Check logs for details.")
|
287
|
+
|
288
|
+
return overall_success
|
289
|
+
|
290
|
+
def list_available_providers(self) -> List[str]:
|
291
|
+
"""列出可用的provider配置文件"""
|
292
|
+
providers = []
|
293
|
+
for config_file in self.config_dir.glob("*_models.yaml"):
|
294
|
+
provider = config_file.stem.replace("_models", "")
|
295
|
+
providers.append(provider)
|
296
|
+
return sorted(providers)
|
297
|
+
|
298
|
+
|
299
|
+
async def main():
|
300
|
+
"""主函数"""
|
301
|
+
parser = argparse.ArgumentParser(
|
302
|
+
description="Register models from YAML configuration files to ModelRegistry",
|
303
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
304
|
+
epilog="""
|
305
|
+
Examples:
|
306
|
+
python register_models.py --all # Register all providers
|
307
|
+
python register_models.py --provider openai # Register only OpenAI models
|
308
|
+
python register_models.py --dry-run --all # Validate all configs without registering
|
309
|
+
python register_models.py --update --provider openai # Update existing OpenAI models
|
310
|
+
"""
|
311
|
+
)
|
312
|
+
|
313
|
+
parser.add_argument("--all", action="store_true",
|
314
|
+
help="Register models from all provider config files")
|
315
|
+
parser.add_argument("--provider", type=str,
|
316
|
+
help="Register models from specific provider (e.g., openai, replicate)")
|
317
|
+
parser.add_argument("--dry-run", action="store_true",
|
318
|
+
help="Validate configuration without actually registering models")
|
319
|
+
parser.add_argument("--update", action="store_true",
|
320
|
+
help="Update existing models if they already exist")
|
321
|
+
parser.add_argument("--list-providers", action="store_true",
|
322
|
+
help="List available provider configuration files")
|
323
|
+
|
324
|
+
args = parser.parse_args()
|
325
|
+
|
326
|
+
# 创建注册脚本实例
|
327
|
+
script = ModelRegistrationScript()
|
328
|
+
|
329
|
+
# 处理list-providers
|
330
|
+
if args.list_providers:
|
331
|
+
providers = script.list_available_providers()
|
332
|
+
logger.info("Available provider configurations:")
|
333
|
+
for provider in providers:
|
334
|
+
logger.info(f" - {provider}")
|
335
|
+
return
|
336
|
+
|
337
|
+
# 验证参数
|
338
|
+
if not args.all and not args.provider:
|
339
|
+
parser.error("Must specify either --all or --provider")
|
340
|
+
|
341
|
+
if args.all and args.provider:
|
342
|
+
parser.error("Cannot specify both --all and --provider")
|
343
|
+
|
344
|
+
# 执行注册
|
345
|
+
try:
|
346
|
+
if args.dry_run:
|
347
|
+
logger.info("🔍 Running in DRY-RUN mode - no models will actually be registered")
|
348
|
+
|
349
|
+
if args.all:
|
350
|
+
success = await script.register_all_providers(args.dry_run, args.update)
|
351
|
+
else:
|
352
|
+
success = await script.register_provider_models(args.provider, args.dry_run, args.update)
|
353
|
+
|
354
|
+
if success:
|
355
|
+
logger.info("✅ Script completed successfully")
|
356
|
+
sys.exit(0)
|
357
|
+
else:
|
358
|
+
logger.error("❌ Script completed with errors")
|
359
|
+
sys.exit(1)
|
360
|
+
|
361
|
+
except KeyboardInterrupt:
|
362
|
+
logger.info("\n⏹️ Registration cancelled by user")
|
363
|
+
sys.exit(1)
|
364
|
+
except Exception as e:
|
365
|
+
logger.error(f"❌ Unexpected error: {e}")
|
366
|
+
sys.exit(1)
|
367
|
+
|
368
|
+
|
369
|
+
if __name__ == "__main__":
|
370
|
+
asyncio.run(main())
|