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.
Files changed (189) hide show
  1. isa_model/client.py +466 -43
  2. isa_model/core/cache/redis_cache.py +12 -3
  3. isa_model/core/config/config_manager.py +230 -3
  4. isa_model/core/config.py +90 -0
  5. isa_model/core/database/direct_db_client.py +114 -0
  6. isa_model/core/database/migration_manager.py +563 -0
  7. isa_model/core/database/migrations.py +21 -1
  8. isa_model/core/database/supabase_client.py +154 -19
  9. isa_model/core/dependencies.py +316 -0
  10. isa_model/core/discovery/__init__.py +19 -0
  11. isa_model/core/discovery/consul_discovery.py +190 -0
  12. isa_model/core/logging/__init__.py +54 -0
  13. isa_model/core/logging/influx_logger.py +523 -0
  14. isa_model/core/logging/loki_logger.py +160 -0
  15. isa_model/core/models/__init__.py +27 -18
  16. isa_model/core/models/config_models.py +625 -0
  17. isa_model/core/models/deployment_billing_tracker.py +430 -0
  18. isa_model/core/models/model_manager.py +35 -80
  19. isa_model/core/models/model_metadata.py +690 -0
  20. isa_model/core/models/model_repo.py +174 -18
  21. isa_model/core/models/system_models.py +857 -0
  22. isa_model/core/repositories/__init__.py +9 -0
  23. isa_model/core/repositories/config_repository.py +912 -0
  24. isa_model/core/services/intelligent_model_selector.py +399 -21
  25. isa_model/core/types.py +1 -0
  26. isa_model/deployment/__init__.py +5 -48
  27. isa_model/deployment/core/__init__.py +2 -31
  28. isa_model/deployment/core/deployment_manager.py +1278 -370
  29. isa_model/deployment/modal/__init__.py +8 -0
  30. isa_model/deployment/modal/config.py +136 -0
  31. isa_model/deployment/{services/auto_hf_modal_deployer.py → modal/deployer.py} +1 -1
  32. isa_model/deployment/modal/services/__init__.py +3 -0
  33. isa_model/deployment/modal/services/audio/__init__.py +1 -0
  34. isa_model/deployment/modal/services/embedding/__init__.py +1 -0
  35. isa_model/deployment/modal/services/llm/__init__.py +1 -0
  36. isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
  37. isa_model/deployment/modal/services/video/__init__.py +1 -0
  38. isa_model/deployment/modal/services/vision/__init__.py +1 -0
  39. isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
  40. isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
  41. isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
  42. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
  43. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
  44. isa_model/deployment/storage/__init__.py +5 -0
  45. isa_model/deployment/storage/deployment_repository.py +824 -0
  46. isa_model/deployment/triton/__init__.py +10 -0
  47. isa_model/deployment/triton/config.py +196 -0
  48. isa_model/deployment/triton/configs/__init__.py +1 -0
  49. isa_model/deployment/triton/provider.py +512 -0
  50. isa_model/deployment/triton/scripts/__init__.py +1 -0
  51. isa_model/deployment/triton/templates/__init__.py +1 -0
  52. isa_model/inference/__init__.py +47 -1
  53. isa_model/inference/ai_factory.py +137 -10
  54. isa_model/inference/legacy_services/__init__.py +21 -0
  55. isa_model/inference/legacy_services/model_evaluation.py +637 -0
  56. isa_model/inference/legacy_services/model_service.py +573 -0
  57. isa_model/inference/legacy_services/model_serving.py +717 -0
  58. isa_model/inference/legacy_services/model_training.py +561 -0
  59. isa_model/inference/models/__init__.py +21 -0
  60. isa_model/inference/models/inference_config.py +551 -0
  61. isa_model/inference/models/inference_record.py +675 -0
  62. isa_model/inference/models/performance_models.py +714 -0
  63. isa_model/inference/repositories/__init__.py +9 -0
  64. isa_model/inference/repositories/inference_repository.py +828 -0
  65. isa_model/inference/services/audio/base_stt_service.py +184 -11
  66. isa_model/inference/services/audio/openai_stt_service.py +22 -6
  67. isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
  68. isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
  69. isa_model/inference/services/llm/__init__.py +10 -2
  70. isa_model/inference/services/llm/base_llm_service.py +335 -24
  71. isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
  72. isa_model/inference/services/llm/helpers/llm_adapter.py +9 -4
  73. isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
  74. isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
  75. isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
  76. isa_model/inference/services/llm/ollama_llm_service.py +9 -2
  77. isa_model/inference/services/llm/openai_llm_service.py +33 -16
  78. isa_model/inference/services/llm/yyds_llm_service.py +8 -2
  79. isa_model/inference/services/vision/__init__.py +22 -1
  80. isa_model/inference/services/vision/helpers/image_utils.py +8 -5
  81. isa_model/inference/services/vision/isa_vision_service.py +65 -4
  82. isa_model/inference/services/vision/openai_vision_service.py +19 -10
  83. isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
  84. isa_model/serving/api/cache_manager.py +245 -0
  85. isa_model/serving/api/dependencies/__init__.py +1 -0
  86. isa_model/serving/api/dependencies/auth.py +194 -0
  87. isa_model/serving/api/dependencies/database.py +139 -0
  88. isa_model/serving/api/error_handlers.py +284 -0
  89. isa_model/serving/api/fastapi_server.py +172 -22
  90. isa_model/serving/api/middleware/auth.py +8 -2
  91. isa_model/serving/api/middleware/security.py +23 -33
  92. isa_model/serving/api/middleware/tenant_context.py +414 -0
  93. isa_model/serving/api/routes/analytics.py +4 -1
  94. isa_model/serving/api/routes/config.py +645 -0
  95. isa_model/serving/api/routes/deployment_billing.py +315 -0
  96. isa_model/serving/api/routes/deployments.py +138 -2
  97. isa_model/serving/api/routes/gpu_gateway.py +440 -0
  98. isa_model/serving/api/routes/health.py +32 -12
  99. isa_model/serving/api/routes/inference_monitoring.py +486 -0
  100. isa_model/serving/api/routes/local_deployments.py +448 -0
  101. isa_model/serving/api/routes/tenants.py +575 -0
  102. isa_model/serving/api/routes/unified.py +680 -18
  103. isa_model/serving/api/routes/webhooks.py +479 -0
  104. isa_model/serving/api/startup.py +68 -54
  105. isa_model/utils/gpu_utils.py +311 -0
  106. {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/METADATA +71 -24
  107. isa_model-0.4.4.dist-info/RECORD +180 -0
  108. isa_model/core/security/secrets.py +0 -358
  109. isa_model/core/storage/hf_storage.py +0 -419
  110. isa_model/core/storage/minio_storage.py +0 -0
  111. isa_model/deployment/cloud/__init__.py +0 -9
  112. isa_model/deployment/cloud/modal/__init__.py +0 -10
  113. isa_model/deployment/core/deployment_config.py +0 -356
  114. isa_model/deployment/core/isa_deployment_service.py +0 -401
  115. isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
  116. isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
  117. isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
  118. isa_model/deployment/runtime/deployed_service.py +0 -338
  119. isa_model/deployment/services/__init__.py +0 -9
  120. isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
  121. isa_model/deployment/services/model_service.py +0 -332
  122. isa_model/deployment/services/service_monitor.py +0 -356
  123. isa_model/deployment/services/service_registry.py +0 -527
  124. isa_model/eval/__init__.py +0 -92
  125. isa_model/eval/benchmarks/__init__.py +0 -27
  126. isa_model/eval/benchmarks/multimodal_datasets.py +0 -460
  127. isa_model/eval/benchmarks.py +0 -701
  128. isa_model/eval/config/__init__.py +0 -10
  129. isa_model/eval/config/evaluation_config.py +0 -108
  130. isa_model/eval/evaluators/__init__.py +0 -24
  131. isa_model/eval/evaluators/audio_evaluator.py +0 -727
  132. isa_model/eval/evaluators/base_evaluator.py +0 -503
  133. isa_model/eval/evaluators/embedding_evaluator.py +0 -742
  134. isa_model/eval/evaluators/llm_evaluator.py +0 -472
  135. isa_model/eval/evaluators/vision_evaluator.py +0 -564
  136. isa_model/eval/example_evaluation.py +0 -395
  137. isa_model/eval/factory.py +0 -798
  138. isa_model/eval/infrastructure/__init__.py +0 -24
  139. isa_model/eval/infrastructure/experiment_tracker.py +0 -466
  140. isa_model/eval/isa_benchmarks.py +0 -700
  141. isa_model/eval/isa_integration.py +0 -582
  142. isa_model/eval/metrics.py +0 -951
  143. isa_model/eval/tests/unit/test_basic.py +0 -396
  144. isa_model/serving/api/routes/evaluations.py +0 -579
  145. isa_model/training/__init__.py +0 -168
  146. isa_model/training/annotation/annotation_schema.py +0 -47
  147. isa_model/training/annotation/processors/annotation_processor.py +0 -126
  148. isa_model/training/annotation/storage/dataset_manager.py +0 -131
  149. isa_model/training/annotation/storage/dataset_schema.py +0 -44
  150. isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
  151. isa_model/training/annotation/tests/test_minio copy.py +0 -113
  152. isa_model/training/annotation/tests/test_minio_upload.py +0 -43
  153. isa_model/training/annotation/views/annotation_controller.py +0 -158
  154. isa_model/training/cloud/__init__.py +0 -22
  155. isa_model/training/cloud/job_orchestrator.py +0 -402
  156. isa_model/training/cloud/runpod_trainer.py +0 -454
  157. isa_model/training/cloud/storage_manager.py +0 -482
  158. isa_model/training/core/__init__.py +0 -26
  159. isa_model/training/core/config.py +0 -181
  160. isa_model/training/core/dataset.py +0 -222
  161. isa_model/training/core/trainer.py +0 -720
  162. isa_model/training/core/utils.py +0 -213
  163. isa_model/training/examples/intelligent_training_example.py +0 -281
  164. isa_model/training/factory.py +0 -424
  165. isa_model/training/intelligent/__init__.py +0 -25
  166. isa_model/training/intelligent/decision_engine.py +0 -643
  167. isa_model/training/intelligent/intelligent_factory.py +0 -888
  168. isa_model/training/intelligent/knowledge_base.py +0 -751
  169. isa_model/training/intelligent/resource_optimizer.py +0 -839
  170. isa_model/training/intelligent/task_classifier.py +0 -576
  171. isa_model/training/storage/__init__.py +0 -24
  172. isa_model/training/storage/core_integration.py +0 -439
  173. isa_model/training/storage/training_repository.py +0 -552
  174. isa_model/training/storage/training_storage.py +0 -628
  175. isa_model-0.4.0.dist-info/RECORD +0 -182
  176. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_chatTTS_service.py +0 -0
  177. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_fish_service.py +0 -0
  178. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_openvoice_service.py +0 -0
  179. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_service_v2.py +0 -0
  180. /isa_model/deployment/{cloud/modal → modal/services/embedding}/isa_embed_rerank_service.py +0 -0
  181. /isa_model/deployment/{cloud/modal → modal/services/video}/isa_video_hunyuan_service.py +0 -0
  182. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ocr_service.py +0 -0
  183. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_qwen25_service.py +0 -0
  184. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_table_service.py +0 -0
  185. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service.py +0 -0
  186. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service_optimized.py +0 -0
  187. /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
  188. {isa_model-0.4.0.dist-info → isa_model-0.4.4.dist-info}/WHEEL +0 -0
  189. {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 = "redis://localhost:6379"
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
- import os
301
+ config_manager = ConfigManager()
293
302
  config = CacheConfig(
294
- redis_url=os.getenv("REDIS_URL", "redis://localhost:6379"),
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
- "api_key": ["MODAL_TOKEN"],
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
- # Other overrides
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)