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
@@ -8,13 +8,31 @@ Provides a singleton Supabase client instance that:
8
8
  """
9
9
 
10
10
  import logging
11
+ import os
11
12
  from typing import Optional
13
+ from urllib.parse import urlparse
12
14
  from supabase import create_client, Client
15
+ from supabase.lib.client_options import ClientOptions
16
+ from dotenv import load_dotenv
17
+ from functools import wraps
18
+ import httpx
13
19
 
14
20
  from ..config.config_manager import ConfigManager
15
21
 
16
22
  logger = logging.getLogger(__name__)
17
23
 
24
+ def require_client(default_return=None):
25
+ """Decorator to check if client is available before executing method"""
26
+ def decorator(func):
27
+ @wraps(func)
28
+ def wrapper(self, *args, **kwargs):
29
+ if self._client is None:
30
+ logger.error(f"Error in {func.__name__}: Supabase client not available")
31
+ return default_return
32
+ return func(self, *args, **kwargs)
33
+ return wrapper
34
+ return decorator
35
+
18
36
  class SupabaseClient:
19
37
  """Singleton Supabase client with environment-aware configuration"""
20
38
 
@@ -32,53 +50,144 @@ class SupabaseClient:
32
50
  self._initialize_client()
33
51
  SupabaseClient._initialized = True
34
52
 
53
+ def _configure_proxy_bypass(self):
54
+ """Configure proxy bypass for Supabase connections"""
55
+ # Check if URL is localhost or Docker internal
56
+ if self.url:
57
+ parsed_url = urlparse(self.url)
58
+ hostname = parsed_url.hostname or ''
59
+
60
+ # Check if it's a local connection
61
+ is_local = hostname in ['localhost', '127.0.0.1', '0.0.0.0', 'host.docker.internal']
62
+ is_docker = hostname.startswith('172.') or hostname.startswith('192.168.')
63
+
64
+ if is_local or is_docker:
65
+ logger.info(f"Detected local Supabase at {hostname}, configuring proxy bypass...")
66
+
67
+ # Get current proxy settings
68
+ http_proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy')
69
+ https_proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
70
+
71
+ if http_proxy or https_proxy:
72
+ # Temporarily disable proxy for this connection
73
+ no_proxy = os.environ.get('NO_PROXY', '') or os.environ.get('no_proxy', '')
74
+
75
+ # Add local addresses to no_proxy
76
+ local_addresses = [
77
+ 'localhost', '127.0.0.1', '0.0.0.0',
78
+ 'host.docker.internal', '*.local',
79
+ '172.*', '192.168.*', '10.*' # Common Docker/local network ranges
80
+ ]
81
+
82
+ no_proxy_list = no_proxy.split(',') if no_proxy else []
83
+ for addr in local_addresses:
84
+ if addr not in no_proxy_list:
85
+ no_proxy_list.append(addr)
86
+
87
+ # Also add the specific hostname if it's not already there
88
+ if hostname and hostname not in no_proxy_list:
89
+ no_proxy_list.append(hostname)
90
+
91
+ # Update environment variables
92
+ updated_no_proxy = ','.join(no_proxy_list)
93
+ os.environ['NO_PROXY'] = updated_no_proxy
94
+ os.environ['no_proxy'] = updated_no_proxy
95
+
96
+ logger.info(f"Updated NO_PROXY to: {updated_no_proxy}")
97
+
98
+ # For httpx-based connections, we might need to unset proxy for local connections
99
+ if is_local or is_docker:
100
+ # Store original values to restore later if needed
101
+ self._original_http_proxy = os.environ.pop('HTTP_PROXY', None)
102
+ self._original_https_proxy = os.environ.pop('HTTPS_PROXY', None)
103
+ os.environ.pop('http_proxy', None)
104
+ os.environ.pop('https_proxy', None)
105
+ logger.info("Temporarily disabled HTTP/HTTPS proxy for local Supabase connection")
106
+
35
107
  def _initialize_client(self):
36
- """Initialize the Supabase client with configuration from ConfigManager"""
108
+ """Initialize the Supabase client with flexible environment handling"""
37
109
  try:
38
- # Get configuration from ConfigManager
39
- config_manager = ConfigManager()
40
- global_config = config_manager.get_global_config()
110
+ # Load environment variables with fallback strategy
111
+ load_dotenv()
112
+
113
+ # Determine environment and load appropriate .env file
114
+ env = os.getenv("ENVIRONMENT", "development")
115
+ current_dir = os.path.dirname(os.path.abspath(__file__))
116
+ project_root = os.path.abspath(os.path.join(current_dir, '../../..'))
41
117
 
42
- # Get database configuration
43
- self.url = global_config.database.supabase_url
44
- self.key = global_config.database.supabase_key
45
- self.schema = global_config.database.supabase_schema or "public"
46
- self.environment = global_config.environment.value
118
+ # Load environment-specific .env file
119
+ if env == "development":
120
+ env_file = os.path.join(project_root, "deployment/dev/.env")
121
+ elif env == "staging":
122
+ env_file = os.path.join(project_root, "deployment/staging/env/.env.staging")
123
+ elif env == "production":
124
+ env_file = os.path.join(project_root, "deployment/production/env/.env.production")
125
+ else:
126
+ env_file = os.path.join(project_root, f"deployment/{env}/.env.{env}")
127
+
128
+ if os.path.exists(env_file):
129
+ load_dotenv(env_file)
130
+ logger.debug(f"Loaded environment from {env_file}")
131
+
132
+ # Try multiple environment variable names for flexibility
133
+ self.url = (
134
+ os.getenv('SUPABASE_CLOUD_URL') or
135
+ os.getenv('NEXT_PUBLIC_SUPABASE_URL') or
136
+ os.getenv('SUPABASE_URL') or
137
+ os.getenv('SUPABASE_LOCAL_URL')
138
+ )
139
+ self.key = (
140
+ os.getenv('SUPABASE_CLOUD_SERVICE_ROLE_KEY') or
141
+ os.getenv('SUPABASE_SERVICE_ROLE_KEY') or
142
+ os.getenv('SUPABASE_LOCAL_SERVICE_ROLE_KEY') or
143
+ os.getenv('SUPABASE_ANON_KEY') or
144
+ os.getenv('SUPABASE_LOCAL_ANON_KEY')
145
+ )
146
+
147
+ # Get schema from environment variable (with dev fallback)
148
+ # Force dev schema for local development
149
+ self.schema = os.getenv('DB_SCHEMA', 'dev')
150
+ self.environment = env
47
151
 
48
152
  if not self.url or not self.key:
49
- raise ValueError("Supabase URL and key must be configured")
153
+ logger.warning("Missing Supabase credentials. Database operations will not be available.")
154
+ logger.warning(f"URL found: {bool(self.url)}, Key found: {bool(self.key)}")
155
+ self._client = None
156
+ return
50
157
 
51
158
  # Create the client
52
159
  self._client = create_client(self.url, self.key)
53
160
 
54
161
  logger.info(f"Supabase client initialized for {self.environment} environment (schema: {self.schema})")
55
162
 
163
+ # Skip connection test to avoid unnecessary database queries and error logs
164
+ # Database operations will fail gracefully when needed
165
+ logger.debug("Supabase client initialized (connection test skipped)")
166
+
56
167
  except Exception as e:
57
168
  logger.error(f"Failed to initialize Supabase client: {e}")
58
- raise
169
+ # Don't raise - allow graceful degradation
170
+ self._client = None
59
171
 
60
- def get_client(self) -> Client:
172
+ def get_client(self) -> Optional[Client]:
61
173
  """Get the Supabase client instance"""
62
174
  if not self._client:
63
- raise RuntimeError("Supabase client not initialized")
175
+ logger.warning("Supabase client not available - database operations will be skipped")
176
+ return None
64
177
  return self._client
65
178
 
179
+ @require_client(default_return=None)
66
180
  def table(self, table_name: str):
67
181
  """Get a table with the correct schema"""
68
- if not self._client:
69
- raise RuntimeError("Supabase client not initialized")
70
-
71
182
  # Use the configured schema for the environment
72
183
  if self.schema and self.schema != "public":
73
184
  return self._client.schema(self.schema).table(table_name)
74
185
  else:
75
186
  return self._client.table(table_name)
76
187
 
188
+ @require_client(default_return=None)
77
189
  def rpc(self, function_name: str, params: Optional[dict] = None):
78
190
  """Call an RPC function with the correct schema"""
79
- if not self._client:
80
- raise RuntimeError("Supabase client not initialized")
81
-
82
191
  # RPC functions typically use the public schema
83
192
  # But we can extend this if needed for schema-specific functions
84
193
  return self._client.rpc(function_name, params)
@@ -101,6 +210,32 @@ class SupabaseClient:
101
210
  except Exception as e:
102
211
  logger.warning(f"Database connection test failed: {e}")
103
212
  return False
213
+
214
+ async def execute_sql(self, sql: str) -> dict:
215
+ """Execute raw SQL command"""
216
+ try:
217
+ result = self._client.rpc('execute_sql', {'sql': sql})
218
+ return {"success": True, "data": result.data}
219
+ except Exception as e:
220
+ logger.error(f"SQL execution failed: {e}")
221
+ return {"success": False, "error": str(e)}
222
+
223
+ async def execute_query(self, query: str, params: tuple = None) -> dict:
224
+ """Execute SQL query with parameters"""
225
+ try:
226
+ # For now, use rpc to execute queries
227
+ # In production, you might want to use prepared statements
228
+ if params:
229
+ # Simple parameter substitution (not recommended for production)
230
+ formatted_query = query % params
231
+ else:
232
+ formatted_query = query
233
+
234
+ result = self._client.rpc('execute_query', {'query': formatted_query})
235
+ return {"success": True, "data": result.data}
236
+ except Exception as e:
237
+ logger.error(f"Query execution failed: {e}")
238
+ return {"success": False, "error": str(e)}
104
239
 
105
240
  # Global singleton instance
106
241
  _supabase_client = None
@@ -0,0 +1,316 @@
1
+ """
2
+ Dependency management and checking utilities for ISA Model.
3
+
4
+ This module provides utilities for checking optional dependencies
5
+ and providing clear error messages when dependencies are missing.
6
+ """
7
+
8
+ import importlib.util
9
+ import logging
10
+ from typing import Dict, List, Optional, Tuple
11
+ from functools import wraps
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class DependencyChecker:
17
+ """Utility class for checking and managing optional dependencies."""
18
+
19
+ # Cache for dependency availability
20
+ _cache: Dict[str, bool] = {}
21
+
22
+ # Dependency groups with their packages
23
+ DEPENDENCY_GROUPS = {
24
+ # LLM Services
25
+ "openai": ["openai"],
26
+ "cerebras": ["cerebras.cloud.sdk"],
27
+ "local_llm": ["torch", "transformers", "accelerate"],
28
+ "vllm": ["vllm"],
29
+
30
+ # Vision Services
31
+ "vision_torch": ["torch", "torchvision", "PIL"],
32
+ "vision_tf": ["tensorflow", "keras"],
33
+ "vision_transformers": ["transformers", "PIL"],
34
+
35
+ # Audio Services
36
+ "audio": ["librosa", "soundfile", "numba"],
37
+ "openai_audio": ["openai"],
38
+
39
+ # Image Generation
40
+ "replicate": ["replicate"],
41
+ "image_gen": ["PIL", "requests"],
42
+
43
+ # Training
44
+ "training_torch": ["torch", "datasets", "peft", "trl"],
45
+ "training_tf": ["tensorflow", "keras"],
46
+
47
+ # Deployment
48
+ "modal": ["modal"],
49
+ "docker": ["docker"],
50
+ "kubernetes": ["kubernetes"],
51
+
52
+ # Storage
53
+ "s3": ["boto3"],
54
+ "gcs": ["google.cloud.storage"],
55
+ "minio": ["minio"],
56
+
57
+ # Monitoring
58
+ "mlflow": ["mlflow"],
59
+ "wandb": ["wandb"],
60
+ "influxdb": ["influxdb_client"],
61
+ "loki": ["python_logging_loki"],
62
+ }
63
+
64
+ @classmethod
65
+ def check_dependency(cls, package: str) -> bool:
66
+ """
67
+ Check if a single package is available.
68
+
69
+ Args:
70
+ package: Package name to check (e.g., 'torch', 'openai')
71
+
72
+ Returns:
73
+ True if package is available, False otherwise
74
+ """
75
+ if package in cls._cache:
76
+ return cls._cache[package]
77
+
78
+ spec = importlib.util.find_spec(package.split('.')[0])
79
+ available = spec is not None
80
+ cls._cache[package] = available
81
+
82
+ if not available:
83
+ logger.debug(f"Package '{package}' is not available")
84
+
85
+ return available
86
+
87
+ @classmethod
88
+ def check_dependencies(cls, packages: List[str]) -> Tuple[bool, List[str]]:
89
+ """
90
+ Check if multiple packages are available.
91
+
92
+ Args:
93
+ packages: List of package names to check
94
+
95
+ Returns:
96
+ Tuple of (all_available, missing_packages)
97
+ """
98
+ missing = []
99
+ for package in packages:
100
+ if not cls.check_dependency(package):
101
+ missing.append(package)
102
+
103
+ return len(missing) == 0, missing
104
+
105
+ @classmethod
106
+ def check_group(cls, group: str) -> Tuple[bool, List[str]]:
107
+ """
108
+ Check if all packages in a dependency group are available.
109
+
110
+ Args:
111
+ group: Name of the dependency group
112
+
113
+ Returns:
114
+ Tuple of (all_available, missing_packages)
115
+ """
116
+ if group not in cls.DEPENDENCY_GROUPS:
117
+ raise ValueError(f"Unknown dependency group: {group}")
118
+
119
+ packages = cls.DEPENDENCY_GROUPS[group]
120
+ return cls.check_dependencies(packages)
121
+
122
+ @classmethod
123
+ def get_install_command(cls, group: Optional[str] = None, packages: Optional[List[str]] = None) -> str:
124
+ """
125
+ Get the pip install command for missing dependencies.
126
+
127
+ Args:
128
+ group: Dependency group name
129
+ packages: List of package names
130
+
131
+ Returns:
132
+ Pip install command string
133
+ """
134
+ if group:
135
+ # Map groups to pyproject.toml extras
136
+ extras_map = {
137
+ "openai": "cloud",
138
+ "cerebras": "cloud",
139
+ "local_llm": "local",
140
+ "vision_torch": "vision",
141
+ "vision_tf": "vision",
142
+ "audio": "audio",
143
+ "training_torch": "training",
144
+ "modal": "cloud",
145
+ "mlflow": "monitoring",
146
+ }
147
+
148
+ if group in extras_map:
149
+ return f"pip install 'isa-model[{extras_map[group]}]'"
150
+
151
+ if packages:
152
+ return f"pip install {' '.join(packages)}"
153
+
154
+ return "pip install isa-model[all]"
155
+
156
+ @classmethod
157
+ def require_dependencies(cls, packages: List[str] = None, group: str = None,
158
+ message: str = None):
159
+ """
160
+ Decorator to check dependencies before running a function.
161
+
162
+ Args:
163
+ packages: List of required packages
164
+ group: Dependency group name
165
+ message: Custom error message
166
+ """
167
+ def decorator(func):
168
+ @wraps(func)
169
+ def wrapper(*args, **kwargs):
170
+ if group:
171
+ available, missing = cls.check_group(group)
172
+ elif packages:
173
+ available, missing = cls.check_dependencies(packages)
174
+ else:
175
+ raise ValueError("Either packages or group must be specified")
176
+
177
+ if not available:
178
+ error_msg = message or f"Missing required dependencies: {', '.join(missing)}"
179
+ install_cmd = cls.get_install_command(group=group, packages=missing)
180
+ raise ImportError(f"{error_msg}\nInstall with: {install_cmd}")
181
+
182
+ return func(*args, **kwargs)
183
+ return wrapper
184
+ return decorator
185
+
186
+
187
+ # Convenience functions for checking common dependencies
188
+ def is_torch_available() -> bool:
189
+ """Check if PyTorch is available."""
190
+ return DependencyChecker.check_dependency("torch")
191
+
192
+
193
+ def is_tensorflow_available() -> bool:
194
+ """Check if TensorFlow is available."""
195
+ return DependencyChecker.check_dependency("tensorflow")
196
+
197
+
198
+ def is_transformers_available() -> bool:
199
+ """Check if Transformers is available."""
200
+ return DependencyChecker.check_dependency("transformers")
201
+
202
+
203
+ def is_openai_available() -> bool:
204
+ """Check if OpenAI SDK is available."""
205
+ return DependencyChecker.check_dependency("openai")
206
+
207
+
208
+ def is_replicate_available() -> bool:
209
+ """Check if Replicate SDK is available."""
210
+ return DependencyChecker.check_dependency("replicate")
211
+
212
+
213
+ def is_modal_available() -> bool:
214
+ """Check if Modal SDK is available."""
215
+ return DependencyChecker.check_dependency("modal")
216
+
217
+
218
+ def is_cerebras_available() -> bool:
219
+ """Check if Cerebras SDK is available."""
220
+ return DependencyChecker.check_dependency("cerebras")
221
+
222
+
223
+ # Conditional imports with proper error handling
224
+ def import_torch():
225
+ """Import PyTorch with proper error handling."""
226
+ if not is_torch_available():
227
+ raise ImportError(
228
+ "PyTorch is not installed. "
229
+ "Install with: pip install 'isa-model[local]' or pip install torch"
230
+ )
231
+ import torch
232
+ return torch
233
+
234
+
235
+ def import_transformers():
236
+ """Import Transformers with proper error handling."""
237
+ if not is_transformers_available():
238
+ raise ImportError(
239
+ "Transformers is not installed. "
240
+ "Install with: pip install 'isa-model[local]' or pip install transformers"
241
+ )
242
+ import transformers
243
+ return transformers
244
+
245
+
246
+ def import_openai():
247
+ """Import OpenAI with proper error handling."""
248
+ if not is_openai_available():
249
+ raise ImportError(
250
+ "OpenAI SDK is not installed. "
251
+ "Install with: pip install 'isa-model[cloud]' or pip install openai"
252
+ )
253
+ import openai
254
+ return openai
255
+
256
+
257
+ def import_replicate():
258
+ """Import Replicate with proper error handling."""
259
+ if not is_replicate_available():
260
+ raise ImportError(
261
+ "Replicate SDK is not installed. "
262
+ "Install with: pip install 'isa-model[cloud]' or pip install replicate"
263
+ )
264
+ import replicate
265
+ return replicate
266
+
267
+
268
+ def import_cerebras():
269
+ """Import Cerebras with proper error handling."""
270
+ if not is_cerebras_available():
271
+ raise ImportError(
272
+ "Cerebras SDK is not installed. "
273
+ "Install with: pip install cerebras-cloud-sdk"
274
+ )
275
+ from cerebras.cloud.sdk import Cerebras
276
+ return Cerebras
277
+
278
+
279
+ # Lazy loading utilities
280
+ class LazyImport:
281
+ """Lazy import wrapper for optional dependencies."""
282
+
283
+ def __init__(self, module_name: str, package_name: str = None,
284
+ install_hint: str = None):
285
+ """
286
+ Initialize lazy import wrapper.
287
+
288
+ Args:
289
+ module_name: Full module name to import
290
+ package_name: Package name to check (defaults to module_name)
291
+ install_hint: Custom installation hint
292
+ """
293
+ self.module_name = module_name
294
+ self.package_name = package_name or module_name.split('.')[0]
295
+ self.install_hint = install_hint
296
+ self._module = None
297
+
298
+ def __getattr__(self, name):
299
+ """Lazy load the module when accessed."""
300
+ if self._module is None:
301
+ if not DependencyChecker.check_dependency(self.package_name):
302
+ hint = self.install_hint or f"pip install {self.package_name}"
303
+ raise ImportError(
304
+ f"{self.module_name} is not installed. Install with: {hint}"
305
+ )
306
+
307
+ import importlib
308
+ self._module = importlib.import_module(self.module_name)
309
+
310
+ return getattr(self._module, name)
311
+
312
+ def __call__(self, *args, **kwargs):
313
+ """Support direct calling for classes."""
314
+ if self._module is None:
315
+ self.__getattr__('__call__') # Trigger lazy loading
316
+ return self._module(*args, **kwargs)
@@ -0,0 +1,19 @@
1
+ """
2
+ Service Discovery Module for ISA Model Core
3
+
4
+ Provides service discovery capabilities using Consul for dynamic service resolution.
5
+ """
6
+
7
+ from .consul_discovery import (
8
+ ConsulServiceDiscovery,
9
+ get_consul_discovery,
10
+ discover_service,
11
+ resolve_url
12
+ )
13
+
14
+ __all__ = [
15
+ "ConsulServiceDiscovery",
16
+ "get_consul_discovery",
17
+ "discover_service",
18
+ "resolve_url"
19
+ ]