isa-model 0.4.0__py3-none-any.whl → 0.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- isa_model/client.py +466 -43
- isa_model/core/cache/redis_cache.py +12 -3
- isa_model/core/config/config_manager.py +230 -3
- isa_model/core/config.py +90 -0
- isa_model/core/database/direct_db_client.py +114 -0
- isa_model/core/database/migration_manager.py +563 -0
- isa_model/core/database/migrations.py +21 -1
- isa_model/core/database/supabase_client.py +154 -19
- isa_model/core/dependencies.py +316 -0
- isa_model/core/discovery/__init__.py +19 -0
- isa_model/core/discovery/consul_discovery.py +190 -0
- isa_model/core/logging/__init__.py +54 -0
- isa_model/core/logging/influx_logger.py +523 -0
- isa_model/core/logging/loki_logger.py +160 -0
- isa_model/core/models/__init__.py +27 -18
- isa_model/core/models/config_models.py +625 -0
- isa_model/core/models/deployment_billing_tracker.py +430 -0
- isa_model/core/models/model_manager.py +35 -80
- isa_model/core/models/model_metadata.py +690 -0
- isa_model/core/models/model_repo.py +174 -18
- isa_model/core/models/system_models.py +857 -0
- isa_model/core/repositories/__init__.py +9 -0
- isa_model/core/repositories/config_repository.py +912 -0
- isa_model/core/services/intelligent_model_selector.py +399 -21
- isa_model/core/types.py +1 -0
- isa_model/deployment/__init__.py +5 -48
- isa_model/deployment/core/__init__.py +2 -31
- isa_model/deployment/core/deployment_manager.py +1278 -370
- isa_model/deployment/modal/__init__.py +8 -0
- isa_model/deployment/modal/config.py +136 -0
- isa_model/deployment/{services/auto_hf_modal_deployer.py → modal/deployer.py} +1 -1
- isa_model/deployment/modal/services/__init__.py +3 -0
- isa_model/deployment/modal/services/audio/__init__.py +1 -0
- isa_model/deployment/modal/services/embedding/__init__.py +1 -0
- isa_model/deployment/modal/services/llm/__init__.py +1 -0
- isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
- isa_model/deployment/modal/services/video/__init__.py +1 -0
- isa_model/deployment/modal/services/vision/__init__.py +1 -0
- isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
- isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
- isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
- isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
- isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
- isa_model/deployment/storage/__init__.py +5 -0
- isa_model/deployment/storage/deployment_repository.py +824 -0
- isa_model/deployment/triton/__init__.py +10 -0
- isa_model/deployment/triton/config.py +196 -0
- isa_model/deployment/triton/configs/__init__.py +1 -0
- isa_model/deployment/triton/provider.py +512 -0
- isa_model/deployment/triton/scripts/__init__.py +1 -0
- isa_model/deployment/triton/templates/__init__.py +1 -0
- isa_model/inference/__init__.py +47 -1
- isa_model/inference/ai_factory.py +137 -10
- isa_model/inference/legacy_services/__init__.py +21 -0
- isa_model/inference/legacy_services/model_evaluation.py +637 -0
- isa_model/inference/legacy_services/model_service.py +573 -0
- isa_model/inference/legacy_services/model_serving.py +717 -0
- isa_model/inference/legacy_services/model_training.py +561 -0
- isa_model/inference/models/__init__.py +21 -0
- isa_model/inference/models/inference_config.py +551 -0
- isa_model/inference/models/inference_record.py +675 -0
- isa_model/inference/models/performance_models.py +714 -0
- isa_model/inference/repositories/__init__.py +9 -0
- isa_model/inference/repositories/inference_repository.py +828 -0
- isa_model/inference/services/audio/base_stt_service.py +184 -11
- isa_model/inference/services/audio/openai_stt_service.py +22 -6
- isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
- isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
- isa_model/inference/services/llm/__init__.py +10 -2
- isa_model/inference/services/llm/base_llm_service.py +335 -24
- isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
- isa_model/inference/services/llm/helpers/llm_adapter.py +9 -4
- isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
- isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
- isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
- isa_model/inference/services/llm/ollama_llm_service.py +9 -2
- isa_model/inference/services/llm/openai_llm_service.py +33 -16
- isa_model/inference/services/llm/yyds_llm_service.py +8 -2
- isa_model/inference/services/vision/__init__.py +22 -1
- isa_model/inference/services/vision/helpers/image_utils.py +8 -5
- isa_model/inference/services/vision/isa_vision_service.py +65 -4
- isa_model/inference/services/vision/openai_vision_service.py +19 -10
- isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
- isa_model/serving/api/cache_manager.py +245 -0
- isa_model/serving/api/dependencies/__init__.py +1 -0
- isa_model/serving/api/dependencies/auth.py +194 -0
- isa_model/serving/api/dependencies/database.py +139 -0
- isa_model/serving/api/error_handlers.py +284 -0
- isa_model/serving/api/fastapi_server.py +172 -22
- isa_model/serving/api/middleware/auth.py +8 -2
- isa_model/serving/api/middleware/security.py +23 -33
- isa_model/serving/api/middleware/tenant_context.py +414 -0
- isa_model/serving/api/routes/analytics.py +4 -1
- isa_model/serving/api/routes/config.py +645 -0
- isa_model/serving/api/routes/deployment_billing.py +315 -0
- isa_model/serving/api/routes/deployments.py +138 -2
- isa_model/serving/api/routes/gpu_gateway.py +440 -0
- isa_model/serving/api/routes/health.py +32 -12
- isa_model/serving/api/routes/inference_monitoring.py +486 -0
- isa_model/serving/api/routes/local_deployments.py +448 -0
- isa_model/serving/api/routes/tenants.py +575 -0
- isa_model/serving/api/routes/unified.py +680 -18
- isa_model/serving/api/routes/webhooks.py +479 -0
- isa_model/serving/api/startup.py +68 -54
- isa_model/utils/gpu_utils.py +311 -0
- {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/METADATA +71 -24
- isa_model-0.4.4.dist-info/RECORD +180 -0
- isa_model/core/security/secrets.py +0 -358
- isa_model/core/storage/hf_storage.py +0 -419
- isa_model/core/storage/minio_storage.py +0 -0
- isa_model/deployment/cloud/__init__.py +0 -9
- isa_model/deployment/cloud/modal/__init__.py +0 -10
- isa_model/deployment/core/deployment_config.py +0 -356
- isa_model/deployment/core/isa_deployment_service.py +0 -401
- isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
- isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
- isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
- isa_model/deployment/runtime/deployed_service.py +0 -338
- isa_model/deployment/services/__init__.py +0 -9
- isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
- isa_model/deployment/services/model_service.py +0 -332
- isa_model/deployment/services/service_monitor.py +0 -356
- isa_model/deployment/services/service_registry.py +0 -527
- isa_model/eval/__init__.py +0 -92
- isa_model/eval/benchmarks/__init__.py +0 -27
- isa_model/eval/benchmarks/multimodal_datasets.py +0 -460
- isa_model/eval/benchmarks.py +0 -701
- isa_model/eval/config/__init__.py +0 -10
- isa_model/eval/config/evaluation_config.py +0 -108
- isa_model/eval/evaluators/__init__.py +0 -24
- isa_model/eval/evaluators/audio_evaluator.py +0 -727
- isa_model/eval/evaluators/base_evaluator.py +0 -503
- isa_model/eval/evaluators/embedding_evaluator.py +0 -742
- isa_model/eval/evaluators/llm_evaluator.py +0 -472
- isa_model/eval/evaluators/vision_evaluator.py +0 -564
- isa_model/eval/example_evaluation.py +0 -395
- isa_model/eval/factory.py +0 -798
- isa_model/eval/infrastructure/__init__.py +0 -24
- isa_model/eval/infrastructure/experiment_tracker.py +0 -466
- isa_model/eval/isa_benchmarks.py +0 -700
- isa_model/eval/isa_integration.py +0 -582
- isa_model/eval/metrics.py +0 -951
- isa_model/eval/tests/unit/test_basic.py +0 -396
- isa_model/serving/api/routes/evaluations.py +0 -579
- isa_model/training/__init__.py +0 -168
- isa_model/training/annotation/annotation_schema.py +0 -47
- isa_model/training/annotation/processors/annotation_processor.py +0 -126
- isa_model/training/annotation/storage/dataset_manager.py +0 -131
- isa_model/training/annotation/storage/dataset_schema.py +0 -44
- isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
- isa_model/training/annotation/tests/test_minio copy.py +0 -113
- isa_model/training/annotation/tests/test_minio_upload.py +0 -43
- isa_model/training/annotation/views/annotation_controller.py +0 -158
- isa_model/training/cloud/__init__.py +0 -22
- isa_model/training/cloud/job_orchestrator.py +0 -402
- isa_model/training/cloud/runpod_trainer.py +0 -454
- isa_model/training/cloud/storage_manager.py +0 -482
- isa_model/training/core/__init__.py +0 -26
- isa_model/training/core/config.py +0 -181
- isa_model/training/core/dataset.py +0 -222
- isa_model/training/core/trainer.py +0 -720
- isa_model/training/core/utils.py +0 -213
- isa_model/training/examples/intelligent_training_example.py +0 -281
- isa_model/training/factory.py +0 -424
- isa_model/training/intelligent/__init__.py +0 -25
- isa_model/training/intelligent/decision_engine.py +0 -643
- isa_model/training/intelligent/intelligent_factory.py +0 -888
- isa_model/training/intelligent/knowledge_base.py +0 -751
- isa_model/training/intelligent/resource_optimizer.py +0 -839
- isa_model/training/intelligent/task_classifier.py +0 -576
- isa_model/training/storage/__init__.py +0 -24
- isa_model/training/storage/core_integration.py +0 -439
- isa_model/training/storage/training_repository.py +0 -552
- isa_model/training/storage/training_storage.py +0 -628
- isa_model-0.4.0.dist-info/RECORD +0 -182
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_chatTTS_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_fish_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_openvoice_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_service_v2.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/embedding}/isa_embed_rerank_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/video}/isa_video_hunyuan_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ocr_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_qwen25_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_table_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service.py +0 -0
- /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service_optimized.py +0 -0
- /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
- {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/WHEEL +0 -0
- {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/top_level.txt +0 -0
@@ -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)}")
|