isa-model 0.3.5__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 (87) 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/{storage/supabase_storage.py → models/model_repo.py} +72 -73
  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/modal/isa_vision_doc_service.py +157 -3
  15. isa_model/deployment/cloud/modal/isa_vision_table_service.py +532 -0
  16. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +104 -3
  17. isa_model/deployment/cloud/modal/register_models.py +321 -0
  18. isa_model/deployment/runtime/deployed_service.py +338 -0
  19. isa_model/deployment/services/__init__.py +9 -0
  20. isa_model/deployment/services/auto_deploy_vision_service.py +537 -0
  21. isa_model/deployment/services/model_service.py +332 -0
  22. isa_model/deployment/services/service_monitor.py +356 -0
  23. isa_model/deployment/services/service_registry.py +527 -0
  24. isa_model/eval/__init__.py +80 -44
  25. isa_model/eval/config/__init__.py +10 -0
  26. isa_model/eval/config/evaluation_config.py +108 -0
  27. isa_model/eval/evaluators/__init__.py +18 -0
  28. isa_model/eval/evaluators/base_evaluator.py +503 -0
  29. isa_model/eval/evaluators/llm_evaluator.py +472 -0
  30. isa_model/eval/factory.py +417 -709
  31. isa_model/eval/infrastructure/__init__.py +24 -0
  32. isa_model/eval/infrastructure/experiment_tracker.py +466 -0
  33. isa_model/eval/metrics.py +191 -21
  34. isa_model/inference/ai_factory.py +181 -605
  35. isa_model/inference/services/audio/base_stt_service.py +65 -1
  36. isa_model/inference/services/audio/base_tts_service.py +75 -1
  37. isa_model/inference/services/audio/openai_stt_service.py +189 -151
  38. isa_model/inference/services/audio/openai_tts_service.py +12 -10
  39. isa_model/inference/services/audio/replicate_tts_service.py +61 -56
  40. isa_model/inference/services/base_service.py +55 -17
  41. isa_model/inference/services/embedding/base_embed_service.py +65 -1
  42. isa_model/inference/services/embedding/ollama_embed_service.py +103 -43
  43. isa_model/inference/services/embedding/openai_embed_service.py +8 -10
  44. isa_model/inference/services/helpers/stacked_config.py +148 -0
  45. isa_model/inference/services/img/__init__.py +18 -0
  46. isa_model/inference/services/{vision → img}/base_image_gen_service.py +80 -1
  47. isa_model/inference/services/{stacked → img}/flux_professional_service.py +25 -1
  48. isa_model/inference/services/{stacked → img/helpers}/base_stacked_service.py +40 -35
  49. isa_model/inference/services/{vision → img}/replicate_image_gen_service.py +44 -31
  50. isa_model/inference/services/llm/__init__.py +3 -3
  51. isa_model/inference/services/llm/base_llm_service.py +492 -40
  52. isa_model/inference/services/llm/helpers/llm_prompts.py +258 -0
  53. isa_model/inference/services/llm/helpers/llm_utils.py +280 -0
  54. isa_model/inference/services/llm/ollama_llm_service.py +51 -17
  55. isa_model/inference/services/llm/openai_llm_service.py +70 -19
  56. isa_model/inference/services/llm/yyds_llm_service.py +24 -23
  57. isa_model/inference/services/vision/__init__.py +38 -4
  58. isa_model/inference/services/vision/base_vision_service.py +218 -117
  59. isa_model/inference/services/vision/{isA_vision_service.py → disabled/isA_vision_service.py} +98 -0
  60. isa_model/inference/services/{stacked → vision}/doc_analysis_service.py +1 -1
  61. isa_model/inference/services/vision/helpers/base_stacked_service.py +274 -0
  62. isa_model/inference/services/vision/helpers/image_utils.py +272 -3
  63. isa_model/inference/services/vision/helpers/vision_prompts.py +297 -0
  64. isa_model/inference/services/vision/openai_vision_service.py +104 -307
  65. isa_model/inference/services/vision/replicate_vision_service.py +140 -325
  66. isa_model/inference/services/{stacked → vision}/ui_analysis_service.py +2 -498
  67. isa_model/scripts/register_models.py +370 -0
  68. isa_model/scripts/register_models_with_embeddings.py +510 -0
  69. isa_model/serving/api/fastapi_server.py +6 -1
  70. isa_model/serving/api/routes/unified.py +202 -0
  71. {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/METADATA +4 -1
  72. {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/RECORD +77 -53
  73. isa_model/config/__init__.py +0 -9
  74. isa_model/config/config_manager.py +0 -213
  75. isa_model/core/model_manager.py +0 -213
  76. isa_model/core/model_registry.py +0 -375
  77. isa_model/core/vision_models_init.py +0 -116
  78. isa_model/inference/billing_tracker.py +0 -406
  79. isa_model/inference/services/llm/triton_llm_service.py +0 -481
  80. isa_model/inference/services/stacked/__init__.py +0 -26
  81. isa_model/inference/services/stacked/config.py +0 -426
  82. isa_model/inference/services/vision/ollama_vision_service.py +0 -194
  83. /isa_model/core/{model_storage.py → models/model_storage.py} +0 -0
  84. /isa_model/inference/services/{vision → embedding}/helpers/text_splitter.py +0 -0
  85. /isa_model/inference/services/llm/{llm_adapter.py → helpers/llm_adapter.py} +0 -0
  86. {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/WHEEL +0 -0
  87. {isa_model-0.3.5.dist-info → isa_model-0.3.6.dist-info}/top_level.txt +0 -0
@@ -1,213 +0,0 @@
1
- from typing import Dict, Optional, List, Any
2
- import logging
3
- from pathlib import Path
4
- from huggingface_hub import hf_hub_download, snapshot_download
5
- from huggingface_hub.errors import HfHubHTTPError
6
- from .model_storage import ModelStorage, LocalModelStorage
7
- from .model_registry import ModelRegistry, ModelType, ModelCapability
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
- class ModelManager:
12
- """Model management service for handling model downloads, versions, and caching"""
13
-
14
- # 统一的模型计费信息 (per 1M tokens)
15
- MODEL_PRICING = {
16
- # OpenAI Models
17
- "openai": {
18
- "gpt-4o-mini": {"input": 0.15, "output": 0.6},
19
- "gpt-4.1-mini": {"input": 0.4, "output": 1.6},
20
- "gpt-4.1-nano": {"input": 0.1, "output": 0.4},
21
- "gpt-4o": {"input": 5.0, "output": 15.0},
22
- "gpt-4-turbo": {"input": 10.0, "output": 30.0},
23
- "gpt-4": {"input": 30.0, "output": 60.0},
24
- "gpt-3.5-turbo": {"input": 0.5, "output": 1.5},
25
- "text-embedding-3-small": {"input": 0.02, "output": 0.0},
26
- "text-embedding-3-large": {"input": 0.13, "output": 0.0},
27
- "whisper-1": {"input": 6.0, "output": 0.0},
28
- "tts-1": {"input": 15.0, "output": 0.0},
29
- "tts-1-hd": {"input": 30.0, "output": 0.0},
30
- },
31
- # Ollama Models (免费本地模型)
32
- "ollama": {
33
- "llama3.2:3b-instruct-fp16": {"input": 0.0, "output": 0.0},
34
- "llama3.2-vision:latest": {"input": 0.0, "output": 0.0},
35
- "bge-m3": {"input": 0.0, "output": 0.0},
36
- },
37
- # Replicate Models
38
- "replicate": {
39
- "black-forest-labs/flux-schnell": {"input": 3.0, "output": 0.0}, # $3 per 1000 images
40
- "black-forest-labs/flux-kontext-pro": {"input": 40.0, "output": 0.0}, # $0.04 per image = $40 per 1000 images
41
- "meta/meta-llama-3-8b-instruct": {"input": 0.05, "output": 0.25},
42
- "kokoro-82m": {"input": 0.0, "output": 0.4}, # ~$0.0004 per second
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
49
- }
50
- }
51
-
52
- def __init__(self,
53
- storage: Optional[ModelStorage] = None,
54
- registry: Optional[ModelRegistry] = None):
55
- self.storage = storage or LocalModelStorage()
56
- self.registry = registry or ModelRegistry()
57
-
58
- def get_model_pricing(self, provider: str, model_name: str) -> Dict[str, float]:
59
- """获取模型定价信息"""
60
- return self.MODEL_PRICING.get(provider, {}).get(model_name, {"input": 0.0, "output": 0.0})
61
-
62
- def calculate_cost(self, provider: str, model_name: str, input_tokens: int, output_tokens: int) -> float:
63
- """计算请求成本"""
64
- pricing = self.get_model_pricing(provider, model_name)
65
- input_cost = (input_tokens / 1_000_000) * pricing["input"]
66
- output_cost = (output_tokens / 1_000_000) * pricing["output"]
67
- return input_cost + output_cost
68
-
69
- def get_cheapest_model(self, provider: str, model_type: str = "llm") -> Optional[str]:
70
- """获取最便宜的模型"""
71
- provider_models = self.MODEL_PRICING.get(provider, {})
72
- if not provider_models:
73
- return None
74
-
75
- # 计算每个模型的平均成本 (假设输入输出比例 1:1)
76
- cheapest_model = None
77
- lowest_cost = float('inf')
78
-
79
- for model_name, pricing in provider_models.items():
80
- avg_cost = (pricing["input"] + pricing["output"]) / 2
81
- if avg_cost < lowest_cost:
82
- lowest_cost = avg_cost
83
- cheapest_model = model_name
84
-
85
- return cheapest_model
86
-
87
- async def get_model(self,
88
- model_id: str,
89
- repo_id: str,
90
- model_type: ModelType,
91
- capabilities: List[ModelCapability],
92
- revision: Optional[str] = None,
93
- force_download: bool = False) -> Optional[Path]:
94
- """
95
- Get model files, downloading if necessary
96
-
97
- Args:
98
- model_id: Unique identifier for the model
99
- repo_id: Hugging Face repository ID
100
- model_type: Type of model (LLM, embedding, etc.)
101
- capabilities: List of model capabilities
102
- revision: Specific model version/tag
103
- force_download: Force re-download even if cached
104
-
105
- Returns:
106
- Path to the model files or None if failed
107
- """
108
- # Check if model is already downloaded
109
- if not force_download:
110
- model_path = await self.storage.load_model(model_id)
111
- if model_path:
112
- logger.info(f"Using cached model {model_id}")
113
- return model_path
114
-
115
- try:
116
- # Download model files
117
- logger.info(f"Downloading model {model_id} from {repo_id}")
118
- model_dir = Path(f"./models/temp/{model_id}")
119
- model_dir.mkdir(parents=True, exist_ok=True)
120
-
121
- snapshot_download(
122
- repo_id=repo_id,
123
- revision=revision,
124
- local_dir=model_dir,
125
- local_dir_use_symlinks=False
126
- )
127
-
128
- # Save model and metadata
129
- metadata = {
130
- "repo_id": repo_id,
131
- "revision": revision,
132
- "downloaded_at": str(Path(model_dir).stat().st_mtime)
133
- }
134
-
135
- # Register model
136
- self.registry.register_model(
137
- model_id=model_id,
138
- model_type=model_type,
139
- capabilities=capabilities,
140
- metadata=metadata
141
- )
142
-
143
- # Save model files
144
- await self.storage.save_model(model_id, str(model_dir), metadata)
145
-
146
- return await self.storage.load_model(model_id)
147
-
148
- except HfHubHTTPError as e:
149
- logger.error(f"Failed to download model {model_id}: {e}")
150
- return None
151
- except Exception as e:
152
- logger.error(f"Unexpected error downloading model {model_id}: {e}")
153
- return None
154
-
155
- async def list_models(self) -> List[Dict[str, Any]]:
156
- """List all downloaded models with their metadata"""
157
- models = await self.storage.list_models()
158
- return [
159
- {
160
- "model_id": model_id,
161
- **metadata,
162
- **(self.registry.get_model_info(model_id) or {})
163
- }
164
- for model_id, metadata in models.items()
165
- ]
166
-
167
- async def remove_model(self, model_id: str) -> bool:
168
- """Remove a model and its metadata"""
169
- try:
170
- # Remove from storage
171
- storage_success = await self.storage.delete_model(model_id)
172
-
173
- # Unregister from registry
174
- registry_success = self.registry.unregister_model(model_id)
175
-
176
- return storage_success and registry_success
177
-
178
- except Exception as e:
179
- logger.error(f"Failed to remove model {model_id}: {e}")
180
- return False
181
-
182
- async def get_model_info(self, model_id: str) -> Optional[Dict[str, Any]]:
183
- """Get information about a specific model"""
184
- storage_info = await self.storage.get_metadata(model_id)
185
- registry_info = self.registry.get_model_info(model_id)
186
-
187
- if not storage_info and not registry_info:
188
- return None
189
-
190
- return {
191
- **(storage_info or {}),
192
- **(registry_info or {})
193
- }
194
-
195
- async def update_model(self,
196
- model_id: str,
197
- repo_id: str,
198
- model_type: ModelType,
199
- capabilities: List[ModelCapability],
200
- revision: Optional[str] = None) -> bool:
201
- """Update a model to a new version"""
202
- try:
203
- return bool(await self.get_model(
204
- model_id=model_id,
205
- repo_id=repo_id,
206
- model_type=model_type,
207
- capabilities=capabilities,
208
- revision=revision,
209
- force_download=True
210
- ))
211
- except Exception as e:
212
- logger.error(f"Failed to update model {model_id}: {e}")
213
- return False
@@ -1,375 +0,0 @@
1
- from typing import Dict, List, Optional, Any
2
- from enum import Enum
3
- import logging
4
- from pathlib import Path
5
- import json
6
- import sqlite3
7
- from datetime import datetime
8
- import threading
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
- class ModelCapability(str, Enum):
13
- """Model capabilities"""
14
- TEXT_GENERATION = "text_generation"
15
- CHAT = "chat"
16
- EMBEDDING = "embedding"
17
- RERANKING = "reranking"
18
- REASONING = "reasoning"
19
- IMAGE_GENERATION = "image_generation"
20
- IMAGE_ANALYSIS = "image_analysis"
21
- AUDIO_TRANSCRIPTION = "audio_transcription"
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"
27
-
28
- class ModelType(str, Enum):
29
- """Model types"""
30
- LLM = "llm"
31
- EMBEDDING = "embedding"
32
- RERANK = "rerank"
33
- IMAGE = "image"
34
- AUDIO = "audio"
35
- VIDEO = "video"
36
- VISION = "vision"
37
-
38
- class ModelRegistry:
39
- """Model registry with SQLite or Supabase backend"""
40
-
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")
66
-
67
- def _initialize_database(self):
68
- """Initialize SQLite database with required tables"""
69
- with sqlite3.connect(self.db_path) as conn:
70
- conn.execute("""
71
- CREATE TABLE IF NOT EXISTS models (
72
- model_id TEXT PRIMARY KEY,
73
- model_type TEXT NOT NULL,
74
- metadata TEXT,
75
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
76
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
77
- )
78
- """)
79
-
80
- conn.execute("""
81
- CREATE TABLE IF NOT EXISTS model_capabilities (
82
- model_id TEXT,
83
- capability TEXT,
84
- PRIMARY KEY (model_id, capability),
85
- FOREIGN KEY (model_id) REFERENCES models(model_id) ON DELETE CASCADE
86
- )
87
- """)
88
-
89
- conn.execute("""
90
- CREATE INDEX IF NOT EXISTS idx_model_type ON models(model_type)
91
- """)
92
-
93
- conn.execute("""
94
- CREATE INDEX IF NOT EXISTS idx_capability ON model_capabilities(capability)
95
- """)
96
-
97
- conn.commit()
98
-
99
- def register_model(self,
100
- model_id: str,
101
- model_type: ModelType,
102
- capabilities: List[ModelCapability],
103
- metadata: Dict[str, Any]) -> bool:
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
114
- try:
115
- with self._lock:
116
- with sqlite3.connect(self.db_path) as conn:
117
- # Insert or update model
118
- conn.execute("""
119
- INSERT OR REPLACE INTO models
120
- (model_id, model_type, metadata, updated_at)
121
- VALUES (?, ?, ?, CURRENT_TIMESTAMP)
122
- """, (model_id, model_type.value, json.dumps(metadata)))
123
-
124
- # Clear existing capabilities
125
- conn.execute("DELETE FROM model_capabilities WHERE model_id = ?", (model_id,))
126
-
127
- # Insert new capabilities
128
- for capability in capabilities:
129
- conn.execute("""
130
- INSERT INTO model_capabilities (model_id, capability)
131
- VALUES (?, ?)
132
- """, (model_id, capability.value))
133
-
134
- conn.commit()
135
-
136
- logger.info(f"Registered model {model_id}")
137
- return True
138
-
139
- except Exception as e:
140
- logger.error(f"Failed to register model {model_id}: {e}")
141
- return False
142
-
143
- def unregister_model(self, model_id: str) -> bool:
144
- """Unregister a model"""
145
- try:
146
- with self._lock:
147
- with sqlite3.connect(self.db_path) as conn:
148
- cursor = conn.execute("DELETE FROM models WHERE model_id = ?", (model_id,))
149
- conn.commit()
150
-
151
- if cursor.rowcount > 0:
152
- logger.info(f"Unregistered model {model_id}")
153
- return True
154
- return False
155
-
156
- except Exception as e:
157
- logger.error(f"Failed to unregister model {model_id}: {e}")
158
- return False
159
-
160
- def get_model_info(self, model_id: str) -> Optional[Dict[str, Any]]:
161
- """Get model information"""
162
- try:
163
- with sqlite3.connect(self.db_path) as conn:
164
- conn.row_factory = sqlite3.Row
165
-
166
- # Get model info
167
- model_row = conn.execute("""
168
- SELECT model_id, model_type, metadata, created_at, updated_at
169
- FROM models WHERE model_id = ?
170
- """, (model_id,)).fetchone()
171
-
172
- if not model_row:
173
- return None
174
-
175
- # Get capabilities
176
- capabilities = conn.execute("""
177
- SELECT capability FROM model_capabilities WHERE model_id = ?
178
- """, (model_id,)).fetchall()
179
-
180
- model_info = {
181
- "model_id": model_row["model_id"],
182
- "type": model_row["model_type"],
183
- "capabilities": [cap["capability"] for cap in capabilities],
184
- "metadata": json.loads(model_row["metadata"]) if model_row["metadata"] else {},
185
- "created_at": model_row["created_at"],
186
- "updated_at": model_row["updated_at"]
187
- }
188
-
189
- return model_info
190
-
191
- except Exception as e:
192
- logger.error(f"Failed to get model info for {model_id}: {e}")
193
- return None
194
-
195
- def get_models_by_type(self, model_type: ModelType) -> Dict[str, Dict[str, Any]]:
196
- """Get all models of a specific type"""
197
- try:
198
- with sqlite3.connect(self.db_path) as conn:
199
- conn.row_factory = sqlite3.Row
200
-
201
- models = conn.execute("""
202
- SELECT model_id, model_type, metadata, created_at, updated_at
203
- FROM models WHERE model_type = ?
204
- """, (model_type.value,)).fetchall()
205
-
206
- result = {}
207
- for model in models:
208
- model_id = model["model_id"]
209
-
210
- # Get capabilities for this model
211
- capabilities = conn.execute("""
212
- SELECT capability FROM model_capabilities WHERE model_id = ?
213
- """, (model_id,)).fetchall()
214
-
215
- result[model_id] = {
216
- "type": model["model_type"],
217
- "capabilities": [cap["capability"] for cap in capabilities],
218
- "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
219
- "created_at": model["created_at"],
220
- "updated_at": model["updated_at"]
221
- }
222
-
223
- return result
224
-
225
- except Exception as e:
226
- logger.error(f"Failed to get models by type {model_type}: {e}")
227
- return {}
228
-
229
- def get_models_by_capability(self, capability: ModelCapability) -> Dict[str, Dict[str, Any]]:
230
- """Get all models with a specific capability"""
231
- try:
232
- with sqlite3.connect(self.db_path) as conn:
233
- conn.row_factory = sqlite3.Row
234
-
235
- models = conn.execute("""
236
- SELECT DISTINCT m.model_id, m.model_type, m.metadata, m.created_at, m.updated_at
237
- FROM models m
238
- JOIN model_capabilities mc ON m.model_id = mc.model_id
239
- WHERE mc.capability = ?
240
- """, (capability.value,)).fetchall()
241
-
242
- result = {}
243
- for model in models:
244
- model_id = model["model_id"]
245
-
246
- # Get all capabilities for this model
247
- capabilities = conn.execute("""
248
- SELECT capability FROM model_capabilities WHERE model_id = ?
249
- """, (model_id,)).fetchall()
250
-
251
- result[model_id] = {
252
- "type": model["model_type"],
253
- "capabilities": [cap["capability"] for cap in capabilities],
254
- "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
255
- "created_at": model["created_at"],
256
- "updated_at": model["updated_at"]
257
- }
258
-
259
- return result
260
-
261
- except Exception as e:
262
- logger.error(f"Failed to get models by capability {capability}: {e}")
263
- return {}
264
-
265
- def has_capability(self, model_id: str, capability: ModelCapability) -> bool:
266
- """Check if a model has a specific capability"""
267
- try:
268
- with sqlite3.connect(self.db_path) as conn:
269
- result = conn.execute("""
270
- SELECT 1 FROM model_capabilities
271
- WHERE model_id = ? AND capability = ?
272
- """, (model_id, capability.value)).fetchone()
273
-
274
- return result is not None
275
-
276
- except Exception as e:
277
- logger.error(f"Failed to check capability for {model_id}: {e}")
278
- return False
279
-
280
- def list_models(self) -> Dict[str, Dict[str, Any]]:
281
- """List all registered models"""
282
- try:
283
- with sqlite3.connect(self.db_path) as conn:
284
- conn.row_factory = sqlite3.Row
285
-
286
- models = conn.execute("""
287
- SELECT model_id, model_type, metadata, created_at, updated_at
288
- FROM models ORDER BY created_at DESC
289
- """).fetchall()
290
-
291
- result = {}
292
- for model in models:
293
- model_id = model["model_id"]
294
-
295
- # Get capabilities for this model
296
- capabilities = conn.execute("""
297
- SELECT capability FROM model_capabilities WHERE model_id = ?
298
- """, (model_id,)).fetchall()
299
-
300
- result[model_id] = {
301
- "type": model["model_type"],
302
- "capabilities": [cap["capability"] for cap in capabilities],
303
- "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
304
- "created_at": model["created_at"],
305
- "updated_at": model["updated_at"]
306
- }
307
-
308
- return result
309
-
310
- except Exception as e:
311
- logger.error(f"Failed to list models: {e}")
312
- return {}
313
-
314
- def get_stats(self) -> Dict[str, Any]:
315
- """Get registry statistics"""
316
- try:
317
- with sqlite3.connect(self.db_path) as conn:
318
- # Count total models
319
- total_models = conn.execute("SELECT COUNT(*) FROM models").fetchone()[0]
320
-
321
- # Count by type
322
- type_counts = dict(conn.execute("""
323
- SELECT model_type, COUNT(*) FROM models GROUP BY model_type
324
- """).fetchall())
325
-
326
- # Count by capability
327
- capability_counts = dict(conn.execute("""
328
- SELECT capability, COUNT(*) FROM model_capabilities GROUP BY capability
329
- """).fetchall())
330
-
331
- return {
332
- "total_models": total_models,
333
- "models_by_type": type_counts,
334
- "models_by_capability": capability_counts
335
- }
336
-
337
- except Exception as e:
338
- logger.error(f"Failed to get stats: {e}")
339
- return {}
340
-
341
- def search_models(self, query: str) -> Dict[str, Dict[str, Any]]:
342
- """Search models by name or metadata"""
343
- try:
344
- with sqlite3.connect(self.db_path) as conn:
345
- conn.row_factory = sqlite3.Row
346
-
347
- models = conn.execute("""
348
- SELECT model_id, model_type, metadata, created_at, updated_at
349
- FROM models
350
- WHERE model_id LIKE ? OR metadata LIKE ?
351
- ORDER BY created_at DESC
352
- """, (f"%{query}%", f"%{query}%")).fetchall()
353
-
354
- result = {}
355
- for model in models:
356
- model_id = model["model_id"]
357
-
358
- # Get capabilities for this model
359
- capabilities = conn.execute("""
360
- SELECT capability FROM model_capabilities WHERE model_id = ?
361
- """, (model_id,)).fetchall()
362
-
363
- result[model_id] = {
364
- "type": model["model_type"],
365
- "capabilities": [cap["capability"] for cap in capabilities],
366
- "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
367
- "created_at": model["created_at"],
368
- "updated_at": model["updated_at"]
369
- }
370
-
371
- return result
372
-
373
- except Exception as e:
374
- logger.error(f"Failed to search models with query '{query}': {e}")
375
- return {}