isa-model 0.3.5__py3-none-any.whl → 0.3.7__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 +937 -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 +538 -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/deployment/services/simple_auto_deploy_vision_service.py +275 -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 +257 -601
- 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 +274 -0
- {isa_model-0.3.5.dist-info → isa_model-0.3.7.dist-info}/METADATA +4 -1
- {isa_model-0.3.5.dist-info → isa_model-0.3.7.dist-info}/RECORD +78 -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.7.dist-info}/WHEEL +0 -0
- {isa_model-0.3.5.dist-info → isa_model-0.3.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,527 @@
|
|
1
|
+
"""
|
2
|
+
ServiceRegistry - Enhanced registry for managing deployed model services
|
3
|
+
|
4
|
+
This registry extends the basic ModelRegistry to provide full service lifecycle management
|
5
|
+
including service discovery, health monitoring, and deployment tracking.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, List, Optional, Any, Tuple
|
9
|
+
import logging
|
10
|
+
from datetime import datetime, timezone
|
11
|
+
import asyncio
|
12
|
+
import json
|
13
|
+
|
14
|
+
from .deployment_service import (
|
15
|
+
DeployedService, ServiceStatus, ServiceType, DeploymentPlatform,
|
16
|
+
HealthMetrics, ServiceMetrics, ResourceRequirements
|
17
|
+
)
|
18
|
+
# Backward compatibility
|
19
|
+
ModelService = DeployedService
|
20
|
+
UsageMetrics = ServiceMetrics
|
21
|
+
from .model_repo import ModelRegistry, ModelType, ModelCapability
|
22
|
+
|
23
|
+
logger = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
class ServiceRegistry:
|
26
|
+
"""
|
27
|
+
Enhanced registry for managing deployed model services in the MaaS platform
|
28
|
+
|
29
|
+
This registry provides:
|
30
|
+
- Service registration and discovery
|
31
|
+
- Health monitoring and status tracking
|
32
|
+
- Deployment management
|
33
|
+
- Usage metrics collection
|
34
|
+
- Integration with existing ModelRegistry
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(self, model_registry: Optional[ModelRegistry] = None):
|
38
|
+
self.model_registry = model_registry or ModelRegistry()
|
39
|
+
self._service_cache: Dict[str, ModelService] = {}
|
40
|
+
self._last_cache_update: Optional[datetime] = None
|
41
|
+
self._cache_ttl_seconds = 300 # 5 minutes cache TTL
|
42
|
+
|
43
|
+
logger.info("ServiceRegistry initialized with Supabase backend")
|
44
|
+
|
45
|
+
# Service Registration and Management
|
46
|
+
|
47
|
+
async def register_service(self, service: DeployedService) -> bool:
|
48
|
+
"""
|
49
|
+
Register a new service in the platform
|
50
|
+
|
51
|
+
Args:
|
52
|
+
service: ModelService instance to register
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
True if registration successful, False otherwise
|
56
|
+
"""
|
57
|
+
try:
|
58
|
+
# First ensure the underlying model is registered
|
59
|
+
if service.model_id:
|
60
|
+
await self._ensure_model_registered(service)
|
61
|
+
|
62
|
+
# Check if using Supabase backend
|
63
|
+
if hasattr(self.model_registry, 'use_supabase') and self.model_registry.use_supabase:
|
64
|
+
return await self._register_service_supabase(service)
|
65
|
+
else:
|
66
|
+
return await self._register_service_sqlite(service)
|
67
|
+
|
68
|
+
except Exception as e:
|
69
|
+
logger.error(f"Failed to register service {service.service_id}: {e}")
|
70
|
+
return False
|
71
|
+
|
72
|
+
async def _register_service_supabase(self, service: DeployedService) -> bool:
|
73
|
+
"""Register service using Supabase backend"""
|
74
|
+
try:
|
75
|
+
backend = self.model_registry.backend
|
76
|
+
|
77
|
+
# Prepare service data
|
78
|
+
service_data = {
|
79
|
+
'service_id': service.deployment_id, # Updated field name
|
80
|
+
'service_name': service.service_name,
|
81
|
+
'model_id': service.model_id,
|
82
|
+
'deployment_platform': service.deployment_platform.value,
|
83
|
+
'service_type': service.service_type.value,
|
84
|
+
'status': service.status.value,
|
85
|
+
'inference_endpoint': service.inference_endpoint,
|
86
|
+
'health_endpoint': service.health_endpoint,
|
87
|
+
'config': json.dumps(service.config),
|
88
|
+
'gpu_type': service.resource_requirements.gpu_type,
|
89
|
+
'memory_mb': service.resource_requirements.memory_mb,
|
90
|
+
'cpu_cores': service.resource_requirements.cpu_cores,
|
91
|
+
'metadata': json.dumps(service.metadata),
|
92
|
+
}
|
93
|
+
|
94
|
+
# Insert service
|
95
|
+
result = backend.supabase.table('services').upsert(service_data).execute()
|
96
|
+
|
97
|
+
if not result.data:
|
98
|
+
logger.error(f"Failed to insert service {service.service_id}")
|
99
|
+
return False
|
100
|
+
|
101
|
+
# Insert service capabilities
|
102
|
+
if service.capabilities:
|
103
|
+
capability_data = [
|
104
|
+
{
|
105
|
+
'service_id': service.service_id,
|
106
|
+
'capability': capability
|
107
|
+
}
|
108
|
+
for capability in service.capabilities
|
109
|
+
]
|
110
|
+
|
111
|
+
# Delete existing capabilities first
|
112
|
+
backend.supabase.table('service_capabilities').delete().eq('service_id', service.service_id).execute()
|
113
|
+
|
114
|
+
# Insert new capabilities
|
115
|
+
cap_result = backend.supabase.table('service_capabilities').insert(capability_data).execute()
|
116
|
+
|
117
|
+
if not cap_result.data:
|
118
|
+
logger.warning(f"Failed to insert capabilities for service {service.service_id}")
|
119
|
+
|
120
|
+
# Update cache
|
121
|
+
self._service_cache[service.service_id] = service
|
122
|
+
|
123
|
+
logger.info(f"Successfully registered service {service.service_id} in Supabase")
|
124
|
+
return True
|
125
|
+
|
126
|
+
except Exception as e:
|
127
|
+
logger.error(f"Supabase service registration failed: {e}")
|
128
|
+
return False
|
129
|
+
|
130
|
+
async def _register_service_sqlite(self, service: ModelService) -> bool:
|
131
|
+
"""Register service using SQLite backend (for development/testing)"""
|
132
|
+
# For SQLite, we'll store services in the models table with a special marker
|
133
|
+
try:
|
134
|
+
success = self.model_registry.register_model(
|
135
|
+
model_id=service.service_id,
|
136
|
+
model_type=ModelType.VISION, # Default type
|
137
|
+
capabilities=[ModelCapability(cap) for cap in service.capabilities if hasattr(ModelCapability, cap.upper())],
|
138
|
+
metadata={
|
139
|
+
**service.metadata,
|
140
|
+
'is_service': True,
|
141
|
+
'service_name': service.service_name,
|
142
|
+
'deployment_platform': service.deployment_platform.value,
|
143
|
+
'service_type': service.service_type.value,
|
144
|
+
'inference_endpoint': service.inference_endpoint,
|
145
|
+
'health_endpoint': service.health_endpoint,
|
146
|
+
'status': service.status.value,
|
147
|
+
}
|
148
|
+
)
|
149
|
+
|
150
|
+
if success:
|
151
|
+
self._service_cache[service.service_id] = service
|
152
|
+
logger.info(f"Successfully registered service {service.service_id} in SQLite")
|
153
|
+
|
154
|
+
return success
|
155
|
+
|
156
|
+
except Exception as e:
|
157
|
+
logger.error(f"SQLite service registration failed: {e}")
|
158
|
+
return False
|
159
|
+
|
160
|
+
async def unregister_service(self, service_id: str) -> bool:
|
161
|
+
"""
|
162
|
+
Unregister a service from the platform
|
163
|
+
|
164
|
+
Args:
|
165
|
+
service_id: ID of the service to unregister
|
166
|
+
|
167
|
+
Returns:
|
168
|
+
True if unregistration successful, False otherwise
|
169
|
+
"""
|
170
|
+
try:
|
171
|
+
if hasattr(self.model_registry, 'use_supabase') and self.model_registry.use_supabase:
|
172
|
+
backend = self.model_registry.backend
|
173
|
+
result = backend.supabase.table('services').delete().eq('service_id', service_id).execute()
|
174
|
+
success = bool(result.data)
|
175
|
+
else:
|
176
|
+
success = self.model_registry.unregister_model(service_id)
|
177
|
+
|
178
|
+
if success and service_id in self._service_cache:
|
179
|
+
del self._service_cache[service_id]
|
180
|
+
logger.info(f"Unregistered service {service_id}")
|
181
|
+
|
182
|
+
return success
|
183
|
+
|
184
|
+
except Exception as e:
|
185
|
+
logger.error(f"Failed to unregister service {service_id}: {e}")
|
186
|
+
return False
|
187
|
+
|
188
|
+
# Service Discovery
|
189
|
+
|
190
|
+
async def get_service(self, service_id: str) -> Optional[DeployedService]:
|
191
|
+
"""Get a specific service by ID"""
|
192
|
+
try:
|
193
|
+
# Check cache first
|
194
|
+
if service_id in self._service_cache:
|
195
|
+
return self._service_cache[service_id]
|
196
|
+
|
197
|
+
if hasattr(self.model_registry, 'use_supabase') and self.model_registry.use_supabase:
|
198
|
+
return await self._get_service_supabase(service_id)
|
199
|
+
else:
|
200
|
+
return await self._get_service_sqlite(service_id)
|
201
|
+
|
202
|
+
except Exception as e:
|
203
|
+
logger.error(f"Failed to get service {service_id}: {e}")
|
204
|
+
return None
|
205
|
+
|
206
|
+
async def _get_service_supabase(self, service_id: str) -> Optional[ModelService]:
|
207
|
+
"""Get service from Supabase backend"""
|
208
|
+
try:
|
209
|
+
backend = self.model_registry.backend
|
210
|
+
|
211
|
+
# Get service data
|
212
|
+
result = backend.supabase.table('services').select('*').eq('service_id', service_id).execute()
|
213
|
+
|
214
|
+
if not result.data:
|
215
|
+
return None
|
216
|
+
|
217
|
+
service_data = result.data[0]
|
218
|
+
|
219
|
+
# Get service capabilities
|
220
|
+
cap_result = backend.supabase.table('service_capabilities').select('capability').eq('service_id', service_id).execute()
|
221
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
222
|
+
|
223
|
+
# Create ModelService instance
|
224
|
+
service = self._create_service_from_data(service_data, capabilities)
|
225
|
+
|
226
|
+
# Cache the service
|
227
|
+
self._service_cache[service_id] = service
|
228
|
+
|
229
|
+
return service
|
230
|
+
|
231
|
+
except Exception as e:
|
232
|
+
logger.error(f"Failed to get service from Supabase: {e}")
|
233
|
+
return None
|
234
|
+
|
235
|
+
async def _get_service_sqlite(self, service_id: str) -> Optional[ModelService]:
|
236
|
+
"""Get service from SQLite backend"""
|
237
|
+
try:
|
238
|
+
model_info = self.model_registry.get_model_info(service_id)
|
239
|
+
if not model_info or not model_info.get('metadata', {}).get('is_service'):
|
240
|
+
return None
|
241
|
+
|
242
|
+
# Convert model info to service
|
243
|
+
metadata = model_info.get('metadata', {})
|
244
|
+
|
245
|
+
# Create basic service from stored metadata
|
246
|
+
service = ModelService(
|
247
|
+
service_id=service_id,
|
248
|
+
service_name=metadata.get('service_name', service_id),
|
249
|
+
model_id=metadata.get('model_id'),
|
250
|
+
deployment_platform=DeploymentPlatform(metadata.get('deployment_platform', 'modal')),
|
251
|
+
service_type=ServiceType(metadata.get('service_type', 'vision')),
|
252
|
+
status=ServiceStatus(metadata.get('status', 'healthy')),
|
253
|
+
inference_endpoint=metadata.get('inference_endpoint'),
|
254
|
+
health_endpoint=metadata.get('health_endpoint'),
|
255
|
+
capabilities=model_info.get('capabilities', []),
|
256
|
+
metadata=metadata,
|
257
|
+
)
|
258
|
+
|
259
|
+
self._service_cache[service_id] = service
|
260
|
+
return service
|
261
|
+
|
262
|
+
except Exception as e:
|
263
|
+
logger.error(f"Failed to get service from SQLite: {e}")
|
264
|
+
return None
|
265
|
+
|
266
|
+
async def get_services_by_name(self, service_name: str) -> List[ModelService]:
|
267
|
+
"""Get all services with a specific name (multiple deployments)"""
|
268
|
+
try:
|
269
|
+
if hasattr(self.model_registry, 'use_supabase') and self.model_registry.use_supabase:
|
270
|
+
backend = self.model_registry.backend
|
271
|
+
result = backend.supabase.rpc('get_healthy_services_by_name', {'name_pattern': service_name}).execute()
|
272
|
+
|
273
|
+
services = []
|
274
|
+
for row in result.data or []:
|
275
|
+
service = ModelService(
|
276
|
+
service_id=row['service_id'],
|
277
|
+
service_name=row['service_name'],
|
278
|
+
model_id=row['model_id'],
|
279
|
+
deployment_platform=DeploymentPlatform(row['deployment_platform']),
|
280
|
+
service_type=ServiceType.VISION, # Default, should be in data
|
281
|
+
inference_endpoint=row['inference_endpoint'],
|
282
|
+
health_endpoint=row['health_endpoint'],
|
283
|
+
status=ServiceStatus.HEALTHY, # From healthy services query
|
284
|
+
)
|
285
|
+
services.append(service)
|
286
|
+
self._service_cache[service.service_id] = service
|
287
|
+
|
288
|
+
return services
|
289
|
+
else:
|
290
|
+
# SQLite fallback - search in model registry
|
291
|
+
models = self.model_registry.search_models(service_name)
|
292
|
+
services = []
|
293
|
+
|
294
|
+
for model_id, model_info in models.items():
|
295
|
+
metadata = model_info.get('metadata', {})
|
296
|
+
if metadata.get('is_service') and metadata.get('service_name') == service_name:
|
297
|
+
service = await self._get_service_sqlite(model_id)
|
298
|
+
if service:
|
299
|
+
services.append(service)
|
300
|
+
|
301
|
+
return services
|
302
|
+
|
303
|
+
except Exception as e:
|
304
|
+
logger.error(f"Failed to get services by name {service_name}: {e}")
|
305
|
+
return []
|
306
|
+
|
307
|
+
async def get_services_by_capability(self, capability: str) -> List[ModelService]:
|
308
|
+
"""Get all services that provide a specific capability"""
|
309
|
+
try:
|
310
|
+
if hasattr(self.model_registry, 'use_supabase') and self.model_registry.use_supabase:
|
311
|
+
backend = self.model_registry.backend
|
312
|
+
result = backend.supabase.rpc('get_services_by_capability', {'capability_name': capability}).execute()
|
313
|
+
|
314
|
+
services = []
|
315
|
+
for row in result.data or []:
|
316
|
+
service = self._create_service_from_supabase_row(row)
|
317
|
+
services.append(service)
|
318
|
+
self._service_cache[service.service_id] = service
|
319
|
+
|
320
|
+
return services
|
321
|
+
else:
|
322
|
+
# SQLite fallback
|
323
|
+
models = self.model_registry.get_models_by_capability(ModelCapability(capability))
|
324
|
+
services = []
|
325
|
+
|
326
|
+
for model_id, model_info in models.items():
|
327
|
+
if model_info.get('metadata', {}).get('is_service'):
|
328
|
+
service = await self._get_service_sqlite(model_id)
|
329
|
+
if service:
|
330
|
+
services.append(service)
|
331
|
+
|
332
|
+
return services
|
333
|
+
|
334
|
+
except Exception as e:
|
335
|
+
logger.error(f"Failed to get services by capability {capability}: {e}")
|
336
|
+
return []
|
337
|
+
|
338
|
+
async def get_active_service(self, service_name: str) -> Optional[ModelService]:
|
339
|
+
"""
|
340
|
+
Get the best active service for a given service name
|
341
|
+
|
342
|
+
Returns the healthiest service with the most recent health check
|
343
|
+
"""
|
344
|
+
services = await self.get_services_by_name(service_name)
|
345
|
+
|
346
|
+
if not services:
|
347
|
+
return None
|
348
|
+
|
349
|
+
# Filter to only healthy services
|
350
|
+
healthy_services = [s for s in services if s.is_healthy()]
|
351
|
+
|
352
|
+
if not healthy_services:
|
353
|
+
logger.warning(f"No healthy services found for {service_name}")
|
354
|
+
return None
|
355
|
+
|
356
|
+
# Return the first healthy service (could add more sophisticated selection logic)
|
357
|
+
return healthy_services[0]
|
358
|
+
|
359
|
+
# Health Monitoring
|
360
|
+
|
361
|
+
async def update_service_health(
|
362
|
+
self,
|
363
|
+
service_id: str,
|
364
|
+
health_metrics: HealthMetrics
|
365
|
+
) -> bool:
|
366
|
+
"""Update health metrics for a service"""
|
367
|
+
try:
|
368
|
+
if hasattr(self.model_registry, 'use_supabase') and self.model_registry.use_supabase:
|
369
|
+
backend = self.model_registry.backend
|
370
|
+
|
371
|
+
# Insert health check record
|
372
|
+
health_data = {
|
373
|
+
'service_id': service_id,
|
374
|
+
'is_healthy': health_metrics.is_healthy,
|
375
|
+
'response_time_ms': health_metrics.response_time_ms,
|
376
|
+
'status_code': health_metrics.status_code,
|
377
|
+
'cpu_usage_percent': health_metrics.cpu_usage_percent,
|
378
|
+
'memory_usage_mb': health_metrics.memory_usage_mb,
|
379
|
+
'gpu_usage_percent': health_metrics.gpu_usage_percent,
|
380
|
+
'error_message': health_metrics.error_message,
|
381
|
+
'checked_at': health_metrics.checked_at.isoformat() if health_metrics.checked_at else datetime.now(timezone.utc).isoformat(),
|
382
|
+
}
|
383
|
+
|
384
|
+
result = backend.supabase.table('service_health').insert(health_data).execute()
|
385
|
+
|
386
|
+
# Update service status
|
387
|
+
new_status = ServiceStatus.HEALTHY if health_metrics.is_healthy else ServiceStatus.UNHEALTHY
|
388
|
+
backend.supabase.table('services').update({'status': new_status.value}).eq('service_id', service_id).execute()
|
389
|
+
|
390
|
+
# Update cached service
|
391
|
+
if service_id in self._service_cache:
|
392
|
+
self._service_cache[service_id].update_health_metrics(health_metrics)
|
393
|
+
|
394
|
+
return bool(result.data)
|
395
|
+
else:
|
396
|
+
# For SQLite, just update cached service
|
397
|
+
if service_id in self._service_cache:
|
398
|
+
self._service_cache[service_id].update_health_metrics(health_metrics)
|
399
|
+
return True
|
400
|
+
|
401
|
+
except Exception as e:
|
402
|
+
logger.error(f"Failed to update health for service {service_id}: {e}")
|
403
|
+
return False
|
404
|
+
|
405
|
+
# Statistics and Monitoring
|
406
|
+
|
407
|
+
async def get_service_statistics(self) -> Dict[str, Any]:
|
408
|
+
"""Get platform-wide service statistics"""
|
409
|
+
try:
|
410
|
+
if hasattr(self.model_registry, 'use_supabase') and self.model_registry.use_supabase:
|
411
|
+
backend = self.model_registry.backend
|
412
|
+
result = backend.supabase.rpc('get_service_statistics').execute()
|
413
|
+
|
414
|
+
if result.data:
|
415
|
+
return result.data[0]
|
416
|
+
|
417
|
+
return {
|
418
|
+
"total_services": 0,
|
419
|
+
"healthy_services": 0,
|
420
|
+
"platforms": {},
|
421
|
+
"service_types": {}
|
422
|
+
}
|
423
|
+
|
424
|
+
except Exception as e:
|
425
|
+
logger.error(f"Failed to get service statistics: {e}")
|
426
|
+
return {}
|
427
|
+
|
428
|
+
# Helper Methods
|
429
|
+
|
430
|
+
def _create_service_from_data(self, service_data: Dict[str, Any], capabilities: List[str]) -> ModelService:
|
431
|
+
"""Create ModelService instance from database row data"""
|
432
|
+
# Parse JSON fields
|
433
|
+
config = json.loads(service_data.get('config', '{}')) if service_data.get('config') else {}
|
434
|
+
metadata = json.loads(service_data.get('metadata', '{}')) if service_data.get('metadata') else {}
|
435
|
+
|
436
|
+
# Create resource requirements
|
437
|
+
resources = ResourceRequirements(
|
438
|
+
gpu_type=service_data.get('gpu_type'),
|
439
|
+
memory_mb=service_data.get('memory_mb'),
|
440
|
+
cpu_cores=service_data.get('cpu_cores'),
|
441
|
+
)
|
442
|
+
|
443
|
+
# Create service
|
444
|
+
service = ModelService(
|
445
|
+
service_id=service_data['service_id'],
|
446
|
+
service_name=service_data['service_name'],
|
447
|
+
model_id=service_data.get('model_id'),
|
448
|
+
deployment_platform=DeploymentPlatform(service_data['deployment_platform']),
|
449
|
+
service_type=ServiceType(service_data['service_type']),
|
450
|
+
status=ServiceStatus(service_data.get('status', 'pending')),
|
451
|
+
inference_endpoint=service_data.get('inference_endpoint'),
|
452
|
+
health_endpoint=service_data.get('health_endpoint'),
|
453
|
+
capabilities=capabilities,
|
454
|
+
config=config,
|
455
|
+
resource_requirements=resources,
|
456
|
+
metadata=metadata,
|
457
|
+
)
|
458
|
+
|
459
|
+
# Set timestamps
|
460
|
+
if service_data.get('created_at'):
|
461
|
+
service.created_at = datetime.fromisoformat(service_data['created_at'].replace('Z', '+00:00'))
|
462
|
+
if service_data.get('updated_at'):
|
463
|
+
service.updated_at = datetime.fromisoformat(service_data['updated_at'].replace('Z', '+00:00'))
|
464
|
+
|
465
|
+
return service
|
466
|
+
|
467
|
+
def _create_service_from_supabase_row(self, row: Dict[str, Any]) -> ModelService:
|
468
|
+
"""Create ModelService from Supabase RPC result row"""
|
469
|
+
# Parse JSON fields safely
|
470
|
+
config = {}
|
471
|
+
metadata = {}
|
472
|
+
|
473
|
+
if row.get('config'):
|
474
|
+
try:
|
475
|
+
config = json.loads(row['config']) if isinstance(row['config'], str) else row['config']
|
476
|
+
except json.JSONDecodeError:
|
477
|
+
config = {}
|
478
|
+
|
479
|
+
if row.get('metadata'):
|
480
|
+
try:
|
481
|
+
metadata = json.loads(row['metadata']) if isinstance(row['metadata'], str) else row['metadata']
|
482
|
+
except json.JSONDecodeError:
|
483
|
+
metadata = {}
|
484
|
+
|
485
|
+
return ModelService(
|
486
|
+
service_id=row['service_id'],
|
487
|
+
service_name=row['service_name'],
|
488
|
+
model_id=row.get('model_id'),
|
489
|
+
deployment_platform=DeploymentPlatform(row['deployment_platform']),
|
490
|
+
service_type=ServiceType(row['service_type']),
|
491
|
+
status=ServiceStatus(row.get('status', 'pending')),
|
492
|
+
inference_endpoint=row.get('inference_endpoint'),
|
493
|
+
health_endpoint=row.get('health_endpoint'),
|
494
|
+
config=config,
|
495
|
+
metadata=metadata,
|
496
|
+
)
|
497
|
+
|
498
|
+
async def _ensure_model_registered(self, service: ModelService) -> None:
|
499
|
+
"""Ensure the underlying model is registered in the model registry"""
|
500
|
+
if not service.model_id:
|
501
|
+
return
|
502
|
+
|
503
|
+
# Check if model exists
|
504
|
+
model_info = self.model_registry.get_model_info(service.model_id)
|
505
|
+
|
506
|
+
if not model_info:
|
507
|
+
# Register the model
|
508
|
+
model_type = ModelType.VISION # Default, could be inferred from service type
|
509
|
+
capabilities = [ModelCapability(cap) for cap in service.capabilities if hasattr(ModelCapability, cap.upper())]
|
510
|
+
|
511
|
+
self.model_registry.register_model(
|
512
|
+
model_id=service.model_id,
|
513
|
+
model_type=model_type,
|
514
|
+
capabilities=capabilities,
|
515
|
+
metadata={
|
516
|
+
"description": f"Model used by service {service.service_name}",
|
517
|
+
"registered_by_service": service.service_id,
|
518
|
+
}
|
519
|
+
)
|
520
|
+
|
521
|
+
logger.info(f"Auto-registered model {service.model_id} for service {service.service_id}")
|
522
|
+
|
523
|
+
def clear_cache(self) -> None:
|
524
|
+
"""Clear the service cache"""
|
525
|
+
self._service_cache.clear()
|
526
|
+
self._last_cache_update = None
|
527
|
+
logger.info("Service cache cleared")
|