isa-model 0.3.91__py3-none-any.whl → 0.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. isa_model/client.py +1166 -584
  2. isa_model/core/cache/redis_cache.py +410 -0
  3. isa_model/core/config/config_manager.py +282 -12
  4. isa_model/core/config.py +91 -1
  5. isa_model/core/database/__init__.py +1 -0
  6. isa_model/core/database/direct_db_client.py +114 -0
  7. isa_model/core/database/migration_manager.py +563 -0
  8. isa_model/core/database/migrations.py +297 -0
  9. isa_model/core/database/supabase_client.py +258 -0
  10. isa_model/core/dependencies.py +316 -0
  11. isa_model/core/discovery/__init__.py +19 -0
  12. isa_model/core/discovery/consul_discovery.py +190 -0
  13. isa_model/core/logging/__init__.py +54 -0
  14. isa_model/core/logging/influx_logger.py +523 -0
  15. isa_model/core/logging/loki_logger.py +160 -0
  16. isa_model/core/models/__init__.py +46 -0
  17. isa_model/core/models/config_models.py +625 -0
  18. isa_model/core/models/deployment_billing_tracker.py +430 -0
  19. isa_model/core/models/model_billing_tracker.py +60 -88
  20. isa_model/core/models/model_manager.py +66 -25
  21. isa_model/core/models/model_metadata.py +690 -0
  22. isa_model/core/models/model_repo.py +217 -55
  23. isa_model/core/models/model_statistics_tracker.py +234 -0
  24. isa_model/core/models/model_storage.py +0 -1
  25. isa_model/core/models/model_version_manager.py +959 -0
  26. isa_model/core/models/system_models.py +857 -0
  27. isa_model/core/pricing_manager.py +2 -249
  28. isa_model/core/repositories/__init__.py +9 -0
  29. isa_model/core/repositories/config_repository.py +912 -0
  30. isa_model/core/resilience/circuit_breaker.py +366 -0
  31. isa_model/core/security/secrets.py +358 -0
  32. isa_model/core/services/__init__.py +2 -4
  33. isa_model/core/services/intelligent_model_selector.py +479 -370
  34. isa_model/core/storage/hf_storage.py +2 -2
  35. isa_model/core/types.py +8 -0
  36. isa_model/deployment/__init__.py +5 -48
  37. isa_model/deployment/core/__init__.py +2 -31
  38. isa_model/deployment/core/deployment_manager.py +1278 -368
  39. isa_model/deployment/local/__init__.py +31 -0
  40. isa_model/deployment/local/config.py +248 -0
  41. isa_model/deployment/local/gpu_gateway.py +607 -0
  42. isa_model/deployment/local/health_checker.py +428 -0
  43. isa_model/deployment/local/provider.py +586 -0
  44. isa_model/deployment/local/tensorrt_service.py +621 -0
  45. isa_model/deployment/local/transformers_service.py +644 -0
  46. isa_model/deployment/local/vllm_service.py +527 -0
  47. isa_model/deployment/modal/__init__.py +8 -0
  48. isa_model/deployment/modal/config.py +136 -0
  49. isa_model/deployment/modal/deployer.py +894 -0
  50. isa_model/deployment/modal/services/__init__.py +3 -0
  51. isa_model/deployment/modal/services/audio/__init__.py +1 -0
  52. isa_model/deployment/modal/services/audio/isa_audio_chatTTS_service.py +520 -0
  53. isa_model/deployment/modal/services/audio/isa_audio_openvoice_service.py +758 -0
  54. isa_model/deployment/modal/services/audio/isa_audio_service_v2.py +1044 -0
  55. isa_model/deployment/modal/services/embedding/__init__.py +1 -0
  56. isa_model/deployment/modal/services/embedding/isa_embed_rerank_service.py +296 -0
  57. isa_model/deployment/modal/services/llm/__init__.py +1 -0
  58. isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
  59. isa_model/deployment/modal/services/video/__init__.py +1 -0
  60. isa_model/deployment/modal/services/video/isa_video_hunyuan_service.py +423 -0
  61. isa_model/deployment/modal/services/vision/__init__.py +1 -0
  62. isa_model/deployment/modal/services/vision/isa_vision_ocr_service.py +519 -0
  63. isa_model/deployment/modal/services/vision/isa_vision_qwen25_service.py +709 -0
  64. isa_model/deployment/modal/services/vision/isa_vision_table_service.py +676 -0
  65. isa_model/deployment/modal/services/vision/isa_vision_ui_service.py +833 -0
  66. isa_model/deployment/modal/services/vision/isa_vision_ui_service_optimized.py +660 -0
  67. isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
  68. isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
  69. isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
  70. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
  71. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
  72. isa_model/deployment/storage/__init__.py +5 -0
  73. isa_model/deployment/storage/deployment_repository.py +824 -0
  74. isa_model/deployment/triton/__init__.py +10 -0
  75. isa_model/deployment/triton/config.py +196 -0
  76. isa_model/deployment/triton/configs/__init__.py +1 -0
  77. isa_model/deployment/triton/provider.py +512 -0
  78. isa_model/deployment/triton/scripts/__init__.py +1 -0
  79. isa_model/deployment/triton/templates/__init__.py +1 -0
  80. isa_model/inference/__init__.py +47 -1
  81. isa_model/inference/ai_factory.py +179 -16
  82. isa_model/inference/legacy_services/__init__.py +21 -0
  83. isa_model/inference/legacy_services/model_evaluation.py +637 -0
  84. isa_model/inference/legacy_services/model_service.py +573 -0
  85. isa_model/inference/legacy_services/model_serving.py +717 -0
  86. isa_model/inference/legacy_services/model_training.py +561 -0
  87. isa_model/inference/models/__init__.py +21 -0
  88. isa_model/inference/models/inference_config.py +551 -0
  89. isa_model/inference/models/inference_record.py +675 -0
  90. isa_model/inference/models/performance_models.py +714 -0
  91. isa_model/inference/repositories/__init__.py +9 -0
  92. isa_model/inference/repositories/inference_repository.py +828 -0
  93. isa_model/inference/services/audio/__init__.py +21 -0
  94. isa_model/inference/services/audio/base_realtime_service.py +225 -0
  95. isa_model/inference/services/audio/base_stt_service.py +184 -11
  96. isa_model/inference/services/audio/isa_tts_service.py +0 -0
  97. isa_model/inference/services/audio/openai_realtime_service.py +320 -124
  98. isa_model/inference/services/audio/openai_stt_service.py +53 -11
  99. isa_model/inference/services/base_service.py +17 -1
  100. isa_model/inference/services/custom_model_manager.py +277 -0
  101. isa_model/inference/services/embedding/__init__.py +13 -0
  102. isa_model/inference/services/embedding/base_embed_service.py +111 -8
  103. isa_model/inference/services/embedding/isa_embed_service.py +305 -0
  104. isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
  105. isa_model/inference/services/embedding/openai_embed_service.py +2 -4
  106. isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
  107. isa_model/inference/services/embedding/tests/test_embedding.py +222 -0
  108. isa_model/inference/services/img/__init__.py +2 -2
  109. isa_model/inference/services/img/base_image_gen_service.py +24 -7
  110. isa_model/inference/services/img/replicate_image_gen_service.py +84 -422
  111. isa_model/inference/services/img/services/replicate_face_swap.py +193 -0
  112. isa_model/inference/services/img/services/replicate_flux.py +226 -0
  113. isa_model/inference/services/img/services/replicate_flux_kontext.py +219 -0
  114. isa_model/inference/services/img/services/replicate_sticker_maker.py +249 -0
  115. isa_model/inference/services/img/tests/test_img_client.py +297 -0
  116. isa_model/inference/services/llm/__init__.py +10 -2
  117. isa_model/inference/services/llm/base_llm_service.py +361 -26
  118. isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
  119. isa_model/inference/services/llm/helpers/llm_adapter.py +71 -12
  120. isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
  121. isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
  122. isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
  123. isa_model/inference/services/llm/local_llm_service.py +747 -0
  124. isa_model/inference/services/llm/ollama_llm_service.py +11 -3
  125. isa_model/inference/services/llm/openai_llm_service.py +670 -56
  126. isa_model/inference/services/llm/yyds_llm_service.py +10 -3
  127. isa_model/inference/services/vision/__init__.py +27 -6
  128. isa_model/inference/services/vision/base_vision_service.py +118 -185
  129. isa_model/inference/services/vision/blip_vision_service.py +359 -0
  130. isa_model/inference/services/vision/helpers/image_utils.py +19 -10
  131. isa_model/inference/services/vision/isa_vision_service.py +634 -0
  132. isa_model/inference/services/vision/openai_vision_service.py +19 -10
  133. isa_model/inference/services/vision/tests/test_ocr_client.py +284 -0
  134. isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
  135. isa_model/serving/api/cache_manager.py +245 -0
  136. isa_model/serving/api/dependencies/__init__.py +1 -0
  137. isa_model/serving/api/dependencies/auth.py +194 -0
  138. isa_model/serving/api/dependencies/database.py +139 -0
  139. isa_model/serving/api/error_handlers.py +284 -0
  140. isa_model/serving/api/fastapi_server.py +240 -18
  141. isa_model/serving/api/middleware/auth.py +317 -0
  142. isa_model/serving/api/middleware/security.py +268 -0
  143. isa_model/serving/api/middleware/tenant_context.py +414 -0
  144. isa_model/serving/api/routes/analytics.py +489 -0
  145. isa_model/serving/api/routes/config.py +645 -0
  146. isa_model/serving/api/routes/deployment_billing.py +315 -0
  147. isa_model/serving/api/routes/deployments.py +475 -0
  148. isa_model/serving/api/routes/gpu_gateway.py +440 -0
  149. isa_model/serving/api/routes/health.py +32 -12
  150. isa_model/serving/api/routes/inference_monitoring.py +486 -0
  151. isa_model/serving/api/routes/local_deployments.py +448 -0
  152. isa_model/serving/api/routes/logs.py +430 -0
  153. isa_model/serving/api/routes/settings.py +582 -0
  154. isa_model/serving/api/routes/tenants.py +575 -0
  155. isa_model/serving/api/routes/unified.py +992 -171
  156. isa_model/serving/api/routes/webhooks.py +479 -0
  157. isa_model/serving/api/startup.py +318 -0
  158. isa_model/serving/modal_proxy_server.py +249 -0
  159. isa_model/utils/gpu_utils.py +311 -0
  160. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/METADATA +76 -22
  161. isa_model-0.4.3.dist-info/RECORD +193 -0
  162. isa_model/deployment/cloud/__init__.py +0 -9
  163. isa_model/deployment/cloud/modal/__init__.py +0 -10
  164. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -766
  165. isa_model/deployment/cloud/modal/isa_vision_table_service.py +0 -532
  166. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +0 -406
  167. isa_model/deployment/cloud/modal/register_models.py +0 -321
  168. isa_model/deployment/core/deployment_config.py +0 -356
  169. isa_model/deployment/core/isa_deployment_service.py +0 -401
  170. isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
  171. isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
  172. isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
  173. isa_model/deployment/runtime/deployed_service.py +0 -338
  174. isa_model/deployment/services/__init__.py +0 -9
  175. isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
  176. isa_model/deployment/services/model_service.py +0 -332
  177. isa_model/deployment/services/service_monitor.py +0 -356
  178. isa_model/deployment/services/service_registry.py +0 -527
  179. isa_model/eval/__init__.py +0 -92
  180. isa_model/eval/benchmarks.py +0 -469
  181. isa_model/eval/config/__init__.py +0 -10
  182. isa_model/eval/config/evaluation_config.py +0 -108
  183. isa_model/eval/evaluators/__init__.py +0 -18
  184. isa_model/eval/evaluators/base_evaluator.py +0 -503
  185. isa_model/eval/evaluators/llm_evaluator.py +0 -472
  186. isa_model/eval/factory.py +0 -531
  187. isa_model/eval/infrastructure/__init__.py +0 -24
  188. isa_model/eval/infrastructure/experiment_tracker.py +0 -466
  189. isa_model/eval/metrics.py +0 -798
  190. isa_model/inference/adapter/unified_api.py +0 -248
  191. isa_model/inference/services/helpers/stacked_config.py +0 -148
  192. isa_model/inference/services/img/flux_professional_service.py +0 -603
  193. isa_model/inference/services/img/helpers/base_stacked_service.py +0 -274
  194. isa_model/inference/services/others/table_transformer_service.py +0 -61
  195. isa_model/inference/services/vision/doc_analysis_service.py +0 -640
  196. isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -274
  197. isa_model/inference/services/vision/ui_analysis_service.py +0 -823
  198. isa_model/scripts/inference_tracker.py +0 -283
  199. isa_model/scripts/mlflow_manager.py +0 -379
  200. isa_model/scripts/model_registry.py +0 -465
  201. isa_model/scripts/register_models.py +0 -370
  202. isa_model/scripts/register_models_with_embeddings.py +0 -510
  203. isa_model/scripts/start_mlflow.py +0 -95
  204. isa_model/scripts/training_tracker.py +0 -257
  205. isa_model/training/__init__.py +0 -74
  206. isa_model/training/annotation/annotation_schema.py +0 -47
  207. isa_model/training/annotation/processors/annotation_processor.py +0 -126
  208. isa_model/training/annotation/storage/dataset_manager.py +0 -131
  209. isa_model/training/annotation/storage/dataset_schema.py +0 -44
  210. isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
  211. isa_model/training/annotation/tests/test_minio copy.py +0 -113
  212. isa_model/training/annotation/tests/test_minio_upload.py +0 -43
  213. isa_model/training/annotation/views/annotation_controller.py +0 -158
  214. isa_model/training/cloud/__init__.py +0 -22
  215. isa_model/training/cloud/job_orchestrator.py +0 -402
  216. isa_model/training/cloud/runpod_trainer.py +0 -454
  217. isa_model/training/cloud/storage_manager.py +0 -482
  218. isa_model/training/core/__init__.py +0 -23
  219. isa_model/training/core/config.py +0 -181
  220. isa_model/training/core/dataset.py +0 -222
  221. isa_model/training/core/trainer.py +0 -720
  222. isa_model/training/core/utils.py +0 -213
  223. isa_model/training/factory.py +0 -424
  224. isa_model-0.3.91.dist-info/RECORD +0 -138
  225. /isa_model/{core/storage/minio_storage.py → deployment/modal/services/audio/isa_audio_fish_service.py} +0 -0
  226. /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
  227. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/WHEEL +0 -0
  228. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/top_level.txt +0 -0
