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.
- isa_model/__init__.py +30 -1
- isa_model/client.py +770 -0
- isa_model/core/config/__init__.py +16 -0
- isa_model/core/config/config_manager.py +514 -0
- isa_model/core/config.py +426 -0
- isa_model/core/models/model_billing_tracker.py +476 -0
- isa_model/core/models/model_manager.py +399 -0
- isa_model/core/models/model_repo.py +343 -0
- isa_model/core/pricing_manager.py +426 -0
- isa_model/core/services/__init__.py +19 -0
- isa_model/core/services/intelligent_model_selector.py +547 -0
- isa_model/core/types.py +291 -0
- isa_model/deployment/__init__.py +2 -0
- isa_model/deployment/cloud/__init__.py +9 -0
- isa_model/deployment/cloud/modal/__init__.py +10 -0
- isa_model/deployment/cloud/modal/isa_vision_doc_service.py +766 -0
- isa_model/deployment/cloud/modal/isa_vision_table_service.py +532 -0
- isa_model/deployment/cloud/modal/isa_vision_ui_service.py +406 -0
- isa_model/deployment/cloud/modal/register_models.py +321 -0
- isa_model/deployment/runtime/deployed_service.py +338 -0
- isa_model/deployment/services/__init__.py +9 -0
- isa_model/deployment/services/auto_deploy_vision_service.py +537 -0
- isa_model/deployment/services/model_service.py +332 -0
- isa_model/deployment/services/service_monitor.py +356 -0
- isa_model/deployment/services/service_registry.py +527 -0
- isa_model/eval/__init__.py +80 -44
- isa_model/eval/config/__init__.py +10 -0
- isa_model/eval/config/evaluation_config.py +108 -0
- isa_model/eval/evaluators/__init__.py +18 -0
- isa_model/eval/evaluators/base_evaluator.py +503 -0
- isa_model/eval/evaluators/llm_evaluator.py +472 -0
- isa_model/eval/factory.py +417 -709
- isa_model/eval/infrastructure/__init__.py +24 -0
- isa_model/eval/infrastructure/experiment_tracker.py +466 -0
- isa_model/eval/metrics.py +191 -21
- isa_model/inference/ai_factory.py +187 -387
- isa_model/inference/providers/modal_provider.py +109 -0
- isa_model/inference/providers/yyds_provider.py +108 -0
- isa_model/inference/services/__init__.py +2 -1
- isa_model/inference/services/audio/base_stt_service.py +65 -1
- isa_model/inference/services/audio/base_tts_service.py +75 -1
- isa_model/inference/services/audio/openai_stt_service.py +189 -151
- isa_model/inference/services/audio/openai_tts_service.py +12 -10
- isa_model/inference/services/audio/replicate_tts_service.py +61 -56
- isa_model/inference/services/base_service.py +55 -55
- isa_model/inference/services/embedding/base_embed_service.py +65 -1
- isa_model/inference/services/embedding/ollama_embed_service.py +103 -43
- isa_model/inference/services/embedding/openai_embed_service.py +8 -10
- isa_model/inference/services/helpers/stacked_config.py +148 -0
- isa_model/inference/services/img/__init__.py +18 -0
- isa_model/inference/services/{vision → img}/base_image_gen_service.py +80 -35
- isa_model/inference/services/img/flux_professional_service.py +603 -0
- isa_model/inference/services/img/helpers/base_stacked_service.py +274 -0
- isa_model/inference/services/{vision → img}/replicate_image_gen_service.py +210 -69
- isa_model/inference/services/llm/__init__.py +3 -3
- isa_model/inference/services/llm/base_llm_service.py +519 -35
- isa_model/inference/services/llm/{llm_adapter.py → helpers/llm_adapter.py} +40 -0
- isa_model/inference/services/llm/helpers/llm_prompts.py +258 -0
- isa_model/inference/services/llm/helpers/llm_utils.py +280 -0
- isa_model/inference/services/llm/ollama_llm_service.py +150 -15
- isa_model/inference/services/llm/openai_llm_service.py +134 -31
- isa_model/inference/services/llm/yyds_llm_service.py +255 -0
- isa_model/inference/services/vision/__init__.py +38 -4
- isa_model/inference/services/vision/base_vision_service.py +241 -96
- isa_model/inference/services/vision/disabled/isA_vision_service.py +500 -0
- isa_model/inference/services/vision/doc_analysis_service.py +640 -0
- isa_model/inference/services/vision/helpers/base_stacked_service.py +274 -0
- isa_model/inference/services/vision/helpers/image_utils.py +272 -3
- isa_model/inference/services/vision/helpers/vision_prompts.py +297 -0
- isa_model/inference/services/vision/openai_vision_service.py +109 -170
- isa_model/inference/services/vision/replicate_vision_service.py +508 -0
- isa_model/inference/services/vision/ui_analysis_service.py +823 -0
- isa_model/scripts/register_models.py +370 -0
- isa_model/scripts/register_models_with_embeddings.py +510 -0
- isa_model/serving/__init__.py +19 -0
- isa_model/serving/api/__init__.py +10 -0
- isa_model/serving/api/fastapi_server.py +89 -0
- isa_model/serving/api/middleware/__init__.py +9 -0
- isa_model/serving/api/middleware/request_logger.py +88 -0
- isa_model/serving/api/routes/__init__.py +5 -0
- isa_model/serving/api/routes/health.py +82 -0
- isa_model/serving/api/routes/llm.py +19 -0
- isa_model/serving/api/routes/ui_analysis.py +223 -0
- isa_model/serving/api/routes/unified.py +202 -0
- isa_model/serving/api/routes/vision.py +19 -0
- isa_model/serving/api/schemas/__init__.py +17 -0
- isa_model/serving/api/schemas/common.py +33 -0
- isa_model/serving/api/schemas/ui_analysis.py +78 -0
- {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/METADATA +4 -1
- isa_model-0.3.6.dist-info/RECORD +147 -0
- isa_model/core/model_manager.py +0 -208
- isa_model/core/model_registry.py +0 -342
- isa_model/inference/billing_tracker.py +0 -406
- isa_model/inference/services/llm/triton_llm_service.py +0 -481
- isa_model/inference/services/vision/ollama_vision_service.py +0 -194
- isa_model-0.3.4.dist-info/RECORD +0 -91
- /isa_model/core/{model_storage.py → models/model_storage.py} +0 -0
- /isa_model/inference/services/{vision → embedding}/helpers/text_splitter.py +0 -0
- {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/WHEEL +0 -0
- {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,343 @@
|
|
1
|
+
"""
|
2
|
+
Unified Model Registry with Supabase Backend
|
3
|
+
|
4
|
+
Simplified architecture using only Supabase for model metadata and capabilities.
|
5
|
+
No SQLite support - uses unified configuration management.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import json
|
10
|
+
import logging
|
11
|
+
from typing import Dict, List, Optional, Any
|
12
|
+
from enum import Enum
|
13
|
+
from datetime import datetime
|
14
|
+
|
15
|
+
try:
|
16
|
+
from supabase import create_client, Client
|
17
|
+
SUPABASE_AVAILABLE = True
|
18
|
+
except ImportError:
|
19
|
+
SUPABASE_AVAILABLE = False
|
20
|
+
|
21
|
+
from ..config import ConfigManager
|
22
|
+
|
23
|
+
logger = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
class ModelCapability(str, Enum):
|
26
|
+
"""Model capabilities"""
|
27
|
+
TEXT_GENERATION = "text_generation"
|
28
|
+
CHAT = "chat"
|
29
|
+
EMBEDDING = "embedding"
|
30
|
+
RERANKING = "reranking"
|
31
|
+
REASONING = "reasoning"
|
32
|
+
IMAGE_GENERATION = "image_generation"
|
33
|
+
IMAGE_ANALYSIS = "image_analysis"
|
34
|
+
AUDIO_TRANSCRIPTION = "audio_transcription"
|
35
|
+
IMAGE_UNDERSTANDING = "image_understanding"
|
36
|
+
UI_DETECTION = "ui_detection"
|
37
|
+
OCR = "ocr"
|
38
|
+
TABLE_DETECTION = "table_detection"
|
39
|
+
TABLE_STRUCTURE_RECOGNITION = "table_structure_recognition"
|
40
|
+
|
41
|
+
class ModelType(str, Enum):
|
42
|
+
"""Model types"""
|
43
|
+
LLM = "llm"
|
44
|
+
EMBEDDING = "embedding"
|
45
|
+
RERANK = "rerank"
|
46
|
+
IMAGE = "image"
|
47
|
+
AUDIO = "audio"
|
48
|
+
VIDEO = "video"
|
49
|
+
VISION = "vision"
|
50
|
+
|
51
|
+
class ModelRegistry:
|
52
|
+
"""Unified Model Registry with Supabase backend"""
|
53
|
+
|
54
|
+
def __init__(self):
|
55
|
+
if not SUPABASE_AVAILABLE:
|
56
|
+
raise ImportError("supabase-py is required. Install with: pip install supabase")
|
57
|
+
|
58
|
+
# Get configuration from unified ConfigManager
|
59
|
+
self.config_manager = ConfigManager()
|
60
|
+
global_config = self.config_manager.get_global_config()
|
61
|
+
|
62
|
+
# Get Supabase configuration from database config
|
63
|
+
self.supabase_url = global_config.database.supabase_url or os.getenv("SUPABASE_URL")
|
64
|
+
self.supabase_key = global_config.database.supabase_key or os.getenv("SUPABASE_ANON_KEY") or os.getenv("SERVICE_ROLE_KEY")
|
65
|
+
|
66
|
+
if not self.supabase_url or not self.supabase_key:
|
67
|
+
raise ValueError("SUPABASE_URL and SUPABASE_ANON_KEY (or SERVICE_ROLE_KEY) must be configured")
|
68
|
+
|
69
|
+
# Initialize Supabase client
|
70
|
+
self.supabase: Client = create_client(self.supabase_url, self.supabase_key)
|
71
|
+
|
72
|
+
# Verify connection
|
73
|
+
self._ensure_tables()
|
74
|
+
|
75
|
+
logger.info("Model registry initialized with Supabase backend")
|
76
|
+
|
77
|
+
def _ensure_tables(self):
|
78
|
+
"""Ensure required tables exist in Supabase"""
|
79
|
+
try:
|
80
|
+
# Check if models table exists by trying to query it
|
81
|
+
result = self.supabase.table('models').select('model_id').limit(1).execute()
|
82
|
+
logger.debug("Models table verified")
|
83
|
+
except Exception as e:
|
84
|
+
logger.warning(f"Models table might not exist: {e}")
|
85
|
+
# In production, tables should be created via Supabase migrations
|
86
|
+
|
87
|
+
def register_model(self,
|
88
|
+
model_id: str,
|
89
|
+
model_type: ModelType,
|
90
|
+
capabilities: List[ModelCapability],
|
91
|
+
metadata: Dict[str, Any]) -> bool:
|
92
|
+
"""Register a model with its capabilities and metadata"""
|
93
|
+
try:
|
94
|
+
current_time = datetime.now().isoformat()
|
95
|
+
|
96
|
+
# Prepare model data
|
97
|
+
model_data = {
|
98
|
+
'model_id': model_id,
|
99
|
+
'model_type': model_type.value,
|
100
|
+
'metadata': json.dumps(metadata),
|
101
|
+
'created_at': current_time,
|
102
|
+
'updated_at': current_time
|
103
|
+
}
|
104
|
+
|
105
|
+
# Insert or update model
|
106
|
+
result = self.supabase.table('models').upsert(model_data).execute()
|
107
|
+
|
108
|
+
if not result.data:
|
109
|
+
logger.error(f"Failed to insert model {model_id}")
|
110
|
+
return False
|
111
|
+
|
112
|
+
# Delete existing capabilities
|
113
|
+
self.supabase.table('model_capabilities').delete().eq('model_id', model_id).execute()
|
114
|
+
|
115
|
+
# Insert new capabilities
|
116
|
+
if capabilities:
|
117
|
+
capability_data = [
|
118
|
+
{
|
119
|
+
'model_id': model_id,
|
120
|
+
'capability': capability.value,
|
121
|
+
'created_at': current_time
|
122
|
+
}
|
123
|
+
for capability in capabilities
|
124
|
+
]
|
125
|
+
|
126
|
+
cap_result = self.supabase.table('model_capabilities').insert(capability_data).execute()
|
127
|
+
|
128
|
+
if not cap_result.data:
|
129
|
+
logger.error(f"Failed to insert capabilities for {model_id}")
|
130
|
+
return False
|
131
|
+
|
132
|
+
logger.info(f"Successfully registered model {model_id} with {len(capabilities)} capabilities")
|
133
|
+
return True
|
134
|
+
|
135
|
+
except Exception as e:
|
136
|
+
logger.error(f"Failed to register model {model_id}: {e}")
|
137
|
+
return False
|
138
|
+
|
139
|
+
def unregister_model(self, model_id: str) -> bool:
|
140
|
+
"""Unregister a model"""
|
141
|
+
try:
|
142
|
+
# Delete model (capabilities will be cascade deleted)
|
143
|
+
result = self.supabase.table('models').delete().eq('model_id', model_id).execute()
|
144
|
+
|
145
|
+
if result.data:
|
146
|
+
logger.info(f"Unregistered model {model_id}")
|
147
|
+
return True
|
148
|
+
return False
|
149
|
+
|
150
|
+
except Exception as e:
|
151
|
+
logger.error(f"Failed to unregister model {model_id}: {e}")
|
152
|
+
return False
|
153
|
+
|
154
|
+
def get_model_info(self, model_id: str) -> Optional[Dict[str, Any]]:
|
155
|
+
"""Get model information"""
|
156
|
+
try:
|
157
|
+
# Get model info
|
158
|
+
model_result = self.supabase.table('models').select('*').eq('model_id', model_id).execute()
|
159
|
+
|
160
|
+
if not model_result.data:
|
161
|
+
return None
|
162
|
+
|
163
|
+
model_row = model_result.data[0]
|
164
|
+
|
165
|
+
# Get capabilities
|
166
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
167
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
168
|
+
|
169
|
+
model_info = {
|
170
|
+
"model_id": model_row["model_id"],
|
171
|
+
"type": model_row["model_type"],
|
172
|
+
"capabilities": capabilities,
|
173
|
+
"metadata": json.loads(model_row["metadata"]) if model_row["metadata"] else {},
|
174
|
+
"created_at": model_row["created_at"],
|
175
|
+
"updated_at": model_row["updated_at"]
|
176
|
+
}
|
177
|
+
|
178
|
+
return model_info
|
179
|
+
|
180
|
+
except Exception as e:
|
181
|
+
logger.error(f"Failed to get model info for {model_id}: {e}")
|
182
|
+
return None
|
183
|
+
|
184
|
+
def get_models_by_type(self, model_type: ModelType) -> Dict[str, Dict[str, Any]]:
|
185
|
+
"""Get all models of a specific type"""
|
186
|
+
try:
|
187
|
+
models_result = self.supabase.table('models').select('*').eq('model_type', model_type.value).execute()
|
188
|
+
|
189
|
+
result = {}
|
190
|
+
for model in models_result.data:
|
191
|
+
model_id = model["model_id"]
|
192
|
+
|
193
|
+
# Get capabilities for this model
|
194
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
195
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
196
|
+
|
197
|
+
result[model_id] = {
|
198
|
+
"type": model["model_type"],
|
199
|
+
"capabilities": capabilities,
|
200
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
201
|
+
"created_at": model["created_at"],
|
202
|
+
"updated_at": model["updated_at"]
|
203
|
+
}
|
204
|
+
|
205
|
+
return result
|
206
|
+
|
207
|
+
except Exception as e:
|
208
|
+
logger.error(f"Failed to get models by type {model_type}: {e}")
|
209
|
+
return {}
|
210
|
+
|
211
|
+
def get_models_by_capability(self, capability: ModelCapability) -> Dict[str, Dict[str, Any]]:
|
212
|
+
"""Get all models with a specific capability"""
|
213
|
+
try:
|
214
|
+
# Get model IDs with specific capability
|
215
|
+
cap_result = self.supabase.table('model_capabilities').select('model_id').eq('capability', capability.value).execute()
|
216
|
+
model_ids = [row['model_id'] for row in cap_result.data]
|
217
|
+
|
218
|
+
if not model_ids:
|
219
|
+
return {}
|
220
|
+
|
221
|
+
# Get model details
|
222
|
+
models_result = self.supabase.table('models').select('*').in_('model_id', model_ids).execute()
|
223
|
+
|
224
|
+
result = {}
|
225
|
+
for model in models_result.data:
|
226
|
+
model_id = model["model_id"]
|
227
|
+
|
228
|
+
# Get all capabilities for this model
|
229
|
+
all_caps_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
230
|
+
capabilities = [cap['capability'] for cap in all_caps_result.data]
|
231
|
+
|
232
|
+
result[model_id] = {
|
233
|
+
"type": model["model_type"],
|
234
|
+
"capabilities": capabilities,
|
235
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
236
|
+
"created_at": model["created_at"],
|
237
|
+
"updated_at": model["updated_at"]
|
238
|
+
}
|
239
|
+
|
240
|
+
return result
|
241
|
+
|
242
|
+
except Exception as e:
|
243
|
+
logger.error(f"Failed to get models by capability {capability}: {e}")
|
244
|
+
return {}
|
245
|
+
|
246
|
+
def has_capability(self, model_id: str, capability: ModelCapability) -> bool:
|
247
|
+
"""Check if a model has a specific capability"""
|
248
|
+
try:
|
249
|
+
result = self.supabase.table('model_capabilities').select('model_id').eq('model_id', model_id).eq('capability', capability.value).execute()
|
250
|
+
|
251
|
+
return len(result.data) > 0
|
252
|
+
|
253
|
+
except Exception as e:
|
254
|
+
logger.error(f"Failed to check capability for {model_id}: {e}")
|
255
|
+
return False
|
256
|
+
|
257
|
+
def list_models(self) -> Dict[str, Dict[str, Any]]:
|
258
|
+
"""List all registered models"""
|
259
|
+
try:
|
260
|
+
models_result = self.supabase.table('models').select('*').order('created_at', desc=True).execute()
|
261
|
+
|
262
|
+
result = {}
|
263
|
+
for model in models_result.data:
|
264
|
+
model_id = model["model_id"]
|
265
|
+
|
266
|
+
# Get capabilities for this model
|
267
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
268
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
269
|
+
|
270
|
+
result[model_id] = {
|
271
|
+
"type": model["model_type"],
|
272
|
+
"capabilities": capabilities,
|
273
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
274
|
+
"created_at": model["created_at"],
|
275
|
+
"updated_at": model["updated_at"]
|
276
|
+
}
|
277
|
+
|
278
|
+
return result
|
279
|
+
|
280
|
+
except Exception as e:
|
281
|
+
logger.error(f"Failed to list models: {e}")
|
282
|
+
return {}
|
283
|
+
|
284
|
+
def get_stats(self) -> Dict[str, Any]:
|
285
|
+
"""Get registry statistics"""
|
286
|
+
try:
|
287
|
+
# Count total models
|
288
|
+
total_result = self.supabase.table('models').select('model_id', count='exact').execute()
|
289
|
+
total_models = total_result.count if total_result.count is not None else 0
|
290
|
+
|
291
|
+
# Count by type (manual aggregation since RPC might not exist)
|
292
|
+
models_result = self.supabase.table('models').select('model_type').execute()
|
293
|
+
type_counts = {}
|
294
|
+
for model in models_result.data:
|
295
|
+
model_type = model['model_type']
|
296
|
+
type_counts[model_type] = type_counts.get(model_type, 0) + 1
|
297
|
+
|
298
|
+
# Count by capability
|
299
|
+
caps_result = self.supabase.table('model_capabilities').select('capability').execute()
|
300
|
+
capability_counts = {}
|
301
|
+
for cap in caps_result.data:
|
302
|
+
capability = cap['capability']
|
303
|
+
capability_counts[capability] = capability_counts.get(capability, 0) + 1
|
304
|
+
|
305
|
+
return {
|
306
|
+
"total_models": total_models,
|
307
|
+
"models_by_type": type_counts,
|
308
|
+
"models_by_capability": capability_counts
|
309
|
+
}
|
310
|
+
|
311
|
+
except Exception as e:
|
312
|
+
logger.error(f"Failed to get stats: {e}")
|
313
|
+
return {"total_models": 0, "models_by_type": {}, "models_by_capability": {}}
|
314
|
+
|
315
|
+
def search_models(self, query: str) -> Dict[str, Dict[str, Any]]:
|
316
|
+
"""Search models by name or metadata"""
|
317
|
+
try:
|
318
|
+
# Search in model_id and metadata
|
319
|
+
models_result = self.supabase.table('models').select('*').or_(
|
320
|
+
f'model_id.ilike.%{query}%,metadata.ilike.%{query}%'
|
321
|
+
).order('created_at', desc=True).execute()
|
322
|
+
|
323
|
+
result = {}
|
324
|
+
for model in models_result.data:
|
325
|
+
model_id = model["model_id"]
|
326
|
+
|
327
|
+
# Get capabilities for this model
|
328
|
+
cap_result = self.supabase.table('model_capabilities').select('capability').eq('model_id', model_id).execute()
|
329
|
+
capabilities = [cap['capability'] for cap in cap_result.data]
|
330
|
+
|
331
|
+
result[model_id] = {
|
332
|
+
"type": model["model_type"],
|
333
|
+
"capabilities": capabilities,
|
334
|
+
"metadata": json.loads(model["metadata"]) if model["metadata"] else {},
|
335
|
+
"created_at": model["created_at"],
|
336
|
+
"updated_at": model["updated_at"]
|
337
|
+
}
|
338
|
+
|
339
|
+
return result
|
340
|
+
|
341
|
+
except Exception as e:
|
342
|
+
logger.error(f"Failed to search models with query '{query}': {e}")
|
343
|
+
return {}
|