isa-model 0.4.0__py3-none-any.whl → 0.4.3__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 (199) hide show
  1. isa_model/client.py +466 -43
  2. isa_model/core/cache/redis_cache.py +12 -3
  3. isa_model/core/config/config_manager.py +230 -3
  4. isa_model/core/config.py +90 -0
  5. isa_model/core/database/direct_db_client.py +114 -0
  6. isa_model/core/database/migration_manager.py +563 -0
  7. isa_model/core/database/migrations.py +21 -1
  8. isa_model/core/database/supabase_client.py +154 -19
  9. isa_model/core/dependencies.py +316 -0
  10. isa_model/core/discovery/__init__.py +19 -0
  11. isa_model/core/discovery/consul_discovery.py +190 -0
  12. isa_model/core/logging/__init__.py +54 -0
  13. isa_model/core/logging/influx_logger.py +523 -0
  14. isa_model/core/logging/loki_logger.py +160 -0
  15. isa_model/core/models/__init__.py +27 -18
  16. isa_model/core/models/config_models.py +625 -0
  17. isa_model/core/models/deployment_billing_tracker.py +430 -0
  18. isa_model/core/models/model_manager.py +40 -17
  19. isa_model/core/models/model_metadata.py +690 -0
  20. isa_model/core/models/model_repo.py +174 -18
  21. isa_model/core/models/system_models.py +857 -0
  22. isa_model/core/repositories/__init__.py +9 -0
  23. isa_model/core/repositories/config_repository.py +912 -0
  24. isa_model/core/services/intelligent_model_selector.py +399 -21
  25. isa_model/core/storage/hf_storage.py +1 -1
  26. isa_model/core/types.py +1 -0
  27. isa_model/deployment/__init__.py +5 -48
  28. isa_model/deployment/core/__init__.py +2 -31
  29. isa_model/deployment/core/deployment_manager.py +1278 -370
  30. isa_model/deployment/local/__init__.py +31 -0
  31. isa_model/deployment/local/config.py +248 -0
  32. isa_model/deployment/local/gpu_gateway.py +607 -0
  33. isa_model/deployment/local/health_checker.py +428 -0
  34. isa_model/deployment/local/provider.py +586 -0
  35. isa_model/deployment/local/tensorrt_service.py +621 -0
  36. isa_model/deployment/local/transformers_service.py +644 -0
  37. isa_model/deployment/local/vllm_service.py +527 -0
  38. isa_model/deployment/modal/__init__.py +8 -0
  39. isa_model/deployment/modal/config.py +136 -0
  40. isa_model/deployment/{services/auto_hf_modal_deployer.py → modal/deployer.py} +1 -1
  41. isa_model/deployment/modal/services/__init__.py +3 -0
  42. isa_model/deployment/modal/services/audio/__init__.py +1 -0
  43. isa_model/deployment/modal/services/embedding/__init__.py +1 -0
  44. isa_model/deployment/modal/services/llm/__init__.py +1 -0
  45. isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
  46. isa_model/deployment/modal/services/video/__init__.py +1 -0
  47. isa_model/deployment/modal/services/vision/__init__.py +1 -0
  48. isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
  49. isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
  50. isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
  51. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
  52. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
  53. isa_model/deployment/storage/__init__.py +5 -0
  54. isa_model/deployment/storage/deployment_repository.py +824 -0
  55. isa_model/deployment/triton/__init__.py +10 -0
  56. isa_model/deployment/triton/config.py +196 -0
  57. isa_model/deployment/triton/configs/__init__.py +1 -0
  58. isa_model/deployment/triton/provider.py +512 -0
  59. isa_model/deployment/triton/scripts/__init__.py +1 -0
  60. isa_model/deployment/triton/templates/__init__.py +1 -0
  61. isa_model/inference/__init__.py +47 -1
  62. isa_model/inference/ai_factory.py +137 -10
  63. isa_model/inference/legacy_services/__init__.py +21 -0
  64. isa_model/inference/legacy_services/model_evaluation.py +637 -0
  65. isa_model/inference/legacy_services/model_service.py +573 -0
  66. isa_model/inference/legacy_services/model_serving.py +717 -0
  67. isa_model/inference/legacy_services/model_training.py +561 -0
  68. isa_model/inference/models/__init__.py +21 -0
  69. isa_model/inference/models/inference_config.py +551 -0
  70. isa_model/inference/models/inference_record.py +675 -0
  71. isa_model/inference/models/performance_models.py +714 -0
  72. isa_model/inference/repositories/__init__.py +9 -0
  73. isa_model/inference/repositories/inference_repository.py +828 -0
  74. isa_model/inference/services/audio/base_stt_service.py +184 -11
  75. isa_model/inference/services/audio/openai_stt_service.py +22 -6
  76. isa_model/inference/services/custom_model_manager.py +277 -0
  77. isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
  78. isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
  79. isa_model/inference/services/llm/__init__.py +10 -2
  80. isa_model/inference/services/llm/base_llm_service.py +335 -24
  81. isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
  82. isa_model/inference/services/llm/helpers/llm_adapter.py +9 -4
  83. isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
  84. isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
  85. isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
  86. isa_model/inference/services/llm/local_llm_service.py +747 -0
  87. isa_model/inference/services/llm/ollama_llm_service.py +9 -2
  88. isa_model/inference/services/llm/openai_llm_service.py +33 -16
  89. isa_model/inference/services/llm/yyds_llm_service.py +8 -2
  90. isa_model/inference/services/vision/__init__.py +22 -1
  91. isa_model/inference/services/vision/blip_vision_service.py +359 -0
  92. isa_model/inference/services/vision/helpers/image_utils.py +8 -5
  93. isa_model/inference/services/vision/isa_vision_service.py +65 -4
  94. isa_model/inference/services/vision/openai_vision_service.py +19 -10
  95. isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
  96. isa_model/serving/api/cache_manager.py +245 -0
  97. isa_model/serving/api/dependencies/__init__.py +1 -0
  98. isa_model/serving/api/dependencies/auth.py +194 -0
  99. isa_model/serving/api/dependencies/database.py +139 -0
  100. isa_model/serving/api/error_handlers.py +284 -0
  101. isa_model/serving/api/fastapi_server.py +172 -22
  102. isa_model/serving/api/middleware/auth.py +8 -2
  103. isa_model/serving/api/middleware/security.py +23 -33
  104. isa_model/serving/api/middleware/tenant_context.py +414 -0
  105. isa_model/serving/api/routes/analytics.py +4 -1
  106. isa_model/serving/api/routes/config.py +645 -0
  107. isa_model/serving/api/routes/deployment_billing.py +315 -0
  108. isa_model/serving/api/routes/deployments.py +138 -2
  109. isa_model/serving/api/routes/gpu_gateway.py +440 -0
  110. isa_model/serving/api/routes/health.py +32 -12
  111. isa_model/serving/api/routes/inference_monitoring.py +486 -0
  112. isa_model/serving/api/routes/local_deployments.py +448 -0
  113. isa_model/serving/api/routes/tenants.py +575 -0
  114. isa_model/serving/api/routes/unified.py +680 -18
  115. isa_model/serving/api/routes/webhooks.py +479 -0
  116. isa_model/serving/api/startup.py +68 -54
  117. isa_model/utils/gpu_utils.py +311 -0
  118. {isa_model-0.4.0.dist-info → isa_model-0.4.3.dist-info}/METADATA +66 -24
  119. isa_model-0.4.3.dist-info/RECORD +193 -0
  120. isa_model/core/storage/minio_storage.py +0 -0
  121. isa_model/deployment/cloud/__init__.py +0 -9
  122. isa_model/deployment/cloud/modal/__init__.py +0 -10
  123. isa_model/deployment/core/deployment_config.py +0 -356
  124. isa_model/deployment/core/isa_deployment_service.py +0 -401
  125. isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
  126. isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
  127. isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
  128. isa_model/deployment/runtime/deployed_service.py +0 -338
  129. isa_model/deployment/services/__init__.py +0 -9
  130. isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
  131. isa_model/deployment/services/model_service.py +0 -332
  132. isa_model/deployment/services/service_monitor.py +0 -356
  133. isa_model/deployment/services/service_registry.py +0 -527
  134. isa_model/eval/__init__.py +0 -92
  135. isa_model/eval/benchmarks/__init__.py +0 -27
  136. isa_model/eval/benchmarks/multimodal_datasets.py +0 -460
  137. isa_model/eval/benchmarks.py +0 -701
  138. isa_model/eval/config/__init__.py +0 -10
  139. isa_model/eval/config/evaluation_config.py +0 -108
  140. isa_model/eval/evaluators/__init__.py +0 -24
  141. isa_model/eval/evaluators/audio_evaluator.py +0 -727
  142. isa_model/eval/evaluators/base_evaluator.py +0 -503
  143. isa_model/eval/evaluators/embedding_evaluator.py +0 -742
  144. isa_model/eval/evaluators/llm_evaluator.py +0 -472
  145. isa_model/eval/evaluators/vision_evaluator.py +0 -564
  146. isa_model/eval/example_evaluation.py +0 -395
  147. isa_model/eval/factory.py +0 -798
  148. isa_model/eval/infrastructure/__init__.py +0 -24
  149. isa_model/eval/infrastructure/experiment_tracker.py +0 -466
  150. isa_model/eval/isa_benchmarks.py +0 -700
  151. isa_model/eval/isa_integration.py +0 -582
  152. isa_model/eval/metrics.py +0 -951
  153. isa_model/eval/tests/unit/test_basic.py +0 -396
  154. isa_model/serving/api/routes/evaluations.py +0 -579
  155. isa_model/training/__init__.py +0 -168
  156. isa_model/training/annotation/annotation_schema.py +0 -47
  157. isa_model/training/annotation/processors/annotation_processor.py +0 -126
  158. isa_model/training/annotation/storage/dataset_manager.py +0 -131
  159. isa_model/training/annotation/storage/dataset_schema.py +0 -44
  160. isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
  161. isa_model/training/annotation/tests/test_minio copy.py +0 -113
  162. isa_model/training/annotation/tests/test_minio_upload.py +0 -43
  163. isa_model/training/annotation/views/annotation_controller.py +0 -158
  164. isa_model/training/cloud/__init__.py +0 -22
  165. isa_model/training/cloud/job_orchestrator.py +0 -402
  166. isa_model/training/cloud/runpod_trainer.py +0 -454
  167. isa_model/training/cloud/storage_manager.py +0 -482
  168. isa_model/training/core/__init__.py +0 -26
  169. isa_model/training/core/config.py +0 -181
  170. isa_model/training/core/dataset.py +0 -222
  171. isa_model/training/core/trainer.py +0 -720
  172. isa_model/training/core/utils.py +0 -213
  173. isa_model/training/examples/intelligent_training_example.py +0 -281
  174. isa_model/training/factory.py +0 -424
  175. isa_model/training/intelligent/__init__.py +0 -25
  176. isa_model/training/intelligent/decision_engine.py +0 -643
  177. isa_model/training/intelligent/intelligent_factory.py +0 -888
  178. isa_model/training/intelligent/knowledge_base.py +0 -751
  179. isa_model/training/intelligent/resource_optimizer.py +0 -839
  180. isa_model/training/intelligent/task_classifier.py +0 -576
  181. isa_model/training/storage/__init__.py +0 -24
  182. isa_model/training/storage/core_integration.py +0 -439
  183. isa_model/training/storage/training_repository.py +0 -552
  184. isa_model/training/storage/training_storage.py +0 -628
  185. isa_model-0.4.0.dist-info/RECORD +0 -182
  186. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_chatTTS_service.py +0 -0
  187. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_fish_service.py +0 -0
  188. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_openvoice_service.py +0 -0
  189. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_service_v2.py +0 -0
  190. /isa_model/deployment/{cloud/modal → modal/services/embedding}/isa_embed_rerank_service.py +0 -0
  191. /isa_model/deployment/{cloud/modal → modal/services/video}/isa_video_hunyuan_service.py +0 -0
  192. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ocr_service.py +0 -0
  193. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_qwen25_service.py +0 -0
  194. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_table_service.py +0 -0
  195. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service.py +0 -0
  196. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service_optimized.py +0 -0
  197. /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
  198. {isa_model-0.4.0.dist-info → isa_model-0.4.3.dist-info}/WHEEL +0 -0
  199. {isa_model-0.4.0.dist-info → isa_model-0.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,824 @@
1
+ """
2
+ Deployment Repository - Data persistence layer for deployment operations
3
+
4
+ Provides standardized data access for deployment records, configurations, and metrics
5
+ following the ISA Model architecture pattern.
6
+ """
7
+
8
+ import logging
9
+ import json
10
+ import uuid
11
+ from datetime import datetime, timezone
12
+ from typing import Dict, List, Optional, Any, Union
13
+ from pathlib import Path
14
+ from dataclasses import dataclass, asdict
15
+ from enum import Enum
16
+
17
+ try:
18
+ # Try to import Supabase for centralized data storage
19
+ from ...core.database.supabase_client import get_supabase_client, SupabaseClient
20
+ SUPABASE_AVAILABLE = True
21
+ except ImportError:
22
+ SUPABASE_AVAILABLE = False
23
+ SupabaseClient = None
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ class DeploymentStatus(str, Enum):
28
+ """Deployment status enumeration"""
29
+ PENDING = "pending"
30
+ DEPLOYING = "deploying"
31
+ RUNNING = "running"
32
+ FAILED = "failed"
33
+ STOPPED = "stopped"
34
+ UPDATING = "updating"
35
+ SCALING = "scaling"
36
+
37
+ class DeploymentPlatform(str, Enum):
38
+ """Deployment platform enumeration"""
39
+ HUGGINGFACE = "huggingface"
40
+ MODAL = "modal"
41
+ AWS = "aws"
42
+ GCP = "gcp"
43
+ AZURE = "azure"
44
+ LOCAL = "local"
45
+ DOCKER = "docker"
46
+ KUBERNETES = "kubernetes"
47
+
48
+ @dataclass
49
+ class DeploymentRecord:
50
+ """Deployment record model"""
51
+ deployment_id: str
52
+ model_id: str
53
+ deployment_name: str
54
+ platform: str
55
+ status: str = DeploymentStatus.PENDING
56
+ endpoint_url: Optional[str] = None
57
+ config: Optional[Dict[str, Any]] = None
58
+ environment: str = "production"
59
+ created_at: datetime = None
60
+ updated_at: datetime = None
61
+ deployed_at: Optional[datetime] = None
62
+ stopped_at: Optional[datetime] = None
63
+ user_id: Optional[str] = None
64
+ project_name: Optional[str] = None
65
+ tags: Optional[Dict[str, str]] = None
66
+ error_message: Optional[str] = None
67
+
68
+ def __post_init__(self):
69
+ if self.created_at is None:
70
+ self.created_at = datetime.now(timezone.utc)
71
+ if self.updated_at is None:
72
+ self.updated_at = self.created_at
73
+
74
+ @dataclass
75
+ class DeploymentMetrics:
76
+ """Deployment metrics record"""
77
+ metric_id: str
78
+ deployment_id: str
79
+ timestamp: datetime
80
+ requests_per_minute: Optional[int] = None
81
+ avg_response_time_ms: Optional[float] = None
82
+ error_rate: Optional[float] = None
83
+ cpu_usage: Optional[float] = None
84
+ memory_usage: Optional[float] = None
85
+ disk_usage: Optional[float] = None
86
+ cost_per_hour: Optional[float] = None
87
+ uptime_percentage: Optional[float] = None
88
+ custom_metrics: Optional[Dict[str, Any]] = None
89
+
90
+ def __post_init__(self):
91
+ if self.timestamp is None:
92
+ self.timestamp = datetime.now(timezone.utc)
93
+
94
+ @dataclass
95
+ class ServiceRegistryEntry:
96
+ """Service registry entry for tracking deployed services"""
97
+ service_id: str
98
+ deployment_id: str
99
+ service_name: str
100
+ service_type: str
101
+ endpoint_url: str
102
+ health_check_url: Optional[str] = None
103
+ is_active: bool = True
104
+ last_health_check: Optional[datetime] = None
105
+ health_status: str = "unknown" # healthy, unhealthy, unknown
106
+ metadata: Optional[Dict[str, Any]] = None
107
+ created_at: datetime = None
108
+
109
+ def __post_init__(self):
110
+ if self.created_at is None:
111
+ self.created_at = datetime.now(timezone.utc)
112
+
113
+ class DeploymentRepository:
114
+ """
115
+ Repository for deployment data persistence
116
+
117
+ Supports multiple backend storage options:
118
+ 1. Supabase (preferred for centralized storage)
119
+ 2. Local file system (fallback for development)
120
+ 3. In-memory storage (for testing)
121
+ """
122
+
123
+ def __init__(self, storage_backend: str = "auto", **kwargs):
124
+ """
125
+ Initialize deployment repository
126
+
127
+ Args:
128
+ storage_backend: "supabase", "file", "memory", or "auto"
129
+ **kwargs: Backend-specific configuration
130
+ """
131
+ self.storage_backend = self._determine_backend(storage_backend)
132
+ self.config = kwargs
133
+
134
+ # Initialize storage backend
135
+ if self.storage_backend == "supabase":
136
+ self._init_supabase()
137
+ elif self.storage_backend == "memory":
138
+ self._init_memory()
139
+ else: # file system fallback
140
+ self._init_file_system()
141
+
142
+ logger.info(f"Deployment repository initialized with {self.storage_backend} backend")
143
+
144
+ def _determine_backend(self, preference: str) -> str:
145
+ """Determine the best available storage backend"""
146
+ if preference == "supabase" and SUPABASE_AVAILABLE:
147
+ return "supabase"
148
+ elif preference in ["supabase", "file", "memory"]:
149
+ return preference
150
+
151
+ # Auto-select best available backend
152
+ if SUPABASE_AVAILABLE:
153
+ return "supabase"
154
+ else:
155
+ return "file"
156
+
157
+ def _init_supabase(self):
158
+ """Initialize Supabase backend"""
159
+ try:
160
+ self.supabase_client = get_supabase_client()
161
+ self._ensure_supabase_tables()
162
+ logger.info("Supabase backend initialized for deployments")
163
+ except Exception as e:
164
+ logger.error(f"Failed to initialize Supabase backend: {e}")
165
+ self.storage_backend = "file"
166
+ self._init_file_system()
167
+
168
+ def _init_file_system(self):
169
+ """Initialize file system backend"""
170
+ self.data_dir = Path(self.config.get("data_dir", "./deployment_data"))
171
+ self.data_dir.mkdir(parents=True, exist_ok=True)
172
+
173
+ # Create subdirectories
174
+ (self.data_dir / "deployments").mkdir(exist_ok=True)
175
+ (self.data_dir / "metrics").mkdir(exist_ok=True)
176
+ (self.data_dir / "services").mkdir(exist_ok=True)
177
+
178
+ logger.info(f"File system backend initialized: {self.data_dir}")
179
+
180
+ def _init_memory(self):
181
+ """Initialize in-memory backend for testing"""
182
+ self.deployments = {}
183
+ self.metrics = {}
184
+ self.services = {}
185
+ logger.info("In-memory backend initialized for deployments")
186
+
187
+ def _ensure_supabase_tables(self):
188
+ """Ensure required Supabase tables exist"""
189
+ try:
190
+ self.supabase_client.table("deployment_records").select("deployment_id").limit(1).execute()
191
+ self.supabase_client.table("deployment_metrics").select("metric_id").limit(1).execute()
192
+ self.supabase_client.table("service_registry").select("service_id").limit(1).execute()
193
+ except Exception as e:
194
+ logger.warning(f"Some deployment tables may not exist in Supabase: {e}")
195
+
196
+ # Deployment Management Methods
197
+
198
+ def create_deployment_record(
199
+ self,
200
+ model_id: str,
201
+ deployment_name: str,
202
+ platform: str,
203
+ config: Optional[Dict[str, Any]] = None,
204
+ environment: str = "production",
205
+ user_id: Optional[str] = None,
206
+ project_name: Optional[str] = None,
207
+ tags: Optional[Dict[str, str]] = None
208
+ ) -> str:
209
+ """Create a new deployment record"""
210
+ deployment_id = f"deploy_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}"
211
+
212
+ deployment = DeploymentRecord(
213
+ deployment_id=deployment_id,
214
+ model_id=model_id,
215
+ deployment_name=deployment_name,
216
+ platform=platform,
217
+ config=config or {},
218
+ environment=environment,
219
+ user_id=user_id,
220
+ project_name=project_name,
221
+ tags=tags
222
+ )
223
+
224
+ if self.storage_backend == "supabase":
225
+ return self._create_deployment_supabase(deployment)
226
+ elif self.storage_backend == "memory":
227
+ return self._create_deployment_memory(deployment)
228
+ else:
229
+ return self._create_deployment_file(deployment)
230
+
231
+ def update_deployment_status(
232
+ self,
233
+ deployment_id: str,
234
+ status: str,
235
+ endpoint_url: Optional[str] = None,
236
+ error_message: Optional[str] = None,
237
+ additional_updates: Optional[Dict[str, Any]] = None
238
+ ) -> bool:
239
+ """Update deployment status and information"""
240
+ updates = {
241
+ "status": status,
242
+ "updated_at": datetime.now(timezone.utc).isoformat()
243
+ }
244
+
245
+ if status == DeploymentStatus.RUNNING and endpoint_url:
246
+ updates["endpoint_url"] = endpoint_url
247
+ updates["deployed_at"] = datetime.now(timezone.utc).isoformat()
248
+ elif status == DeploymentStatus.STOPPED:
249
+ updates["stopped_at"] = datetime.now(timezone.utc).isoformat()
250
+
251
+ if error_message:
252
+ updates["error_message"] = error_message
253
+
254
+ if additional_updates:
255
+ updates.update(additional_updates)
256
+
257
+ if self.storage_backend == "supabase":
258
+ return self._update_deployment_supabase(deployment_id, updates)
259
+ elif self.storage_backend == "memory":
260
+ return self._update_deployment_memory(deployment_id, updates)
261
+ else:
262
+ return self._update_deployment_file(deployment_id, updates)
263
+
264
+ def get_deployment_info(self, deployment_id: str) -> Optional[DeploymentRecord]:
265
+ """Get deployment information by ID"""
266
+ if self.storage_backend == "supabase":
267
+ return self._get_deployment_supabase(deployment_id)
268
+ elif self.storage_backend == "memory":
269
+ return self._get_deployment_memory(deployment_id)
270
+ else:
271
+ return self._get_deployment_file(deployment_id)
272
+
273
+ def list_active_deployments(
274
+ self,
275
+ platform: Optional[str] = None,
276
+ environment: Optional[str] = None,
277
+ user_id: Optional[str] = None,
278
+ project_name: Optional[str] = None,
279
+ limit: int = 100
280
+ ) -> List[DeploymentRecord]:
281
+ """List active deployments with optional filtering"""
282
+ if self.storage_backend == "supabase":
283
+ return self._list_deployments_supabase(platform, environment, user_id, project_name, limit)
284
+ elif self.storage_backend == "memory":
285
+ return self._list_deployments_memory(platform, environment, user_id, project_name, limit)
286
+ else:
287
+ return self._list_deployments_file(platform, environment, user_id, project_name, limit)
288
+
289
+ def delete_deployment_record(self, deployment_id: str) -> bool:
290
+ """Delete deployment record and associated data"""
291
+ if self.storage_backend == "supabase":
292
+ return self._delete_deployment_supabase(deployment_id)
293
+ elif self.storage_backend == "memory":
294
+ return self._delete_deployment_memory(deployment_id)
295
+ else:
296
+ return self._delete_deployment_file(deployment_id)
297
+
298
+ # Metrics Management Methods
299
+
300
+ def record_deployment_metrics(
301
+ self,
302
+ deployment_id: str,
303
+ requests_per_minute: Optional[int] = None,
304
+ avg_response_time_ms: Optional[float] = None,
305
+ error_rate: Optional[float] = None,
306
+ cpu_usage: Optional[float] = None,
307
+ memory_usage: Optional[float] = None,
308
+ disk_usage: Optional[float] = None,
309
+ cost_per_hour: Optional[float] = None,
310
+ uptime_percentage: Optional[float] = None,
311
+ custom_metrics: Optional[Dict[str, Any]] = None
312
+ ) -> str:
313
+ """Record deployment metrics"""
314
+ metric_id = f"metric_{deployment_id}_{uuid.uuid4().hex[:8]}"
315
+
316
+ metrics = DeploymentMetrics(
317
+ metric_id=metric_id,
318
+ deployment_id=deployment_id,
319
+ timestamp=datetime.now(timezone.utc),
320
+ requests_per_minute=requests_per_minute,
321
+ avg_response_time_ms=avg_response_time_ms,
322
+ error_rate=error_rate,
323
+ cpu_usage=cpu_usage,
324
+ memory_usage=memory_usage,
325
+ disk_usage=disk_usage,
326
+ cost_per_hour=cost_per_hour,
327
+ uptime_percentage=uptime_percentage,
328
+ custom_metrics=custom_metrics
329
+ )
330
+
331
+ if self.storage_backend == "supabase":
332
+ return self._record_metrics_supabase(metrics)
333
+ elif self.storage_backend == "memory":
334
+ return self._record_metrics_memory(metrics)
335
+ else:
336
+ return self._record_metrics_file(metrics)
337
+
338
+ def get_deployment_metrics(
339
+ self,
340
+ deployment_id: str,
341
+ hours: int = 24,
342
+ limit: int = 1000
343
+ ) -> List[DeploymentMetrics]:
344
+ """Get deployment metrics for specified time period"""
345
+ if self.storage_backend == "supabase":
346
+ return self._get_metrics_supabase(deployment_id, hours, limit)
347
+ elif self.storage_backend == "memory":
348
+ return self._get_metrics_memory(deployment_id, hours, limit)
349
+ else:
350
+ return self._get_metrics_file(deployment_id, hours, limit)
351
+
352
+ def get_deployment_statistics(self, deployment_id: str) -> Dict[str, Any]:
353
+ """Get aggregated deployment statistics"""
354
+ metrics = self.get_deployment_metrics(deployment_id, hours=24)
355
+
356
+ if not metrics:
357
+ return {"total_metrics": 0, "period_hours": 24}
358
+
359
+ # Calculate basic statistics
360
+ response_times = [m.avg_response_time_ms for m in metrics if m.avg_response_time_ms is not None]
361
+ error_rates = [m.error_rate for m in metrics if m.error_rate is not None]
362
+ cpu_usage = [m.cpu_usage for m in metrics if m.cpu_usage is not None]
363
+
364
+ stats = {
365
+ "total_metrics": len(metrics),
366
+ "period_hours": 24,
367
+ "latest_timestamp": max(m.timestamp for m in metrics).isoformat(),
368
+ "avg_response_time_ms": sum(response_times) / len(response_times) if response_times else None,
369
+ "avg_error_rate": sum(error_rates) / len(error_rates) if error_rates else None,
370
+ "avg_cpu_usage": sum(cpu_usage) / len(cpu_usage) if cpu_usage else None,
371
+ "total_requests": sum(m.requests_per_minute or 0 for m in metrics),
372
+ "uptime_percentage": metrics[-1].uptime_percentage if metrics and metrics[-1].uptime_percentage else None
373
+ }
374
+
375
+ return stats
376
+
377
+ # Service Registry Methods
378
+
379
+ def register_service(
380
+ self,
381
+ deployment_id: str,
382
+ service_name: str,
383
+ service_type: str,
384
+ endpoint_url: str,
385
+ health_check_url: Optional[str] = None,
386
+ metadata: Optional[Dict[str, Any]] = None
387
+ ) -> str:
388
+ """Register a service in the service registry"""
389
+ service_id = f"service_{deployment_id}_{uuid.uuid4().hex[:6]}"
390
+
391
+ service = ServiceRegistryEntry(
392
+ service_id=service_id,
393
+ deployment_id=deployment_id,
394
+ service_name=service_name,
395
+ service_type=service_type,
396
+ endpoint_url=endpoint_url,
397
+ health_check_url=health_check_url,
398
+ metadata=metadata
399
+ )
400
+
401
+ if self.storage_backend == "supabase":
402
+ return self._register_service_supabase(service)
403
+ elif self.storage_backend == "memory":
404
+ return self._register_service_memory(service)
405
+ else:
406
+ return self._register_service_file(service)
407
+
408
+ def update_service_health(
409
+ self,
410
+ service_id: str,
411
+ health_status: str,
412
+ last_check_time: Optional[datetime] = None
413
+ ) -> bool:
414
+ """Update service health status"""
415
+ updates = {
416
+ "health_status": health_status,
417
+ "last_health_check": (last_check_time or datetime.now(timezone.utc)).isoformat()
418
+ }
419
+
420
+ if self.storage_backend == "supabase":
421
+ return self._update_service_supabase(service_id, updates)
422
+ elif self.storage_backend == "memory":
423
+ return self._update_service_memory(service_id, updates)
424
+ else:
425
+ return self._update_service_file(service_id, updates)
426
+
427
+ def list_services(
428
+ self,
429
+ deployment_id: Optional[str] = None,
430
+ service_type: Optional[str] = None,
431
+ is_active: bool = True
432
+ ) -> List[ServiceRegistryEntry]:
433
+ """List registered services"""
434
+ if self.storage_backend == "supabase":
435
+ return self._list_services_supabase(deployment_id, service_type, is_active)
436
+ elif self.storage_backend == "memory":
437
+ return self._list_services_memory(deployment_id, service_type, is_active)
438
+ else:
439
+ return self._list_services_file(deployment_id, service_type, is_active)
440
+
441
+ def deregister_service(self, service_id: str) -> bool:
442
+ """Deregister a service"""
443
+ if self.storage_backend == "supabase":
444
+ return self._deregister_service_supabase(service_id)
445
+ elif self.storage_backend == "memory":
446
+ return self._deregister_service_memory(service_id)
447
+ else:
448
+ return self._deregister_service_file(service_id)
449
+
450
+ # Backend-specific implementations
451
+
452
+ def _create_deployment_file(self, deployment: DeploymentRecord) -> str:
453
+ """Create deployment in file system"""
454
+ try:
455
+ deployment_file = self.data_dir / "deployments" / f"{deployment.deployment_id}.json"
456
+ deployment_data = asdict(deployment)
457
+
458
+ # Convert datetime objects to ISO strings
459
+ for key in ['created_at', 'updated_at', 'deployed_at', 'stopped_at']:
460
+ if deployment_data[key] and isinstance(deployment_data[key], datetime):
461
+ deployment_data[key] = deployment_data[key].isoformat()
462
+
463
+ with open(deployment_file, 'w') as f:
464
+ json.dump(deployment_data, f, indent=2, ensure_ascii=False)
465
+
466
+ return deployment.deployment_id
467
+ except Exception as e:
468
+ logger.error(f"Failed to create deployment in file system: {e}")
469
+ raise
470
+
471
+ def _create_deployment_memory(self, deployment: DeploymentRecord) -> str:
472
+ """Create deployment in memory"""
473
+ self.deployments[deployment.deployment_id] = deployment
474
+ return deployment.deployment_id
475
+
476
+ def _update_deployment_file(self, deployment_id: str, updates: Dict[str, Any]) -> bool:
477
+ """Update deployment in file system"""
478
+ try:
479
+ deployment_file = self.data_dir / "deployments" / f"{deployment_id}.json"
480
+ if not deployment_file.exists():
481
+ return False
482
+
483
+ with open(deployment_file, 'r') as f:
484
+ deployment_data = json.load(f)
485
+
486
+ deployment_data.update(updates)
487
+
488
+ with open(deployment_file, 'w') as f:
489
+ json.dump(deployment_data, f, indent=2, ensure_ascii=False)
490
+
491
+ return True
492
+ except Exception as e:
493
+ logger.error(f"Failed to update deployment in file system: {e}")
494
+ return False
495
+
496
+ def _update_deployment_memory(self, deployment_id: str, updates: Dict[str, Any]) -> bool:
497
+ """Update deployment in memory"""
498
+ if deployment_id not in self.deployments:
499
+ return False
500
+
501
+ deployment_dict = asdict(self.deployments[deployment_id])
502
+ deployment_dict.update(updates)
503
+
504
+ # Convert datetime strings back to datetime objects if needed
505
+ for key in ['created_at', 'updated_at', 'deployed_at', 'stopped_at']:
506
+ if key in deployment_dict and isinstance(deployment_dict[key], str):
507
+ deployment_dict[key] = datetime.fromisoformat(deployment_dict[key])
508
+
509
+ self.deployments[deployment_id] = DeploymentRecord(**deployment_dict)
510
+ return True
511
+
512
+ def _get_deployment_file(self, deployment_id: str) -> Optional[DeploymentRecord]:
513
+ """Get deployment from file system"""
514
+ try:
515
+ deployment_file = self.data_dir / "deployments" / f"{deployment_id}.json"
516
+ if not deployment_file.exists():
517
+ return None
518
+
519
+ with open(deployment_file, 'r') as f:
520
+ deployment_data = json.load(f)
521
+
522
+ # Convert ISO strings back to datetime objects
523
+ for key in ['created_at', 'updated_at', 'deployed_at', 'stopped_at']:
524
+ if deployment_data[key]:
525
+ deployment_data[key] = datetime.fromisoformat(deployment_data[key])
526
+
527
+ return DeploymentRecord(**deployment_data)
528
+ except Exception as e:
529
+ logger.error(f"Failed to get deployment from file system: {e}")
530
+ return None
531
+
532
+ def _get_deployment_memory(self, deployment_id: str) -> Optional[DeploymentRecord]:
533
+ """Get deployment from memory"""
534
+ return self.deployments.get(deployment_id)
535
+
536
+ def _list_deployments_file(
537
+ self, platform: Optional[str], environment: Optional[str],
538
+ user_id: Optional[str], project_name: Optional[str], limit: int
539
+ ) -> List[DeploymentRecord]:
540
+ """List deployments from file system"""
541
+ try:
542
+ deployments = []
543
+ deployments_dir = self.data_dir / "deployments"
544
+
545
+ for deployment_file in deployments_dir.glob("*.json"):
546
+ with open(deployment_file, 'r') as f:
547
+ deployment_data = json.load(f)
548
+
549
+ # Apply filters
550
+ if platform and deployment_data.get('platform') != platform:
551
+ continue
552
+ if environment and deployment_data.get('environment') != environment:
553
+ continue
554
+ if user_id and deployment_data.get('user_id') != user_id:
555
+ continue
556
+ if project_name and deployment_data.get('project_name') != project_name:
557
+ continue
558
+
559
+ # Convert datetime fields
560
+ for key in ['created_at', 'updated_at', 'deployed_at', 'stopped_at']:
561
+ if deployment_data[key]:
562
+ deployment_data[key] = datetime.fromisoformat(deployment_data[key])
563
+
564
+ deployments.append(DeploymentRecord(**deployment_data))
565
+
566
+ if len(deployments) >= limit:
567
+ break
568
+
569
+ return sorted(deployments, key=lambda x: x.created_at, reverse=True)
570
+ except Exception as e:
571
+ logger.error(f"Failed to list deployments from file system: {e}")
572
+ return []
573
+
574
+ def _list_deployments_memory(
575
+ self, platform: Optional[str], environment: Optional[str],
576
+ user_id: Optional[str], project_name: Optional[str], limit: int
577
+ ) -> List[DeploymentRecord]:
578
+ """List deployments from memory"""
579
+ deployments = []
580
+
581
+ for deployment in self.deployments.values():
582
+ # Apply filters
583
+ if platform and deployment.platform != platform:
584
+ continue
585
+ if environment and deployment.environment != environment:
586
+ continue
587
+ if user_id and deployment.user_id != user_id:
588
+ continue
589
+ if project_name and deployment.project_name != project_name:
590
+ continue
591
+
592
+ deployments.append(deployment)
593
+
594
+ if len(deployments) >= limit:
595
+ break
596
+
597
+ return sorted(deployments, key=lambda x: x.created_at, reverse=True)
598
+
599
+ def _record_metrics_file(self, metrics: DeploymentMetrics) -> str:
600
+ """Record metrics in file system"""
601
+ try:
602
+ metrics_file = self.data_dir / "metrics" / f"{metrics.metric_id}.json"
603
+ metrics_data = asdict(metrics)
604
+
605
+ if metrics_data['timestamp'] and isinstance(metrics_data['timestamp'], datetime):
606
+ metrics_data['timestamp'] = metrics_data['timestamp'].isoformat()
607
+
608
+ with open(metrics_file, 'w') as f:
609
+ json.dump(metrics_data, f, indent=2, ensure_ascii=False)
610
+
611
+ return metrics.metric_id
612
+ except Exception as e:
613
+ logger.error(f"Failed to record metrics in file system: {e}")
614
+ raise
615
+
616
+ def _record_metrics_memory(self, metrics: DeploymentMetrics) -> str:
617
+ """Record metrics in memory"""
618
+ if metrics.deployment_id not in self.metrics:
619
+ self.metrics[metrics.deployment_id] = []
620
+ self.metrics[metrics.deployment_id].append(metrics)
621
+ return metrics.metric_id
622
+
623
+ def _get_metrics_file(self, deployment_id: str, hours: int, limit: int) -> List[DeploymentMetrics]:
624
+ """Get metrics from file system"""
625
+ try:
626
+ metrics = []
627
+ metrics_dir = self.data_dir / "metrics"
628
+ cutoff_time = datetime.now(timezone.utc) - timedelta(hours=hours)
629
+
630
+ for metrics_file in metrics_dir.glob("*.json"):
631
+ with open(metrics_file, 'r') as f:
632
+ metrics_data = json.load(f)
633
+
634
+ if metrics_data.get('deployment_id') != deployment_id:
635
+ continue
636
+
637
+ if metrics_data['timestamp']:
638
+ timestamp = datetime.fromisoformat(metrics_data['timestamp'])
639
+ if timestamp < cutoff_time:
640
+ continue
641
+ metrics_data['timestamp'] = timestamp
642
+
643
+ metrics.append(DeploymentMetrics(**metrics_data))
644
+
645
+ if len(metrics) >= limit:
646
+ break
647
+
648
+ return sorted(metrics, key=lambda x: x.timestamp, reverse=True)
649
+ except Exception as e:
650
+ logger.error(f"Failed to get metrics from file system: {e}")
651
+ return []
652
+
653
+ def _get_metrics_memory(self, deployment_id: str, hours: int, limit: int) -> List[DeploymentMetrics]:
654
+ """Get metrics from memory"""
655
+ from datetime import timedelta
656
+ cutoff_time = datetime.now(timezone.utc) - timedelta(hours=hours)
657
+
658
+ deployment_metrics = self.metrics.get(deployment_id, [])
659
+ filtered_metrics = [
660
+ m for m in deployment_metrics
661
+ if m.timestamp >= cutoff_time
662
+ ]
663
+
664
+ return sorted(filtered_metrics[:limit], key=lambda x: x.timestamp, reverse=True)
665
+
666
+ # Service registry implementations (simplified)
667
+ def _register_service_file(self, service: ServiceRegistryEntry) -> str:
668
+ """Register service in file system"""
669
+ try:
670
+ service_file = self.data_dir / "services" / f"{service.service_id}.json"
671
+ service_data = asdict(service)
672
+
673
+ if service_data['created_at'] and isinstance(service_data['created_at'], datetime):
674
+ service_data['created_at'] = service_data['created_at'].isoformat()
675
+ if service_data['last_health_check'] and isinstance(service_data['last_health_check'], datetime):
676
+ service_data['last_health_check'] = service_data['last_health_check'].isoformat()
677
+
678
+ with open(service_file, 'w') as f:
679
+ json.dump(service_data, f, indent=2, ensure_ascii=False)
680
+
681
+ return service.service_id
682
+ except Exception as e:
683
+ logger.error(f"Failed to register service in file system: {e}")
684
+ raise
685
+
686
+ def _register_service_memory(self, service: ServiceRegistryEntry) -> str:
687
+ """Register service in memory"""
688
+ self.services[service.service_id] = service
689
+ return service.service_id
690
+
691
+ # Placeholder implementations for Supabase backend
692
+ def _create_deployment_supabase(self, deployment: DeploymentRecord) -> str:
693
+ return deployment.deployment_id # Implementation needed
694
+
695
+ def _update_deployment_supabase(self, deployment_id: str, updates: Dict[str, Any]) -> bool:
696
+ return False # Implementation needed
697
+
698
+ def _get_deployment_supabase(self, deployment_id: str) -> Optional[DeploymentRecord]:
699
+ return None # Implementation needed
700
+
701
+ def _list_deployments_supabase(self, platform, environment, user_id, project_name, limit) -> List[DeploymentRecord]:
702
+ return [] # Implementation needed
703
+
704
+ def _delete_deployment_supabase(self, deployment_id: str) -> bool:
705
+ return False # Implementation needed
706
+
707
+ def _delete_deployment_file(self, deployment_id: str) -> bool:
708
+ try:
709
+ deployment_file = self.data_dir / "deployments" / f"{deployment_id}.json"
710
+ if deployment_file.exists():
711
+ deployment_file.unlink()
712
+ return True
713
+ except Exception as e:
714
+ logger.error(f"Failed to delete deployment from file system: {e}")
715
+ return False
716
+
717
+ def _delete_deployment_memory(self, deployment_id: str) -> bool:
718
+ return self.deployments.pop(deployment_id, None) is not None
719
+
720
+ # Additional placeholder implementations
721
+ def _record_metrics_supabase(self, metrics: DeploymentMetrics) -> str:
722
+ return metrics.metric_id
723
+
724
+ def _get_metrics_supabase(self, deployment_id: str, hours: int, limit: int) -> List[DeploymentMetrics]:
725
+ return []
726
+
727
+ def _register_service_supabase(self, service: ServiceRegistryEntry) -> str:
728
+ return service.service_id
729
+
730
+ def _update_service_supabase(self, service_id: str, updates: Dict[str, Any]) -> bool:
731
+ return False
732
+
733
+ def _update_service_memory(self, service_id: str, updates: Dict[str, Any]) -> bool:
734
+ if service_id not in self.services:
735
+ return False
736
+
737
+ service_dict = asdict(self.services[service_id])
738
+ service_dict.update(updates)
739
+
740
+ # Convert datetime strings back if needed
741
+ for key in ['created_at', 'last_health_check']:
742
+ if key in service_dict and isinstance(service_dict[key], str):
743
+ service_dict[key] = datetime.fromisoformat(service_dict[key])
744
+
745
+ self.services[service_id] = ServiceRegistryEntry(**service_dict)
746
+ return True
747
+
748
+ def _update_service_file(self, service_id: str, updates: Dict[str, Any]) -> bool:
749
+ try:
750
+ service_file = self.data_dir / "services" / f"{service_id}.json"
751
+ if not service_file.exists():
752
+ return False
753
+
754
+ with open(service_file, 'r') as f:
755
+ service_data = json.load(f)
756
+
757
+ service_data.update(updates)
758
+
759
+ with open(service_file, 'w') as f:
760
+ json.dump(service_data, f, indent=2, ensure_ascii=False)
761
+
762
+ return True
763
+ except Exception as e:
764
+ logger.error(f"Failed to update service in file system: {e}")
765
+ return False
766
+
767
+ def _list_services_supabase(self, deployment_id, service_type, is_active) -> List[ServiceRegistryEntry]:
768
+ return []
769
+
770
+ def _list_services_memory(self, deployment_id, service_type, is_active) -> List[ServiceRegistryEntry]:
771
+ services = []
772
+ for service in self.services.values():
773
+ if deployment_id and service.deployment_id != deployment_id:
774
+ continue
775
+ if service_type and service.service_type != service_type:
776
+ continue
777
+ if service.is_active != is_active:
778
+ continue
779
+ services.append(service)
780
+ return services
781
+
782
+ def _list_services_file(self, deployment_id, service_type, is_active) -> List[ServiceRegistryEntry]:
783
+ try:
784
+ services = []
785
+ services_dir = self.data_dir / "services"
786
+
787
+ for service_file in services_dir.glob("*.json"):
788
+ with open(service_file, 'r') as f:
789
+ service_data = json.load(f)
790
+
791
+ if deployment_id and service_data.get('deployment_id') != deployment_id:
792
+ continue
793
+ if service_type and service_data.get('service_type') != service_type:
794
+ continue
795
+ if service_data.get('is_active') != is_active:
796
+ continue
797
+
798
+ # Convert datetime fields
799
+ for key in ['created_at', 'last_health_check']:
800
+ if service_data[key]:
801
+ service_data[key] = datetime.fromisoformat(service_data[key])
802
+
803
+ services.append(ServiceRegistryEntry(**service_data))
804
+
805
+ return services
806
+ except Exception as e:
807
+ logger.error(f"Failed to list services from file system: {e}")
808
+ return []
809
+
810
+ def _deregister_service_supabase(self, service_id: str) -> bool:
811
+ return False
812
+
813
+ def _deregister_service_memory(self, service_id: str) -> bool:
814
+ return self.services.pop(service_id, None) is not None
815
+
816
+ def _deregister_service_file(self, service_id: str) -> bool:
817
+ try:
818
+ service_file = self.data_dir / "services" / f"{service_id}.json"
819
+ if service_file.exists():
820
+ service_file.unlink()
821
+ return True
822
+ except Exception as e:
823
+ logger.error(f"Failed to deregister service from file system: {e}")
824
+ return False