isa-model 0.3.91__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 (228) hide show
  1. isa_model/client.py +1166 -584
  2. isa_model/core/cache/redis_cache.py +410 -0
  3. isa_model/core/config/config_manager.py +282 -12
  4. isa_model/core/config.py +91 -1
  5. isa_model/core/database/__init__.py +1 -0
  6. isa_model/core/database/direct_db_client.py +114 -0
  7. isa_model/core/database/migration_manager.py +563 -0
  8. isa_model/core/database/migrations.py +297 -0
  9. isa_model/core/database/supabase_client.py +258 -0
  10. isa_model/core/dependencies.py +316 -0
  11. isa_model/core/discovery/__init__.py +19 -0
  12. isa_model/core/discovery/consul_discovery.py +190 -0
  13. isa_model/core/logging/__init__.py +54 -0
  14. isa_model/core/logging/influx_logger.py +523 -0
  15. isa_model/core/logging/loki_logger.py +160 -0
  16. isa_model/core/models/__init__.py +46 -0
  17. isa_model/core/models/config_models.py +625 -0
  18. isa_model/core/models/deployment_billing_tracker.py +430 -0
  19. isa_model/core/models/model_billing_tracker.py +60 -88
  20. isa_model/core/models/model_manager.py +66 -25
  21. isa_model/core/models/model_metadata.py +690 -0
  22. isa_model/core/models/model_repo.py +217 -55
  23. isa_model/core/models/model_statistics_tracker.py +234 -0
  24. isa_model/core/models/model_storage.py +0 -1
  25. isa_model/core/models/model_version_manager.py +959 -0
  26. isa_model/core/models/system_models.py +857 -0
  27. isa_model/core/pricing_manager.py +2 -249
  28. isa_model/core/repositories/__init__.py +9 -0
  29. isa_model/core/repositories/config_repository.py +912 -0
  30. isa_model/core/resilience/circuit_breaker.py +366 -0
  31. isa_model/core/security/secrets.py +358 -0
  32. isa_model/core/services/__init__.py +2 -4
  33. isa_model/core/services/intelligent_model_selector.py +479 -370
  34. isa_model/core/storage/hf_storage.py +2 -2
  35. isa_model/core/types.py +8 -0
  36. isa_model/deployment/__init__.py +5 -48
  37. isa_model/deployment/core/__init__.py +2 -31
  38. isa_model/deployment/core/deployment_manager.py +1278 -368
  39. isa_model/deployment/local/__init__.py +31 -0
  40. isa_model/deployment/local/config.py +248 -0
  41. isa_model/deployment/local/gpu_gateway.py +607 -0
  42. isa_model/deployment/local/health_checker.py +428 -0
  43. isa_model/deployment/local/provider.py +586 -0
  44. isa_model/deployment/local/tensorrt_service.py +621 -0
  45. isa_model/deployment/local/transformers_service.py +644 -0
  46. isa_model/deployment/local/vllm_service.py +527 -0
  47. isa_model/deployment/modal/__init__.py +8 -0
  48. isa_model/deployment/modal/config.py +136 -0
  49. isa_model/deployment/modal/deployer.py +894 -0
  50. isa_model/deployment/modal/services/__init__.py +3 -0
  51. isa_model/deployment/modal/services/audio/__init__.py +1 -0
  52. isa_model/deployment/modal/services/audio/isa_audio_chatTTS_service.py +520 -0
  53. isa_model/deployment/modal/services/audio/isa_audio_openvoice_service.py +758 -0
  54. isa_model/deployment/modal/services/audio/isa_audio_service_v2.py +1044 -0
  55. isa_model/deployment/modal/services/embedding/__init__.py +1 -0
  56. isa_model/deployment/modal/services/embedding/isa_embed_rerank_service.py +296 -0
  57. isa_model/deployment/modal/services/llm/__init__.py +1 -0
  58. isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
  59. isa_model/deployment/modal/services/video/__init__.py +1 -0
  60. isa_model/deployment/modal/services/video/isa_video_hunyuan_service.py +423 -0
  61. isa_model/deployment/modal/services/vision/__init__.py +1 -0
  62. isa_model/deployment/modal/services/vision/isa_vision_ocr_service.py +519 -0
  63. isa_model/deployment/modal/services/vision/isa_vision_qwen25_service.py +709 -0
  64. isa_model/deployment/modal/services/vision/isa_vision_table_service.py +676 -0
  65. isa_model/deployment/modal/services/vision/isa_vision_ui_service.py +833 -0
  66. isa_model/deployment/modal/services/vision/isa_vision_ui_service_optimized.py +660 -0
  67. isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
  68. isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
  69. isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
  70. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
  71. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
  72. isa_model/deployment/storage/__init__.py +5 -0
  73. isa_model/deployment/storage/deployment_repository.py +824 -0
  74. isa_model/deployment/triton/__init__.py +10 -0
  75. isa_model/deployment/triton/config.py +196 -0
  76. isa_model/deployment/triton/configs/__init__.py +1 -0
  77. isa_model/deployment/triton/provider.py +512 -0
  78. isa_model/deployment/triton/scripts/__init__.py +1 -0
  79. isa_model/deployment/triton/templates/__init__.py +1 -0
  80. isa_model/inference/__init__.py +47 -1
  81. isa_model/inference/ai_factory.py +179 -16
  82. isa_model/inference/legacy_services/__init__.py +21 -0
  83. isa_model/inference/legacy_services/model_evaluation.py +637 -0
  84. isa_model/inference/legacy_services/model_service.py +573 -0
  85. isa_model/inference/legacy_services/model_serving.py +717 -0
  86. isa_model/inference/legacy_services/model_training.py +561 -0
  87. isa_model/inference/models/__init__.py +21 -0
  88. isa_model/inference/models/inference_config.py +551 -0
  89. isa_model/inference/models/inference_record.py +675 -0
  90. isa_model/inference/models/performance_models.py +714 -0
  91. isa_model/inference/repositories/__init__.py +9 -0
  92. isa_model/inference/repositories/inference_repository.py +828 -0
  93. isa_model/inference/services/audio/__init__.py +21 -0
  94. isa_model/inference/services/audio/base_realtime_service.py +225 -0
  95. isa_model/inference/services/audio/base_stt_service.py +184 -11
  96. isa_model/inference/services/audio/isa_tts_service.py +0 -0
  97. isa_model/inference/services/audio/openai_realtime_service.py +320 -124
  98. isa_model/inference/services/audio/openai_stt_service.py +53 -11
  99. isa_model/inference/services/base_service.py +17 -1
  100. isa_model/inference/services/custom_model_manager.py +277 -0
  101. isa_model/inference/services/embedding/__init__.py +13 -0
  102. isa_model/inference/services/embedding/base_embed_service.py +111 -8
  103. isa_model/inference/services/embedding/isa_embed_service.py +305 -0
  104. isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
  105. isa_model/inference/services/embedding/openai_embed_service.py +2 -4
  106. isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
  107. isa_model/inference/services/embedding/tests/test_embedding.py +222 -0
  108. isa_model/inference/services/img/__init__.py +2 -2
  109. isa_model/inference/services/img/base_image_gen_service.py +24 -7
  110. isa_model/inference/services/img/replicate_image_gen_service.py +84 -422
  111. isa_model/inference/services/img/services/replicate_face_swap.py +193 -0
  112. isa_model/inference/services/img/services/replicate_flux.py +226 -0
  113. isa_model/inference/services/img/services/replicate_flux_kontext.py +219 -0
  114. isa_model/inference/services/img/services/replicate_sticker_maker.py +249 -0
  115. isa_model/inference/services/img/tests/test_img_client.py +297 -0
  116. isa_model/inference/services/llm/__init__.py +10 -2
  117. isa_model/inference/services/llm/base_llm_service.py +361 -26
  118. isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
  119. isa_model/inference/services/llm/helpers/llm_adapter.py +71 -12
  120. isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
  121. isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
  122. isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
  123. isa_model/inference/services/llm/local_llm_service.py +747 -0
  124. isa_model/inference/services/llm/ollama_llm_service.py +11 -3
  125. isa_model/inference/services/llm/openai_llm_service.py +670 -56
  126. isa_model/inference/services/llm/yyds_llm_service.py +10 -3
  127. isa_model/inference/services/vision/__init__.py +27 -6
  128. isa_model/inference/services/vision/base_vision_service.py +118 -185
  129. isa_model/inference/services/vision/blip_vision_service.py +359 -0
  130. isa_model/inference/services/vision/helpers/image_utils.py +19 -10
  131. isa_model/inference/services/vision/isa_vision_service.py +634 -0
  132. isa_model/inference/services/vision/openai_vision_service.py +19 -10
  133. isa_model/inference/services/vision/tests/test_ocr_client.py +284 -0
  134. isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
  135. isa_model/serving/api/cache_manager.py +245 -0
  136. isa_model/serving/api/dependencies/__init__.py +1 -0
  137. isa_model/serving/api/dependencies/auth.py +194 -0
  138. isa_model/serving/api/dependencies/database.py +139 -0
  139. isa_model/serving/api/error_handlers.py +284 -0
  140. isa_model/serving/api/fastapi_server.py +240 -18
  141. isa_model/serving/api/middleware/auth.py +317 -0
  142. isa_model/serving/api/middleware/security.py +268 -0
  143. isa_model/serving/api/middleware/tenant_context.py +414 -0
  144. isa_model/serving/api/routes/analytics.py +489 -0
  145. isa_model/serving/api/routes/config.py +645 -0
  146. isa_model/serving/api/routes/deployment_billing.py +315 -0
  147. isa_model/serving/api/routes/deployments.py +475 -0
  148. isa_model/serving/api/routes/gpu_gateway.py +440 -0
  149. isa_model/serving/api/routes/health.py +32 -12
  150. isa_model/serving/api/routes/inference_monitoring.py +486 -0
  151. isa_model/serving/api/routes/local_deployments.py +448 -0
  152. isa_model/serving/api/routes/logs.py +430 -0
  153. isa_model/serving/api/routes/settings.py +582 -0
  154. isa_model/serving/api/routes/tenants.py +575 -0
  155. isa_model/serving/api/routes/unified.py +992 -171
  156. isa_model/serving/api/routes/webhooks.py +479 -0
  157. isa_model/serving/api/startup.py +318 -0
  158. isa_model/serving/modal_proxy_server.py +249 -0
  159. isa_model/utils/gpu_utils.py +311 -0
  160. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/METADATA +76 -22
  161. isa_model-0.4.3.dist-info/RECORD +193 -0
  162. isa_model/deployment/cloud/__init__.py +0 -9
  163. isa_model/deployment/cloud/modal/__init__.py +0 -10
  164. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -766
  165. isa_model/deployment/cloud/modal/isa_vision_table_service.py +0 -532
  166. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +0 -406
  167. isa_model/deployment/cloud/modal/register_models.py +0 -321
  168. isa_model/deployment/core/deployment_config.py +0 -356
  169. isa_model/deployment/core/isa_deployment_service.py +0 -401
  170. isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
  171. isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
  172. isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
  173. isa_model/deployment/runtime/deployed_service.py +0 -338
  174. isa_model/deployment/services/__init__.py +0 -9
  175. isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
  176. isa_model/deployment/services/model_service.py +0 -332
  177. isa_model/deployment/services/service_monitor.py +0 -356
  178. isa_model/deployment/services/service_registry.py +0 -527
  179. isa_model/eval/__init__.py +0 -92
  180. isa_model/eval/benchmarks.py +0 -469
  181. isa_model/eval/config/__init__.py +0 -10
  182. isa_model/eval/config/evaluation_config.py +0 -108
  183. isa_model/eval/evaluators/__init__.py +0 -18
  184. isa_model/eval/evaluators/base_evaluator.py +0 -503
  185. isa_model/eval/evaluators/llm_evaluator.py +0 -472
  186. isa_model/eval/factory.py +0 -531
  187. isa_model/eval/infrastructure/__init__.py +0 -24
  188. isa_model/eval/infrastructure/experiment_tracker.py +0 -466
  189. isa_model/eval/metrics.py +0 -798
  190. isa_model/inference/adapter/unified_api.py +0 -248
  191. isa_model/inference/services/helpers/stacked_config.py +0 -148
  192. isa_model/inference/services/img/flux_professional_service.py +0 -603
  193. isa_model/inference/services/img/helpers/base_stacked_service.py +0 -274
  194. isa_model/inference/services/others/table_transformer_service.py +0 -61
  195. isa_model/inference/services/vision/doc_analysis_service.py +0 -640
  196. isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -274
  197. isa_model/inference/services/vision/ui_analysis_service.py +0 -823
  198. isa_model/scripts/inference_tracker.py +0 -283
  199. isa_model/scripts/mlflow_manager.py +0 -379
  200. isa_model/scripts/model_registry.py +0 -465
  201. isa_model/scripts/register_models.py +0 -370
  202. isa_model/scripts/register_models_with_embeddings.py +0 -510
  203. isa_model/scripts/start_mlflow.py +0 -95
  204. isa_model/scripts/training_tracker.py +0 -257
  205. isa_model/training/__init__.py +0 -74
  206. isa_model/training/annotation/annotation_schema.py +0 -47
  207. isa_model/training/annotation/processors/annotation_processor.py +0 -126
  208. isa_model/training/annotation/storage/dataset_manager.py +0 -131
  209. isa_model/training/annotation/storage/dataset_schema.py +0 -44
  210. isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
  211. isa_model/training/annotation/tests/test_minio copy.py +0 -113
  212. isa_model/training/annotation/tests/test_minio_upload.py +0 -43
  213. isa_model/training/annotation/views/annotation_controller.py +0 -158
  214. isa_model/training/cloud/__init__.py +0 -22
  215. isa_model/training/cloud/job_orchestrator.py +0 -402
  216. isa_model/training/cloud/runpod_trainer.py +0 -454
  217. isa_model/training/cloud/storage_manager.py +0 -482
  218. isa_model/training/core/__init__.py +0 -23
  219. isa_model/training/core/config.py +0 -181
  220. isa_model/training/core/dataset.py +0 -222
  221. isa_model/training/core/trainer.py +0 -720
  222. isa_model/training/core/utils.py +0 -213
  223. isa_model/training/factory.py +0 -424
  224. isa_model-0.3.91.dist-info/RECORD +0 -138
  225. /isa_model/{core/storage/minio_storage.py → deployment/modal/services/audio/isa_audio_fish_service.py} +0 -0
  226. /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
  227. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/WHEEL +0 -0
  228. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,645 @@
