isa-model 0.3.9__py3-none-any.whl → 0.4.0__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 (124) hide show
  1. isa_model/__init__.py +1 -1
  2. isa_model/client.py +732 -565
  3. isa_model/core/cache/redis_cache.py +401 -0
  4. isa_model/core/config/config_manager.py +53 -10
  5. isa_model/core/config.py +1 -1
  6. isa_model/core/database/__init__.py +1 -0
  7. isa_model/core/database/migrations.py +277 -0
  8. isa_model/core/database/supabase_client.py +123 -0
  9. isa_model/core/models/__init__.py +37 -0
  10. isa_model/core/models/model_billing_tracker.py +60 -88
  11. isa_model/core/models/model_manager.py +36 -18
  12. isa_model/core/models/model_repo.py +44 -38
  13. isa_model/core/models/model_statistics_tracker.py +234 -0
  14. isa_model/core/models/model_storage.py +0 -1
  15. isa_model/core/models/model_version_manager.py +959 -0
  16. isa_model/core/pricing_manager.py +2 -249
  17. isa_model/core/resilience/circuit_breaker.py +366 -0
  18. isa_model/core/security/secrets.py +358 -0
  19. isa_model/core/services/__init__.py +2 -4
  20. isa_model/core/services/intelligent_model_selector.py +101 -370
  21. isa_model/core/storage/hf_storage.py +1 -1
  22. isa_model/core/types.py +7 -0
  23. isa_model/deployment/cloud/modal/isa_audio_chatTTS_service.py +520 -0
  24. isa_model/deployment/cloud/modal/isa_audio_fish_service.py +0 -0
  25. isa_model/deployment/cloud/modal/isa_audio_openvoice_service.py +758 -0
  26. isa_model/deployment/cloud/modal/isa_audio_service_v2.py +1044 -0
  27. isa_model/deployment/cloud/modal/isa_embed_rerank_service.py +296 -0
  28. isa_model/deployment/cloud/modal/isa_video_hunyuan_service.py +423 -0
  29. isa_model/deployment/cloud/modal/isa_vision_ocr_service.py +519 -0
  30. isa_model/deployment/cloud/modal/isa_vision_qwen25_service.py +709 -0
  31. isa_model/deployment/cloud/modal/isa_vision_table_service.py +467 -323
  32. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +607 -180
  33. isa_model/deployment/cloud/modal/isa_vision_ui_service_optimized.py +660 -0
  34. isa_model/deployment/core/deployment_manager.py +6 -4
  35. isa_model/deployment/services/auto_hf_modal_deployer.py +894 -0
  36. isa_model/eval/benchmarks/__init__.py +27 -0
  37. isa_model/eval/benchmarks/multimodal_datasets.py +460 -0
  38. isa_model/eval/benchmarks.py +244 -12
  39. isa_model/eval/evaluators/__init__.py +8 -2
  40. isa_model/eval/evaluators/audio_evaluator.py +727 -0
  41. isa_model/eval/evaluators/embedding_evaluator.py +742 -0
  42. isa_model/eval/evaluators/vision_evaluator.py +564 -0
  43. isa_model/eval/example_evaluation.py +395 -0
  44. isa_model/eval/factory.py +272 -5
  45. isa_model/eval/isa_benchmarks.py +700 -0
  46. isa_model/eval/isa_integration.py +582 -0
  47. isa_model/eval/metrics.py +159 -6
  48. isa_model/eval/tests/unit/test_basic.py +396 -0
  49. isa_model/inference/ai_factory.py +44 -8
  50. isa_model/inference/services/audio/__init__.py +21 -0
  51. isa_model/inference/services/audio/base_realtime_service.py +225 -0
  52. isa_model/inference/services/audio/isa_tts_service.py +0 -0
  53. isa_model/inference/services/audio/openai_realtime_service.py +320 -124
  54. isa_model/inference/services/audio/openai_stt_service.py +32 -6
  55. isa_model/inference/services/base_service.py +17 -1
  56. isa_model/inference/services/embedding/__init__.py +13 -0
  57. isa_model/inference/services/embedding/base_embed_service.py +111 -8
  58. isa_model/inference/services/embedding/isa_embed_service.py +305 -0
  59. isa_model/inference/services/embedding/openai_embed_service.py +2 -4
  60. isa_model/inference/services/embedding/tests/test_embedding.py +222 -0
  61. isa_model/inference/services/img/__init__.py +2 -2
  62. isa_model/inference/services/img/base_image_gen_service.py +24 -7
  63. isa_model/inference/services/img/replicate_image_gen_service.py +84 -422
  64. isa_model/inference/services/img/services/replicate_face_swap.py +193 -0
  65. isa_model/inference/services/img/services/replicate_flux.py +226 -0
  66. isa_model/inference/services/img/services/replicate_flux_kontext.py +219 -0
  67. isa_model/inference/services/img/services/replicate_sticker_maker.py +249 -0
  68. isa_model/inference/services/img/tests/test_img_client.py +297 -0
  69. isa_model/inference/services/llm/base_llm_service.py +30 -6
  70. isa_model/inference/services/llm/helpers/llm_adapter.py +63 -9
  71. isa_model/inference/services/llm/ollama_llm_service.py +2 -1
  72. isa_model/inference/services/llm/openai_llm_service.py +652 -55
  73. isa_model/inference/services/llm/yyds_llm_service.py +2 -1
  74. isa_model/inference/services/vision/__init__.py +5 -5
  75. isa_model/inference/services/vision/base_vision_service.py +118 -185
  76. isa_model/inference/services/vision/helpers/image_utils.py +11 -5
  77. isa_model/inference/services/vision/isa_vision_service.py +573 -0
  78. isa_model/inference/services/vision/tests/test_ocr_client.py +284 -0
  79. isa_model/serving/api/fastapi_server.py +88 -16
  80. isa_model/serving/api/middleware/auth.py +311 -0
  81. isa_model/serving/api/middleware/security.py +278 -0
  82. isa_model/serving/api/routes/analytics.py +486 -0
  83. isa_model/serving/api/routes/deployments.py +339 -0
  84. isa_model/serving/api/routes/evaluations.py +579 -0
  85. isa_model/serving/api/routes/logs.py +430 -0
  86. isa_model/serving/api/routes/settings.py +582 -0
  87. isa_model/serving/api/routes/unified.py +324 -165
  88. isa_model/serving/api/startup.py +304 -0
  89. isa_model/serving/modal_proxy_server.py +249 -0
  90. isa_model/training/__init__.py +100 -6
  91. isa_model/training/core/__init__.py +4 -1
  92. isa_model/training/examples/intelligent_training_example.py +281 -0
  93. isa_model/training/intelligent/__init__.py +25 -0
  94. isa_model/training/intelligent/decision_engine.py +643 -0
  95. isa_model/training/intelligent/intelligent_factory.py +888 -0
  96. isa_model/training/intelligent/knowledge_base.py +751 -0
  97. isa_model/training/intelligent/resource_optimizer.py +839 -0
  98. isa_model/training/intelligent/task_classifier.py +576 -0
  99. isa_model/training/storage/__init__.py +24 -0
  100. isa_model/training/storage/core_integration.py +439 -0
  101. isa_model/training/storage/training_repository.py +552 -0
  102. isa_model/training/storage/training_storage.py +628 -0
  103. {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/METADATA +13 -1
  104. isa_model-0.4.0.dist-info/RECORD +182 -0
  105. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -766
  106. isa_model/deployment/cloud/modal/register_models.py +0 -321
  107. isa_model/inference/adapter/unified_api.py +0 -248
  108. isa_model/inference/services/helpers/stacked_config.py +0 -148
  109. isa_model/inference/services/img/flux_professional_service.py +0 -603
  110. isa_model/inference/services/img/helpers/base_stacked_service.py +0 -274
  111. isa_model/inference/services/others/table_transformer_service.py +0 -61
  112. isa_model/inference/services/vision/doc_analysis_service.py +0 -640
  113. isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -274
  114. isa_model/inference/services/vision/ui_analysis_service.py +0 -823
  115. isa_model/scripts/inference_tracker.py +0 -283
  116. isa_model/scripts/mlflow_manager.py +0 -379
  117. isa_model/scripts/model_registry.py +0 -465
  118. isa_model/scripts/register_models.py +0 -370
  119. isa_model/scripts/register_models_with_embeddings.py +0 -510
  120. isa_model/scripts/start_mlflow.py +0 -95
  121. isa_model/scripts/training_tracker.py +0 -257
  122. isa_model-0.3.9.dist-info/RECORD +0 -138
  123. {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/WHEEL +0 -0
  124. {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,401 @@
1
+ """
2
+ Redis-based Caching Strategy for ISA Model
3
+
4
+ Provides multi-level caching for:
5
+ - Model responses and completions
6
+ - Model metadata and configurations
7
+ - User sessions and authentication
8
+ - Rate limiting data
9
+ """
10
+
11
+ import json
12
+ import hashlib
13
+ import pickle
14
+ import asyncio
15
+ import time
16
+ import logging
17
+ from typing import Any, Dict, Optional, Union, List, Callable
18
+ from dataclasses import dataclass
19
+ import redis.asyncio as redis
20
+ import structlog
21
+ from functools import wraps
22
+
23
+ logger = structlog.get_logger(__name__)
24
+
25
+ @dataclass
26
+ class CacheConfig:
27
+ """Configuration for Redis cache"""
28
+ redis_url: str = "redis://localhost:6379"
29
+ default_ttl: int = 3600 # 1 hour
30
+ model_cache_ttl: int = 3600 # 1 hour for model responses
31
+ config_cache_ttl: int = 7200 # 2 hours for configurations
32
+ session_cache_ttl: int = 86400 # 24 hours for sessions
33
+ rate_limit_ttl: int = 3600 # 1 hour for rate limiting
34
+ max_key_length: int = 250
35
+ compression_enabled: bool = True
36
+ serialization_method: str = "json" # "json" or "pickle"
37
+
38
+ class RedisCache:
39
+ """Redis-based cache with advanced features"""
40
+
41
+ def __init__(self, config: CacheConfig):
42
+ self.config = config
43
+ self.redis_client = None
44
+ self._connected = False
45
+ self._stats = {
46
+ "hits": 0,
47
+ "misses": 0,
48
+ "sets": 0,
49
+ "deletes": 0,
50
+ "errors": 0
51
+ }
52
+
53
+ async def connect(self):
54
+ """Connect to Redis"""
55
+ try:
56
+ self.redis_client = redis.from_url(
57
+ self.config.redis_url,
58
+ decode_responses=False, # Handle binary data
59
+ retry_on_timeout=True,
60
+ health_check_interval=30
61
+ )
62
+
63
+ # Test connection
64
+ await self.redis_client.ping()
65
+ self._connected = True
66
+
67
+ logger.info("Redis cache connected", url=self.config.redis_url)
68
+
69
+ except Exception as e:
70
+ logger.error("Failed to connect to Redis", error=str(e))
71
+ self._connected = False
72
+ raise
73
+
74
+ async def disconnect(self):
75
+ """Disconnect from Redis"""
76
+ if self.redis_client:
77
+ await self.redis_client.close()
78
+ self._connected = False
79
+ logger.info("Redis cache disconnected")
80
+
81
+ def _generate_key(self, namespace: str, key: str) -> str:
82
+ """Generate a cache key with namespace"""
83
+ full_key = f"isa_model:{namespace}:{key}"
84
+
85
+ # Hash long keys to avoid Redis key length limits
86
+ if len(full_key) > self.config.max_key_length:
87
+ hash_suffix = hashlib.md5(full_key.encode()).hexdigest()[:8]
88
+ full_key = f"isa_model:{namespace}:hash_{hash_suffix}"
89
+
90
+ return full_key
91
+
92
+ def _serialize_value(self, value: Any) -> bytes:
93
+ """Serialize value for storage"""
94
+ try:
95
+ if self.config.serialization_method == "pickle":
96
+ return pickle.dumps(value)
97
+ else:
98
+ # JSON serialization
99
+ json_str = json.dumps(value, default=str, ensure_ascii=False)
100
+ return json_str.encode('utf-8')
101
+ except Exception as e:
102
+ logger.error("Serialization failed", error=str(e))
103
+ raise
104
+
105
+ def _deserialize_value(self, data: bytes) -> Any:
106
+ """Deserialize value from storage"""
107
+ try:
108
+ if self.config.serialization_method == "pickle":
109
+ return pickle.loads(data)
110
+ else:
111
+ # JSON deserialization
112
+ json_str = data.decode('utf-8')
113
+ return json.loads(json_str)
114
+ except Exception as e:
115
+ logger.error("Deserialization failed", error=str(e))
116
+ raise
117
+
118
+ async def get(self, namespace: str, key: str) -> Optional[Any]:
119
+ """Get value from cache"""
120
+ if not self._connected:
121
+ return None
122
+
123
+ try:
124
+ cache_key = self._generate_key(namespace, key)
125
+ data = await self.redis_client.get(cache_key)
126
+
127
+ if data is None:
128
+ self._stats["misses"] += 1
129
+ return None
130
+
131
+ value = self._deserialize_value(data)
132
+ self._stats["hits"] += 1
133
+
134
+ logger.debug("Cache hit", namespace=namespace, key=key)
135
+ return value
136
+
137
+ except Exception as e:
138
+ self._stats["errors"] += 1
139
+ logger.error("Cache get failed", namespace=namespace, key=key, error=str(e))
140
+ return None
141
+
142
+ async def set(
143
+ self,
144
+ namespace: str,
145
+ key: str,
146
+ value: Any,
147
+ ttl: Optional[int] = None
148
+ ) -> bool:
149
+ """Set value in cache"""
150
+ if not self._connected:
151
+ return False
152
+
153
+ try:
154
+ cache_key = self._generate_key(namespace, key)
155
+ serialized_value = self._serialize_value(value)
156
+
157
+ # Use namespace-specific TTL if not provided
158
+ if ttl is None:
159
+ ttl = self._get_namespace_ttl(namespace)
160
+
161
+ await self.redis_client.setex(cache_key, ttl, serialized_value)
162
+ self._stats["sets"] += 1
163
+
164
+ logger.debug("Cache set", namespace=namespace, key=key, ttl=ttl)
165
+ return True
166
+
167
+ except Exception as e:
168
+ self._stats["errors"] += 1
169
+ logger.error("Cache set failed", namespace=namespace, key=key, error=str(e))
170
+ return False
171
+
172
+ async def delete(self, namespace: str, key: str) -> bool:
173
+ """Delete value from cache"""
174
+ if not self._connected:
175
+ return False
176
+
177
+ try:
178
+ cache_key = self._generate_key(namespace, key)
179
+ result = await self.redis_client.delete(cache_key)
180
+ self._stats["deletes"] += 1
181
+
182
+ logger.debug("Cache delete", namespace=namespace, key=key, existed=bool(result))
183
+ return bool(result)
184
+
185
+ except Exception as e:
186
+ self._stats["errors"] += 1
187
+ logger.error("Cache delete failed", namespace=namespace, key=key, error=str(e))
188
+ return False
189
+
190
+ async def exists(self, namespace: str, key: str) -> bool:
191
+ """Check if key exists in cache"""
192
+ if not self._connected:
193
+ return False
194
+
195
+ try:
196
+ cache_key = self._generate_key(namespace, key)
197
+ return bool(await self.redis_client.exists(cache_key))
198
+ except Exception as e:
199
+ logger.error("Cache exists check failed", error=str(e))
200
+ return False
201
+
202
+ async def increment(self, namespace: str, key: str, amount: int = 1, ttl: Optional[int] = None) -> Optional[int]:
203
+ """Increment a counter in cache"""
204
+ if not self._connected:
205
+ return None
206
+
207
+ try:
208
+ cache_key = self._generate_key(namespace, key)
209
+
210
+ # Use pipeline for atomic operations
211
+ pipe = self.redis_client.pipeline()
212
+ pipe.incrby(cache_key, amount)
213
+
214
+ if ttl:
215
+ pipe.expire(cache_key, ttl)
216
+
217
+ results = await pipe.execute()
218
+ return results[0]
219
+
220
+ except Exception as e:
221
+ logger.error("Cache increment failed", error=str(e))
222
+ return None
223
+
224
+ async def clear_namespace(self, namespace: str) -> int:
225
+ """Clear all keys in a namespace"""
226
+ if not self._connected:
227
+ return 0
228
+
229
+ try:
230
+ pattern = f"isa_model:{namespace}:*"
231
+ keys = []
232
+
233
+ # Use SCAN to avoid blocking Redis
234
+ async for key in self.redis_client.scan_iter(pattern):
235
+ keys.append(key)
236
+
237
+ if keys:
238
+ deleted = await self.redis_client.delete(*keys)
239
+ logger.info("Namespace cleared", namespace=namespace, deleted_keys=deleted)
240
+ return deleted
241
+
242
+ return 0
243
+
244
+ except Exception as e:
245
+ logger.error("Cache namespace clear failed", namespace=namespace, error=str(e))
246
+ return 0
247
+
248
+ def _get_namespace_ttl(self, namespace: str) -> int:
249
+ """Get TTL for a specific namespace"""
250
+ ttl_mapping = {
251
+ "models": self.config.model_cache_ttl,
252
+ "config": self.config.config_cache_ttl,
253
+ "sessions": self.config.session_cache_ttl,
254
+ "rate_limit": self.config.rate_limit_ttl,
255
+ "responses": self.config.model_cache_ttl,
256
+ }
257
+ return ttl_mapping.get(namespace, self.config.default_ttl)
258
+
259
+ async def get_stats(self) -> Dict[str, Any]:
260
+ """Get cache statistics"""
261
+ stats = dict(self._stats)
262
+
263
+ # Calculate hit rate
264
+ total_requests = stats["hits"] + stats["misses"]
265
+ stats["hit_rate"] = stats["hits"] / total_requests if total_requests > 0 else 0
266
+ stats["connected"] = self._connected
267
+
268
+ # Redis info if connected
269
+ if self._connected:
270
+ try:
271
+ redis_info = await self.redis_client.info()
272
+ stats["redis_info"] = {
273
+ "used_memory": redis_info.get("used_memory"),
274
+ "connected_clients": redis_info.get("connected_clients"),
275
+ "total_commands_processed": redis_info.get("total_commands_processed"),
276
+ "keyspace_hits": redis_info.get("keyspace_hits"),
277
+ "keyspace_misses": redis_info.get("keyspace_misses")
278
+ }
279
+ except Exception as e:
280
+ logger.error("Failed to get Redis info", error=str(e))
281
+
282
+ return stats
283
+
284
+ # Global cache instance
285
+ _cache: Optional[RedisCache] = None
286
+
287
+ async def get_cache() -> RedisCache:
288
+ """Get the global cache instance"""
289
+ global _cache
290
+
291
+ if _cache is None:
292
+ import os
293
+ config = CacheConfig(
294
+ redis_url=os.getenv("REDIS_URL", "redis://localhost:6379"),
295
+ default_ttl=int(os.getenv("CACHE_DEFAULT_TTL", "3600")),
296
+ model_cache_ttl=int(os.getenv("MODEL_CACHE_TTL", "3600")),
297
+ compression_enabled=os.getenv("CACHE_COMPRESSION", "true").lower() == "true"
298
+ )
299
+ _cache = RedisCache(config)
300
+ await _cache.connect()
301
+
302
+ return _cache
303
+
304
+ # Caching decorators
305
+ def cached_response(namespace: str = "responses", ttl: Optional[int] = None):
306
+ """Decorator for caching function responses"""
307
+
308
+ def decorator(func: Callable):
309
+ @wraps(func)
310
+ async def wrapper(*args, **kwargs):
311
+ # Generate cache key from function name and arguments
312
+ cache_key = f"{func.__name__}:{hashlib.md5(str(args).encode() + str(kwargs).encode()).hexdigest()}"
313
+
314
+ cache = await get_cache()
315
+
316
+ # Try to get from cache first
317
+ cached_result = await cache.get(namespace, cache_key)
318
+ if cached_result is not None:
319
+ logger.debug("Function result served from cache", function=func.__name__)
320
+ return cached_result
321
+
322
+ # Execute function and cache result
323
+ if asyncio.iscoroutinefunction(func):
324
+ result = await func(*args, **kwargs)
325
+ else:
326
+ result = func(*args, **kwargs)
327
+
328
+ # Cache the result
329
+ await cache.set(namespace, cache_key, result, ttl)
330
+ logger.debug("Function result cached", function=func.__name__)
331
+
332
+ return result
333
+
334
+ return wrapper
335
+ return decorator
336
+
337
+ def cached_model_response(ttl: Optional[int] = None):
338
+ """Decorator specifically for model responses"""
339
+ return cached_response(namespace="models", ttl=ttl)
340
+
341
+ # Specialized cache functions
342
+ async def cache_model_response(
343
+ model_id: str,
344
+ input_hash: str,
345
+ response: Any,
346
+ ttl: Optional[int] = None
347
+ ):
348
+ """Cache a model response"""
349
+ cache = await get_cache()
350
+ cache_key = f"{model_id}:{input_hash}"
351
+ await cache.set("models", cache_key, response, ttl)
352
+
353
+ async def get_cached_model_response(
354
+ model_id: str,
355
+ input_hash: str
356
+ ) -> Optional[Any]:
357
+ """Get cached model response"""
358
+ cache = await get_cache()
359
+ cache_key = f"{model_id}:{input_hash}"
360
+ return await cache.get("models", cache_key)
361
+
362
+ async def cache_user_session(user_id: str, session_data: Dict[str, Any]):
363
+ """Cache user session data"""
364
+ cache = await get_cache()
365
+ await cache.set("sessions", user_id, session_data)
366
+
367
+ async def get_user_session(user_id: str) -> Optional[Dict[str, Any]]:
368
+ """Get user session data"""
369
+ cache = await get_cache()
370
+ return await cache.get("sessions", user_id)
371
+
372
+ # Rate limiting cache functions
373
+ async def increment_rate_limit(key: str, window_seconds: int = 3600) -> int:
374
+ """Increment rate limit counter"""
375
+ cache = await get_cache()
376
+ return await cache.increment("rate_limit", key, amount=1, ttl=window_seconds) or 0
377
+
378
+ async def get_rate_limit_count(key: str) -> int:
379
+ """Get current rate limit count"""
380
+ cache = await get_cache()
381
+ count = await cache.get("rate_limit", key)
382
+ return count or 0
383
+
384
+ # Health check
385
+ async def check_cache_health() -> Dict[str, Any]:
386
+ """Check cache health"""
387
+ try:
388
+ cache = await get_cache()
389
+ stats = await cache.get_stats()
390
+
391
+ return {
392
+ "cache": "redis",
393
+ "status": "healthy" if stats["connected"] else "disconnected",
394
+ "stats": stats
395
+ }
396
+ except Exception as e:
397
+ return {
398
+ "cache": "redis",
399
+ "status": "error",
400
+ "error": str(e)
401
+ }
@@ -47,6 +47,7 @@ class DatabaseConfig:
47
47
  use_supabase: bool = True
48
48
  supabase_url: Optional[str] = None
49
49
  supabase_key: Optional[str] = None
50
+ supabase_schema: Optional[str] = None
50
51
  fallback_to_sqlite: bool = False
51
52
  sqlite_path: str = "./isa_model.db"
52
53
  connection_pool_size: int = 10
@@ -177,13 +178,15 @@ class ConfigManager:
177
178
  logger.info(f"Configuration loaded for environment: {environment}")
178
179
 
179
180
  def _load_env_files(self):
180
- """Load environment variables from .env files"""
181
- # Load from project root
181
+ """Load environment variables from deployment environment files"""
182
+ # Load from deployment environment directories only
182
183
  project_root = self._find_project_root()
184
+
185
+ # Check environment-specific deployment directories
183
186
  env_files = [
184
- project_root / ".env",
185
- project_root / ".env.local",
186
- Path.cwd() / ".env",
187
+ project_root / "deployment" / "dev" / ".env",
188
+ project_root / "deployment" / "staging" / "env" / ".env.staging",
189
+ project_root / "deployment" / "production" / "env" / ".env.production",
187
190
  ]
188
191
 
189
192
  for env_file in env_files:
@@ -384,16 +387,56 @@ class ConfigManager:
384
387
 
385
388
  logger.info(f"Loaded {len(self.model_definitions)} model definitions")
386
389
 
390
+ def _configure_database_for_environment(self):
391
+ """Configure database settings based on environment"""
392
+ if not self.global_config:
393
+ return
394
+
395
+ env = self.global_config.environment.value
396
+
397
+ if env in ["development", "testing"]:
398
+ # Local Supabase with schema isolation
399
+ supabase_url = os.getenv("SUPABASE_LOCAL_URL")
400
+ supabase_key = os.getenv("SUPABASE_LOCAL_ANON_KEY") or os.getenv("SUPABASE_LOCAL_SERVICE_ROLE_KEY")
401
+ schema = "dev" if env == "development" else "test"
402
+
403
+ elif env == "staging":
404
+ # Supabase Cloud staging
405
+ supabase_url = os.getenv("SUPABASE_STAGING_URL") or os.getenv("SUPABASE_URL")
406
+ supabase_key = os.getenv("SUPABASE_STAGING_KEY") or os.getenv("SUPABASE_ANON_KEY")
407
+ schema = "staging"
408
+
409
+ elif env == "production":
410
+ # Supabase Cloud production
411
+ supabase_url = os.getenv("SUPABASE_URL")
412
+ supabase_key = os.getenv("SUPABASE_ANON_KEY") or os.getenv("SERVICE_ROLE_KEY")
413
+ schema = "public" # or "production" if using schema isolation
414
+
415
+ else:
416
+ logger.warning(f"Unknown environment '{env}', using default configuration")
417
+ supabase_url = os.getenv("SUPABASE_URL")
418
+ supabase_key = os.getenv("SUPABASE_ANON_KEY")
419
+ schema = "public"
420
+
421
+ # Apply configuration
422
+ if supabase_url:
423
+ self.global_config.database.supabase_url = supabase_url
424
+ if supabase_key:
425
+ self.global_config.database.supabase_key = supabase_key
426
+ if schema:
427
+ self.global_config.database.supabase_schema = schema
428
+
429
+ logger.debug(f"Database configured for {env}: schema={schema}, url={supabase_url}")
430
+
387
431
  def _apply_env_overrides(self):
388
432
  """Apply environment variable overrides"""
389
433
  if not self.global_config:
390
434
  return
391
435
 
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")
436
+ # Environment-based database configuration
437
+ self._configure_database_for_environment()
438
+
439
+ # Other overrides
397
440
 
398
441
  # Serving overrides
399
442
  port_env = os.getenv("PORT")
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:
@@ -0,0 +1 @@
1
+ # Database package