isa-model 0.3.4__tar.gz → 0.3.5__tar.gz
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.
- {isa_model-0.3.4 → isa_model-0.3.5}/PKG-INFO +1 -1
- isa_model-0.3.5/isa_model/config/__init__.py +9 -0
- isa_model-0.3.5/isa_model/config/config_manager.py +213 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/core/model_manager.py +5 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/core/model_registry.py +39 -6
- isa_model-0.3.5/isa_model/core/storage/supabase_storage.py +344 -0
- isa_model-0.3.5/isa_model/core/vision_models_init.py +116 -0
- isa_model-0.3.5/isa_model/deployment/cloud/__init__.py +9 -0
- isa_model-0.3.5/isa_model/deployment/cloud/modal/__init__.py +10 -0
- isa_model-0.3.5/isa_model/deployment/cloud/modal/isa_vision_doc_service.py +612 -0
- isa_model-0.3.5/isa_model/deployment/cloud/modal/isa_vision_ui_service.py +305 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/ai_factory.py +238 -14
- isa_model-0.3.5/isa_model/inference/providers/modal_provider.py +109 -0
- isa_model-0.3.5/isa_model/inference/providers/yyds_provider.py +108 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/__init__.py +2 -1
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/base_service.py +0 -38
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/llm/base_llm_service.py +32 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/llm/llm_adapter.py +40 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/llm/ollama_llm_service.py +104 -3
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/llm/openai_llm_service.py +67 -15
- isa_model-0.3.5/isa_model/inference/services/llm/yyds_llm_service.py +254 -0
- isa_model-0.3.5/isa_model/inference/services/stacked/__init__.py +26 -0
- isa_model-0.3.5/isa_model/inference/services/stacked/base_stacked_service.py +269 -0
- isa_model-0.3.5/isa_model/inference/services/stacked/config.py +426 -0
- isa_model-0.3.5/isa_model/inference/services/stacked/doc_analysis_service.py +640 -0
- isa_model-0.3.5/isa_model/inference/services/stacked/flux_professional_service.py +579 -0
- isa_model-0.3.5/isa_model/inference/services/stacked/ui_analysis_service.py +1319 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/base_image_gen_service.py +0 -34
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/base_vision_service.py +46 -2
- isa_model-0.3.5/isa_model/inference/services/vision/isA_vision_service.py +402 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/openai_vision_service.py +151 -9
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/replicate_image_gen_service.py +166 -38
- isa_model-0.3.5/isa_model/inference/services/vision/replicate_vision_service.py +693 -0
- isa_model-0.3.5/isa_model/serving/__init__.py +19 -0
- isa_model-0.3.5/isa_model/serving/api/__init__.py +10 -0
- isa_model-0.3.5/isa_model/serving/api/fastapi_server.py +84 -0
- isa_model-0.3.5/isa_model/serving/api/middleware/__init__.py +9 -0
- isa_model-0.3.5/isa_model/serving/api/middleware/request_logger.py +88 -0
- isa_model-0.3.5/isa_model/serving/api/routes/__init__.py +5 -0
- isa_model-0.3.5/isa_model/serving/api/routes/health.py +82 -0
- isa_model-0.3.5/isa_model/serving/api/routes/llm.py +19 -0
- isa_model-0.3.5/isa_model/serving/api/routes/ui_analysis.py +223 -0
- isa_model-0.3.5/isa_model/serving/api/routes/vision.py +19 -0
- isa_model-0.3.5/isa_model/serving/api/schemas/__init__.py +17 -0
- isa_model-0.3.5/isa_model/serving/api/schemas/common.py +33 -0
- isa_model-0.3.5/isa_model/serving/api/schemas/ui_analysis.py +78 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model.egg-info/PKG-INFO +1 -1
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model.egg-info/SOURCES.txt +32 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/pyproject.toml +1 -1
- {isa_model-0.3.4 → isa_model-0.3.5}/MANIFEST.in +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/README.md +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/core/model_storage.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/core/storage/hf_storage.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/core/storage/local_storage.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/core/storage/minio_storage.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/core/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/core/deployment_config.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/core/deployment_manager.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/core/isa_deployment_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/gpu_int8_ds8/app/server.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/eval/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/eval/benchmarks.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/eval/factory.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/eval/metrics.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/adapter/unified_api.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/base.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/billing_tracker.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/base_provider.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/ml_provider.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/model_cache_manager.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/ollama_provider.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/openai_provider.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/replicate_provider.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/providers/triton_provider.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/audio/base_stt_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/audio/base_tts_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/audio/openai_realtime_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/audio/openai_stt_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/audio/openai_tts_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/audio/replicate_tts_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/embedding/base_embed_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/embedding/ollama_embed_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/embedding/openai_embed_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/llm/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/llm/triton_llm_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/ml/base_ml_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/ml/sklearn_ml_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/others/table_transformer_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/helpers/image_utils.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/helpers/text_splitter.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/services/vision/ollama_vision_service.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/utils/conversion/bge_rerank_convert.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/utils/conversion/onnx_converter.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/inference/utils/conversion/torch_converter.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/scripts/inference_tracker.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/scripts/mlflow_manager.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/scripts/model_registry.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/scripts/start_mlflow.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/scripts/training_tracker.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/annotation_schema.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/processors/annotation_processor.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/storage/dataset_manager.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/storage/dataset_schema.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/tests/test_annotation_flow.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/tests/test_minio copy.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/tests/test_minio_upload.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/annotation/views/annotation_controller.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/cloud/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/cloud/job_orchestrator.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/cloud/runpod_trainer.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/cloud/storage_manager.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/core/__init__.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/core/config.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/core/dataset.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/core/trainer.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/core/utils.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model/training/factory.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model.egg-info/dependency_links.txt +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model.egg-info/requires.txt +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/isa_model.egg-info/top_level.txt +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/setup.cfg +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/setup.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/tests/test_all_services.py +0 -0
- {isa_model-0.3.4 → isa_model-0.3.5}/tests/test_training_setup.py +0 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
"""
|
2
|
+
Configuration Management Module
|
3
|
+
|
4
|
+
Unified configuration system for all ISA Model components
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .config_manager import ConfigManager, get_config, DeploymentConfig, ModelConfig
|
8
|
+
|
9
|
+
__all__ = ["ConfigManager", "get_config", "DeploymentConfig", "ModelConfig"]
|
@@ -0,0 +1,213 @@
|
|
1
|
+
"""
|
2
|
+
Configuration Manager
|
3
|
+
|
4
|
+
Central configuration management with environment support
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import yaml
|
9
|
+
from typing import Dict, Any, Optional
|
10
|
+
from pathlib import Path
|
11
|
+
from dataclasses import dataclass
|
12
|
+
import logging
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
@dataclass
|
17
|
+
class ConfigSection:
|
18
|
+
"""Base configuration section"""
|
19
|
+
def to_dict(self) -> Dict[str, Any]:
|
20
|
+
return self.__dict__
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class DeploymentConfig(ConfigSection):
|
24
|
+
"""Deployment configuration"""
|
25
|
+
platform: str = "replicate" # replicate, modal, aws, local
|
26
|
+
modal_app_name: str = "isa-ui-analysis"
|
27
|
+
modal_gpu_type: str = "A100-40GB"
|
28
|
+
modal_memory: int = 32768
|
29
|
+
modal_timeout: int = 1800
|
30
|
+
modal_keep_warm: int = 1
|
31
|
+
|
32
|
+
@dataclass
|
33
|
+
class ModelConfig(ConfigSection):
|
34
|
+
"""Model configuration"""
|
35
|
+
ui_detection_model: str = "microsoft/omniparser-v2"
|
36
|
+
ui_planning_model: str = "gpt-4o-mini"
|
37
|
+
fallback_detection: str = "yolov8n"
|
38
|
+
quantization: bool = False
|
39
|
+
batch_size: int = 1
|
40
|
+
|
41
|
+
@dataclass
|
42
|
+
class ServingConfig(ConfigSection):
|
43
|
+
"""Serving configuration"""
|
44
|
+
host: str = "0.0.0.0"
|
45
|
+
port: int = 8000
|
46
|
+
workers: int = 1
|
47
|
+
reload: bool = False
|
48
|
+
log_level: str = "info"
|
49
|
+
cors_origins: list = None
|
50
|
+
|
51
|
+
def __post_init__(self):
|
52
|
+
if self.cors_origins is None:
|
53
|
+
self.cors_origins = ["*"]
|
54
|
+
|
55
|
+
@dataclass
|
56
|
+
class APIConfig(ConfigSection):
|
57
|
+
"""API configuration"""
|
58
|
+
rate_limit: int = 100 # requests per minute
|
59
|
+
max_file_size: int = 10 * 1024 * 1024 # 10MB
|
60
|
+
cache_ttl: int = 3600 # 1 hour
|
61
|
+
enable_auth: bool = False
|
62
|
+
|
63
|
+
@dataclass
|
64
|
+
class ISAConfig:
|
65
|
+
"""Complete ISA configuration"""
|
66
|
+
environment: str
|
67
|
+
deployment: DeploymentConfig
|
68
|
+
models: ModelConfig
|
69
|
+
serving: ServingConfig
|
70
|
+
api: APIConfig
|
71
|
+
|
72
|
+
def to_dict(self) -> Dict[str, Any]:
|
73
|
+
return {
|
74
|
+
"environment": self.environment,
|
75
|
+
"deployment": self.deployment.to_dict(),
|
76
|
+
"models": self.models.to_dict(),
|
77
|
+
"serving": self.serving.to_dict(),
|
78
|
+
"api": self.api.to_dict()
|
79
|
+
}
|
80
|
+
|
81
|
+
class ConfigManager:
|
82
|
+
"""Configuration manager with environment support"""
|
83
|
+
|
84
|
+
_instance = None
|
85
|
+
_config = None
|
86
|
+
|
87
|
+
def __new__(cls):
|
88
|
+
if cls._instance is None:
|
89
|
+
cls._instance = super(ConfigManager, cls).__new__(cls)
|
90
|
+
return cls._instance
|
91
|
+
|
92
|
+
def __init__(self):
|
93
|
+
if self._config is None:
|
94
|
+
self._load_config()
|
95
|
+
|
96
|
+
def _load_config(self):
|
97
|
+
"""Load configuration from environment and files"""
|
98
|
+
env = os.getenv("ISA_ENV", "development")
|
99
|
+
|
100
|
+
# Default configurations
|
101
|
+
default_config = {
|
102
|
+
"deployment": DeploymentConfig(),
|
103
|
+
"models": ModelConfig(),
|
104
|
+
"serving": ServingConfig(),
|
105
|
+
"api": APIConfig()
|
106
|
+
}
|
107
|
+
|
108
|
+
# Load environment-specific configuration
|
109
|
+
config_file = self._get_config_file(env)
|
110
|
+
if config_file and config_file.exists():
|
111
|
+
try:
|
112
|
+
with open(config_file, 'r') as f:
|
113
|
+
file_config = yaml.safe_load(f)
|
114
|
+
|
115
|
+
# Merge configurations
|
116
|
+
self._config = self._merge_configs(default_config, file_config, env)
|
117
|
+
logger.info(f"Loaded configuration for environment: {env}")
|
118
|
+
|
119
|
+
except Exception as e:
|
120
|
+
logger.warning(f"Failed to load config file {config_file}: {e}")
|
121
|
+
self._config = ISAConfig(environment=env, **default_config)
|
122
|
+
else:
|
123
|
+
logger.info(f"No config file found for {env}, using defaults")
|
124
|
+
self._config = ISAConfig(environment=env, **default_config)
|
125
|
+
|
126
|
+
# Override with environment variables
|
127
|
+
self._apply_env_overrides()
|
128
|
+
|
129
|
+
def _get_config_file(self, env: str) -> Optional[Path]:
|
130
|
+
"""Get configuration file path for environment"""
|
131
|
+
# Try to find config file in multiple locations
|
132
|
+
possible_paths = [
|
133
|
+
Path(__file__).parent / "environments" / f"{env}.yaml",
|
134
|
+
Path.cwd() / "config" / f"{env}.yaml",
|
135
|
+
Path.cwd() / f"config_{env}.yaml"
|
136
|
+
]
|
137
|
+
|
138
|
+
for path in possible_paths:
|
139
|
+
if path.exists():
|
140
|
+
return path
|
141
|
+
return None
|
142
|
+
|
143
|
+
def _merge_configs(self, default: Dict, file_config: Dict, env: str) -> ISAConfig:
|
144
|
+
"""Merge default and file configurations"""
|
145
|
+
|
146
|
+
# Update deployment config
|
147
|
+
deployment_data = {**default["deployment"].__dict__}
|
148
|
+
if "deployment" in file_config:
|
149
|
+
deployment_data.update(file_config["deployment"])
|
150
|
+
deployment = DeploymentConfig(**deployment_data)
|
151
|
+
|
152
|
+
# Update model config
|
153
|
+
models_data = {**default["models"].__dict__}
|
154
|
+
if "models" in file_config:
|
155
|
+
models_data.update(file_config["models"])
|
156
|
+
models = ModelConfig(**models_data)
|
157
|
+
|
158
|
+
# Update serving config
|
159
|
+
serving_data = {**default["serving"].__dict__}
|
160
|
+
if "serving" in file_config:
|
161
|
+
serving_data.update(file_config["serving"])
|
162
|
+
serving = ServingConfig(**serving_data)
|
163
|
+
|
164
|
+
# Update API config
|
165
|
+
api_data = {**default["api"].__dict__}
|
166
|
+
if "api" in file_config:
|
167
|
+
api_data.update(file_config["api"])
|
168
|
+
api = APIConfig(**api_data)
|
169
|
+
|
170
|
+
return ISAConfig(
|
171
|
+
environment=env,
|
172
|
+
deployment=deployment,
|
173
|
+
models=models,
|
174
|
+
serving=serving,
|
175
|
+
api=api
|
176
|
+
)
|
177
|
+
|
178
|
+
def _apply_env_overrides(self):
|
179
|
+
"""Apply environment variable overrides"""
|
180
|
+
# Deployment overrides
|
181
|
+
if os.getenv("ISA_DEPLOYMENT_PLATFORM"):
|
182
|
+
self._config.deployment.platform = os.getenv("ISA_DEPLOYMENT_PLATFORM")
|
183
|
+
|
184
|
+
# Model overrides
|
185
|
+
if os.getenv("ISA_UI_DETECTION_MODEL"):
|
186
|
+
self._config.models.ui_detection_model = os.getenv("ISA_UI_DETECTION_MODEL")
|
187
|
+
|
188
|
+
# Serving overrides
|
189
|
+
if os.getenv("ISA_SERVING_PORT"):
|
190
|
+
self._config.serving.port = int(os.getenv("ISA_SERVING_PORT"))
|
191
|
+
|
192
|
+
if os.getenv("ISA_SERVING_HOST"):
|
193
|
+
self._config.serving.host = os.getenv("ISA_SERVING_HOST")
|
194
|
+
|
195
|
+
def get_config(self) -> ISAConfig:
|
196
|
+
"""Get current configuration"""
|
197
|
+
return self._config
|
198
|
+
|
199
|
+
def reload(self):
|
200
|
+
"""Reload configuration"""
|
201
|
+
self._config = None
|
202
|
+
self._load_config()
|
203
|
+
|
204
|
+
# Singleton instance
|
205
|
+
_config_manager = ConfigManager()
|
206
|
+
|
207
|
+
def get_config() -> ISAConfig:
|
208
|
+
"""Get configuration instance"""
|
209
|
+
return _config_manager.get_config()
|
210
|
+
|
211
|
+
def reload_config():
|
212
|
+
"""Reload configuration"""
|
213
|
+
_config_manager.reload()
|
@@ -41,6 +41,11 @@ class ModelManager:
|
|
41
41
|
"meta/meta-llama-3-8b-instruct": {"input": 0.05, "output": 0.25},
|
42
42
|
"kokoro-82m": {"input": 0.0, "output": 0.4}, # ~$0.0004 per second
|
43
43
|
"jaaari/kokoro-82m:f559560eb822dc509045f3921a1921234918b91739db4bf3daab2169b71c7a13": {"input": 0.0, "output": 0.4},
|
44
|
+
},
|
45
|
+
# YYDS Models
|
46
|
+
"yyds": {
|
47
|
+
"claude-sonnet-4-20250514": {"input": 4.5, "output": 22.5}, # $0.0045/1K = $4.5/1M, $0.0225/1K = $22.5/1M
|
48
|
+
"claude-3-5-sonnet-20241022": {"input": 3.0, "output": 15.0}, # $0.003/1K = $3.0/1M, $0.015/1K = $15.0/1M
|
44
49
|
}
|
45
50
|
}
|
46
51
|
|
@@ -20,6 +20,10 @@ class ModelCapability(str, Enum):
|
|
20
20
|
IMAGE_ANALYSIS = "image_analysis"
|
21
21
|
AUDIO_TRANSCRIPTION = "audio_transcription"
|
22
22
|
IMAGE_UNDERSTANDING = "image_understanding"
|
23
|
+
UI_DETECTION = "ui_detection"
|
24
|
+
OCR = "ocr"
|
25
|
+
TABLE_DETECTION = "table_detection"
|
26
|
+
TABLE_STRUCTURE_RECOGNITION = "table_structure_recognition"
|
23
27
|
|
24
28
|
class ModelType(str, Enum):
|
25
29
|
"""Model types"""
|
@@ -32,13 +36,33 @@ class ModelType(str, Enum):
|
|
32
36
|
VISION = "vision"
|
33
37
|
|
34
38
|
class ModelRegistry:
|
35
|
-
"""
|
39
|
+
"""Model registry with SQLite or Supabase backend"""
|
36
40
|
|
37
|
-
def __init__(self, db_path: str = "./models/model_registry.db"):
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
41
|
+
def __init__(self, db_path: str = "./models/model_registry.db", use_supabase: bool = None):
|
42
|
+
# Auto-detect Supabase if environment variables are set
|
43
|
+
if use_supabase is None:
|
44
|
+
import os
|
45
|
+
use_supabase = bool(os.getenv("SUPABASE_URL") and os.getenv("SUPABASE_ANON_KEY"))
|
46
|
+
|
47
|
+
self.use_supabase = use_supabase
|
48
|
+
|
49
|
+
if self.use_supabase:
|
50
|
+
try:
|
51
|
+
from .storage.supabase_storage import SupabaseModelRegistry
|
52
|
+
self.backend = SupabaseModelRegistry()
|
53
|
+
logger.info("Using Supabase backend for model registry")
|
54
|
+
except ImportError as e:
|
55
|
+
logger.warning(f"Supabase not available, falling back to SQLite: {e}")
|
56
|
+
self.use_supabase = False
|
57
|
+
|
58
|
+
if not self.use_supabase:
|
59
|
+
# Use SQLite backend
|
60
|
+
self.db_path = Path(db_path)
|
61
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
62
|
+
self._lock = threading.Lock()
|
63
|
+
self._initialize_database()
|
64
|
+
self.backend = None
|
65
|
+
logger.info("Using SQLite backend for model registry")
|
42
66
|
|
43
67
|
def _initialize_database(self):
|
44
68
|
"""Initialize SQLite database with required tables"""
|
@@ -78,6 +102,15 @@ class ModelRegistry:
|
|
78
102
|
capabilities: List[ModelCapability],
|
79
103
|
metadata: Dict[str, Any]) -> bool:
|
80
104
|
"""Register a model with its capabilities and metadata"""
|
105
|
+
if self.use_supabase:
|
106
|
+
return self.backend.register_model(
|
107
|
+
model_id=model_id,
|
108
|
+
model_type=model_type.value,
|
109
|
+
capabilities=[cap.value for cap in capabilities],
|
110
|
+
metadata=metadata
|
111
|
+
)
|
112
|
+
|
113
|
+
# SQLite implementation
|
81
114
|
try:
|
82
115
|
with self._lock:
|
83
116
|
with sqlite3.connect(self.db_path) as conn:
|
@@ -0,0 +1,344 @@
|
|
1
|
+
"""
|
2
|
+
Supabase Storage Implementation for Model Registry
|
3
|
+
|
4
|
+
Uses Supabase as the backend database for model metadata and capabilities
|
5
|
+
Supports the full model lifecycle with cloud-based storage
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import json
|
10
|
+
import logging
|
11
|
+
from typing import Optional, Dict, Any, List
|
12
|
+
from datetime import datetime
|
13
|
+
from pathlib import Path
|
14
|
+
|
15
|
+
try:
|
16
|
+
from supabase import create_client, Client
|
17
|
+
from dotenv import load_dotenv
|
18
|
+
SUPABASE_AVAILABLE = True
|
19
|
+
except ImportError:
|
20
|
+
SUPABASE_AVAILABLE = False
|
21
|
+
|
22
|
+
from ..model_storage import ModelStorage
|
23
|
+
|
24
|
+
logger = logging.getLogger(__name__)
|
25
|
+
|
26
|
+
class SupabaseModelRegistry:
|
27
|
+
"""
|
28
|
+
Supabase-based model registry for metadata and capabilities
|
29
|
+
|
30
|
+
Replaces SQLite with cloud-based Supabase database
|
31
|
+
"""
|
32
|
+
|
33
|
+
def __init__(self):
|
34
|
+
if not SUPABASE_AVAILABLE:
|
35
|
+
raise ImportError("supabase-py is required. Install with: pip install supabase")
|
36
|
+
|
37
|
+
# Load environment variables
|
38
|
+
load_dotenv()
|
39
|
+
|
40
|
+
self.supabase_url = os.getenv("SUPABASE_URL")
|
41
|
+
self.supabase_key = os.getenv("SUPABASE_ANON_KEY")
|
42
|
+
|
43
|
+
if not self.supabase_url or not self.supabase_key:
|
44
|
+
raise ValueError("SUPABASE_URL and SUPABASE_ANON_KEY must be set in environment")
|
45
|
+
|
46
|
+
# Initialize Supabase client
|
47
|
+
self.supabase: Client = create_client(self.supabase_url, self.supabase_key)
|
48
|
+
|
49
|
+
# Initialize tables if needed
|
50
|
+
self._ensure_tables()
|
51
|
+
|
52
|
+
logger.info("Supabase model registry initialized")
|
53
|
+
|
54
|
+
def _ensure_tables(self):
|
55
|
+
"""Ensure required tables exist in Supabase"""
|
56
|
+
# Note: In production, these tables should be created via Supabase migrations
|
57
|
+
# This is just for development/initialization
|
58
|
+
try:
|
59
|
+
# Check if models table exists by trying to query it
|
60
|
+
result = self.supabase.table('models').select('model_id').limit(1).execute()
|
61
|
+
except Exception as e:
|
62
|
+
logger.warning(f"Models table might not exist: {e}")
|
63
|
+
# In production, you would run proper migrations here
|
64
|
+
|
65
|
+
def register_model(self,
|
66
|
+
model_id: str,
|
67
|
+
model_type: str,
|
68
|
+
capabilities: List[str],
|
69
|
+
metadata: Dict[str, Any]) -> bool:
|
70
|
+
"""Register a model with its capabilities and metadata"""
|
71
|
+
try:
|
72
|
+
current_time = datetime.now().isoformat()
|
73
|
+
|
74
|
+
# Prepare model data
|
75
|
+
model_data = {
|
76
|
+
'model_id': model_id,
|
77
|
+
'model_type': model_type,
|
78
|
+
'metadata': json.dumps(metadata),
|
79
|
+
'created_at': current_time,
|
80
|
+
'updated_at': current_time
|
81
|
+
}
|
82
|
+
|
83
|
+
# Insert or update model
|
84
|
+
result = self.supabase.table('models').upsert(model_data).execute()
|
85
|
+
|
86
|
+
if not result.data:
|
87
|
+
logger.error(f"Failed to insert model {model_id}")
|
88
|
+
return False
|
89
|
+
|
90
|
+
# Delete existing capabilities
|
91
|
+
self.supabase.table('model_capabilities').delete().eq('model_id', model_id).execute()
|
92
|
+
|
93
|
+
# Insert new capabilities
|
94
|
+
if capabilities:
|
95
|
+
capability_data = [
|
96
|
+
{
|
97
|
+
'model_id': model_id,
|
98
|
+
'capability': capability,
|
99
|
+
'created_at': current_time
|
100
|
+
}
|
101
|
+
for capability in capabilities
|
102
|
+
]
|
103
|
+
|
104
|
+
cap_result = self.supabase.table('model_capabilities').insert(capability_data).execute()
|
105
|
+
|
106
|
+
if not cap_result.data:
|
107
|
+
logger.error(f"Failed to insert capabilities for {model_id}")
|
108
|
+
return False
|
109
|
+
|
110
|
+
logger.info(f"Successfully registered model {model_id}")
|
111
|
+
return True
|
112
|
+
|
113
|
+
except Exception as e:
|
114
|
+
logger.error(f"Failed to register model {model_id}: {e}")
|
115
|
+
return False
|
116
|
+
|
117
|
+
def unregister_model(self, model_id: str) -> bool:
|
118
|
+
"""Unregister a model"""
|
119
|
+
try:
|
120
|
+
# Delete model (capabilities will be cascade deleted)
|
121
|
+
result = self.supabase.table('models').delete().eq('model_id', model_id).execute()
|
122
|
+
|
123
|
+
if result.data:
|
124
|
+
logger.info(f"Unregistered model {model_id}")
|
125
|
+
return True
|
126
|
+
return False
|
127
|
+
|
128
|
+
except Exception as e:
|
129
|
+
logger.error(f"Failed to unregister model {model_id}: {e}")
|
130
|
+
return False
|
131
|
+
|
132
|
+
def get_model_info(self, model_id: str) -> Optional[Dict[str, Any]]:
|
133
|
+
"""Get model information"""
|
134
|
+
try:
|
135
|
+
# Get model info
|
136
|
+
model_result = self.supabase.table('models').select('*').eq('model_id', model_id).execute()
|
137
|
+
|
138
|
+
if not model_result.data:
|
139
|
+
return None
|
140
|
+
|
141
|
+
model_row = model_result.data[0]
|
142
|
+
|
143
|
+
# Get capabilities
|
144
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
145
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
146
|
+
|
147
|
+
model_info = {
|
148
|
+
"model_id": model_row["model_id"],
|
149
|
+
"type": model_row["model_type"],
|
150
|
+
"capabilities": capabilities,
|
151
|
+
"metadata": json.loads(model_row["metadata"]) if model_row["metadata"] else {},
|
152
|
+
"created_at": model_row["created_at"],
|
153
|
+
"updated_at": model_row["updated_at"]
|
154
|
+
}
|
155
|
+
|
156
|
+
return model_info
|
157
|
+
|
158
|
+
except Exception as e:
|
159
|
+
logger.error(f"Failed to get model info for {model_id}: {e}")
|
160
|
+
return None
|
161
|
+
|
162
|
+
def get_models_by_type(self, model_type: str) -> Dict[str, Dict[str, Any]]:
|
163
|
+
"""Get all models of a specific type"""
|
164
|
+
try:
|
165
|
+
models_result = self.supabase.table('models').select('*').eq('model_type', model_type).execute()
|
166
|
+
|
167
|
+
result = {}
|
168
|
+
for model in models_result.data:
|
169
|
+
model_id = model["model_id"]
|
170
|
+
|
171
|
+
# Get capabilities for this model
|
172
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
173
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
174
|
+
|
175
|
+
result[model_id] = {
|
176
|
+
"type": model["model_type"],
|
177
|
+
"capabilities": capabilities,
|
178
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
179
|
+
"created_at": model["created_at"],
|
180
|
+
"updated_at": model["updated_at"]
|
181
|
+
}
|
182
|
+
|
183
|
+
return result
|
184
|
+
|
185
|
+
except Exception as e:
|
186
|
+
logger.error(f"Failed to get models by type {model_type}: {e}")
|
187
|
+
return {}
|
188
|
+
|
189
|
+
def get_models_by_capability(self, capability: str) -> Dict[str, Dict[str, Any]]:
|
190
|
+
"""Get all models with a specific capability"""
|
191
|
+
try:
|
192
|
+
# Join query to get models with specific capability
|
193
|
+
query = """
|
194
|
+
SELECT DISTINCT m.*, mc.capability
|
195
|
+
FROM models m
|
196
|
+
INNER JOIN model_capabilities mc ON m.model_id = mc.model_id
|
197
|
+
WHERE mc.capability = %s
|
198
|
+
"""
|
199
|
+
|
200
|
+
# Use RPC for complex queries
|
201
|
+
result = self.supabase.rpc('get_models_by_capability', {'capability_name': capability}).execute()
|
202
|
+
|
203
|
+
if result.data:
|
204
|
+
models_dict = {}
|
205
|
+
for row in result.data:
|
206
|
+
model_id = row['model_id']
|
207
|
+
if model_id not in models_dict:
|
208
|
+
# Get all capabilities for this model
|
209
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
210
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
211
|
+
|
212
|
+
models_dict[model_id] = {
|
213
|
+
"type": row["model_type"],
|
214
|
+
"capabilities": capabilities,
|
215
|
+
"metadata": json.loads(row["metadata"]) if row["metadata"] else {},
|
216
|
+
"created_at": row["created_at"],
|
217
|
+
"updated_at": row["updated_at"]
|
218
|
+
}
|
219
|
+
|
220
|
+
return models_dict
|
221
|
+
|
222
|
+
# Fallback: manual join if RPC not available
|
223
|
+
cap_result = self.supabase.table('model_capabilities').select('model_id').eq('capability', capability).execute()
|
224
|
+
model_ids = [row['model_id'] for row in cap_result.data]
|
225
|
+
|
226
|
+
if not model_ids:
|
227
|
+
return {}
|
228
|
+
|
229
|
+
models_result = self.supabase.table('models').select('*').in_('model_id', model_ids).execute()
|
230
|
+
|
231
|
+
result_dict = {}
|
232
|
+
for model in models_result.data:
|
233
|
+
model_id = model["model_id"]
|
234
|
+
|
235
|
+
# Get all capabilities for this model
|
236
|
+
all_caps_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
237
|
+
capabilities = [cap['capability'] for cap in all_caps_result.data]
|
238
|
+
|
239
|
+
result_dict[model_id] = {
|
240
|
+
"type": model["model_type"],
|
241
|
+
"capabilities": capabilities,
|
242
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
243
|
+
"created_at": model["created_at"],
|
244
|
+
"updated_at": model["updated_at"]
|
245
|
+
}
|
246
|
+
|
247
|
+
return result_dict
|
248
|
+
|
249
|
+
except Exception as e:
|
250
|
+
logger.error(f"Failed to get models by capability {capability}: {e}")
|
251
|
+
return {}
|
252
|
+
|
253
|
+
def has_capability(self, model_id: str, capability: str) -> bool:
|
254
|
+
"""Check if a model has a specific capability"""
|
255
|
+
try:
|
256
|
+
result = self.supabase.table('model_capabilities').select('model_id').eq('model_id', model_id).eq('capability', capability).execute()
|
257
|
+
|
258
|
+
return len(result.data) > 0
|
259
|
+
|
260
|
+
except Exception as e:
|
261
|
+
logger.error(f"Failed to check capability for {model_id}: {e}")
|
262
|
+
return False
|
263
|
+
|
264
|
+
def list_models(self) -> Dict[str, Dict[str, Any]]:
|
265
|
+
"""List all registered models"""
|
266
|
+
try:
|
267
|
+
models_result = self.supabase.table('models').select('*').order('created_at', desc=True).execute()
|
268
|
+
|
269
|
+
result = {}
|
270
|
+
for model in models_result.data:
|
271
|
+
model_id = model["model_id"]
|
272
|
+
|
273
|
+
# Get capabilities for this model
|
274
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
275
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
276
|
+
|
277
|
+
result[model_id] = {
|
278
|
+
"type": model["model_type"],
|
279
|
+
"capabilities": capabilities,
|
280
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
281
|
+
"created_at": model["created_at"],
|
282
|
+
"updated_at": model["updated_at"]
|
283
|
+
}
|
284
|
+
|
285
|
+
return result
|
286
|
+
|
287
|
+
except Exception as e:
|
288
|
+
logger.error(f"Failed to list models: {e}")
|
289
|
+
return {}
|
290
|
+
|
291
|
+
def get_stats(self) -> Dict[str, Any]:
|
292
|
+
"""Get registry statistics"""
|
293
|
+
try:
|
294
|
+
# Count total models
|
295
|
+
total_result = self.supabase.table('models').select('model_id', count='exact').execute()
|
296
|
+
total_models = total_result.count if total_result.count is not None else 0
|
297
|
+
|
298
|
+
# Count by type
|
299
|
+
type_result = self.supabase.rpc('get_model_type_counts').execute()
|
300
|
+
type_counts = {row['model_type']: row['count'] for row in type_result.data} if type_result.data else {}
|
301
|
+
|
302
|
+
# Count by capability
|
303
|
+
cap_result = self.supabase.rpc('get_capability_counts').execute()
|
304
|
+
capability_counts = {row['capability']: row['count'] for row in cap_result.data} if cap_result.data else {}
|
305
|
+
|
306
|
+
return {
|
307
|
+
"total_models": total_models,
|
308
|
+
"models_by_type": type_counts,
|
309
|
+
"models_by_capability": capability_counts
|
310
|
+
}
|
311
|
+
|
312
|
+
except Exception as e:
|
313
|
+
logger.error(f"Failed to get stats: {e}")
|
314
|
+
return {"total_models": 0, "models_by_type": {}, "models_by_capability": {}}
|
315
|
+
|
316
|
+
def search_models(self, query: str) -> Dict[str, Dict[str, Any]]:
|
317
|
+
"""Search models by name or metadata"""
|
318
|
+
try:
|
319
|
+
# Search in model_id and metadata
|
320
|
+
models_result = self.supabase.table('models').select('*').or_(
|
321
|
+
f'model_id.ilike.%{query}%,metadata.ilike.%{query}%'
|
322
|
+
).order('created_at', desc=True).execute()
|
323
|
+
|
324
|
+
result = {}
|
325
|
+
for model in models_result.data:
|
326
|
+
model_id = model["model_id"]
|
327
|
+
|
328
|
+
# Get capabilities for this model
|
329
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
330
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
331
|
+
|
332
|
+
result[model_id] = {
|
333
|
+
"type": model["model_type"],
|
334
|
+
"capabilities": capabilities,
|
335
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
336
|
+
"created_at": model["created_at"],
|
337
|
+
"updated_at": model["updated_at"]
|
338
|
+
}
|
339
|
+
|
340
|
+
return result
|
341
|
+
|
342
|
+
except Exception as e:
|
343
|
+
logger.error(f"Failed to search models with query '{query}': {e}")
|
344
|
+
return {}
|