isa-model 0.3.4__py3-none-any.whl → 0.3.6__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 (100) hide show
  1. isa_model/__init__.py +30 -1
  2. isa_model/client.py +770 -0
  3. isa_model/core/config/__init__.py +16 -0
  4. isa_model/core/config/config_manager.py +514 -0
  5. isa_model/core/config.py +426 -0
  6. isa_model/core/models/model_billing_tracker.py +476 -0
  7. isa_model/core/models/model_manager.py +399 -0
  8. isa_model/core/models/model_repo.py +343 -0
  9. isa_model/core/pricing_manager.py +426 -0
  10. isa_model/core/services/__init__.py +19 -0
  11. isa_model/core/services/intelligent_model_selector.py +547 -0
  12. isa_model/core/types.py +291 -0
  13. isa_model/deployment/__init__.py +2 -0
  14. isa_model/deployment/cloud/__init__.py +9 -0
  15. isa_model/deployment/cloud/modal/__init__.py +10 -0
  16. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +766 -0
  17. isa_model/deployment/cloud/modal/isa_vision_table_service.py +532 -0
  18. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +406 -0
  19. isa_model/deployment/cloud/modal/register_models.py +321 -0
  20. isa_model/deployment/runtime/deployed_service.py +338 -0
  21. isa_model/deployment/services/__init__.py +9 -0
  22. isa_model/deployment/services/auto_deploy_vision_service.py +537 -0
  23. isa_model/deployment/services/model_service.py +332 -0
  24. isa_model/deployment/services/service_monitor.py +356 -0
  25. isa_model/deployment/services/service_registry.py +527 -0
  26. isa_model/eval/__init__.py +80 -44
  27. isa_model/eval/config/__init__.py +10 -0
  28. isa_model/eval/config/evaluation_config.py +108 -0
  29. isa_model/eval/evaluators/__init__.py +18 -0
  30. isa_model/eval/evaluators/base_evaluator.py +503 -0
  31. isa_model/eval/evaluators/llm_evaluator.py +472 -0
  32. isa_model/eval/factory.py +417 -709
  33. isa_model/eval/infrastructure/__init__.py +24 -0
  34. isa_model/eval/infrastructure/experiment_tracker.py +466 -0
  35. isa_model/eval/metrics.py +191 -21
  36. isa_model/inference/ai_factory.py +187 -387
  37. isa_model/inference/providers/modal_provider.py +109 -0
  38. isa_model/inference/providers/yyds_provider.py +108 -0
  39. isa_model/inference/services/__init__.py +2 -1
  40. isa_model/inference/services/audio/base_stt_service.py +65 -1
  41. isa_model/inference/services/audio/base_tts_service.py +75 -1
  42. isa_model/inference/services/audio/openai_stt_service.py +189 -151
  43. isa_model/inference/services/audio/openai_tts_service.py +12 -10
  44. isa_model/inference/services/audio/replicate_tts_service.py +61 -56
  45. isa_model/inference/services/base_service.py +55 -55
  46. isa_model/inference/services/embedding/base_embed_service.py +65 -1
  47. isa_model/inference/services/embedding/ollama_embed_service.py +103 -43
  48. isa_model/inference/services/embedding/openai_embed_service.py +8 -10
  49. isa_model/inference/services/helpers/stacked_config.py +148 -0
  50. isa_model/inference/services/img/__init__.py +18 -0
  51. isa_model/inference/services/{vision → img}/base_image_gen_service.py +80 -35
  52. isa_model/inference/services/img/flux_professional_service.py +603 -0
  53. isa_model/inference/services/img/helpers/base_stacked_service.py +274 -0
  54. isa_model/inference/services/{vision → img}/replicate_image_gen_service.py +210 -69
  55. isa_model/inference/services/llm/__init__.py +3 -3
  56. isa_model/inference/services/llm/base_llm_service.py +519 -35
  57. isa_model/inference/services/llm/{llm_adapter.py → helpers/llm_adapter.py} +40 -0
  58. isa_model/inference/services/llm/helpers/llm_prompts.py +258 -0
  59. isa_model/inference/services/llm/helpers/llm_utils.py +280 -0
  60. isa_model/inference/services/llm/ollama_llm_service.py +150 -15
  61. isa_model/inference/services/llm/openai_llm_service.py +134 -31
  62. isa_model/inference/services/llm/yyds_llm_service.py +255 -0
  63. isa_model/inference/services/vision/__init__.py +38 -4
  64. isa_model/inference/services/vision/base_vision_service.py +241 -96
  65. isa_model/inference/services/vision/disabled/isA_vision_service.py +500 -0
  66. isa_model/inference/services/vision/doc_analysis_service.py +640 -0
  67. isa_model/inference/services/vision/helpers/base_stacked_service.py +274 -0
  68. isa_model/inference/services/vision/helpers/image_utils.py +272 -3
  69. isa_model/inference/services/vision/helpers/vision_prompts.py +297 -0
  70. isa_model/inference/services/vision/openai_vision_service.py +109 -170
  71. isa_model/inference/services/vision/replicate_vision_service.py +508 -0
  72. isa_model/inference/services/vision/ui_analysis_service.py +823 -0
  73. isa_model/scripts/register_models.py +370 -0
  74. isa_model/scripts/register_models_with_embeddings.py +510 -0
  75. isa_model/serving/__init__.py +19 -0
  76. isa_model/serving/api/__init__.py +10 -0
  77. isa_model/serving/api/fastapi_server.py +89 -0
  78. isa_model/serving/api/middleware/__init__.py +9 -0
  79. isa_model/serving/api/middleware/request_logger.py +88 -0
  80. isa_model/serving/api/routes/__init__.py +5 -0
  81. isa_model/serving/api/routes/health.py +82 -0
  82. isa_model/serving/api/routes/llm.py +19 -0
  83. isa_model/serving/api/routes/ui_analysis.py +223 -0
  84. isa_model/serving/api/routes/unified.py +202 -0
  85. isa_model/serving/api/routes/vision.py +19 -0
  86. isa_model/serving/api/schemas/__init__.py +17 -0
  87. isa_model/serving/api/schemas/common.py +33 -0
  88. isa_model/serving/api/schemas/ui_analysis.py +78 -0
  89. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/METADATA +4 -1
  90. isa_model-0.3.6.dist-info/RECORD +147 -0
  91. isa_model/core/model_manager.py +0 -208
  92. isa_model/core/model_registry.py +0 -342
  93. isa_model/inference/billing_tracker.py +0 -406
  94. isa_model/inference/services/llm/triton_llm_service.py +0 -481
  95. isa_model/inference/services/vision/ollama_vision_service.py +0 -194
  96. isa_model-0.3.4.dist-info/RECORD +0 -91
  97. /isa_model/core/{model_storage.py → models/model_storage.py} +0 -0
  98. /isa_model/inference/services/{vision → embedding}/helpers/text_splitter.py +0 -0
  99. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/WHEEL +0 -0
  100. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,16 @@
