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
@@ -14,18 +14,27 @@ import pickle
|
|
14
14
|
import asyncio
|
15
15
|
import time
|
16
16
|
import logging
|
17
|
+
import os
|
17
18
|
from typing import Any, Dict, Optional, Union, List, Callable
|
18
19
|
from dataclasses import dataclass
|
19
20
|
import redis.asyncio as redis
|
20
21
|
import structlog
|
21
22
|
from functools import wraps
|
22
23
|
|
24
|
+
from ..config.config_manager import ConfigManager
|
25
|
+
|
23
26
|
logger = structlog.get_logger(__name__)
|
24
27
|
|
25
28
|
@dataclass
|
26
29
|
class CacheConfig:
|
27
30
|
"""Configuration for Redis cache"""
|
28
|
-
redis_url: str =
|
31
|
+
redis_url: str = None
|
32
|
+
|
33
|
+
def __post_init__(self):
|
34
|
+
if self.redis_url is None:
|
35
|
+
config_manager = ConfigManager()
|
36
|
+
# Use Consul discovery for Redis URL with fallback
|
37
|
+
self.redis_url = config_manager.get_redis_url()
|
29
38
|
default_ttl: int = 3600 # 1 hour
|
30
39
|
model_cache_ttl: int = 3600 # 1 hour for model responses
|
31
40
|
config_cache_ttl: int = 7200 # 2 hours for configurations
|
@@ -289,9 +298,9 @@ async def get_cache() -> RedisCache:
|
|
289
298
|
global _cache
|
290
299
|
|
291
300
|
if _cache is None:
|
292
|
-
|
301
|
+
config_manager = ConfigManager()
|
293
302
|
config = CacheConfig(
|
294
|
-
redis_url=os.getenv("REDIS_URL",
|
303
|
+
redis_url=os.getenv("REDIS_URL", config_manager.get_redis_url()),
|
295
304
|
default_ttl=int(os.getenv("CACHE_DEFAULT_TTL", "3600")),
|
296
305
|
model_cache_ttl=int(os.getenv("MODEL_CACHE_TTL", "3600")),
|
297
306
|
compression_enabled=os.getenv("CACHE_COMPRESSION", "true").lower() == "true"
|
@@ -41,6 +41,38 @@ class ProviderConfig:
|
|
41
41
|
models: List[Dict[str, Any]] = field(default_factory=list)
|
42
42
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
43
43
|
|
44
|
+
@dataclass
|
45
|
+
class ConsulConfig:
|
46
|
+
"""Consul service discovery configuration"""
|
47
|
+
enabled: bool = True
|
48
|
+
host: str = "localhost"
|
49
|
+
port: int = 8500
|
50
|
+
timeout: int = 10
|
51
|
+
|
52
|
+
@dataclass
|
53
|
+
class ServiceConfig:
|
54
|
+
"""Service endpoint configuration with Consul discovery support"""
|
55
|
+
# Enable Consul discovery
|
56
|
+
use_consul_discovery: bool = True
|
57
|
+
|
58
|
+
# Fallback URLs when Consul is unavailable
|
59
|
+
mcp_base_url: str = "http://localhost:8081"
|
60
|
+
ollama_base_url: str = "http://localhost:11434"
|
61
|
+
influxdb_url: str = "http://localhost:8086"
|
62
|
+
loki_url: str = "http://localhost:3100"
|
63
|
+
minio_endpoint: str = "localhost:9000"
|
64
|
+
redis_url: str = "redis://localhost:6379"
|
65
|
+
api_port: int = 8000
|
66
|
+
redis_port: int = 6379
|
67
|
+
|
68
|
+
# Consul service names for discovery
|
69
|
+
mcp_service_name: str = "mcp"
|
70
|
+
ollama_service_name: str = "ollama"
|
71
|
+
influxdb_service_name: str = "influxdb"
|
72
|
+
loki_service_name: str = "loki"
|
73
|
+
minio_service_name: str = "minio"
|
74
|
+
redis_service_name: str = "redis"
|
75
|
+
|
44
76
|
@dataclass
|
45
77
|
class DatabaseConfig:
|
46
78
|
"""Database configuration"""
|
@@ -50,6 +82,7 @@ class DatabaseConfig:
|
|
50
82
|
supabase_schema: Optional[str] = None
|
51
83
|
fallback_to_sqlite: bool = False
|
52
84
|
sqlite_path: str = "./isa_model.db"
|
85
|
+
default_database_url: str = "postgresql://postgres:postgres@127.0.0.1:54322/postgres?options=-c%20search_path%3Ddev"
|
53
86
|
connection_pool_size: int = 10
|
54
87
|
max_retries: int = 3
|
55
88
|
|
@@ -110,6 +143,8 @@ class GlobalConfig:
|
|
110
143
|
debug: bool = False
|
111
144
|
|
112
145
|
# Component configurations
|
146
|
+
consul: ConsulConfig = field(default_factory=ConsulConfig)
|
147
|
+
services: ServiceConfig = field(default_factory=ServiceConfig)
|
113
148
|
database: DatabaseConfig = field(default_factory=DatabaseConfig)
|
114
149
|
deployment: DeploymentConfig = field(default_factory=DeploymentConfig)
|
115
150
|
api: APIConfig = field(default_factory=APIConfig)
|
@@ -143,8 +178,10 @@ class ConfigManager:
|
|
143
178
|
self.global_config: Optional[GlobalConfig] = None
|
144
179
|
self.provider_configs: Dict[str, ProviderConfig] = {}
|
145
180
|
self.model_definitions: Dict[str, Dict[str, Any]] = {}
|
181
|
+
self._consul_discovery = None
|
146
182
|
|
147
183
|
self._load_configuration()
|
184
|
+
self._initialize_consul_discovery()
|
148
185
|
ConfigManager._initialized = True
|
149
186
|
|
150
187
|
def _load_configuration(self):
|
@@ -220,6 +257,8 @@ class ConfigManager:
|
|
220
257
|
self.global_config = GlobalConfig(
|
221
258
|
environment=environment,
|
222
259
|
debug=env_data.get('debug', False),
|
260
|
+
consul=ConsulConfig(**env_data.get('consul', {})),
|
261
|
+
services=ServiceConfig(**env_data.get('services', {})),
|
223
262
|
database=DatabaseConfig(**env_data.get('database', {})),
|
224
263
|
deployment=DeploymentConfig(**env_data.get('deployment', {})),
|
225
264
|
api=APIConfig(**env_data.get('api', {})),
|
@@ -265,6 +304,10 @@ class ConfigManager:
|
|
265
304
|
"api_key": ["YYDS_API_KEY"],
|
266
305
|
"api_base_url": ["YYDS_API_BASE", "YYDS_BASE_URL"],
|
267
306
|
},
|
307
|
+
Provider.CEREBRAS: {
|
308
|
+
"api_key": ["CEREBRAS_API_KEY"],
|
309
|
+
"api_base_url": ["CEREBRAS_API_BASE", "CEREBRAS_BASE_URL"],
|
310
|
+
},
|
268
311
|
}
|
269
312
|
|
270
313
|
for provider, env_vars in provider_env_mapping.items():
|
@@ -344,7 +387,9 @@ class ConfigManager:
|
|
344
387
|
# This can be enhanced later with deployment-specific YAML files
|
345
388
|
deployment_env_mapping = {
|
346
389
|
DeploymentPlatform.MODAL: {
|
347
|
-
"
|
390
|
+
"token_id": ["MODAL_TOKEN_ID"],
|
391
|
+
"token_secret": ["MODAL_TOKEN_SECRET"],
|
392
|
+
"environment": ["MODAL_ENVIRONMENT"],
|
348
393
|
"endpoint": ["MODAL_ENDPOINT"],
|
349
394
|
},
|
350
395
|
DeploymentPlatform.RUNPOD: {
|
@@ -428,6 +473,92 @@ class ConfigManager:
|
|
428
473
|
|
429
474
|
logger.debug(f"Database configured for {env}: schema={schema}, url={supabase_url}")
|
430
475
|
|
476
|
+
def _initialize_consul_discovery(self):
|
477
|
+
"""Initialize Consul service discovery client"""
|
478
|
+
if not self.global_config or not self.global_config.consul.enabled:
|
479
|
+
logger.info("Consul discovery disabled")
|
480
|
+
return
|
481
|
+
|
482
|
+
try:
|
483
|
+
from ..discovery.consul_discovery import ConsulServiceDiscovery
|
484
|
+
self._consul_discovery = ConsulServiceDiscovery(
|
485
|
+
consul_host=self.global_config.consul.host,
|
486
|
+
consul_port=self.global_config.consul.port
|
487
|
+
)
|
488
|
+
|
489
|
+
if self._consul_discovery.is_available():
|
490
|
+
logger.info("Consul service discovery initialized successfully")
|
491
|
+
else:
|
492
|
+
logger.warning("Consul not available, using fallback URLs")
|
493
|
+
self._consul_discovery = None
|
494
|
+
|
495
|
+
except ImportError as e:
|
496
|
+
logger.warning(f"Consul discovery not available: {e}")
|
497
|
+
self._consul_discovery = None
|
498
|
+
except Exception as e:
|
499
|
+
logger.error(f"Failed to initialize Consul discovery: {e}")
|
500
|
+
self._consul_discovery = None
|
501
|
+
|
502
|
+
def _apply_service_env_overrides(self):
|
503
|
+
"""Apply service endpoint environment variable overrides"""
|
504
|
+
if not self.global_config:
|
505
|
+
return
|
506
|
+
|
507
|
+
# Consul configuration overrides
|
508
|
+
consul_env_mapping = {
|
509
|
+
"enabled": ["CONSUL_ENABLED"],
|
510
|
+
"host": ["CONSUL_HOST"],
|
511
|
+
"port": ["CONSUL_PORT"],
|
512
|
+
}
|
513
|
+
|
514
|
+
for setting, env_vars in consul_env_mapping.items():
|
515
|
+
for env_var in env_vars:
|
516
|
+
value = os.getenv(env_var)
|
517
|
+
if value:
|
518
|
+
if setting == "enabled":
|
519
|
+
setattr(self.global_config.consul, setting, value.lower() == "true")
|
520
|
+
elif setting == "port":
|
521
|
+
try:
|
522
|
+
setattr(self.global_config.consul, setting, int(value))
|
523
|
+
except ValueError:
|
524
|
+
continue
|
525
|
+
else:
|
526
|
+
setattr(self.global_config.consul, setting, value)
|
527
|
+
break
|
528
|
+
|
529
|
+
# Service endpoint overrides
|
530
|
+
service_env_mapping = {
|
531
|
+
"use_consul_discovery": ["USE_CONSUL_DISCOVERY"],
|
532
|
+
"mcp_base_url": ["MCP_BASE_URL", "MCP_URL"],
|
533
|
+
"ollama_base_url": ["OLLAMA_BASE_URL", "OLLAMA_URL"],
|
534
|
+
"influxdb_url": ["INFLUXDB_URL", "INFLUX_URL"],
|
535
|
+
"loki_url": ["LOKI_URL"],
|
536
|
+
"minio_endpoint": ["MINIO_ENDPOINT", "MINIO_HOST"],
|
537
|
+
"redis_url": ["REDIS_URL"],
|
538
|
+
"redis_port": ["REDIS_PORT"],
|
539
|
+
"mcp_service_name": ["MCP_SERVICE_NAME"],
|
540
|
+
"ollama_service_name": ["OLLAMA_SERVICE_NAME"],
|
541
|
+
"influxdb_service_name": ["INFLUXDB_SERVICE_NAME"],
|
542
|
+
"loki_service_name": ["LOKI_SERVICE_NAME"],
|
543
|
+
"minio_service_name": ["MINIO_SERVICE_NAME"],
|
544
|
+
"redis_service_name": ["REDIS_SERVICE_NAME"],
|
545
|
+
}
|
546
|
+
|
547
|
+
for setting, env_vars in service_env_mapping.items():
|
548
|
+
for env_var in env_vars:
|
549
|
+
value = os.getenv(env_var)
|
550
|
+
if value:
|
551
|
+
if setting == "use_consul_discovery":
|
552
|
+
setattr(self.global_config.services, setting, value.lower() == "true")
|
553
|
+
elif setting in ["redis_port"]:
|
554
|
+
try:
|
555
|
+
setattr(self.global_config.services, setting, int(value))
|
556
|
+
except ValueError:
|
557
|
+
continue
|
558
|
+
else:
|
559
|
+
setattr(self.global_config.services, setting, value)
|
560
|
+
break
|
561
|
+
|
431
562
|
def _apply_env_overrides(self):
|
432
563
|
"""Apply environment variable overrides"""
|
433
564
|
if not self.global_config:
|
@@ -436,13 +567,15 @@ class ConfigManager:
|
|
436
567
|
# Environment-based database configuration
|
437
568
|
self._configure_database_for_environment()
|
438
569
|
|
439
|
-
#
|
570
|
+
# Service configuration overrides
|
571
|
+
self._apply_service_env_overrides()
|
440
572
|
|
441
573
|
# Serving overrides
|
442
|
-
port_env = os.getenv("PORT")
|
574
|
+
port_env = os.getenv("PORT") or os.getenv("API_PORT")
|
443
575
|
if port_env:
|
444
576
|
try:
|
445
577
|
self.global_config.serving.port = int(port_env)
|
578
|
+
self.global_config.services.api_port = int(port_env)
|
446
579
|
except ValueError:
|
447
580
|
pass
|
448
581
|
|
@@ -458,6 +591,100 @@ class ConfigManager:
|
|
458
591
|
raise RuntimeError("Configuration not loaded")
|
459
592
|
return self.global_config
|
460
593
|
|
594
|
+
def get_service_config(self) -> ServiceConfig:
|
595
|
+
"""Get service configuration"""
|
596
|
+
if not self.global_config:
|
597
|
+
raise RuntimeError("Configuration not loaded")
|
598
|
+
return self.global_config.services
|
599
|
+
|
600
|
+
def get_consul_config(self) -> ConsulConfig:
|
601
|
+
"""Get Consul configuration"""
|
602
|
+
if not self.global_config:
|
603
|
+
raise RuntimeError("Configuration not loaded")
|
604
|
+
return self.global_config.consul
|
605
|
+
|
606
|
+
def discover_service_url(self, service_name: str, fallback_url: Optional[str] = None) -> str:
|
607
|
+
"""Discover service URL using Consul or return fallback"""
|
608
|
+
if not self.global_config or not self.global_config.services.use_consul_discovery:
|
609
|
+
return fallback_url or self._get_fallback_service_url(service_name)
|
610
|
+
|
611
|
+
if not self._consul_discovery or not self._consul_discovery.is_available():
|
612
|
+
logger.debug(f"Consul not available, using fallback for {service_name}")
|
613
|
+
return fallback_url or self._get_fallback_service_url(service_name)
|
614
|
+
|
615
|
+
try:
|
616
|
+
discovered_url = self._consul_discovery.get_service_url(
|
617
|
+
service_name,
|
618
|
+
fallback_url or self._get_fallback_service_url(service_name)
|
619
|
+
)
|
620
|
+
return discovered_url
|
621
|
+
except Exception as e:
|
622
|
+
logger.warning(f"Failed to discover {service_name}: {e}")
|
623
|
+
return fallback_url or self._get_fallback_service_url(service_name)
|
624
|
+
|
625
|
+
def _get_fallback_service_url(self, service_name: str) -> str:
|
626
|
+
"""Get fallback URL for service based on service name"""
|
627
|
+
if not self.global_config:
|
628
|
+
raise RuntimeError("Configuration not loaded")
|
629
|
+
|
630
|
+
service_config = self.global_config.services
|
631
|
+
|
632
|
+
# Map service names to fallback URLs
|
633
|
+
service_mapping = {
|
634
|
+
service_config.mcp_service_name: service_config.mcp_base_url,
|
635
|
+
service_config.ollama_service_name: service_config.ollama_base_url,
|
636
|
+
service_config.influxdb_service_name: service_config.influxdb_url,
|
637
|
+
service_config.loki_service_name: service_config.loki_url,
|
638
|
+
service_config.minio_service_name: f"http://{service_config.minio_endpoint}",
|
639
|
+
service_config.redis_service_name: service_config.redis_url,
|
640
|
+
}
|
641
|
+
|
642
|
+
return service_mapping.get(service_name, f"http://localhost:8080")
|
643
|
+
|
644
|
+
def get_mcp_url(self) -> str:
|
645
|
+
"""Get MCP service URL (Consul discovery + fallback)"""
|
646
|
+
service_name = self.global_config.services.mcp_service_name
|
647
|
+
return self.discover_service_url(service_name, self.global_config.services.mcp_base_url)
|
648
|
+
|
649
|
+
def get_ollama_url(self) -> str:
|
650
|
+
"""Get Ollama service URL (Consul discovery + fallback)"""
|
651
|
+
service_name = self.global_config.services.ollama_service_name
|
652
|
+
return self.discover_service_url(service_name, self.global_config.services.ollama_base_url)
|
653
|
+
|
654
|
+
def get_influxdb_url(self) -> str:
|
655
|
+
"""Get InfluxDB service URL (Consul discovery + fallback)"""
|
656
|
+
service_name = self.global_config.services.influxdb_service_name
|
657
|
+
return self.discover_service_url(service_name, self.global_config.services.influxdb_url)
|
658
|
+
|
659
|
+
def get_loki_url(self) -> str:
|
660
|
+
"""Get Loki service URL (Consul discovery + fallback)"""
|
661
|
+
service_name = self.global_config.services.loki_service_name
|
662
|
+
return self.discover_service_url(service_name, self.global_config.services.loki_url)
|
663
|
+
|
664
|
+
def get_minio_url(self) -> str:
|
665
|
+
"""Get MinIO service URL (Consul discovery + fallback)"""
|
666
|
+
service_name = self.global_config.services.minio_service_name
|
667
|
+
fallback = f"http://{self.global_config.services.minio_endpoint}"
|
668
|
+
return self.discover_service_url(service_name, fallback)
|
669
|
+
|
670
|
+
def get_redis_url(self) -> str:
|
671
|
+
"""Get Redis service URL (Consul discovery + fallback)"""
|
672
|
+
service_name = self.global_config.services.redis_service_name
|
673
|
+
return self.discover_service_url(service_name, self.global_config.services.redis_url)
|
674
|
+
|
675
|
+
def resolve_consul_url(self, url: str) -> str:
|
676
|
+
"""Resolve consul:// URLs to actual service addresses"""
|
677
|
+
if not url or not url.startswith("consul://"):
|
678
|
+
return url
|
679
|
+
|
680
|
+
if not self._consul_discovery:
|
681
|
+
# If Consul not available, try to extract service name and use fallback
|
682
|
+
parts = url.replace("consul://", "").split("/", 1)
|
683
|
+
service_name = parts[0]
|
684
|
+
return self._get_fallback_service_url(service_name)
|
685
|
+
|
686
|
+
return self._consul_discovery.resolve_service_url(url)
|
687
|
+
|
461
688
|
def get_provider_config(self, provider: str) -> Optional[ProviderConfig]:
|
462
689
|
"""Get provider configuration"""
|
463
690
|
return self.provider_configs.get(provider)
|
isa_model/core/config.py
CHANGED
@@ -48,6 +48,35 @@ class DeploymentConfig:
|
|
48
48
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
49
49
|
|
50
50
|
|
51
|
+
@dataclass
|
52
|
+
class LocalGPUGlobalConfig:
|
53
|
+
"""Global configuration for local GPU deployment"""
|
54
|
+
enable_local_gpu: bool = True
|
55
|
+
auto_detect_gpu: bool = True
|
56
|
+
workspace_dir: str = "./local_deployments"
|
57
|
+
preferred_backend: str = "api" # cloud api only
|
58
|
+
|
59
|
+
# Default resource settings
|
60
|
+
default_gpu_memory_fraction: float = 0.9
|
61
|
+
default_max_batch_size: int = 8
|
62
|
+
default_max_model_len: int = 2048
|
63
|
+
default_precision: str = "float16"
|
64
|
+
|
65
|
+
# Health monitoring
|
66
|
+
health_check_interval: int = 30 # seconds
|
67
|
+
auto_restart_unhealthy: bool = True
|
68
|
+
max_consecutive_failures: int = 3
|
69
|
+
|
70
|
+
# Service limits
|
71
|
+
max_concurrent_services: int = 3
|
72
|
+
max_services_per_gpu: int = 2
|
73
|
+
|
74
|
+
# Performance settings
|
75
|
+
enable_model_compilation: bool = True
|
76
|
+
enable_memory_optimization: bool = True
|
77
|
+
cleanup_on_shutdown: bool = True
|
78
|
+
|
79
|
+
|
51
80
|
@dataclass
|
52
81
|
class GlobalConfig:
|
53
82
|
"""Global configuration settings"""
|
@@ -79,6 +108,18 @@ class GlobalConfig:
|
|
79
108
|
enable_model_cache: bool = True
|
80
109
|
cache_size_gb: int = 50
|
81
110
|
cache_cleanup_interval: int = 3600 # 1 hour
|
111
|
+
|
112
|
+
# Local GPU settings
|
113
|
+
enable_local_gpu: bool = True
|
114
|
+
local_gpu_memory_fraction: float = 0.9
|
115
|
+
local_workspace_dir: str = "./local_deployments"
|
116
|
+
auto_detect_gpu: bool = True
|
117
|
+
preferred_local_backend: str = "api" # cloud api only
|
118
|
+
|
119
|
+
# Local service defaults
|
120
|
+
local_health_check_interval: int = 30 # seconds
|
121
|
+
local_auto_restart_unhealthy: bool = True
|
122
|
+
local_max_concurrent_services: int = 3
|
82
123
|
|
83
124
|
|
84
125
|
class ConfigManager:
|
@@ -121,6 +162,7 @@ class ConfigManager:
|
|
121
162
|
"""Initialize configuration manager"""
|
122
163
|
if not self._initialized:
|
123
164
|
self.global_config = GlobalConfig()
|
165
|
+
self.local_gpu_config = LocalGPUGlobalConfig()
|
124
166
|
self.provider_configs: Dict[str, ProviderConfig] = {}
|
125
167
|
self.deployment_configs: Dict[str, DeploymentConfig] = {}
|
126
168
|
|
@@ -153,6 +195,7 @@ class ConfigManager:
|
|
153
195
|
env_files = [
|
154
196
|
project_root / ".env",
|
155
197
|
project_root / ".env.local",
|
198
|
+
project_root / "deployment" / "dev" / ".env",
|
156
199
|
Path.cwd() / ".env",
|
157
200
|
]
|
158
201
|
|
@@ -197,6 +240,38 @@ class ConfigManager:
|
|
197
240
|
for key, value in global_settings.items():
|
198
241
|
if hasattr(self.global_config, key):
|
199
242
|
setattr(self.global_config, key, value)
|
243
|
+
|
244
|
+
# Load database configuration from environment
|
245
|
+
# Check multiple possible environment variable names for compatibility
|
246
|
+
supabase_url = os.getenv("SUPABASE_URL") or os.getenv("SUPABASE_LOCAL_URL")
|
247
|
+
supabase_key = os.getenv("SUPABASE_ANON_KEY") or os.getenv("SUPABASE_LOCAL_ANON_KEY")
|
248
|
+
|
249
|
+
if supabase_url and supabase_key:
|
250
|
+
self.global_config.use_supabase = True
|
251
|
+
self.global_config.supabase_url = supabase_url
|
252
|
+
self.global_config.supabase_key = supabase_key
|
253
|
+
logger.debug(f"Supabase configured: URL={supabase_url[:30]}...")
|
254
|
+
else:
|
255
|
+
# Check if explicitly disabled
|
256
|
+
use_supabase_env = os.getenv("ISA_USE_SUPABASE", "").lower()
|
257
|
+
if use_supabase_env == "false":
|
258
|
+
self.global_config.use_supabase = False
|
259
|
+
logger.debug("Supabase not configured, using default storage")
|
260
|
+
|
261
|
+
# Load local GPU configuration
|
262
|
+
local_gpu_settings = {
|
263
|
+
"enable_local_gpu": os.getenv("ISA_ENABLE_LOCAL_GPU", "true").lower() == "true",
|
264
|
+
"auto_detect_gpu": os.getenv("ISA_AUTO_DETECT_GPU", "true").lower() == "true",
|
265
|
+
"workspace_dir": os.getenv("ISA_LOCAL_WORKSPACE_DIR", "./local_deployments"),
|
266
|
+
"preferred_backend": os.getenv("ISA_PREFERRED_LOCAL_BACKEND", "api"),
|
267
|
+
"default_gpu_memory_fraction": float(os.getenv("ISA_GPU_MEMORY_FRACTION", "0.9")),
|
268
|
+
"health_check_interval": int(os.getenv("ISA_LOCAL_HEALTH_CHECK_INTERVAL", "30")),
|
269
|
+
"max_concurrent_services": int(os.getenv("ISA_MAX_CONCURRENT_SERVICES", "3")),
|
270
|
+
}
|
271
|
+
|
272
|
+
for key, value in local_gpu_settings.items():
|
273
|
+
if hasattr(self.local_gpu_config, key):
|
274
|
+
setattr(self.local_gpu_config, key, value)
|
200
275
|
|
201
276
|
def _load_provider_configs(self):
|
202
277
|
"""Load provider configurations from environment"""
|
@@ -310,6 +385,21 @@ class ConfigManager:
|
|
310
385
|
"""Get configuration for a specific deployment platform"""
|
311
386
|
return self.deployment_configs.get(platform.value)
|
312
387
|
|
388
|
+
def get_local_gpu_config(self) -> LocalGPUGlobalConfig:
|
389
|
+
"""Get local GPU global configuration"""
|
390
|
+
return self.local_gpu_config
|
391
|
+
|
392
|
+
def is_local_gpu_enabled(self) -> bool:
|
393
|
+
"""Check if local GPU deployment is enabled and available"""
|
394
|
+
if not self.local_gpu_config.enable_local_gpu:
|
395
|
+
return False
|
396
|
+
|
397
|
+
try:
|
398
|
+
from ..utils.gpu_utils import check_cuda_availability
|
399
|
+
return check_cuda_availability()
|
400
|
+
except ImportError:
|
401
|
+
return False
|
402
|
+
|
313
403
|
def is_provider_enabled(self, provider: Provider) -> bool:
|
314
404
|
"""Check if a provider is enabled and configured"""
|
315
405
|
config = self.get_provider_config(provider)
|
@@ -0,0 +1,114 @@
|
|
1
|
+
"""
|
2
|
+
Direct Database Client for Migrations
|
3
|
+
|
4
|
+
Uses direct PostgreSQL connection for database operations like migrations.
|
5
|
+
This bypasses Supabase API and connects directly to PostgreSQL for admin operations.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
import asyncio
|
10
|
+
from typing import Dict, List, Optional, Any, Tuple
|
11
|
+
import asyncpg
|
12
|
+
import os
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
class DirectDBClient:
|
17
|
+
"""
|
18
|
+
Direct PostgreSQL client for database administration operations
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self, database_url: Optional[str] = None):
|
22
|
+
"""Initialize direct database client"""
|
23
|
+
self.database_url = database_url or os.getenv("DATABASE_URL")
|
24
|
+
if not self.database_url:
|
25
|
+
# Fallback to individual components
|
26
|
+
host = os.getenv("DB_HOST", "127.0.0.1")
|
27
|
+
port = os.getenv("DB_PORT", "54322")
|
28
|
+
database = os.getenv("DB_NAME", "postgres")
|
29
|
+
user = os.getenv("DB_USER", "postgres")
|
30
|
+
password = os.getenv("DB_PASSWORD", "postgres")
|
31
|
+
self.database_url = f"postgresql://{user}:{password}@{host}:{port}/{database}"
|
32
|
+
|
33
|
+
self.connection = None
|
34
|
+
|
35
|
+
async def connect(self) -> bool:
|
36
|
+
"""Establish database connection"""
|
37
|
+
try:
|
38
|
+
self.connection = await asyncpg.connect(self.database_url)
|
39
|
+
logger.info("Direct database connection established")
|
40
|
+
return True
|
41
|
+
except Exception as e:
|
42
|
+
logger.error(f"Failed to connect to database: {e}")
|
43
|
+
return False
|
44
|
+
|
45
|
+
async def disconnect(self):
|
46
|
+
"""Close database connection"""
|
47
|
+
if self.connection:
|
48
|
+
await self.connection.close()
|
49
|
+
self.connection = None
|
50
|
+
|
51
|
+
async def execute_sql(self, sql: str) -> Dict[str, Any]:
|
52
|
+
"""Execute raw SQL command"""
|
53
|
+
if not self.connection:
|
54
|
+
if not await self.connect():
|
55
|
+
return {"success": False, "error": "Failed to connect to database"}
|
56
|
+
|
57
|
+
try:
|
58
|
+
await self.connection.execute(sql)
|
59
|
+
return {"success": True}
|
60
|
+
except Exception as e:
|
61
|
+
logger.error(f"SQL execution failed: {e}")
|
62
|
+
return {"success": False, "error": str(e)}
|
63
|
+
|
64
|
+
async def execute_query(self, query: str, params: tuple = None) -> Dict[str, Any]:
|
65
|
+
"""Execute SQL query with parameters and return results"""
|
66
|
+
if not self.connection:
|
67
|
+
if not await self.connect():
|
68
|
+
return {"success": False, "error": "Failed to connect to database"}
|
69
|
+
|
70
|
+
try:
|
71
|
+
if params:
|
72
|
+
result = await self.connection.fetch(query, *params)
|
73
|
+
else:
|
74
|
+
result = await self.connection.fetch(query)
|
75
|
+
|
76
|
+
# Convert asyncpg records to dicts
|
77
|
+
data = [dict(record) for record in result]
|
78
|
+
return {"success": True, "data": data}
|
79
|
+
except Exception as e:
|
80
|
+
logger.error(f"Query execution failed: {e}")
|
81
|
+
return {"success": False, "error": str(e)}
|
82
|
+
|
83
|
+
async def execute_transaction(self, sql_commands: List[str]) -> Dict[str, Any]:
|
84
|
+
"""Execute multiple SQL commands in a transaction"""
|
85
|
+
if not self.connection:
|
86
|
+
if not await self.connect():
|
87
|
+
return {"success": False, "error": "Failed to connect to database"}
|
88
|
+
|
89
|
+
try:
|
90
|
+
async with self.connection.transaction():
|
91
|
+
for sql in sql_commands:
|
92
|
+
await self.connection.execute(sql)
|
93
|
+
return {"success": True}
|
94
|
+
except Exception as e:
|
95
|
+
logger.error(f"Transaction failed: {e}")
|
96
|
+
return {"success": False, "error": str(e)}
|
97
|
+
|
98
|
+
async def test_connection(self) -> bool:
|
99
|
+
"""Test database connection"""
|
100
|
+
try:
|
101
|
+
if not self.connection:
|
102
|
+
if not await self.connect():
|
103
|
+
return False
|
104
|
+
|
105
|
+
await self.connection.fetchval("SELECT 1")
|
106
|
+
return True
|
107
|
+
except Exception as e:
|
108
|
+
logger.error(f"Connection test failed: {e}")
|
109
|
+
return False
|
110
|
+
|
111
|
+
# Factory function
|
112
|
+
def create_direct_db_client(database_url: Optional[str] = None) -> DirectDBClient:
|
113
|
+
"""Create a direct database client"""
|
114
|
+
return DirectDBClient(database_url)
|