1
+ """
2
+ Configuration API Routes
3
+
4
+ Provides comprehensive configuration management capabilities including:
5
+ - Provider configuration management
6
+ - Environment configuration
7
+ - System settings management
8
+ - Configuration validation
9
+ - Configuration backup and restore
10
+ """
11
+
12
+ from fastapi import APIRouter, HTTPException, Query, Depends
13
+ from pydantic import BaseModel, Field
14
+ from typing import Optional, List, Dict, Any, Union
15
+ import logging
16
+ from datetime import datetime
17
+ import json
18
+ import os
19
+
20
+ try:
21
+ from ..middleware.auth import optional_auth, require_read_access, require_write_access
22
+ except ImportError:
23
+ # For development/testing when auth is not required
24
+ def optional_auth():
25
+ return {"user_id": "test_user"}
26
+
27
+ def require_read_access():
28
+ return {"user_id": "test_user"}
29
+
30
+ def require_write_access():
31
+ return {"user_id": "test_user"}
32
+
33
+ # Import configuration modules
34
+ from isa_model.core.config.config_manager import ConfigManager
35
+
36
+ logger = logging.getLogger(__name__)
37
+ router = APIRouter()
38
+
39
+ # Request/Response Models
40
+ class ProviderConfigRequest(BaseModel):
41
+ """Request model for provider configuration"""
42
+ provider_name: str = Field(..., description="Provider name (e.g., 'openai', 'replicate')")
43
+ config_data: Dict[str, Any] = Field(..., description="Provider configuration data")
44
+ is_active: bool = Field(True, description="Whether the provider is active")
45
+
46
+ class EnvironmentConfigRequest(BaseModel):
47
+ """Request model for environment configuration"""
48
+ environment: str = Field(..., description="Environment name (e.g., 'development', 'production')")
49
+ config_data: Dict[str, Any] = Field(..., description="Environment configuration data")
50
+
51
+ class ConfigValidationRequest(BaseModel):
52
+ """Request model for configuration validation"""
53
+ config_type: str = Field(..., description="Type of configuration to validate")
54
+ config_data: Dict[str, Any] = Field(..., description="Configuration data to validate")
55
+
56
+ class ConfigBackupRequest(BaseModel):
57
+ """Request model for configuration backup"""
58
+ backup_name: str = Field(..., description="Name for the backup")
59
+ include_secrets: bool = Field(False, description="Whether to include sensitive data")
60
+ components: Optional[List[str]] = Field(None, description="Specific components to backup")
61
+
62
+ class ConfigResponse(BaseModel):
63
+ """Response model for configuration operations"""
64
+ success: bool
65
+ message: str
66
+ data: Optional[Dict[str, Any]] = None
67
+ error: Optional[str] = None
68
+
69
+ # Global config manager instance
70
+ config_manager = ConfigManager()
71
+
72
+ @router.get("/health")
73
+ async def config_health():
74
+ """Health check for configuration service"""
75
+ try:
76
+ health_status = config_manager.get_health_status()
77
+ return {
78
+ "status": "healthy",
79
+ "service": "configuration",
80
+ "components": health_status
81
+ }
82
+ except Exception as e:
83
+ logger.error(f"Config health check failed: {e}")
84
+ return {
85
+ "status": "error",
86
+ "service": "configuration",
87
+ "error": str(e)
88
+ }
89
+
90
+ @router.get("/providers")
91
+ async def get_all_providers(
92
+ include_inactive: bool = Query(False, description="Include inactive providers"),
93
+ user = Depends(require_read_access)
94
+ ):
95
+ """
96
+ Get all provider configurations
97
+ """
98
+ try:
99
+ providers = config_manager.get_all_providers(include_inactive=include_inactive)
100
+
101
+ return {
102
+ "success": True,
103
+ "providers": providers,
104
+ "total_count": len(providers)
105
+ }
106
+
107
+ except Exception as e:
108
+ logger.error(f"Failed to get providers: {e}")
109
+ raise HTTPException(status_code=500, detail=f"Failed to get providers: {str(e)}")
110
+
111
+ @router.get("/providers/{provider_name}")
112
+ async def get_provider_config(
113
+ provider_name: str,
114
+ mask_secrets: bool = Query(True, description="Whether to mask sensitive data"),
115
+ user = Depends(require_read_access)
116
+ ):
117
+ """
118
+ Get configuration for a specific provider
119
+ """
120
+ try:
121
+ config = config_manager.get_provider_config(
122
+ provider_name=provider_name,
123
+ mask_secrets=mask_secrets
124
+ )
125
+
126
+ if not config:
127
+ raise HTTPException(status_code=404, detail=f"Provider not found: {provider_name}")
128
+
129
+ return {
130
+ "success": True,
131
+ "provider_name": provider_name,
132
+ "config": config,
133
+ "masked_secrets": mask_secrets
134
+ }
135
+
136
+ except HTTPException:
137
+ raise
138
+ except Exception as e:
139
+ logger.error(f"Failed to get provider config for {provider_name}: {e}")
140
+ raise HTTPException(status_code=500, detail=f"Failed to get provider config: {str(e)}")
141
+
142
+ @router.put("/providers/{provider_name}")
143
+ async def update_provider_config(
144
+ provider_name: str,
145
+ request: ProviderConfigRequest,
146
+ user = Depends(require_write_access)
147
+ ):
148
+ """
149
+ Update configuration for a specific provider
150
+ """
151
+ try:
152
+ # Validate configuration first
153
+ validation_result = config_manager.validate_provider_config(
154
+ provider_name=provider_name,
155
+ config_data=request.config_data
156
+ )
157
+
158
+ if not validation_result["valid"]:
159
+ return ConfigResponse(
160
+ success=False,
161
+ message="Configuration validation failed",
162
+ error=validation_result.get("error"),
163
+ data={"validation_errors": validation_result.get("errors", [])}
164
+ )
165
+
166
+ # Update configuration
167
+ success = config_manager.update_provider_config(
168
+ provider_name=provider_name,
169
+ config_data=request.config_data,
170
+ is_active=request.is_active,
171
+ updated_by=user.get("user_id") if user else None
172
+ )
173
+
174
+ if not success:
175
+ raise HTTPException(status_code=500, detail="Failed to update provider configuration")
176
+
177
+ return ConfigResponse(
178
+ success=True,
179
+ message=f"Provider {provider_name} configuration updated successfully",
180
+ data={
181
+ "provider_name": provider_name,
182
+ "is_active": request.is_active,
183
+ "config_keys": list(request.config_data.keys())
184
+ }
185
+ )
186
+
187
+ except HTTPException:
188
+ raise
189
+ except Exception as e:
190
+ logger.error(f"Failed to update provider config for {provider_name}: {e}")
191
+ raise HTTPException(status_code=500, detail=f"Failed to update provider config: {str(e)}")
192
+
193
+ @router.post("/providers/{provider_name}/test")
194
+ async def test_provider_config(
195
+ provider_name: str,
196
+ config_data: Optional[Dict[str, Any]] = None,
197
+ user = Depends(require_read_access)
198
+ ):
199
+ """
200
+ Test provider configuration connectivity
201
+ """
202
+ try:
203
+ # Use provided config or get existing config
204
+ test_config = config_data
205
+ if not test_config:
206
+ test_config = config_manager.get_provider_config(provider_name, mask_secrets=False)
207
+ if not test_config:
208
+ raise HTTPException(status_code=404, detail=f"Provider not found: {provider_name}")
209
+
210
+ # Test the configuration
211
+ test_result = config_manager.test_provider_connection(
212
+ provider_name=provider_name,
213
+ config_data=test_config
214
+ )
215
+
216
+ return {
217
+ "success": test_result["success"],
218
+ "provider_name": provider_name,
219
+ "test_result": test_result,
220
+ "message": test_result.get("message", "")
221
+ }
222
+
223
+ except HTTPException:
224
+ raise
225
+ except Exception as e:
226
+ logger.error(f"Failed to test provider config for {provider_name}: {e}")
227
+ raise HTTPException(status_code=500, detail=f"Failed to test provider config: {str(e)}")
228
+
229
+ @router.delete("/providers/{provider_name}")
230
+ async def delete_provider_config(
231
+ provider_name: str,
232
+ user = Depends(require_write_access)
233
+ ):
234
+ """
235
+ Delete provider configuration
236
+ """
237
+ try:
238
+ success = config_manager.delete_provider_config(
239
+ provider_name=provider_name,
240
+ deleted_by=user.get("user_id") if user else None
241
+ )
242
+
243
+ if not success:
244
+ raise HTTPException(status_code=404, detail=f"Provider not found: {provider_name}")
245
+
246
+ return {
247
+ "success": True,
248
+ "message": f"Provider {provider_name} configuration deleted successfully"
249
+ }
250
+
251
+ except HTTPException:
252
+ raise
253
+ except Exception as e:
254
+ logger.error(f"Failed to delete provider config for {provider_name}: {e}")
255
+ raise HTTPException(status_code=500, detail=f"Failed to delete provider config: {str(e)}")
256
+
257
+ @router.get("/environments")
258
+ async def get_all_environments(user = Depends(require_read_access)):
259
+ """
260
+ Get all environment configurations
261
+ """
262
+ try:
263
+ environments = config_manager.get_all_environments()
264
+
265
+ return {
266
+ "success": True,
267
+ "environments": environments,
268
+ "total_count": len(environments)
269
+ }
270
+
271
+ except Exception as e:
272
+ logger.error(f"Failed to get environments: {e}")
273
+ raise HTTPException(status_code=500, detail=f"Failed to get environments: {str(e)}")
274
+
275
+ @router.get("/environments/{environment}")
276
+ async def get_environment_config(
277
+ environment: str,
278
+ mask_secrets: bool = Query(True, description="Whether to mask sensitive data"),
279
+ user = Depends(require_read_access)
280
+ ):
281
+ """
282
+ Get configuration for a specific environment
283
+ """
284
+ try:
285
+ config = config_manager.get_environment_config(
286
+ environment=environment,
287
+ mask_secrets=mask_secrets
288
+ )
289
+
290
+ if not config:
291
+ raise HTTPException(status_code=404, detail=f"Environment not found: {environment}")
292
+
293
+ return {
294
+ "success": True,
295
+ "environment": environment,
296
+ "config": config,
297
+ "masked_secrets": mask_secrets
298
+ }
299
+
300
+ except HTTPException:
301
+ raise
302
+ except Exception as e:
303
+ logger.error(f"Failed to get environment config for {environment}: {e}")
304
+ raise HTTPException(status_code=500, detail=f"Failed to get environment config: {str(e)}")
305
+
306
+ @router.put("/environments/{environment}")
307
+ async def update_environment_config(
308
+ environment: str,
309
+ request: EnvironmentConfigRequest,
310
+ user = Depends(require_write_access)
311
+ ):
312
+ """
313
+ Update configuration for a specific environment
314
+ """
315
+ try:
316
+ # Validate environment configuration
317
+ validation_result = config_manager.validate_environment_config(
318
+ environment=environment,
319
+ config_data=request.config_data
320
+ )
321
+
322
+ if not validation_result["valid"]:
323
+ return ConfigResponse(
324
+ success=False,
325
+ message="Environment configuration validation failed",
326
+ error=validation_result.get("error"),
327
+ data={"validation_errors": validation_result.get("errors", [])}
328
+ )
329
+
330
+ # Update configuration
331
+ success = config_manager.update_environment_config(
332
+ environment=environment,
333
+ config_data=request.config_data,
334
+ updated_by=user.get("user_id") if user else None
335
+ )
336
+
337
+ if not success:
338
+ raise HTTPException(status_code=500, detail="Failed to update environment configuration")
339
+
340
+ return ConfigResponse(
341
+ success=True,
342
+ message=f"Environment {environment} configuration updated successfully",
343
+ data={
344
+ "environment": environment,
345
+ "config_keys": list(request.config_data.keys())
346
+ }
347
+ )
348
+
349
+ except HTTPException:
350
+ raise
351
+ except Exception as e:
352
+ logger.error(f"Failed to update environment config for {environment}: {e}")
353
+ raise HTTPException(status_code=500, detail=f"Failed to update environment config: {str(e)}")
354
+
355
+ @router.post("/validate")
356
+ async def validate_configuration(
357
+ request: ConfigValidationRequest,
358
+ user = Depends(require_read_access)
359
+ ):
360
+ """
361
+ Validate configuration data
362
+ """
363
+ try:
364
+ if request.config_type == "provider":
365
+ # Extract provider name from config data or use a default validation
366
+ provider_name = request.config_data.get("provider_name", "generic")
367
+ validation_result = config_manager.validate_provider_config(
368
+ provider_name=provider_name,
369
+ config_data=request.config_data
370
+ )
371
+ elif request.config_type == "environment":
372
+ environment = request.config_data.get("environment", "generic")
373
+ validation_result = config_manager.validate_environment_config(
374
+ environment=environment,
375
+ config_data=request.config_data
376
+ )
377
+ else:
378
+ # Generic validation
379
+ validation_result = config_manager.validate_generic_config(
380
+ config_type=request.config_type,
381
+ config_data=request.config_data
382
+ )
383
+
384
+ return {
385
+ "success": True,
386
+ "config_type": request.config_type,
387
+ "validation_result": validation_result
388
+ }
389
+
390
+ except Exception as e:
391
+ logger.error(f"Failed to validate configuration: {e}")
392
+ raise HTTPException(status_code=500, detail=f"Failed to validate configuration: {str(e)}")
393
+
394
+ @router.get("/settings")
395
+ async def get_system_settings(
396
+ category: Optional[str] = Query(None, description="Filter by settings category"),
397
+ user = Depends(require_read_access)
398
+ ):
399
+ """
400
+ Get system settings
401
+ """
402
+ try:
403
+ settings = config_manager.get_system_settings(category=category)
404
+
405
+ return {
406
+ "success": True,
407
+ "settings": settings,
408
+ "category": category
409
+ }
410
+
411
+ except Exception as e:
412
+ logger.error(f"Failed to get system settings: {e}")
413
+ raise HTTPException(status_code=500, detail=f"Failed to get system settings: {str(e)}")
414
+
415
+ @router.put("/settings/{setting_key}")
416
+ async def update_system_setting(
417
+ setting_key: str,
418
+ setting_value: Any,
419
+ user = Depends(require_write_access)
420
+ ):
421
+ """
422
+ Update a specific system setting
423
+ """
424
+ try:
425
+ success = config_manager.update_system_setting(
426
+ setting_key=setting_key,
427
+ setting_value=setting_value,
428
+ updated_by=user.get("user_id") if user else None
429
+ )
430
+
431
+ if not success:
432
+ raise HTTPException(status_code=500, detail="Failed to update system setting")
433
+
434
+ return {
435
+ "success": True,
436
+ "message": f"System setting {setting_key} updated successfully",
437
+ "setting_key": setting_key,
438
+ "setting_value": setting_value
439
+ }
440
+
441
+ except HTTPException:
442
+ raise
443
+ except Exception as e:
444
+ logger.error(f"Failed to update system setting {setting_key}: {e}")
445
+ raise HTTPException(status_code=500, detail=f"Failed to update system setting: {str(e)}")
446
+
447
+ @router.post("/backup")
448
+ async def create_configuration_backup(
449
+ request: ConfigBackupRequest,
450
+ user = Depends(require_write_access)
451
+ ):
452
+ """
453
+ Create a backup of configuration data
454
+ """
455
+ try:
456
+ backup_result = config_manager.create_backup(
457
+ backup_name=request.backup_name,
458
+ include_secrets=request.include_secrets,
459
+ components=request.components,
460
+ created_by=user.get("user_id") if user else None
461
+ )
462
+
463
+ return {
464
+ "success": backup_result["success"],
465
+ "message": backup_result.get("message", "Backup created successfully"),
466
+ "backup_info": {
467
+ "backup_name": request.backup_name,
468
+ "backup_id": backup_result.get("backup_id"),
469
+ "created_at": backup_result.get("created_at"),
470
+ "include_secrets": request.include_secrets,
471
+ "components": request.components or "all"
472
+ }
473
+ }
474
+
475
+ except Exception as e:
476
+ logger.error(f"Failed to create configuration backup: {e}")
477
+ raise HTTPException(status_code=500, detail=f"Failed to create backup: {str(e)}")
478
+
479
+ @router.get("/backups")
480
+ async def list_configuration_backups(
481
+ limit: int = Query(50, ge=1, le=200, description="Maximum number of backups to return"),
482
+ user = Depends(require_read_access)
483
+ ):
484
+ """
485
+ List available configuration backups
486
+ """
487
+ try:
488
+ backups = config_manager.list_backups(limit=limit)
489
+
490
+ return {
491
+ "success": True,
492
+ "backups": backups,
493
+ "total_count": len(backups)
494
+ }
495
+
496
+ except Exception as e:
497
+ logger.error(f"Failed to list configuration backups: {e}")
498
+ raise HTTPException(status_code=500, detail=f"Failed to list backups: {str(e)}")
499
+
500
+ @router.post("/restore/{backup_id}")
501
+ async def restore_configuration_backup(
502
+ backup_id: str,
503
+ components: Optional[List[str]] = None,
504
+ dry_run: bool = Query(False, description="Perform a dry run without making changes"),
505
+ user = Depends(require_write_access)
506
+ ):
507
+ """
508
+ Restore configuration from a backup
509
+ """
510
+ try:
511
+ restore_result = config_manager.restore_backup(
512
+ backup_id=backup_id,
513
+ components=components,
514
+ dry_run=dry_run,
515
+ restored_by=user.get("user_id") if user else None
516
+ )
517
+
518
+ return {
519
+ "success": restore_result["success"],
520
+ "message": restore_result.get("message", "Configuration restored successfully"),
521
+ "restore_info": {
522
+ "backup_id": backup_id,
523
+ "components": components or "all",
524
+ "dry_run": dry_run,
525
+ "changes_applied": restore_result.get("changes_applied", [])
526
+ }
527
+ }
528
+
529
+ except Exception as e:
530
+ logger.error(f"Failed to restore configuration backup {backup_id}: {e}")
531
+ raise HTTPException(status_code=500, detail=f"Failed to restore backup: {str(e)}")
532
+
533
+ @router.get("/export")
534
+ async def export_configuration(
535
+ format_type: str = Query("json", description="Export format (json, yaml)"),
536
+ components: Optional[List[str]] = Query(None, description="Specific components to export"),
537
+ mask_secrets: bool = Query(True, description="Whether to mask sensitive data"),
538
+ user = Depends(require_read_access)
539
+ ):
540
+ """
541
+ Export configuration data
542
+ """
543
+ try:
544
+ export_result = config_manager.export_configuration(
545
+ format_type=format_type,
546
+ components=components,
547
+ mask_secrets=mask_secrets
548
+ )
549
+
550
+ return {
551
+ "success": True,
552
+ "export_format": format_type,
553
+ "components": components or "all",
554
+ "masked_secrets": mask_secrets,
555
+ "data": export_result["data"],
556
+ "metadata": export_result.get("metadata", {})
557
+ }
558
+
559
+ except Exception as e:
560
+ logger.error(f"Failed to export configuration: {e}")
561
+ raise HTTPException(status_code=500, detail=f"Failed to export configuration: {str(e)}")
562
+
563
+ @router.post("/import")
564
+ async def import_configuration(
565
+ import_data: Dict[str, Any],
566
+ merge_strategy: str = Query("merge", description="Import strategy (merge, replace)"),
567
+ validate_only: bool = Query(False, description="Only validate without importing"),
568
+ user = Depends(require_write_access)
569
+ ):
570
+ """
571
+ Import configuration data
572
+ """
573
+ try:
574
+ import_result = config_manager.import_configuration(
575
+ import_data=import_data,
576
+ merge_strategy=merge_strategy,
577
+ validate_only=validate_only,
578
+ imported_by=user.get("user_id") if user else None
579
+ )
580
+
581
+ return {
582
+ "success": import_result["success"],
583
+ "message": import_result.get("message", "Configuration imported successfully"),
584
+ "import_info": {
585
+ "merge_strategy": merge_strategy,
586
+ "validate_only": validate_only,
587
+ "changes_applied": import_result.get("changes_applied", []),
588
+ "validation_errors": import_result.get("validation_errors", [])
589
+ }
590
+ }
591
+
592
+ except Exception as e:
593
+ logger.error(f"Failed to import configuration: {e}")
594
+ raise HTTPException(status_code=500, detail=f"Failed to import configuration: {str(e)}")
595
+
596
+ @router.get("/audit")
597
+ async def get_configuration_audit_log(
598
+ component: Optional[str] = Query(None, description="Filter by component"),
599
+ action: Optional[str] = Query(None, description="Filter by action type"),
600
+ user_id: Optional[str] = Query(None, description="Filter by user ID"),
601
+ start_date: Optional[str] = Query(None, description="Start date (ISO format)"),
602
+ end_date: Optional[str] = Query(None, description="End date (ISO format)"),
603
+ limit: int = Query(100, ge=1, le=1000, description="Maximum number of records"),
604
+ user = Depends(require_read_access)
605
+ ):
606
+ """
607
+ Get configuration audit log
608
+ """
609
+ try:
610
+ # Parse dates if provided
611
+ from datetime import datetime
612
+ start_dt = None
613
+ end_dt = None
614
+
615
+ if start_date:
616
+ start_dt = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
617
+ if end_date:
618
+ end_dt = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
619
+
620
+ audit_log = config_manager.get_audit_log(
621
+ component=component,
622
+ action=action,
623
+ user_id=user_id,
624
+ start_date=start_dt,
625
+ end_date=end_dt,
626
+ limit=limit
627
+ )
628
+
629
+ return {
630
+ "success": True,
631
+ "audit_log": audit_log,
632
+ "filters": {
633
+ "component": component,
634
+ "action": action,
635
+ "user_id": user_id,
636
+ "start_date": start_date,
637
+ "end_date": end_date,
638
+ "limit": limit
639
+ },
640
+ "total_records": len(audit_log)
641
+ }
642
+
643
+ except Exception as e:
644
+ logger.error(f"Failed to get configuration audit log: {e}")
645
+ raise HTTPException(status_code=500, detail=f"Failed to get audit log: {str(e)}")