1
+ """
2
+ ISA Model Core Configuration Management
3
+
4
+ Centralized configuration system for:
5
+ - Environment settings (dev, prod, etc.)
6
+ - Provider API keys and settings
7
+ - Database configuration and initialization
8
+ - Model definitions and capabilities
9
+ - Deployment platform settings
10
+ """
11
+
12
+ from .config_manager import ConfigManager
13
+
14
+ __all__ = [
15
+ 'ConfigManager'
16
+ ]
@@ -0,0 +1,514 @@
1
+ """
2
+ Unified Configuration Manager for ISA Model Core
3
+
4
+ Centralizes all configuration management:
5
+ - Environment settings (dev, prod, etc.)
6
+ - Provider API keys and configurations
7
+ - Database setup and initialization
8
+ - Model definitions and capabilities
9
+ - Deployment platform settings
10
+ """
11
+
12
+ import os
13
+ import yaml
14
+ import logging
15
+ from typing import Dict, Any, Optional, List
16
+ from pathlib import Path
17
+ from dataclasses import dataclass, field
18
+ from enum import Enum
19
+ from dotenv import load_dotenv
20
+
21
+ from ..types import Provider, DeploymentPlatform
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ class Environment(str, Enum):
26
+ DEVELOPMENT = "development"
27
+ STAGING = "staging"
28
+ PRODUCTION = "production"
29
+ TESTING = "testing"
30
+
31
+ @dataclass
32
+ class ProviderConfig:
33
+ """Configuration for a provider"""
34
+ name: str
35
+ api_key: Optional[str] = None
36
+ api_base_url: Optional[str] = None
37
+ organization: Optional[str] = None
38
+ rate_limit_rpm: Optional[int] = None
39
+ rate_limit_tpm: Optional[int] = None
40
+ enabled: bool = True
41
+ models: List[Dict[str, Any]] = field(default_factory=list)
42
+ metadata: Dict[str, Any] = field(default_factory=dict)
43
+
44
+ @dataclass
45
+ class DatabaseConfig:
46
+ """Database configuration"""
47
+ use_supabase: bool = True
48
+ supabase_url: Optional[str] = None
49
+ supabase_key: Optional[str] = None
50
+ fallback_to_sqlite: bool = False
51
+ sqlite_path: str = "./isa_model.db"
52
+ connection_pool_size: int = 10
53
+ max_retries: int = 3
54
+
55
+ @dataclass
56
+ class DeploymentConfig:
57
+ """Deployment configuration"""
58
+ platform: str = "local"
59
+ auto_scaling: bool = False
60
+ scale_to_zero: bool = False
61
+ default_gpu: str = "cpu"
62
+ default_memory_mb: int = 8192
63
+ keep_warm: int = 0
64
+ timeout_seconds: int = 300
65
+
66
+ @dataclass
67
+ class APIConfig:
68
+ """API configuration"""
69
+ rate_limit_rpm: int = 100
70
+ max_file_size_mb: int = 20
71
+ cache_ttl_seconds: int = 3600
72
+ enable_auth: bool = False
73
+ cors_origins: List[str] = field(default_factory=lambda: ["*"])
74
+
75
+ @dataclass
76
+ class ServingConfig:
77
+ """Serving configuration"""
78
+ host: str = "0.0.0.0"
79
+ port: int = 8000
80
+ workers: int = 1
81
+ reload: bool = False
82
+ log_level: str = "info"
83
+
84
+ @dataclass
85
+ class BillingConfig:
86
+ """Billing and cost tracking configuration"""
87
+ track_costs: bool = True
88
+ cost_alerts_enabled: bool = True
89
+ monthly_budget_usd: Optional[float] = None
90
+
91
+ @dataclass
92
+ class HealthConfig:
93
+ """Health monitoring configuration"""
94
+ check_interval_seconds: int = 300
95
+ timeout_seconds: int = 30
96
+ enabled: bool = True
97
+
98
+ @dataclass
99
+ class CacheConfig:
100
+ """Model caching configuration"""
101
+ enabled: bool = True
102
+ size_gb: int = 50
103
+ cleanup_interval_seconds: int = 3600
104
+
105
+ @dataclass
106
+ class GlobalConfig:
107
+ """Complete global configuration"""
108
+ environment: Environment
109
+ debug: bool = False
110
+
111
+ # Component configurations
112
+ database: DatabaseConfig = field(default_factory=DatabaseConfig)
113
+ deployment: DeploymentConfig = field(default_factory=DeploymentConfig)
114
+ api: APIConfig = field(default_factory=APIConfig)
115
+ serving: ServingConfig = field(default_factory=ServingConfig)
116
+ billing: BillingConfig = field(default_factory=BillingConfig)
117
+ health: HealthConfig = field(default_factory=HealthConfig)
118
+ cache: CacheConfig = field(default_factory=CacheConfig)
119
+
120
+ class ConfigManager:
121
+ """
122
+ Unified Configuration Manager for ISA Model Core
123
+
124
+ Manages all configuration from a centralized location:
125
+ - Environment-specific settings
126
+ - Provider configurations
127
+ - Database and deployment settings
128
+ - Model definitions
129
+ """
130
+
131
+ _instance = None
132
+ _initialized = False
133
+
134
+ def __new__(cls):
135
+ if cls._instance is None:
136
+ cls._instance = super().__new__(cls)
137
+ return cls._instance
138
+
139
+ def __init__(self):
140
+ if not self._initialized:
141
+ self.config_dir = Path(__file__).parent
142
+ self.global_config: Optional[GlobalConfig] = None
143
+ self.provider_configs: Dict[str, ProviderConfig] = {}
144
+ self.model_definitions: Dict[str, Dict[str, Any]] = {}
145
+
146
+ self._load_configuration()
147
+ ConfigManager._initialized = True
148
+
149
+ def _load_configuration(self):
150
+ """Load all configuration from files and environment"""
151
+ # 0. Load environment variables from .env files
152
+ self._load_env_files()
153
+
154
+ # 1. Determine environment
155
+ env_name = os.getenv("ISA_ENV", "development")
156
+ try:
157
+ environment = Environment(env_name)
158
+ except ValueError:
159
+ logger.warning(f"Unknown environment '{env_name}', defaulting to development")
160
+ environment = Environment.DEVELOPMENT
161
+
162
+ # 2. Load environment-specific config
163
+ self._load_environment_config(environment)
164
+
165
+ # 3. Load provider configurations
166
+ self._load_provider_configs()
167
+
168
+ # 4. Load deployment configurations
169
+ self._load_deployment_configs()
170
+
171
+ # 5. Load model definitions
172
+ self._load_model_definitions()
173
+
174
+ # 6. Apply environment variable overrides
175
+ self._apply_env_overrides()
176
+
177
+ logger.info(f"Configuration loaded for environment: {environment}")
178
+
179
+ def _load_env_files(self):
180
+ """Load environment variables from .env files"""
181
+ # Load from project root
182
+ project_root = self._find_project_root()
183
+ env_files = [
184
+ project_root / ".env",
185
+ project_root / ".env.local",
186
+ Path.cwd() / ".env",
187
+ ]
188
+
189
+ for env_file in env_files:
190
+ if env_file.exists():
191
+ load_dotenv(env_file)
192
+ logger.debug(f"Loaded environment from {env_file}")
193
+
194
+ def _find_project_root(self) -> Path:
195
+ """Find the project root directory"""
196
+ current = Path(__file__).parent
197
+ while current != current.parent:
198
+ if (current / "pyproject.toml").exists() or (current / "setup.py").exists():
199
+ return current
200
+ current = current.parent
201
+ return Path.cwd()
202
+
203
+ def _load_environment_config(self, environment: Environment):
204
+ """Load environment-specific configuration"""
205
+ env_file = self.config_dir / "environments" / f"{environment.value}.yaml"
206
+
207
+ if not env_file.exists():
208
+ logger.warning(f"Environment config not found: {env_file}")
209
+ self.global_config = GlobalConfig(environment=environment)
210
+ return
211
+
212
+ try:
213
+ with open(env_file, 'r') as f:
214
+ env_data = yaml.safe_load(f)
215
+
216
+ # Build configuration from YAML
217
+ self.global_config = GlobalConfig(
218
+ environment=environment,
219
+ debug=env_data.get('debug', False),
220
+ database=DatabaseConfig(**env_data.get('database', {})),
221
+ deployment=DeploymentConfig(**env_data.get('deployment', {})),
222
+ api=APIConfig(**env_data.get('api', {})),
223
+ serving=ServingConfig(**env_data.get('serving', {})),
224
+ billing=BillingConfig(**env_data.get('billing', {})),
225
+ health=HealthConfig(**env_data.get('health', {})),
226
+ cache=CacheConfig(**env_data.get('cache', {}))
227
+ )
228
+
229
+ logger.debug(f"Loaded environment config from {env_file}")
230
+
231
+ except Exception as e:
232
+ logger.error(f"Failed to load environment config: {e}")
233
+ self.global_config = GlobalConfig(environment=environment)
234
+
235
+ def _load_provider_configs(self):
236
+ """Load provider configurations from YAML files and environment variables"""
237
+ # First load from environment variables (legacy support)
238
+ self._load_provider_configs_from_env()
239
+
240
+ # Then load from YAML files (enhanced configs)
241
+ self._load_provider_configs_from_yaml()
242
+
243
+ def _load_provider_configs_from_env(self):
244
+ """Load provider configurations from environment variables (legacy)"""
245
+ # Define provider environment variable patterns from original config.py
246
+ provider_env_mapping = {
247
+ Provider.OPENAI: {
248
+ "api_key": ["OPENAI_API_KEY"],
249
+ "organization": ["OPENAI_ORG_ID", "OPENAI_ORGANIZATION"],
250
+ "api_base_url": ["OPENAI_API_BASE", "OPENAI_BASE_URL"],
251
+ },
252
+ Provider.REPLICATE: {
253
+ "api_key": ["REPLICATE_API_TOKEN", "REPLICATE_API_KEY"],
254
+ },
255
+ Provider.ANTHROPIC: {
256
+ "api_key": ["ANTHROPIC_API_KEY"],
257
+ },
258
+ Provider.GOOGLE: {
259
+ "api_key": ["GOOGLE_API_KEY", "GEMINI_API_KEY"],
260
+ },
261
+ Provider.YYDS: {
262
+ "api_key": ["YYDS_API_KEY"],
263
+ "api_base_url": ["YYDS_API_BASE", "YYDS_BASE_URL"],
264
+ },
265
+ }
266
+
267
+ for provider, env_vars in provider_env_mapping.items():
268
+ config = ProviderConfig(name=provider.value)
269
+
270
+ # Load API key
271
+ for env_var in env_vars.get("api_key", []):
272
+ if os.getenv(env_var):
273
+ config.api_key = os.getenv(env_var)
274
+ break
275
+
276
+ # Load other settings
277
+ for setting, env_var_list in env_vars.items():
278
+ if setting == "api_key":
279
+ continue
280
+ for env_var in env_var_list:
281
+ if os.getenv(env_var):
282
+ setattr(config, setting, os.getenv(env_var))
283
+ break
284
+
285
+ # Check if provider is enabled
286
+ config.enabled = bool(config.api_key)
287
+
288
+ self.provider_configs[provider.value] = config
289
+
290
+ def _load_provider_configs_from_yaml(self):
291
+ """Load provider configurations from YAML files"""
292
+ providers_dir = self.config_dir / "providers"
293
+
294
+ if not providers_dir.exists():
295
+ logger.warning(f"Providers directory not found: {providers_dir}")
296
+ return
297
+
298
+ for provider_file in providers_dir.glob("*.yaml"):
299
+ try:
300
+ with open(provider_file, 'r') as f:
301
+ provider_data = yaml.safe_load(f)
302
+
303
+ provider_name = provider_data.get('provider')
304
+ if not provider_name:
305
+ logger.warning(f"No provider name in {provider_file}")
306
+ continue
307
+
308
+ # Get existing config (from env) or create new one
309
+ config = self.provider_configs.get(provider_name, ProviderConfig(name=provider_name))
310
+
311
+ # Load API configuration
312
+ api_config = provider_data.get('api', {})
313
+ api_key_env = api_config.get('api_key_env')
314
+ org_env = api_config.get('organization_env')
315
+
316
+ # Update config with YAML data (environment takes precedence)
317
+ if not config.api_key and api_key_env:
318
+ config.api_key = os.getenv(api_key_env)
319
+ if not config.api_base_url:
320
+ config.api_base_url = api_config.get('base_url')
321
+ if not config.organization and org_env:
322
+ config.organization = os.getenv(org_env)
323
+ if not config.rate_limit_rpm:
324
+ config.rate_limit_rpm = api_config.get('rate_limits', {}).get('requests_per_minute')
325
+ if not config.rate_limit_tpm:
326
+ config.rate_limit_tpm = api_config.get('rate_limits', {}).get('tokens_per_minute')
327
+
328
+ # Enable if API key is available
329
+ config.enabled = bool(config.api_key)
330
+ config.models = provider_data.get('models', [])
331
+ config.metadata = provider_data
332
+
333
+ self.provider_configs[provider_name] = config
334
+ logger.debug(f"Loaded provider config for {provider_name}")
335
+
336
+ except Exception as e:
337
+ logger.error(f"Failed to load provider config from {provider_file}: {e}")
338
+
339
+ def _load_deployment_configs(self):
340
+ """Load deployment platform configurations"""
341
+ # This can be enhanced later with deployment-specific YAML files
342
+ deployment_env_mapping = {
343
+ DeploymentPlatform.MODAL: {
344
+ "api_key": ["MODAL_TOKEN"],
345
+ "endpoint": ["MODAL_ENDPOINT"],
346
+ },
347
+ DeploymentPlatform.RUNPOD: {
348
+ "api_key": ["RUNPOD_API_KEY"],
349
+ "endpoint": ["RUNPOD_ENDPOINT"],
350
+ },
351
+ DeploymentPlatform.KUBERNETES: {
352
+ "endpoint": ["K8S_ENDPOINT", "KUBERNETES_ENDPOINT"],
353
+ "api_key": ["K8S_TOKEN", "KUBERNETES_TOKEN"],
354
+ },
355
+ }
356
+
357
+ # Store deployment configs if needed
358
+ self.deployment_configs = {}
359
+ for platform, env_vars in deployment_env_mapping.items():
360
+ config = {
361
+ "platform": platform,
362
+ "enabled": False
363
+ }
364
+
365
+ # Load settings from environment
366
+ for setting, env_var_list in env_vars.items():
367
+ for env_var in env_var_list:
368
+ if os.getenv(env_var):
369
+ config[setting] = os.getenv(env_var)
370
+ config["enabled"] = True
371
+ break
372
+
373
+ self.deployment_configs[platform.value] = config
374
+
375
+ def _load_model_definitions(self):
376
+ """Load model definitions from provider configs"""
377
+ for provider_name, provider_config in self.provider_configs.items():
378
+ for model_data in provider_config.models:
379
+ model_id = model_data.get('model_id')
380
+ if model_id:
381
+ # Add provider context to model definition
382
+ model_data['provider'] = provider_name
383
+ self.model_definitions[model_id] = model_data
384
+
385
+ logger.info(f"Loaded {len(self.model_definitions)} model definitions")
386
+
387
+ def _apply_env_overrides(self):
388
+ """Apply environment variable overrides"""
389
+ if not self.global_config:
390
+ return
391
+
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")
397
+
398
+ # Serving overrides
399
+ port_env = os.getenv("PORT")
400
+ if port_env:
401
+ try:
402
+ self.global_config.serving.port = int(port_env)
403
+ except ValueError:
404
+ pass
405
+
406
+ # Debug override
407
+ debug_env = os.getenv("DEBUG")
408
+ if debug_env:
409
+ self.global_config.debug = debug_env.lower() in ('true', '1', 'yes')
410
+
411
+ # Public API
412
+ def get_global_config(self) -> GlobalConfig:
413
+ """Get global configuration"""
414
+ if not self.global_config:
415
+ raise RuntimeError("Configuration not loaded")
416
+ return self.global_config
417
+
418
+ def get_provider_config(self, provider: str) -> Optional[ProviderConfig]:
419
+ """Get provider configuration"""
420
+ return self.provider_configs.get(provider)
421
+
422
+ def get_provider_api_key(self, provider: str) -> Optional[str]:
423
+ """Get API key for provider"""
424
+ config = self.get_provider_config(provider)
425
+ return config.api_key if config else None
426
+
427
+ def is_provider_enabled(self, provider: str) -> bool:
428
+ """Check if provider is enabled"""
429
+ config = self.get_provider_config(provider)
430
+ return config is not None and config.enabled and config.api_key is not None
431
+
432
+ def get_enabled_providers(self) -> List[str]:
433
+ """Get list of enabled providers"""
434
+ return [name for name, config in self.provider_configs.items() if config.enabled]
435
+
436
+ def get_model_definition(self, model_id: str) -> Optional[Dict[str, Any]]:
437
+ """Get model definition"""
438
+ return self.model_definitions.get(model_id)
439
+
440
+ def get_models_by_provider(self, provider: str) -> List[Dict[str, Any]]:
441
+ """Get all models for a provider"""
442
+ return [model for model in self.model_definitions.values()
443
+ if model.get('provider') == provider]
444
+
445
+ def get_models_by_capability(self, capability: str) -> List[Dict[str, Any]]:
446
+ """Get models that support a specific capability"""
447
+ return [model for model in self.model_definitions.values()
448
+ if capability in model.get('capabilities', [])]
449
+
450
+ def reload(self):
451
+ """Reload configuration"""
452
+ self._load_configuration()
453
+ logger.info("Configuration reloaded")
454
+
455
+ def get_deployment_config(self, platform: str) -> Optional[Dict[str, Any]]:
456
+ """Get deployment platform configuration"""
457
+ return getattr(self, 'deployment_configs', {}).get(platform)
458
+
459
+ def is_deployment_enabled(self, platform: str) -> bool:
460
+ """Check if deployment platform is enabled"""
461
+ config = self.get_deployment_config(platform)
462
+ return config is not None and config.get("enabled", False)
463
+
464
+ def save_config(self, config_file: Optional[Path] = None):
465
+ """Save current configuration to YAML file (for compatibility)"""
466
+ if config_file is None:
467
+ config_file = Path.cwd() / "isa_model_config.yaml"
468
+
469
+ if not self.global_config:
470
+ logger.error("No configuration to save")
471
+ return
472
+
473
+ config_data = {
474
+ "environment": self.global_config.environment.value,
475
+ "debug": self.global_config.debug,
476
+ "database": {
477
+ "use_supabase": self.global_config.database.use_supabase,
478
+ "fallback_to_sqlite": self.global_config.database.fallback_to_sqlite,
479
+ },
480
+ "providers": {},
481
+ }
482
+
483
+ # Add provider configs (without API keys for security)
484
+ for provider_name, config in self.provider_configs.items():
485
+ config_data["providers"][provider_name] = {
486
+ "enabled": config.enabled,
487
+ "rate_limit_rpm": config.rate_limit_rpm,
488
+ "rate_limit_tpm": config.rate_limit_tpm,
489
+ "metadata": config.metadata,
490
+ }
491
+
492
+ try:
493
+ with open(config_file, 'w') as f:
494
+ yaml.dump(config_data, f, default_flow_style=False)
495
+ logger.info(f"Configuration saved to {config_file}")
496
+ except Exception as e:
497
+ logger.error(f"Failed to save configuration: {e}")
498
+
499
+ def get_summary(self) -> Dict[str, Any]:
500
+ """Get configuration summary"""
501
+ enabled_providers = self.get_enabled_providers()
502
+ configured_deployments = [p for p in getattr(self, 'deployment_configs', {}).keys()]
503
+
504
+ return {
505
+ "environment": self.global_config.environment.value if self.global_config else "unknown",
506
+ "debug": self.global_config.debug if self.global_config else False,
507
+ "enabled_providers": enabled_providers,
508
+ "configured_deployments": configured_deployments,
509
+ "total_models": len(self.model_definitions),
510
+ "database_backend": "supabase" if (self.global_config and self.global_config.database.use_supabase) else "sqlite",
511
+ "deployment_platform": self.global_config.deployment.platform if self.global_config else "unknown",
512
+ "cost_tracking": self.global_config.billing.track_costs if self.global_config else False,
513
+ "model_caching": self.global_config.cache.enabled if self.global_config else False,
514
+ }