isa-model 0.3.4__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.
Files changed (100) hide show
  1. isa_model/__init__.py +30 -1
  2. isa_model/client.py +770 -0
  3. isa_model/core/config/__init__.py +16 -0
  4. isa_model/core/config/config_manager.py +514 -0
  5. isa_model/core/config.py +426 -0
  6. isa_model/core/models/model_billing_tracker.py +476 -0
  7. isa_model/core/models/model_manager.py +399 -0
  8. isa_model/core/models/model_repo.py +343 -0
  9. isa_model/core/pricing_manager.py +426 -0
  10. isa_model/core/services/__init__.py +19 -0
  11. isa_model/core/services/intelligent_model_selector.py +547 -0
  12. isa_model/core/types.py +291 -0
  13. isa_model/deployment/__init__.py +2 -0
  14. isa_model/deployment/cloud/__init__.py +9 -0
  15. isa_model/deployment/cloud/modal/__init__.py +10 -0
  16. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +766 -0
  17. isa_model/deployment/cloud/modal/isa_vision_table_service.py +532 -0
  18. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +406 -0
  19. isa_model/deployment/cloud/modal/register_models.py +321 -0
  20. isa_model/deployment/runtime/deployed_service.py +338 -0
  21. isa_model/deployment/services/__init__.py +9 -0
  22. isa_model/deployment/services/auto_deploy_vision_service.py +537 -0
  23. isa_model/deployment/services/model_service.py +332 -0
  24. isa_model/deployment/services/service_monitor.py +356 -0
  25. isa_model/deployment/services/service_registry.py +527 -0
  26. isa_model/eval/__init__.py +80 -44
  27. isa_model/eval/config/__init__.py +10 -0
  28. isa_model/eval/config/evaluation_config.py +108 -0
  29. isa_model/eval/evaluators/__init__.py +18 -0
  30. isa_model/eval/evaluators/base_evaluator.py +503 -0
  31. isa_model/eval/evaluators/llm_evaluator.py +472 -0
  32. isa_model/eval/factory.py +417 -709
  33. isa_model/eval/infrastructure/__init__.py +24 -0
  34. isa_model/eval/infrastructure/experiment_tracker.py +466 -0
  35. isa_model/eval/metrics.py +191 -21
  36. isa_model/inference/ai_factory.py +187 -387
  37. isa_model/inference/providers/modal_provider.py +109 -0
  38. isa_model/inference/providers/yyds_provider.py +108 -0
  39. isa_model/inference/services/__init__.py +2 -1
  40. isa_model/inference/services/audio/base_stt_service.py +65 -1
  41. isa_model/inference/services/audio/base_tts_service.py +75 -1
  42. isa_model/inference/services/audio/openai_stt_service.py +189 -151
  43. isa_model/inference/services/audio/openai_tts_service.py +12 -10
  44. isa_model/inference/services/audio/replicate_tts_service.py +61 -56
  45. isa_model/inference/services/base_service.py +55 -55
  46. isa_model/inference/services/embedding/base_embed_service.py +65 -1
  47. isa_model/inference/services/embedding/ollama_embed_service.py +103 -43
  48. isa_model/inference/services/embedding/openai_embed_service.py +8 -10
  49. isa_model/inference/services/helpers/stacked_config.py +148 -0
  50. isa_model/inference/services/img/__init__.py +18 -0
  51. isa_model/inference/services/{vision → img}/base_image_gen_service.py +80 -35
  52. isa_model/inference/services/img/flux_professional_service.py +603 -0
  53. isa_model/inference/services/img/helpers/base_stacked_service.py +274 -0
  54. isa_model/inference/services/{vision → img}/replicate_image_gen_service.py +210 -69
  55. isa_model/inference/services/llm/__init__.py +3 -3
  56. isa_model/inference/services/llm/base_llm_service.py +519 -35
  57. isa_model/inference/services/llm/{llm_adapter.py → helpers/llm_adapter.py} +40 -0
  58. isa_model/inference/services/llm/helpers/llm_prompts.py +258 -0
  59. isa_model/inference/services/llm/helpers/llm_utils.py +280 -0
  60. isa_model/inference/services/llm/ollama_llm_service.py +150 -15
  61. isa_model/inference/services/llm/openai_llm_service.py +134 -31
  62. isa_model/inference/services/llm/yyds_llm_service.py +255 -0
  63. isa_model/inference/services/vision/__init__.py +38 -4
  64. isa_model/inference/services/vision/base_vision_service.py +241 -96
  65. isa_model/inference/services/vision/disabled/isA_vision_service.py +500 -0
  66. isa_model/inference/services/vision/doc_analysis_service.py +640 -0
  67. isa_model/inference/services/vision/helpers/base_stacked_service.py +274 -0
  68. isa_model/inference/services/vision/helpers/image_utils.py +272 -3
  69. isa_model/inference/services/vision/helpers/vision_prompts.py +297 -0
  70. isa_model/inference/services/vision/openai_vision_service.py +109 -170
  71. isa_model/inference/services/vision/replicate_vision_service.py +508 -0
  72. isa_model/inference/services/vision/ui_analysis_service.py +823 -0
  73. isa_model/scripts/register_models.py +370 -0
  74. isa_model/scripts/register_models_with_embeddings.py +510 -0
  75. isa_model/serving/__init__.py +19 -0
  76. isa_model/serving/api/__init__.py +10 -0
  77. isa_model/serving/api/fastapi_server.py +89 -0
  78. isa_model/serving/api/middleware/__init__.py +9 -0
  79. isa_model/serving/api/middleware/request_logger.py +88 -0
  80. isa_model/serving/api/routes/__init__.py +5 -0
  81. isa_model/serving/api/routes/health.py +82 -0
  82. isa_model/serving/api/routes/llm.py +19 -0
  83. isa_model/serving/api/routes/ui_analysis.py +223 -0
  84. isa_model/serving/api/routes/unified.py +202 -0
  85. isa_model/serving/api/routes/vision.py +19 -0
  86. isa_model/serving/api/schemas/__init__.py +17 -0
  87. isa_model/serving/api/schemas/common.py +33 -0
  88. isa_model/serving/api/schemas/ui_analysis.py +78 -0
  89. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/METADATA +4 -1
  90. isa_model-0.3.6.dist-info/RECORD +147 -0
  91. isa_model/core/model_manager.py +0 -208
  92. isa_model/core/model_registry.py +0 -342
  93. isa_model/inference/billing_tracker.py +0 -406
  94. isa_model/inference/services/llm/triton_llm_service.py +0 -481
  95. isa_model/inference/services/vision/ollama_vision_service.py +0 -194
  96. isa_model-0.3.4.dist-info/RECORD +0 -91
  97. /isa_model/core/{model_storage.py → models/model_storage.py} +0 -0
  98. /isa_model/inference/services/{vision → embedding}/helpers/text_splitter.py +0 -0
  99. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/WHEEL +0 -0
  100. {isa_model-0.3.4.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())