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
@@ -1,37 +1,46 @@
|
|
1
1
|
"""
|
2
|
-
Core Models
|
2
|
+
Core Models
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
This module includes:
|
7
|
-
- ModelRegistry: Central model registry and metadata management
|
8
|
-
- ModelManager: High-level model lifecycle management
|
9
|
-
- ModelVersionManager: Version control and lineage tracking
|
10
|
-
- ModelBillingTracker: Cost tracking and billing
|
11
|
-
- ModelStatisticsTracker: Usage statistics and analytics
|
4
|
+
Data models for core operations following the ISA Model architecture pattern.
|
12
5
|
"""
|
13
6
|
|
7
|
+
# Configuration models
|
8
|
+
from .config_models import ConfigRecord, ProviderConfig, EnvironmentConfig, ConfigAuditLog
|
9
|
+
|
10
|
+
# Model metadata models
|
11
|
+
from .model_metadata import ModelMetadata, ModelVersion, ModelBilling
|
12
|
+
|
13
|
+
# System monitoring models
|
14
|
+
from .system_models import SystemHealth, ResourceUsage, ServiceStatus
|
15
|
+
|
16
|
+
# Legacy model management (existing)
|
14
17
|
from .model_repo import ModelRegistry, ModelType, ModelCapability
|
15
18
|
from .model_manager import ModelManager
|
16
|
-
from .model_version_manager import ModelVersionManager, ModelVersion, VersionType
|
19
|
+
from .model_version_manager import ModelVersionManager, ModelVersion as LegacyModelVersion, VersionType
|
17
20
|
from .model_billing_tracker import ModelBillingTracker
|
18
21
|
from .model_statistics_tracker import ModelStatisticsTracker
|
19
22
|
|
20
23
|
__all__ = [
|
21
|
-
#
|
24
|
+
# New standardized models
|
25
|
+
"ConfigRecord",
|
26
|
+
"ProviderConfig",
|
27
|
+
"EnvironmentConfig",
|
28
|
+
"ConfigAuditLog",
|
29
|
+
"ModelMetadata",
|
30
|
+
"ModelVersion",
|
31
|
+
"ModelBilling",
|
32
|
+
"SystemHealth",
|
33
|
+
"ResourceUsage",
|
34
|
+
"ServiceStatus",
|
35
|
+
|
36
|
+
# Legacy model management (existing)
|
22
37
|
'ModelRegistry',
|
23
38
|
'ModelType',
|
24
39
|
'ModelCapability',
|
25
|
-
|
26
|
-
# Model management
|
27
40
|
'ModelManager',
|
28
|
-
|
29
|
-
# Version management
|
30
41
|
'ModelVersionManager',
|
31
|
-
'
|
42
|
+
'LegacyModelVersion',
|
32
43
|
'VersionType',
|
33
|
-
|
34
|
-
# Tracking and analytics
|
35
44
|
'ModelBillingTracker',
|
36
45
|
'ModelStatisticsTracker'
|
37
46
|
]
|
@@ -0,0 +1,625 @@
|
|
1
|
+
"""
|
2
|
+
Configuration Models
|
3
|
+
|
4
|
+
Core data models for configuration management, extracted from repository layer
|
5
|
+
to follow the standard ISA Model architecture pattern.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
from datetime import datetime, timezone
|
10
|
+
from typing import Dict, List, Optional, Any, Union
|
11
|
+
from dataclasses import dataclass, field
|
12
|
+
from enum import Enum
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
class ConfigType(str, Enum):
|
17
|
+
"""Configuration type enumeration"""
|
18
|
+
PROVIDER = "provider"
|
19
|
+
ENVIRONMENT = "environment"
|
20
|
+
SYSTEM = "system"
|
21
|
+
USER = "user"
|
22
|
+
API_KEY = "api_key"
|
23
|
+
FEATURE_FLAG = "feature_flag"
|
24
|
+
DEPLOYMENT = "deployment"
|
25
|
+
SECURITY = "security"
|
26
|
+
|
27
|
+
class ConfigStatus(str, Enum):
|
28
|
+
"""Configuration status enumeration"""
|
29
|
+
ACTIVE = "active"
|
30
|
+
INACTIVE = "inactive"
|
31
|
+
DEPRECATED = "deprecated"
|
32
|
+
PENDING = "pending"
|
33
|
+
ARCHIVED = "archived"
|
34
|
+
|
35
|
+
class SecurityLevel(str, Enum):
|
36
|
+
"""Security level enumeration"""
|
37
|
+
PUBLIC = "public"
|
38
|
+
INTERNAL = "internal"
|
39
|
+
CONFIDENTIAL = "confidential"
|
40
|
+
SECRET = "secret"
|
41
|
+
TOP_SECRET = "top_secret"
|
42
|
+
|
43
|
+
@dataclass
|
44
|
+
class ConfigRecord:
|
45
|
+
"""
|
46
|
+
Core configuration record model
|
47
|
+
|
48
|
+
Represents a single configuration entry with metadata, versioning,
|
49
|
+
and audit information for comprehensive configuration management.
|
50
|
+
"""
|
51
|
+
config_id: str
|
52
|
+
config_type: str
|
53
|
+
config_key: str
|
54
|
+
config_value: Any
|
55
|
+
environment: str = "production"
|
56
|
+
is_active: bool = True
|
57
|
+
is_encrypted: bool = False
|
58
|
+
security_level: str = SecurityLevel.INTERNAL
|
59
|
+
version: int = 1
|
60
|
+
previous_version_id: Optional[str] = None
|
61
|
+
created_at: datetime = None
|
62
|
+
updated_at: datetime = None
|
63
|
+
effective_from: Optional[datetime] = None
|
64
|
+
expires_at: Optional[datetime] = None
|
65
|
+
created_by: Optional[str] = None
|
66
|
+
updated_by: Optional[str] = None
|
67
|
+
description: Optional[str] = None
|
68
|
+
tags: Optional[Dict[str, str]] = None
|
69
|
+
metadata: Optional[Dict[str, Any]] = None
|
70
|
+
validation_rules: Optional[Dict[str, Any]] = None
|
71
|
+
change_reason: Optional[str] = None
|
72
|
+
|
73
|
+
def __post_init__(self):
|
74
|
+
if self.created_at is None:
|
75
|
+
self.created_at = datetime.now(timezone.utc)
|
76
|
+
if self.updated_at is None:
|
77
|
+
self.updated_at = self.created_at
|
78
|
+
if self.effective_from is None:
|
79
|
+
self.effective_from = self.created_at
|
80
|
+
if self.tags is None:
|
81
|
+
self.tags = {}
|
82
|
+
if self.metadata is None:
|
83
|
+
self.metadata = {}
|
84
|
+
if self.validation_rules is None:
|
85
|
+
self.validation_rules = {}
|
86
|
+
|
87
|
+
@property
|
88
|
+
def is_effective(self) -> bool:
|
89
|
+
"""Check if configuration is currently effective"""
|
90
|
+
now = datetime.now(timezone.utc)
|
91
|
+
|
92
|
+
if not self.is_active:
|
93
|
+
return False
|
94
|
+
|
95
|
+
if self.effective_from and now < self.effective_from:
|
96
|
+
return False
|
97
|
+
|
98
|
+
if self.expires_at and now > self.expires_at:
|
99
|
+
return False
|
100
|
+
|
101
|
+
return True
|
102
|
+
|
103
|
+
@property
|
104
|
+
def is_expired(self) -> bool:
|
105
|
+
"""Check if configuration has expired"""
|
106
|
+
if not self.expires_at:
|
107
|
+
return False
|
108
|
+
return datetime.now(timezone.utc) > self.expires_at
|
109
|
+
|
110
|
+
@property
|
111
|
+
def is_sensitive(self) -> bool:
|
112
|
+
"""Check if configuration contains sensitive data"""
|
113
|
+
return (self.is_encrypted or
|
114
|
+
self.security_level in [SecurityLevel.CONFIDENTIAL, SecurityLevel.SECRET, SecurityLevel.TOP_SECRET] or
|
115
|
+
any(keyword in self.config_key.lower() for keyword in ['password', 'key', 'secret', 'token']))
|
116
|
+
|
117
|
+
@property
|
118
|
+
def age_days(self) -> int:
|
119
|
+
"""Get configuration age in days"""
|
120
|
+
return (datetime.now(timezone.utc) - self.created_at).days
|
121
|
+
|
122
|
+
def update_value(self, new_value: Any, updated_by: Optional[str] = None,
|
123
|
+
change_reason: Optional[str] = None, increment_version: bool = True):
|
124
|
+
"""Update configuration value with versioning"""
|
125
|
+
if increment_version:
|
126
|
+
self.previous_version_id = f"{self.config_id}_v{self.version}"
|
127
|
+
self.version += 1
|
128
|
+
|
129
|
+
self.config_value = new_value
|
130
|
+
self.updated_at = datetime.now(timezone.utc)
|
131
|
+
|
132
|
+
if updated_by:
|
133
|
+
self.updated_by = updated_by
|
134
|
+
if change_reason:
|
135
|
+
self.change_reason = change_reason
|
136
|
+
|
137
|
+
def add_tag(self, key: str, value: str):
|
138
|
+
"""Add or update a configuration tag"""
|
139
|
+
self.tags[key] = value
|
140
|
+
self.updated_at = datetime.now(timezone.utc)
|
141
|
+
|
142
|
+
def validate(self) -> List[str]:
|
143
|
+
"""Validate configuration according to rules"""
|
144
|
+
issues = []
|
145
|
+
|
146
|
+
if not self.config_key:
|
147
|
+
issues.append("Configuration key is required")
|
148
|
+
|
149
|
+
if self.config_value is None:
|
150
|
+
issues.append("Configuration value cannot be None")
|
151
|
+
|
152
|
+
if self.version < 1:
|
153
|
+
issues.append("Version must be positive")
|
154
|
+
|
155
|
+
# Apply validation rules
|
156
|
+
for rule_name, rule_config in self.validation_rules.items():
|
157
|
+
rule_type = rule_config.get("type")
|
158
|
+
|
159
|
+
if rule_type == "required" and not self.config_value:
|
160
|
+
issues.append(f"Value is required by rule '{rule_name}'")
|
161
|
+
|
162
|
+
elif rule_type == "type_check":
|
163
|
+
expected_type = rule_config.get("expected_type")
|
164
|
+
if expected_type and not isinstance(self.config_value, eval(expected_type)):
|
165
|
+
issues.append(f"Value type mismatch for rule '{rule_name}': expected {expected_type}")
|
166
|
+
|
167
|
+
elif rule_type == "range" and isinstance(self.config_value, (int, float)):
|
168
|
+
min_val = rule_config.get("min")
|
169
|
+
max_val = rule_config.get("max")
|
170
|
+
if min_val is not None and self.config_value < min_val:
|
171
|
+
issues.append(f"Value below minimum for rule '{rule_name}': {min_val}")
|
172
|
+
if max_val is not None and self.config_value > max_val:
|
173
|
+
issues.append(f"Value above maximum for rule '{rule_name}': {max_val}")
|
174
|
+
|
175
|
+
elif rule_type == "enum" and isinstance(self.config_value, str):
|
176
|
+
allowed_values = rule_config.get("allowed_values", [])
|
177
|
+
if allowed_values and self.config_value not in allowed_values:
|
178
|
+
issues.append(f"Value not in allowed list for rule '{rule_name}': {allowed_values}")
|
179
|
+
|
180
|
+
return issues
|
181
|
+
|
182
|
+
def get_masked_value(self) -> Any:
|
183
|
+
"""Get masked version of sensitive configuration values"""
|
184
|
+
if not self.is_sensitive:
|
185
|
+
return self.config_value
|
186
|
+
|
187
|
+
if isinstance(self.config_value, str):
|
188
|
+
if len(self.config_value) > 8:
|
189
|
+
return self.config_value[:4] + "***" + self.config_value[-4:]
|
190
|
+
else:
|
191
|
+
return "***"
|
192
|
+
elif isinstance(self.config_value, dict):
|
193
|
+
masked = {}
|
194
|
+
for key, value in self.config_value.items():
|
195
|
+
if any(keyword in key.lower() for keyword in ['password', 'key', 'secret', 'token']):
|
196
|
+
masked[key] = "***"
|
197
|
+
else:
|
198
|
+
masked[key] = value
|
199
|
+
return masked
|
200
|
+
else:
|
201
|
+
return "***"
|
202
|
+
|
203
|
+
@dataclass
|
204
|
+
class ProviderConfig:
|
205
|
+
"""
|
206
|
+
Provider configuration model
|
207
|
+
|
208
|
+
Contains provider-specific settings, credentials, and operational parameters
|
209
|
+
for external service integrations.
|
210
|
+
"""
|
211
|
+
provider_name: str
|
212
|
+
config_data: Dict[str, Any]
|
213
|
+
is_active: bool = True
|
214
|
+
environment: str = "production"
|
215
|
+
priority: int = 5 # 1-10 scale
|
216
|
+
health_check_enabled: bool = True
|
217
|
+
health_check_interval_seconds: int = 300
|
218
|
+
last_health_check: Optional[datetime] = None
|
219
|
+
health_status: str = "unknown" # healthy, unhealthy, unknown, maintenance
|
220
|
+
rate_limit_config: Optional[Dict[str, Any]] = None
|
221
|
+
retry_config: Optional[Dict[str, Any]] = None
|
222
|
+
timeout_config: Optional[Dict[str, Any]] = None
|
223
|
+
cost_tracking_enabled: bool = True
|
224
|
+
usage_limits: Optional[Dict[str, Any]] = None
|
225
|
+
created_at: datetime = None
|
226
|
+
updated_at: datetime = None
|
227
|
+
created_by: Optional[str] = None
|
228
|
+
updated_by: Optional[str] = None
|
229
|
+
|
230
|
+
def __post_init__(self):
|
231
|
+
if self.created_at is None:
|
232
|
+
self.created_at = datetime.now(timezone.utc)
|
233
|
+
if self.updated_at is None:
|
234
|
+
self.updated_at = self.created_at
|
235
|
+
if self.rate_limit_config is None:
|
236
|
+
self.rate_limit_config = {}
|
237
|
+
if self.retry_config is None:
|
238
|
+
self.retry_config = {"max_retries": 3, "backoff_factor": 2}
|
239
|
+
if self.timeout_config is None:
|
240
|
+
self.timeout_config = {"default_timeout": 300}
|
241
|
+
if self.usage_limits is None:
|
242
|
+
self.usage_limits = {}
|
243
|
+
|
244
|
+
@property
|
245
|
+
def is_healthy(self) -> bool:
|
246
|
+
"""Check if provider is currently healthy"""
|
247
|
+
return self.health_status == "healthy" and self.is_active
|
248
|
+
|
249
|
+
@property
|
250
|
+
def time_since_last_check(self) -> Optional[int]:
|
251
|
+
"""Get seconds since last health check"""
|
252
|
+
if self.last_health_check:
|
253
|
+
return int((datetime.now(timezone.utc) - self.last_health_check).total_seconds())
|
254
|
+
return None
|
255
|
+
|
256
|
+
@property
|
257
|
+
def needs_health_check(self) -> bool:
|
258
|
+
"""Check if health check is due"""
|
259
|
+
if not self.health_check_enabled:
|
260
|
+
return False
|
261
|
+
|
262
|
+
if not self.last_health_check:
|
263
|
+
return True
|
264
|
+
|
265
|
+
time_since = self.time_since_last_check
|
266
|
+
return time_since is None or time_since >= self.health_check_interval_seconds
|
267
|
+
|
268
|
+
def get_config_value(self, key: str, default: Any = None) -> Any:
|
269
|
+
"""Get configuration value with default fallback"""
|
270
|
+
return self.config_data.get(key, default)
|
271
|
+
|
272
|
+
def update_config_value(self, key: str, value: Any, updated_by: Optional[str] = None):
|
273
|
+
"""Update specific configuration value"""
|
274
|
+
self.config_data[key] = value
|
275
|
+
self.updated_at = datetime.now(timezone.utc)
|
276
|
+
if updated_by:
|
277
|
+
self.updated_by = updated_by
|
278
|
+
|
279
|
+
def update_health_status(self, status: str, check_time: Optional[datetime] = None):
|
280
|
+
"""Update health status with timestamp"""
|
281
|
+
self.health_status = status
|
282
|
+
self.last_health_check = check_time or datetime.now(timezone.utc)
|
283
|
+
self.updated_at = self.last_health_check
|
284
|
+
|
285
|
+
def validate_config(self) -> List[str]:
|
286
|
+
"""Validate provider configuration"""
|
287
|
+
issues = []
|
288
|
+
|
289
|
+
if not self.provider_name:
|
290
|
+
issues.append("Provider name is required")
|
291
|
+
|
292
|
+
if not self.config_data:
|
293
|
+
issues.append("Configuration data is required")
|
294
|
+
|
295
|
+
# Provider-specific validation
|
296
|
+
if self.provider_name.lower() == "openai":
|
297
|
+
if "api_key" not in self.config_data:
|
298
|
+
issues.append("OpenAI API key is required")
|
299
|
+
if "base_url" not in self.config_data:
|
300
|
+
self.config_data["base_url"] = "https://api.openai.com/v1"
|
301
|
+
|
302
|
+
elif self.provider_name.lower() == "anthropic":
|
303
|
+
if "api_key" not in self.config_data:
|
304
|
+
issues.append("Anthropic API key is required")
|
305
|
+
if "base_url" not in self.config_data:
|
306
|
+
self.config_data["base_url"] = "https://api.anthropic.com"
|
307
|
+
|
308
|
+
elif self.provider_name.lower() == "replicate":
|
309
|
+
if "api_token" not in self.config_data:
|
310
|
+
issues.append("Replicate API token is required")
|
311
|
+
|
312
|
+
# Validate numeric configurations
|
313
|
+
if self.priority < 1 or self.priority > 10:
|
314
|
+
issues.append("Priority must be between 1 and 10")
|
315
|
+
|
316
|
+
if self.health_check_interval_seconds < 60:
|
317
|
+
issues.append("Health check interval must be at least 60 seconds")
|
318
|
+
|
319
|
+
return issues
|
320
|
+
|
321
|
+
def get_masked_config(self) -> Dict[str, Any]:
|
322
|
+
"""Get configuration with sensitive values masked"""
|
323
|
+
masked_config = {}
|
324
|
+
sensitive_keys = ['api_key', 'api_token', 'secret', 'password', 'private_key']
|
325
|
+
|
326
|
+
for key, value in self.config_data.items():
|
327
|
+
if any(sensitive_key in key.lower() for sensitive_key in sensitive_keys):
|
328
|
+
if isinstance(value, str) and len(value) > 8:
|
329
|
+
masked_config[key] = value[:4] + "***" + value[-4:]
|
330
|
+
else:
|
331
|
+
masked_config[key] = "***"
|
332
|
+
else:
|
333
|
+
masked_config[key] = value
|
334
|
+
|
335
|
+
return masked_config
|
336
|
+
|
337
|
+
@dataclass
|
338
|
+
class EnvironmentConfig:
|
339
|
+
"""
|
340
|
+
Environment configuration model
|
341
|
+
|
342
|
+
Manages environment-specific settings and configurations for different
|
343
|
+
deployment environments (development, staging, production, etc.).
|
344
|
+
"""
|
345
|
+
environment: str
|
346
|
+
config_data: Dict[str, Any]
|
347
|
+
is_active: bool = True
|
348
|
+
is_default: bool = False
|
349
|
+
deployment_settings: Optional[Dict[str, Any]] = None
|
350
|
+
resource_limits: Optional[Dict[str, Any]] = None
|
351
|
+
feature_flags: Optional[Dict[str, bool]] = None
|
352
|
+
monitoring_config: Optional[Dict[str, Any]] = None
|
353
|
+
security_settings: Optional[Dict[str, Any]] = None
|
354
|
+
created_at: datetime = None
|
355
|
+
updated_at: datetime = None
|
356
|
+
created_by: Optional[str] = None
|
357
|
+
updated_by: Optional[str] = None
|
358
|
+
|
359
|
+
def __post_init__(self):
|
360
|
+
if self.created_at is None:
|
361
|
+
self.created_at = datetime.now(timezone.utc)
|
362
|
+
if self.updated_at is None:
|
363
|
+
self.updated_at = self.created_at
|
364
|
+
if self.deployment_settings is None:
|
365
|
+
self.deployment_settings = {}
|
366
|
+
if self.resource_limits is None:
|
367
|
+
self.resource_limits = {}
|
368
|
+
if self.feature_flags is None:
|
369
|
+
self.feature_flags = {}
|
370
|
+
if self.monitoring_config is None:
|
371
|
+
self.monitoring_config = {}
|
372
|
+
if self.security_settings is None:
|
373
|
+
self.security_settings = {}
|
374
|
+
|
375
|
+
@property
|
376
|
+
def environment_type(self) -> str:
|
377
|
+
"""Classify environment type"""
|
378
|
+
env_lower = self.environment.lower()
|
379
|
+
|
380
|
+
if any(keyword in env_lower for keyword in ['prod', 'production']):
|
381
|
+
return "production"
|
382
|
+
elif any(keyword in env_lower for keyword in ['stag', 'staging']):
|
383
|
+
return "staging"
|
384
|
+
elif any(keyword in env_lower for keyword in ['dev', 'development']):
|
385
|
+
return "development"
|
386
|
+
elif any(keyword in env_lower for keyword in ['test', 'testing']):
|
387
|
+
return "testing"
|
388
|
+
else:
|
389
|
+
return "custom"
|
390
|
+
|
391
|
+
@property
|
392
|
+
def is_production(self) -> bool:
|
393
|
+
"""Check if this is a production environment"""
|
394
|
+
return self.environment_type == "production"
|
395
|
+
|
396
|
+
@property
|
397
|
+
def security_level(self) -> str:
|
398
|
+
"""Get security level based on environment type"""
|
399
|
+
if self.is_production:
|
400
|
+
return "high"
|
401
|
+
elif self.environment_type == "staging":
|
402
|
+
return "medium"
|
403
|
+
else:
|
404
|
+
return "low"
|
405
|
+
|
406
|
+
def get_feature_flag(self, flag_name: str, default: bool = False) -> bool:
|
407
|
+
"""Get feature flag value"""
|
408
|
+
return self.feature_flags.get(flag_name, default)
|
409
|
+
|
410
|
+
def set_feature_flag(self, flag_name: str, enabled: bool, updated_by: Optional[str] = None):
|
411
|
+
"""Set feature flag value"""
|
412
|
+
self.feature_flags[flag_name] = enabled
|
413
|
+
self.updated_at = datetime.now(timezone.utc)
|
414
|
+
if updated_by:
|
415
|
+
self.updated_by = updated_by
|
416
|
+
|
417
|
+
def get_resource_limit(self, resource_type: str, default: Any = None) -> Any:
|
418
|
+
"""Get resource limit value"""
|
419
|
+
return self.resource_limits.get(resource_type, default)
|
420
|
+
|
421
|
+
def set_resource_limit(self, resource_type: str, limit: Any, updated_by: Optional[str] = None):
|
422
|
+
"""Set resource limit"""
|
423
|
+
self.resource_limits[resource_type] = limit
|
424
|
+
self.updated_at = datetime.now(timezone.utc)
|
425
|
+
if updated_by:
|
426
|
+
self.updated_by = updated_by
|
427
|
+
|
428
|
+
def validate_environment(self) -> List[str]:
|
429
|
+
"""Validate environment configuration"""
|
430
|
+
issues = []
|
431
|
+
|
432
|
+
if not self.environment:
|
433
|
+
issues.append("Environment name is required")
|
434
|
+
|
435
|
+
# Environment name validation
|
436
|
+
if self.environment not in ["development", "staging", "production", "testing"]:
|
437
|
+
if not self.environment.replace("_", "").replace("-", "").isalnum():
|
438
|
+
issues.append("Environment name should contain only alphanumeric characters, hyphens, and underscores")
|
439
|
+
|
440
|
+
# Production environment specific validations
|
441
|
+
if self.is_production:
|
442
|
+
required_security_settings = ["authentication_required", "encryption_enabled", "audit_logging"]
|
443
|
+
for setting in required_security_settings:
|
444
|
+
if setting not in self.security_settings:
|
445
|
+
issues.append(f"Production environment missing required security setting: {setting}")
|
446
|
+
|
447
|
+
return issues
|
448
|
+
|
449
|
+
@dataclass
|
450
|
+
class ConfigAuditLog:
|
451
|
+
"""
|
452
|
+
Configuration audit log model
|
453
|
+
|
454
|
+
Tracks all configuration changes for compliance, debugging,
|
455
|
+
and security auditing purposes.
|
456
|
+
"""
|
457
|
+
audit_id: str
|
458
|
+
config_id: str
|
459
|
+
config_key: str
|
460
|
+
action: str # create, update, delete, read, activate, deactivate
|
461
|
+
old_value: Optional[Any] = None
|
462
|
+
new_value: Optional[Any] = None
|
463
|
+
user_id: Optional[str] = None
|
464
|
+
session_id: Optional[str] = None
|
465
|
+
timestamp: datetime = None
|
466
|
+
ip_address: Optional[str] = None
|
467
|
+
user_agent: Optional[str] = None
|
468
|
+
change_reason: Optional[str] = None
|
469
|
+
approval_status: Optional[str] = None # pending, approved, rejected
|
470
|
+
approved_by: Optional[str] = None
|
471
|
+
approval_timestamp: Optional[datetime] = None
|
472
|
+
risk_level: str = "low" # low, medium, high, critical
|
473
|
+
compliance_flags: Optional[List[str]] = None
|
474
|
+
metadata: Optional[Dict[str, Any]] = None
|
475
|
+
|
476
|
+
def __post_init__(self):
|
477
|
+
if self.timestamp is None:
|
478
|
+
self.timestamp = datetime.now(timezone.utc)
|
479
|
+
if self.compliance_flags is None:
|
480
|
+
self.compliance_flags = []
|
481
|
+
if self.metadata is None:
|
482
|
+
self.metadata = {}
|
483
|
+
|
484
|
+
@property
|
485
|
+
def is_sensitive_change(self) -> bool:
|
486
|
+
"""Check if this represents a sensitive configuration change"""
|
487
|
+
sensitive_keys = ['api_key', 'password', 'secret', 'token', 'private_key']
|
488
|
+
sensitive_actions = ['create', 'update', 'delete']
|
489
|
+
|
490
|
+
return (self.action in sensitive_actions and
|
491
|
+
any(keyword in self.config_key.lower() for keyword in sensitive_keys))
|
492
|
+
|
493
|
+
@property
|
494
|
+
def requires_approval(self) -> bool:
|
495
|
+
"""Check if change requires approval"""
|
496
|
+
return (self.risk_level in ["high", "critical"] or
|
497
|
+
self.is_sensitive_change or
|
498
|
+
self.action == "delete")
|
499
|
+
|
500
|
+
@property
|
501
|
+
def is_approved(self) -> bool:
|
502
|
+
"""Check if change is approved"""
|
503
|
+
return self.approval_status == "approved"
|
504
|
+
|
505
|
+
@property
|
506
|
+
def change_summary(self) -> str:
|
507
|
+
"""Generate human-readable change summary"""
|
508
|
+
action_descriptions = {
|
509
|
+
"create": "created",
|
510
|
+
"update": "updated",
|
511
|
+
"delete": "deleted",
|
512
|
+
"read": "accessed",
|
513
|
+
"activate": "activated",
|
514
|
+
"deactivate": "deactivated"
|
515
|
+
}
|
516
|
+
|
517
|
+
action_desc = action_descriptions.get(self.action, self.action)
|
518
|
+
return f"Configuration '{self.config_key}' was {action_desc}"
|
519
|
+
|
520
|
+
def add_compliance_flag(self, flag: str):
|
521
|
+
"""Add compliance flag"""
|
522
|
+
if flag not in self.compliance_flags:
|
523
|
+
self.compliance_flags.append(flag)
|
524
|
+
|
525
|
+
def approve_change(self, approved_by: str, approval_reason: Optional[str] = None):
|
526
|
+
"""Approve the configuration change"""
|
527
|
+
self.approval_status = "approved"
|
528
|
+
self.approved_by = approved_by
|
529
|
+
self.approval_timestamp = datetime.now(timezone.utc)
|
530
|
+
|
531
|
+
if approval_reason:
|
532
|
+
self.metadata["approval_reason"] = approval_reason
|
533
|
+
|
534
|
+
def reject_change(self, rejected_by: str, rejection_reason: Optional[str] = None):
|
535
|
+
"""Reject the configuration change"""
|
536
|
+
self.approval_status = "rejected"
|
537
|
+
self.approved_by = rejected_by
|
538
|
+
self.approval_timestamp = datetime.now(timezone.utc)
|
539
|
+
|
540
|
+
if rejection_reason:
|
541
|
+
self.metadata["rejection_reason"] = rejection_reason
|
542
|
+
|
543
|
+
# Utility functions for working with configuration models
|
544
|
+
|
545
|
+
def create_config_record(
|
546
|
+
config_key: str,
|
547
|
+
config_value: Any,
|
548
|
+
config_type: str = ConfigType.SYSTEM,
|
549
|
+
environment: str = "production",
|
550
|
+
created_by: Optional[str] = None,
|
551
|
+
description: Optional[str] = None
|
552
|
+
) -> ConfigRecord:
|
553
|
+
"""Factory function to create a new configuration record"""
|
554
|
+
import uuid
|
555
|
+
|
556
|
+
config_id = f"config_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}"
|
557
|
+
|
558
|
+
return ConfigRecord(
|
559
|
+
config_id=config_id,
|
560
|
+
config_type=config_type,
|
561
|
+
config_key=config_key,
|
562
|
+
config_value=config_value,
|
563
|
+
environment=environment,
|
564
|
+
created_by=created_by,
|
565
|
+
description=description
|
566
|
+
)
|
567
|
+
|
568
|
+
def create_provider_config(
|
569
|
+
provider_name: str,
|
570
|
+
config_data: Dict[str, Any],
|
571
|
+
environment: str = "production",
|
572
|
+
created_by: Optional[str] = None
|
573
|
+
) -> ProviderConfig:
|
574
|
+
"""Factory function to create a new provider configuration"""
|
575
|
+
return ProviderConfig(
|
576
|
+
provider_name=provider_name,
|
577
|
+
config_data=config_data,
|
578
|
+
environment=environment,
|
579
|
+
created_by=created_by
|
580
|
+
)
|
581
|
+
|
582
|
+
def create_environment_config(
|
583
|
+
environment: str,
|
584
|
+
config_data: Dict[str, Any],
|
585
|
+
created_by: Optional[str] = None
|
586
|
+
) -> EnvironmentConfig:
|
587
|
+
"""Factory function to create a new environment configuration"""
|
588
|
+
return EnvironmentConfig(
|
589
|
+
environment=environment,
|
590
|
+
config_data=config_data,
|
591
|
+
created_by=created_by
|
592
|
+
)
|
593
|
+
|
594
|
+
def create_audit_log(
|
595
|
+
config_id: str,
|
596
|
+
config_key: str,
|
597
|
+
action: str,
|
598
|
+
user_id: Optional[str] = None,
|
599
|
+
old_value: Optional[Any] = None,
|
600
|
+
new_value: Optional[Any] = None,
|
601
|
+
change_reason: Optional[str] = None
|
602
|
+
) -> ConfigAuditLog:
|
603
|
+
"""Factory function to create a new audit log entry"""
|
604
|
+
import uuid
|
605
|
+
|
606
|
+
audit_id = f"audit_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}"
|
607
|
+
|
608
|
+
# Determine risk level based on action and values
|
609
|
+
risk_level = "low"
|
610
|
+
if action in ["delete", "deactivate"]:
|
611
|
+
risk_level = "high"
|
612
|
+
elif action == "update" and old_value != new_value:
|
613
|
+
risk_level = "medium"
|
614
|
+
|
615
|
+
return ConfigAuditLog(
|
616
|
+
audit_id=audit_id,
|
617
|
+
config_id=config_id,
|
618
|
+
config_key=config_key,
|
619
|
+
action=action,
|
620
|
+
old_value=old_value,
|
621
|
+
new_value=new_value,
|
622
|
+
user_id=user_id,
|
623
|
+
change_reason=change_reason,
|
624
|
+
risk_level=risk_level
|
625
|
+
)
|