isa-model 0.4.0__py3-none-any.whl → 0.4.4__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/client.py +466 -43
- isa_model/core/cache/redis_cache.py +12 -3
- isa_model/core/config/config_manager.py +230 -3
- isa_model/core/config.py +90 -0
- isa_model/core/database/direct_db_client.py +114 -0
- isa_model/core/database/migration_manager.py +563 -0
- isa_model/core/database/migrations.py +21 -1
- isa_model/core/database/supabase_client.py +154 -19
- isa_model/core/dependencies.py +316 -0
- isa_model/core/discovery/__init__.py +19 -0
- isa_model/core/discovery/consul_discovery.py +190 -0
- isa_model/core/logging/__init__.py +54 -0
- isa_model/core/logging/influx_logger.py +523 -0
- isa_model/core/logging/loki_logger.py +160 -0
- isa_model/core/models/__init__.py +27 -18
- isa_model/core/models/config_models.py +625 -0
- isa_model/core/models/deployment_billing_tracker.py +430 -0
- isa_model/core/models/model_manager.py +35 -80
- isa_model/core/models/model_metadata.py +690 -0
- isa_model/core/models/model_repo.py +174 -18
- isa_model/core/models/system_models.py +857 -0
- isa_model/core/repositories/__init__.py +9 -0
- isa_model/core/repositories/config_repository.py +912 -0
- isa_model/core/services/intelligent_model_selector.py +399 -21
- isa_model/core/types.py +1 -0
- isa_model/deployment/__init__.py +5 -48
- isa_model/deployment/core/__init__.py +2 -31
- isa_model/deployment/core/deployment_manager.py +1278 -370
- isa_model/deployment/modal/__init__.py +8 -0
- isa_model/deployment/modal/config.py +136 -0
- isa_model/deployment/{services/auto_hf_modal_deployer.py → modal/deployer.py} +1 -1
- isa_model/deployment/modal/services/__init__.py +3 -0
- isa_model/deployment/modal/services/audio/__init__.py +1 -0
- isa_model/deployment/modal/services/embedding/__init__.py +1 -0
- isa_model/deployment/modal/services/llm/__init__.py +1 -0
- isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
- isa_model/deployment/modal/services/video/__init__.py +1 -0
- isa_model/deployment/modal/services/vision/__init__.py +1 -0
- isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
- isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
- isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
- isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
- isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
- isa_model/deployment/storage/__init__.py +5 -0
- isa_model/deployment/storage/deployment_repository.py +824 -0
- isa_model/deployment/triton/__init__.py +10 -0
- isa_model/deployment/triton/config.py +196 -0
- isa_model/deployment/triton/configs/__init__.py +1 -0
- isa_model/deployment/triton/provider.py +512 -0
- isa_model/deployment/triton/scripts/__init__.py +1 -0
- isa_model/deployment/triton/templates/__init__.py +1 -0
- isa_model/inference/__init__.py +47 -1
- isa_model/inference/ai_factory.py +137 -10
- isa_model/inference/legacy_services/__init__.py +21 -0
- isa_model/inference/legacy_services/model_evaluation.py +637 -0
- isa_model/inference/legacy_services/model_service.py +573 -0
- isa_model/inference/legacy_services/model_serving.py +717 -0
- isa_model/inference/legacy_services/model_training.py +561 -0
- isa_model/inference/models/__init__.py +21 -0
- isa_model/inference/models/inference_config.py +551 -0
- isa_model/inference/models/inference_record.py +675 -0
- isa_model/inference/models/performance_models.py +714 -0
- isa_model/inference/repositories/__init__.py +9 -0
- isa_model/inference/repositories/inference_repository.py +828 -0
- isa_model/inference/services/audio/base_stt_service.py +184 -11
- isa_model/inference/services/audio/openai_stt_service.py +22 -6
- isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
- isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
- isa_model/inference/services/llm/__init__.py +10 -2
- isa_model/inference/services/llm/base_llm_service.py +335 -24
- isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
- isa_model/inference/services/llm/helpers/llm_adapter.py +9 -4
- isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
- isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
- isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
- isa_model/inference/services/llm/ollama_llm_service.py +9 -2
- isa_model/inference/services/llm/openai_llm_service.py +33 -16
- isa_model/inference/services/llm/yyds_llm_service.py +8 -2
- isa_model/inference/services/vision/__init__.py +22 -1
- isa_model/inference/services/vision/helpers/image_utils.py +8 -5
- isa_model/inference/services/vision/isa_vision_service.py +65 -4
- isa_model/inference/services/vision/openai_vision_service.py +19 -10
- isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
- isa_model/serving/api/cache_manager.py +245 -0
- isa_model/serving/api/dependencies/__init__.py +1 -0
- isa_model/serving/api/dependencies/auth.py +194 -0
- isa_model/serving/api/dependencies/database.py +139 -0
- isa_model/serving/api/error_handlers.py +284 -0
- isa_model/serving/api/fastapi_server.py +172 -22
- isa_model/serving/api/middleware/auth.py +8 -2
- isa_model/serving/api/middleware/security.py +23 -33
- isa_model/serving/api/middleware/tenant_context.py +414 -0
- isa_model/serving/api/routes/analytics.py +4 -1
- isa_model/serving/api/routes/config.py +645 -0
- isa_model/serving/api/routes/deployment_billing.py +315 -0
- isa_model/serving/api/routes/deployments.py +138 -2
- isa_model/serving/api/routes/gpu_gateway.py +440 -0
- isa_model/serving/api/routes/health.py +32 -12
- isa_model/serving/api/routes/inference_monitoring.py +486 -0
- isa_model/serving/api/routes/local_deployments.py +448 -0
- isa_model/serving/api/routes/tenants.py +575 -0
- isa_model/serving/api/routes/unified.py +680 -18
- isa_model/serving/api/routes/webhooks.py +479 -0
- isa_model/serving/api/startup.py +68 -54
- isa_model/utils/gpu_utils.py +311 -0
- {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/METADATA +71 -24
- isa_model-0.4.4.dist-info/RECORD +180 -0
- isa_model/core/security/secrets.py +0 -358
- isa_model/core/storage/hf_storage.py +0 -419
- isa_model/core/storage/minio_storage.py +0 -0
- isa_model/deployment/cloud/__init__.py +0 -9
- isa_model/deployment/cloud/modal/__init__.py +0 -10
- isa_model/deployment/core/deployment_config.py +0 -356
- isa_model/deployment/core/isa_deployment_service.py +0 -401
- isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
- isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
- isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
- isa_model/deployment/runtime/deployed_service.py +0 -338
- isa_model/deployment/services/__init__.py +0 -9
- isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
- isa_model/deployment/services/model_service.py +0 -332
- isa_model/deployment/services/service_monitor.py +0 -356
- isa_model/deployment/services/service_registry.py +0 -527
- isa_model/eval/__init__.py +0 -92
- isa_model/eval/benchmarks/__init__.py +0 -27
- isa_model/eval/benchmarks/multimodal_datasets.py +0 -460
- isa_model/eval/benchmarks.py +0 -701
- isa_model/eval/config/__init__.py +0 -10
- isa_model/eval/config/evaluation_config.py +0 -108
- isa_model/eval/evaluators/__init__.py +0 -24
- isa_model/eval/evaluators/audio_evaluator.py +0 -727
- isa_model/eval/evaluators/base_evaluator.py +0 -503
- isa_model/eval/evaluators/embedding_evaluator.py +0 -742
- isa_model/eval/evaluators/llm_evaluator.py +0 -472
- isa_model/eval/evaluators/vision_evaluator.py +0 -564
- isa_model/eval/example_evaluation.py +0 -395
- isa_model/eval/factory.py +0 -798
- isa_model/eval/infrastructure/__init__.py +0 -24
- isa_model/eval/infrastructure/experiment_tracker.py +0 -466
- isa_model/eval/isa_benchmarks.py +0 -700
- isa_model/eval/isa_integration.py +0 -582
- isa_model/eval/metrics.py +0 -951
- isa_model/eval/tests/unit/test_basic.py +0 -396
- isa_model/serving/api/routes/evaluations.py +0 -579
- isa_model/training/__init__.py +0 -168
- isa_model/training/annotation/annotation_schema.py +0 -47
- isa_model/training/annotation/processors/annotation_processor.py +0 -126
- isa_model/training/annotation/storage/dataset_manager.py +0 -131
- isa_model/training/annotation/storage/dataset_schema.py +0 -44
- isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
- isa_model/training/annotation/tests/test_minio copy.py +0 -113
- isa_model/training/annotation/tests/test_minio_upload.py +0 -43
- isa_model/training/annotation/views/annotation_controller.py +0 -158
- isa_model/training/cloud/__init__.py +0 -22
- isa_model/training/cloud/job_orchestrator.py +0 -402
- isa_model/training/cloud/runpod_trainer.py +0 -454
- isa_model/training/cloud/storage_manager.py +0 -482
- isa_model/training/core/__init__.py +0 -26
- isa_model/training/core/config.py +0 -181
- isa_model/training/core/dataset.py +0 -222
- isa_model/training/core/trainer.py +0 -720
- isa_model/training/core/utils.py +0 -213
- isa_model/training/examples/intelligent_training_example.py +0 -281
- isa_model/training/factory.py +0 -424
- isa_model/training/intelligent/__init__.py +0 -25
- isa_model/training/intelligent/decision_engine.py +0 -643
- isa_model/training/intelligent/intelligent_factory.py +0 -888
- isa_model/training/intelligent/knowledge_base.py +0 -751
- isa_model/training/intelligent/resource_optimizer.py +0 -839
- isa_model/training/intelligent/task_classifier.py +0 -576
- isa_model/training/storage/__init__.py +0 -24
- isa_model/training/storage/core_integration.py +0 -439
- isa_model/training/storage/training_repository.py +0 -552
- isa_model/training/storage/training_storage.py +0 -628
- isa_model-0.4.0.dist-info/RECORD +0 -182
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_chatTTS_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_fish_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_openvoice_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_service_v2.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/embedding}/isa_embed_rerank_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/video}/isa_video_hunyuan_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ocr_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_qwen25_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_table_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service_optimized.py +0 -0
- /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
- {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/WHEEL +0 -0
- {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@ import logging
|
|
11
11
|
from typing import Dict, List, Optional, Any
|
12
12
|
from enum import Enum
|
13
13
|
from datetime import datetime
|
14
|
+
from dataclasses import dataclass
|
14
15
|
|
15
16
|
try:
|
16
17
|
from ..database.supabase_client import get_supabase_client, get_supabase_table
|
@@ -22,6 +23,46 @@ from ..config import ConfigManager
|
|
22
23
|
|
23
24
|
logger = logging.getLogger(__name__)
|
24
25
|
|
26
|
+
def _parse_metadata(metadata: Any) -> Dict[str, Any]:
|
27
|
+
"""
|
28
|
+
Parse metadata field that might be a dict, JSON string, or None.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
metadata: The metadata value from database (dict, str, or None)
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Dict with metadata, or empty dict if None
|
35
|
+
"""
|
36
|
+
if not metadata:
|
37
|
+
return {}
|
38
|
+
|
39
|
+
# If already a dict (from JSONB column), return as-is
|
40
|
+
if isinstance(metadata, dict):
|
41
|
+
return metadata
|
42
|
+
|
43
|
+
# If string, parse as JSON
|
44
|
+
if isinstance(metadata, str):
|
45
|
+
try:
|
46
|
+
return json.loads(metadata)
|
47
|
+
except json.JSONDecodeError:
|
48
|
+
logger.warning(f"Failed to parse metadata as JSON: {metadata[:100]}")
|
49
|
+
return {}
|
50
|
+
|
51
|
+
# Unknown type
|
52
|
+
logger.warning(f"Unexpected metadata type: {type(metadata)}")
|
53
|
+
return {}
|
54
|
+
|
55
|
+
@dataclass
|
56
|
+
class RegisteredModel:
|
57
|
+
"""Simple model representation for registration results"""
|
58
|
+
model_id: str
|
59
|
+
model_type: str
|
60
|
+
provider: str
|
61
|
+
capabilities: List[str]
|
62
|
+
metadata: Dict[str, Any]
|
63
|
+
created_at: str
|
64
|
+
updated_at: str
|
65
|
+
|
25
66
|
class ModelCapability(str, Enum):
|
26
67
|
"""Model capabilities"""
|
27
68
|
TEXT_GENERATION = "text_generation"
|
@@ -88,7 +129,7 @@ class ModelRegistry:
|
|
88
129
|
model_type: ModelType,
|
89
130
|
capabilities: List[ModelCapability],
|
90
131
|
metadata: Dict[str, Any],
|
91
|
-
provider: Optional[str] = None) ->
|
132
|
+
provider: Optional[str] = None) -> Optional[RegisteredModel]:
|
92
133
|
"""Register a model with its capabilities and metadata"""
|
93
134
|
try:
|
94
135
|
current_time = datetime.now().isoformat()
|
@@ -113,7 +154,7 @@ class ModelRegistry:
|
|
113
154
|
|
114
155
|
if not result.data:
|
115
156
|
logger.error(f"Failed to insert model {model_id}")
|
116
|
-
return
|
157
|
+
return None
|
117
158
|
|
118
159
|
# Delete existing capabilities
|
119
160
|
self._table('model_capabilities').delete().eq('model_id', model_id).execute()
|
@@ -133,14 +174,24 @@ class ModelRegistry:
|
|
133
174
|
|
134
175
|
if not cap_result.data:
|
135
176
|
logger.error(f"Failed to insert capabilities for {model_id}")
|
136
|
-
return
|
177
|
+
return None
|
137
178
|
|
138
179
|
logger.info(f"Successfully registered model {model_id} with {len(capabilities)} capabilities")
|
139
|
-
|
180
|
+
|
181
|
+
# Return the registered model object
|
182
|
+
return RegisteredModel(
|
183
|
+
model_id=model_id,
|
184
|
+
model_type=model_type.value,
|
185
|
+
provider=provider_value,
|
186
|
+
capabilities=[cap.value for cap in capabilities],
|
187
|
+
metadata=metadata,
|
188
|
+
created_at=current_time,
|
189
|
+
updated_at=current_time
|
190
|
+
)
|
140
191
|
|
141
192
|
except Exception as e:
|
142
193
|
logger.error(f"Failed to register model {model_id}: {e}")
|
143
|
-
return
|
194
|
+
return None
|
144
195
|
|
145
196
|
def unregister_model(self, model_id: str) -> bool:
|
146
197
|
"""Unregister a model"""
|
@@ -203,13 +254,13 @@ class ModelRegistry:
|
|
203
254
|
result[model_id] = {
|
204
255
|
"type": model["model_type"],
|
205
256
|
"capabilities": capabilities,
|
206
|
-
"metadata":
|
257
|
+
"metadata": _parse_metadata(model["metadata"]),
|
207
258
|
"created_at": model["created_at"],
|
208
259
|
"updated_at": model["updated_at"]
|
209
260
|
}
|
210
|
-
|
261
|
+
|
211
262
|
return result
|
212
|
-
|
263
|
+
|
213
264
|
except Exception as e:
|
214
265
|
logger.error(f"Failed to get models by type {model_type}: {e}")
|
215
266
|
return {}
|
@@ -238,13 +289,13 @@ class ModelRegistry:
|
|
238
289
|
result[model_id] = {
|
239
290
|
"type": model["model_type"],
|
240
291
|
"capabilities": capabilities,
|
241
|
-
"metadata":
|
292
|
+
"metadata": _parse_metadata(model["metadata"]),
|
242
293
|
"created_at": model["created_at"],
|
243
294
|
"updated_at": model["updated_at"]
|
244
295
|
}
|
245
|
-
|
296
|
+
|
246
297
|
return result
|
247
|
-
|
298
|
+
|
248
299
|
except Exception as e:
|
249
300
|
logger.error(f"Failed to get models by capability {capability}: {e}")
|
250
301
|
return {}
|
@@ -276,13 +327,13 @@ class ModelRegistry:
|
|
276
327
|
result[model_id] = {
|
277
328
|
"type": model["model_type"],
|
278
329
|
"capabilities": capabilities,
|
279
|
-
"metadata":
|
330
|
+
"metadata": _parse_metadata(model["metadata"]),
|
280
331
|
"created_at": model["created_at"],
|
281
332
|
"updated_at": model["updated_at"]
|
282
333
|
}
|
283
|
-
|
334
|
+
|
284
335
|
return result
|
285
|
-
|
336
|
+
|
286
337
|
except Exception as e:
|
287
338
|
logger.error(f"Failed to list models: {e}")
|
288
339
|
return {}
|
@@ -337,13 +388,118 @@ class ModelRegistry:
|
|
337
388
|
result[model_id] = {
|
338
389
|
"type": model["model_type"],
|
339
390
|
"capabilities": capabilities,
|
340
|
-
"metadata":
|
391
|
+
"metadata": _parse_metadata(model["metadata"]),
|
341
392
|
"created_at": model["created_at"],
|
342
393
|
"updated_at": model["updated_at"]
|
343
394
|
}
|
344
|
-
|
395
|
+
|
345
396
|
return result
|
346
|
-
|
397
|
+
|
347
398
|
except Exception as e:
|
348
399
|
logger.error(f"Failed to search models with query '{query}': {e}")
|
349
|
-
return {}
|
400
|
+
return {}
|
401
|
+
|
402
|
+
|
403
|
+
class ModelRepo:
|
404
|
+
"""Compatibility wrapper for unified API"""
|
405
|
+
|
406
|
+
def __init__(self):
|
407
|
+
self.registry = ModelRegistry()
|
408
|
+
|
409
|
+
def get_model_by_id(self, model_id: str):
|
410
|
+
"""Get model by ID - returns a simple object with basic properties"""
|
411
|
+
info = self.registry.get_model_info(model_id)
|
412
|
+
if not info:
|
413
|
+
return None
|
414
|
+
|
415
|
+
# Return a simple object that has the expected properties
|
416
|
+
class ModelObject:
|
417
|
+
def __init__(self, data):
|
418
|
+
self.model_id = data["model_id"]
|
419
|
+
self.model_type = data["type"]
|
420
|
+
self.provider = data["metadata"].get("provider", "unknown")
|
421
|
+
self.metadata = data["metadata"]
|
422
|
+
self.capabilities = data["capabilities"]
|
423
|
+
self.created_at = datetime.fromisoformat(data["created_at"]) if data["created_at"] else None
|
424
|
+
self.updated_at = datetime.fromisoformat(data["updated_at"]) if data["updated_at"] else None
|
425
|
+
|
426
|
+
return ModelObject(info)
|
427
|
+
|
428
|
+
def update_model_metadata(self, model_id: str, metadata_updates: Dict[str, Any], updated_by: str = None):
|
429
|
+
"""Update model metadata - simplified implementation"""
|
430
|
+
# Get existing model
|
431
|
+
info = self.registry.get_model_info(model_id)
|
432
|
+
if not info:
|
433
|
+
return False
|
434
|
+
|
435
|
+
# Merge metadata
|
436
|
+
new_metadata = info["metadata"].copy()
|
437
|
+
new_metadata.update(metadata_updates)
|
438
|
+
|
439
|
+
# Update via re-registration (simplified)
|
440
|
+
capabilities = [ModelCapability(cap) for cap in info["capabilities"] if cap in [c.value for c in ModelCapability]]
|
441
|
+
model_type = ModelType(info["type"])
|
442
|
+
|
443
|
+
return self.registry.register_model(
|
444
|
+
model_id=model_id,
|
445
|
+
model_type=model_type,
|
446
|
+
capabilities=capabilities,
|
447
|
+
metadata=new_metadata,
|
448
|
+
provider=info["metadata"].get("provider")
|
449
|
+
)
|
450
|
+
|
451
|
+
def search_models(self, query: str, model_type: str = None, provider: str = None, capabilities: List[str] = None, limit: int = 50):
|
452
|
+
"""Search models with filters"""
|
453
|
+
results = self.registry.search_models(query)
|
454
|
+
|
455
|
+
# Apply filters
|
456
|
+
if model_type:
|
457
|
+
results = {k: v for k, v in results.items() if v["type"] == model_type}
|
458
|
+
|
459
|
+
if provider:
|
460
|
+
results = {k: v for k, v in results.items() if v["metadata"].get("provider") == provider}
|
461
|
+
|
462
|
+
if capabilities:
|
463
|
+
results = {k: v for k, v in results.items() if any(cap in v["capabilities"] for cap in capabilities)}
|
464
|
+
|
465
|
+
# Convert to expected format
|
466
|
+
final_results = []
|
467
|
+
for model_id, data in list(results.items())[:limit]:
|
468
|
+
class ModelObject:
|
469
|
+
def __init__(self, model_id, data):
|
470
|
+
self.model_id = model_id
|
471
|
+
self.model_type = data["type"]
|
472
|
+
self.provider = data["metadata"].get("provider", "unknown")
|
473
|
+
self.capabilities = data["capabilities"]
|
474
|
+
self.metadata = data["metadata"]
|
475
|
+
self.updated_at = datetime.fromisoformat(data["updated_at"]) if data["updated_at"] else None
|
476
|
+
|
477
|
+
final_results.append(ModelObject(model_id, data))
|
478
|
+
|
479
|
+
return final_results
|
480
|
+
|
481
|
+
def get_providers_summary(self):
|
482
|
+
"""Get summary of available providers"""
|
483
|
+
models = self.registry.list_models()
|
484
|
+
providers = {}
|
485
|
+
|
486
|
+
for model_id, data in models.items():
|
487
|
+
provider = data["metadata"].get("provider", "unknown")
|
488
|
+
if provider not in providers:
|
489
|
+
providers[provider] = {
|
490
|
+
"provider": provider,
|
491
|
+
"model_count": 0,
|
492
|
+
"model_types": set(),
|
493
|
+
"capabilities": set()
|
494
|
+
}
|
495
|
+
|
496
|
+
providers[provider]["model_count"] += 1
|
497
|
+
providers[provider]["model_types"].add(data["type"])
|
498
|
+
providers[provider]["capabilities"].update(data["capabilities"])
|
499
|
+
|
500
|
+
# Convert sets to lists for JSON serialization
|
501
|
+
for provider_data in providers.values():
|
502
|
+
provider_data["model_types"] = list(provider_data["model_types"])
|
503
|
+
provider_data["capabilities"] = list(provider_data["capabilities"])
|
504
|
+
|
505
|
+
return list(providers.values())
|