@@ -41,14 +41,48 @@ 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"""
47
79
  use_supabase: bool = True
48
80
  supabase_url: Optional[str] = None
49
81
  supabase_key: Optional[str] = None
82
+ supabase_schema: Optional[str] = None
50
83
  fallback_to_sqlite: bool = False
51
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"
52
86
  connection_pool_size: int = 10
53
87
  max_retries: int = 3
54
88
 
@@ -109,6 +143,8 @@ class GlobalConfig:
109
143
  debug: bool = False
110
144
 
111
145
  # Component configurations
146
+ consul: ConsulConfig = field(default_factory=ConsulConfig)
147
+ services: ServiceConfig = field(default_factory=ServiceConfig)
112
148
  database: DatabaseConfig = field(default_factory=DatabaseConfig)
113
149
  deployment: DeploymentConfig = field(default_factory=DeploymentConfig)
114
150
  api: APIConfig = field(default_factory=APIConfig)
@@ -142,8 +178,10 @@ class ConfigManager:
142
178
  self.global_config: Optional[GlobalConfig] = None
143
179
  self.provider_configs: Dict[str, ProviderConfig] = {}
144
180
  self.model_definitions: Dict[str, Dict[str, Any]] = {}
181
+ self._consul_discovery = None
145
182
 
146
183
  self._load_configuration()
184
+ self._initialize_consul_discovery()
147
185
  ConfigManager._initialized = True
148
186
 
149
187
  def _load_configuration(self):
@@ -177,13 +215,15 @@ class ConfigManager:
177
215
  logger.info(f"Configuration loaded for environment: {environment}")
178
216
 
179
217
  def _load_env_files(self):
180
- """Load environment variables from .env files"""
181
- # Load from project root
218
+ """Load environment variables from deployment environment files"""
219
+ # Load from deployment environment directories only
182
220
  project_root = self._find_project_root()
221
+
222
+ # Check environment-specific deployment directories
183
223
  env_files = [
184
- project_root / ".env",
185
- project_root / ".env.local",
186
- Path.cwd() / ".env",
224
+ project_root / "deployment" / "dev" / ".env",
225
+ project_root / "deployment" / "staging" / "env" / ".env.staging",
226
+ project_root / "deployment" / "production" / "env" / ".env.production",
187
227
  ]
188
228
 
189
229
  for env_file in env_files:
@@ -217,6 +257,8 @@ class ConfigManager:
217
257
  self.global_config = GlobalConfig(
218
258
  environment=environment,
219
259
  debug=env_data.get('debug', False),
260
+ consul=ConsulConfig(**env_data.get('consul', {})),
261
+ services=ServiceConfig(**env_data.get('services', {})),
220
262
  database=DatabaseConfig(**env_data.get('database', {})),
221
263
  deployment=DeploymentConfig(**env_data.get('deployment', {})),
222
264
  api=APIConfig(**env_data.get('api', {})),
@@ -262,6 +304,10 @@ class ConfigManager:
262
304
  "api_key": ["YYDS_API_KEY"],
263
305
  "api_base_url": ["YYDS_API_BASE", "YYDS_BASE_URL"],
264
306
  },
307
+ Provider.CEREBRAS: {
308
+ "api_key": ["CEREBRAS_API_KEY"],
309
+ "api_base_url": ["CEREBRAS_API_BASE", "CEREBRAS_BASE_URL"],
310
+ },
265
311
  }
266
312
 
267
313
  for provider, env_vars in provider_env_mapping.items():
@@ -341,7 +387,9 @@ class ConfigManager:
341
387
  # This can be enhanced later with deployment-specific YAML files
342
388
  deployment_env_mapping = {
343
389
  DeploymentPlatform.MODAL: {
344
- "api_key": ["MODAL_TOKEN"],
390
+ "token_id": ["MODAL_TOKEN_ID"],
391
+ "token_secret": ["MODAL_TOKEN_SECRET"],
392
+ "environment": ["MODAL_ENVIRONMENT"],
345
393
  "endpoint": ["MODAL_ENDPOINT"],
346
394
  },
347
395
  DeploymentPlatform.RUNPOD: {
@@ -384,22 +432,150 @@ class ConfigManager:
384
432
 
385
433
  logger.info(f"Loaded {len(self.model_definitions)} model definitions")
386
434
 
435
+ def _configure_database_for_environment(self):
436
+ """Configure database settings based on environment"""
437
+ if not self.global_config:
438
+ return
439
+
440
+ env = self.global_config.environment.value
441
+
442
+ if env in ["development", "testing"]:
443
+ # Local Supabase with schema isolation
444
+ supabase_url = os.getenv("SUPABASE_LOCAL_URL")
445
+ supabase_key = os.getenv("SUPABASE_LOCAL_ANON_KEY") or os.getenv("SUPABASE_LOCAL_SERVICE_ROLE_KEY")
446
+ schema = "dev" if env == "development" else "test"
447
+
448
+ elif env == "staging":
449
+ # Supabase Cloud staging
450
+ supabase_url = os.getenv("SUPABASE_STAGING_URL") or os.getenv("SUPABASE_URL")
451
+ supabase_key = os.getenv("SUPABASE_STAGING_KEY") or os.getenv("SUPABASE_ANON_KEY")
452
+ schema = "staging"
453
+
454
+ elif env == "production":
455
+ # Supabase Cloud production
456
+ supabase_url = os.getenv("SUPABASE_URL")
457
+ supabase_key = os.getenv("SUPABASE_ANON_KEY") or os.getenv("SERVICE_ROLE_KEY")
458
+ schema = "public" # or "production" if using schema isolation
459
+
460
+ else:
461
+ logger.warning(f"Unknown environment '{env}', using default configuration")
462
+ supabase_url = os.getenv("SUPABASE_URL")
463
+ supabase_key = os.getenv("SUPABASE_ANON_KEY")
464
+ schema = "public"
465
+
466
+ # Apply configuration
467
+ if supabase_url:
468
+ self.global_config.database.supabase_url = supabase_url
469
+ if supabase_key:
470
+ self.global_config.database.supabase_key = supabase_key
471
+ if schema:
472
+ self.global_config.database.supabase_schema = schema
473
+
474
+ logger.debug(f"Database configured for {env}: schema={schema}, url={supabase_url}")
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
+
387
562
  def _apply_env_overrides(self):
388
563
  """Apply environment variable overrides"""
389
564
  if not self.global_config:
390
565
  return
391
566
 
392
- # Database overrides
393
- if os.getenv("SUPABASE_URL"):
394
- self.global_config.database.supabase_url = os.getenv("SUPABASE_URL")
395
- if os.getenv("SUPABASE_ANON_KEY"):
396
- self.global_config.database.supabase_key = os.getenv("SUPABASE_ANON_KEY")
567
+ # Environment-based database configuration
568
+ self._configure_database_for_environment()
569
+
570
+ # Service configuration overrides
571
+ self._apply_service_env_overrides()
397
572
 
398
573
  # Serving overrides
399
- port_env = os.getenv("PORT")
574
+ port_env = os.getenv("PORT") or os.getenv("API_PORT")
400
575
  if port_env:
401
576
  try:
402
577
  self.global_config.serving.port = int(port_env)
578
+ self.global_config.services.api_port = int(port_env)
403
579
  except ValueError:
404
580
  pass
405
581
 
@@ -415,6 +591,100 @@ class ConfigManager:
415
591
  raise RuntimeError("Configuration not loaded")
416
592
  return self.global_config
417
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
+
418
688
  def get_provider_config(self, provider: str) -> Optional[ProviderConfig]:
419
689
  """Get provider configuration"""
420
690
  return self.provider_configs.get(provider)
isa_model/core/config.py CHANGED
@@ -1,4 +1,4 @@
1
- """
1
+ z"""
2
2
  Centralized Configuration Management for ISA Model SDK
3
3
 
4
4
  This module provides unified configuration management across all modules:
@@ -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 = "transformers" # vllm, tensorrt_llm, transformers
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 = "transformers" # vllm, tensorrt_llm, transformers
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", "transformers"),
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 @@
1
+ # Database package
@@ -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)