isa-model 0.0.2__py3-none-any.whl → 0.3.1__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 (93) hide show
  1. isa_model/__init__.py +1 -1
  2. isa_model/core/model_manager.py +69 -4
  3. isa_model/core/model_registry.py +273 -46
  4. isa_model/core/storage/hf_storage.py +419 -0
  5. isa_model/deployment/__init__.py +52 -0
  6. isa_model/deployment/core/__init__.py +34 -0
  7. isa_model/deployment/core/deployment_config.py +356 -0
  8. isa_model/deployment/core/deployment_manager.py +549 -0
  9. isa_model/deployment/core/isa_deployment_service.py +401 -0
  10. isa_model/eval/factory.py +381 -140
  11. isa_model/inference/ai_factory.py +427 -236
  12. isa_model/inference/billing_tracker.py +406 -0
  13. isa_model/inference/providers/base_provider.py +51 -4
  14. isa_model/inference/providers/ml_provider.py +50 -0
  15. isa_model/inference/providers/ollama_provider.py +37 -18
  16. isa_model/inference/providers/openai_provider.py +65 -36
  17. isa_model/inference/providers/replicate_provider.py +42 -30
  18. isa_model/inference/services/audio/base_stt_service.py +21 -2
  19. isa_model/inference/services/audio/openai_realtime_service.py +353 -0
  20. isa_model/inference/services/audio/openai_stt_service.py +252 -0
  21. isa_model/inference/services/audio/openai_tts_service.py +149 -9
  22. isa_model/inference/services/audio/replicate_tts_service.py +239 -0
  23. isa_model/inference/services/base_service.py +36 -1
  24. isa_model/inference/services/embedding/base_embed_service.py +112 -0
  25. isa_model/inference/services/embedding/ollama_embed_service.py +28 -2
  26. isa_model/inference/services/embedding/openai_embed_service.py +223 -0
  27. isa_model/inference/services/llm/__init__.py +2 -0
  28. isa_model/inference/services/llm/base_llm_service.py +158 -86
  29. isa_model/inference/services/llm/llm_adapter.py +414 -0
  30. isa_model/inference/services/llm/ollama_llm_service.py +252 -63
  31. isa_model/inference/services/llm/openai_llm_service.py +231 -93
  32. isa_model/inference/services/llm/triton_llm_service.py +481 -0
  33. isa_model/inference/services/ml/base_ml_service.py +78 -0
  34. isa_model/inference/services/ml/sklearn_ml_service.py +140 -0
  35. isa_model/inference/services/vision/__init__.py +3 -3
  36. isa_model/inference/services/vision/base_image_gen_service.py +161 -0
  37. isa_model/inference/services/vision/base_vision_service.py +177 -0
  38. isa_model/inference/services/vision/helpers/image_utils.py +4 -3
  39. isa_model/inference/services/vision/ollama_vision_service.py +151 -17
  40. isa_model/inference/services/vision/openai_vision_service.py +275 -41
  41. isa_model/inference/services/vision/replicate_image_gen_service.py +278 -118
  42. isa_model/training/__init__.py +62 -32
  43. isa_model/training/cloud/__init__.py +22 -0
  44. isa_model/training/cloud/job_orchestrator.py +402 -0
  45. isa_model/training/cloud/runpod_trainer.py +454 -0
  46. isa_model/training/cloud/storage_manager.py +482 -0
  47. isa_model/training/core/__init__.py +23 -0
  48. isa_model/training/core/config.py +181 -0
  49. isa_model/training/core/dataset.py +222 -0
  50. isa_model/training/core/trainer.py +720 -0
  51. isa_model/training/core/utils.py +213 -0
  52. isa_model/training/factory.py +229 -198
  53. isa_model-0.3.1.dist-info/METADATA +465 -0
  54. isa_model-0.3.1.dist-info/RECORD +91 -0
  55. isa_model/core/model_router.py +0 -226
  56. isa_model/core/model_version.py +0 -0
  57. isa_model/core/resource_manager.py +0 -202
  58. isa_model/deployment/gpu_fp16_ds8/models/deepseek_r1/1/model.py +0 -120
  59. isa_model/deployment/gpu_fp16_ds8/scripts/download_model.py +0 -18
  60. isa_model/training/engine/llama_factory/__init__.py +0 -39
  61. isa_model/training/engine/llama_factory/config.py +0 -115
  62. isa_model/training/engine/llama_factory/data_adapter.py +0 -284
  63. isa_model/training/engine/llama_factory/examples/__init__.py +0 -6
  64. isa_model/training/engine/llama_factory/examples/finetune_with_tracking.py +0 -185
  65. isa_model/training/engine/llama_factory/examples/rlhf_with_tracking.py +0 -163
  66. isa_model/training/engine/llama_factory/factory.py +0 -331
  67. isa_model/training/engine/llama_factory/rl.py +0 -254
  68. isa_model/training/engine/llama_factory/trainer.py +0 -171
  69. isa_model/training/image_model/configs/create_config.py +0 -37
  70. isa_model/training/image_model/configs/create_flux_config.py +0 -26
  71. isa_model/training/image_model/configs/create_lora_config.py +0 -21
  72. isa_model/training/image_model/prepare_massed_compute.py +0 -97
  73. isa_model/training/image_model/prepare_upload.py +0 -17
  74. isa_model/training/image_model/raw_data/create_captions.py +0 -16
  75. isa_model/training/image_model/raw_data/create_lora_captions.py +0 -20
  76. isa_model/training/image_model/raw_data/pre_processing.py +0 -200
  77. isa_model/training/image_model/train/train.py +0 -42
  78. isa_model/training/image_model/train/train_flux.py +0 -41
  79. isa_model/training/image_model/train/train_lora.py +0 -57
  80. isa_model/training/image_model/train_main.py +0 -25
  81. isa_model-0.0.2.dist-info/METADATA +0 -327
  82. isa_model-0.0.2.dist-info/RECORD +0 -92
  83. isa_model-0.0.2.dist-info/licenses/LICENSE +0 -21
  84. /isa_model/training/{llm_model/annotation → annotation}/annotation_schema.py +0 -0
  85. /isa_model/training/{llm_model/annotation → annotation}/processors/annotation_processor.py +0 -0
  86. /isa_model/training/{llm_model/annotation → annotation}/storage/dataset_manager.py +0 -0
  87. /isa_model/training/{llm_model/annotation → annotation}/storage/dataset_schema.py +0 -0
  88. /isa_model/training/{llm_model/annotation → annotation}/tests/test_annotation_flow.py +0 -0
  89. /isa_model/training/{llm_model/annotation → annotation}/tests/test_minio copy.py +0 -0
  90. /isa_model/training/{llm_model/annotation → annotation}/tests/test_minio_upload.py +0 -0
  91. /isa_model/training/{llm_model/annotation → annotation}/views/annotation_controller.py +0 -0
  92. {isa_model-0.0.2.dist-info → isa_model-0.3.1.dist-info}/WHEEL +0 -0
  93. {isa_model-0.0.2.dist-info → isa_model-0.3.1.dist-info}/top_level.txt +0 -0
isa_model/__init__.py CHANGED
@@ -2,4 +2,4 @@
2
2
  isA_Model - A simple interface for AI model integration
3
3
  """
4
4
 
5
- __version__ = "0.1.0"
5
+ __version__ = "0.0.1"
@@ -2,7 +2,7 @@ from typing import Dict, Optional, List, Any
2
2
  import logging
3
3
  from pathlib import Path
4
4
  from huggingface_hub import hf_hub_download, snapshot_download
5
- from huggingface_hub.utils import HfHubHTTPError
5
+ from huggingface_hub.errors import HfHubHTTPError
6
6
  from .model_storage import ModelStorage, LocalModelStorage
7
7
  from .model_registry import ModelRegistry, ModelType, ModelCapability
8
8
 
@@ -11,19 +11,81 @@ logger = logging.getLogger(__name__)
11
11
  class ModelManager:
12
12
  """Model management service for handling model downloads, versions, and caching"""
13
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
+ }
46
+
14
47
  def __init__(self,
15
48
  storage: Optional[ModelStorage] = None,
16
49
  registry: Optional[ModelRegistry] = None):
17
50
  self.storage = storage or LocalModelStorage()
18
51
  self.registry = registry or ModelRegistry()
19
52
 
53
+ def get_model_pricing(self, provider: str, model_name: str) -> Dict[str, float]:
54
+ """获取模型定价信息"""
55
+ return self.MODEL_PRICING.get(provider, {}).get(model_name, {"input": 0.0, "output": 0.0})
56
+
57
+ def calculate_cost(self, provider: str, model_name: str, input_tokens: int, output_tokens: int) -> float:
58
+ """计算请求成本"""
59
+ pricing = self.get_model_pricing(provider, model_name)
60
+ input_cost = (input_tokens / 1_000_000) * pricing["input"]
61
+ output_cost = (output_tokens / 1_000_000) * pricing["output"]
62
+ return input_cost + output_cost
63
+
64
+ def get_cheapest_model(self, provider: str, model_type: str = "llm") -> Optional[str]:
65
+ """获取最便宜的模型"""
66
+ provider_models = self.MODEL_PRICING.get(provider, {})
67
+ if not provider_models:
68
+ return None
69
+
70
+ # 计算每个模型的平均成本 (假设输入输出比例 1:1)
71
+ cheapest_model = None
72
+ lowest_cost = float('inf')
73
+
74
+ for model_name, pricing in provider_models.items():
75
+ avg_cost = (pricing["input"] + pricing["output"]) / 2
76
+ if avg_cost < lowest_cost:
77
+ lowest_cost = avg_cost
78
+ cheapest_model = model_name
79
+
80
+ return cheapest_model
81
+
20
82
  async def get_model(self,
21
83
  model_id: str,
22
84
  repo_id: str,
23
85
  model_type: ModelType,
24
86
  capabilities: List[ModelCapability],
25
87
  revision: Optional[str] = None,
26
- force_download: bool = False) -> Path:
88
+ force_download: bool = False) -> Optional[Path]:
27
89
  """
28
90
  Get model files, downloading if necessary
29
91
 
@@ -36,7 +98,7 @@ class ModelManager:
36
98
  force_download: Force re-download even if cached
37
99
 
38
100
  Returns:
39
- Path to the model files
101
+ Path to the model files or None if failed
40
102
  """
41
103
  # Check if model is already downloaded
42
104
  if not force_download:
@@ -80,7 +142,10 @@ class ModelManager:
80
142
 
81
143
  except HfHubHTTPError as e:
82
144
  logger.error(f"Failed to download model {model_id}: {e}")
83
- raise
145
+ return None
146
+ except Exception as e:
147
+ logger.error(f"Unexpected error downloading model {model_id}: {e}")
148
+ return None
84
149
 
85
150
  async def list_models(self) -> List[Dict[str, Any]]:
86
151
  """List all downloaded models with their metadata"""
@@ -3,6 +3,9 @@ from enum import Enum
3
3
  import logging
4
4
  from pathlib import Path
5
5
  import json
6
+ import sqlite3
7
+ from datetime import datetime
8
+ import threading
6
9
 
7
10
  logger = logging.getLogger(__name__)
8
11
 
@@ -29,27 +32,45 @@ class ModelType(str, Enum):
29
32
  VISION = "vision"
30
33
 
31
34
  class ModelRegistry:
32
- """Registry for model metadata and capabilities"""
35
+ """SQLite-based registry for model metadata and capabilities"""
33
36
 
34
- def __init__(self, registry_file: str = "./models/model_registry.json"):
35
- self.registry_file = Path(registry_file)
36
- self.registry: Dict[str, Dict[str, Any]] = {}
37
- self._load_registry()
37
+ def __init__(self, db_path: str = "./models/model_registry.db"):
38
+ self.db_path = Path(db_path)
39
+ self.db_path.parent.mkdir(parents=True, exist_ok=True)
40
+ self._lock = threading.Lock()
41
+ self._initialize_database()
38
42
 
39
- def _load_registry(self):
40
- """Load model registry from file"""
41
- if self.registry_file.exists():
42
- with open(self.registry_file, 'r') as f:
43
- self.registry = json.load(f)
44
- else:
45
- self.registry = {}
46
- self._save_registry()
47
-
48
- def _save_registry(self):
49
- """Save model registry to file"""
50
- self.registry_file.parent.mkdir(parents=True, exist_ok=True)
51
- with open(self.registry_file, 'w') as f:
52
- json.dump(self.registry, f, indent=2)
43
+ def _initialize_database(self):
44
+ """Initialize SQLite database with required tables"""
45
+ with sqlite3.connect(self.db_path) as conn:
46
+ conn.execute("""
47
+ CREATE TABLE IF NOT EXISTS models (
48
+ model_id TEXT PRIMARY KEY,
49
+ model_type TEXT NOT NULL,
50
+ metadata TEXT,
51
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
52
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
53
+ )
54
+ """)
55
+
56
+ conn.execute("""
57
+ CREATE TABLE IF NOT EXISTS model_capabilities (
58
+ model_id TEXT,
59
+ capability TEXT,
60
+ PRIMARY KEY (model_id, capability),
61
+ FOREIGN KEY (model_id) REFERENCES models(model_id) ON DELETE CASCADE
62
+ )
63
+ """)
64
+
65
+ conn.execute("""
66
+ CREATE INDEX IF NOT EXISTS idx_model_type ON models(model_type)
67
+ """)
68
+
69
+ conn.execute("""
70
+ CREATE INDEX IF NOT EXISTS idx_capability ON model_capabilities(capability)
71
+ """)
72
+
73
+ conn.commit()
53
74
 
54
75
  def register_model(self,
55
76
  model_id: str,
@@ -58,14 +79,30 @@ class ModelRegistry:
58
79
  metadata: Dict[str, Any]) -> bool:
59
80
  """Register a model with its capabilities and metadata"""
60
81
  try:
61
- self.registry[model_id] = {
62
- "type": model_type,
63
- "capabilities": [cap.value for cap in capabilities],
64
- "metadata": metadata
65
- }
66
- self._save_registry()
82
+ with self._lock:
83
+ with sqlite3.connect(self.db_path) as conn:
84
+ # Insert or update model
85
+ conn.execute("""
86
+ INSERT OR REPLACE INTO models
87
+ (model_id, model_type, metadata, updated_at)
88
+ VALUES (?, ?, ?, CURRENT_TIMESTAMP)
89
+ """, (model_id, model_type.value, json.dumps(metadata)))
90
+
91
+ # Clear existing capabilities
92
+ conn.execute("DELETE FROM model_capabilities WHERE model_id = ?", (model_id,))
93
+
94
+ # Insert new capabilities
95
+ for capability in capabilities:
96
+ conn.execute("""
97
+ INSERT INTO model_capabilities (model_id, capability)
98
+ VALUES (?, ?)
99
+ """, (model_id, capability.value))
100
+
101
+ conn.commit()
102
+
67
103
  logger.info(f"Registered model {model_id}")
68
104
  return True
105
+
69
106
  except Exception as e:
70
107
  logger.error(f"Failed to register model {model_id}: {e}")
71
108
  return False
@@ -73,43 +110,233 @@ class ModelRegistry:
73
110
  def unregister_model(self, model_id: str) -> bool:
74
111
  """Unregister a model"""
75
112
  try:
76
- if model_id in self.registry:
77
- del self.registry[model_id]
78
- self._save_registry()
79
- logger.info(f"Unregistered model {model_id}")
80
- return True
81
- return False
113
+ with self._lock:
114
+ with sqlite3.connect(self.db_path) as conn:
115
+ cursor = conn.execute("DELETE FROM models WHERE model_id = ?", (model_id,))
116
+ conn.commit()
117
+
118
+ if cursor.rowcount > 0:
119
+ logger.info(f"Unregistered model {model_id}")
120
+ return True
121
+ return False
122
+
82
123
  except Exception as e:
83
124
  logger.error(f"Failed to unregister model {model_id}: {e}")
84
125
  return False
85
126
 
86
127
  def get_model_info(self, model_id: str) -> Optional[Dict[str, Any]]:
87
128
  """Get model information"""
88
- return self.registry.get(model_id)
129
+ try:
130
+ with sqlite3.connect(self.db_path) as conn:
131
+ conn.row_factory = sqlite3.Row
132
+
133
+ # Get model info
134
+ model_row = conn.execute("""
135
+ SELECT model_id, model_type, metadata, created_at, updated_at
136
+ FROM models WHERE model_id = ?
137
+ """, (model_id,)).fetchone()
138
+
139
+ if not model_row:
140
+ return None
141
+
142
+ # Get capabilities
143
+ capabilities = conn.execute("""
144
+ SELECT capability FROM model_capabilities WHERE model_id = ?
145
+ """, (model_id,)).fetchall()
146
+
147
+ model_info = {
148
+ "model_id": model_row["model_id"],
149
+ "type": model_row["model_type"],
150
+ "capabilities": [cap["capability"] for cap in 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
89
161
 
90
162
  def get_models_by_type(self, model_type: ModelType) -> Dict[str, Dict[str, Any]]:
91
163
  """Get all models of a specific type"""
92
- return {
93
- model_id: info
94
- for model_id, info in self.registry.items()
95
- if info["type"] == model_type
96
- }
164
+ try:
165
+ with sqlite3.connect(self.db_path) as conn:
166
+ conn.row_factory = sqlite3.Row
167
+
168
+ models = conn.execute("""
169
+ SELECT model_id, model_type, metadata, created_at, updated_at
170
+ FROM models WHERE model_type = ?
171
+ """, (model_type.value,)).fetchall()
172
+
173
+ result = {}
174
+ for model in models:
175
+ model_id = model["model_id"]
176
+
177
+ # Get capabilities for this model
178
+ capabilities = conn.execute("""
179
+ SELECT capability FROM model_capabilities WHERE model_id = ?
180
+ """, (model_id,)).fetchall()
181
+
182
+ result[model_id] = {
183
+ "type": model["model_type"],
184
+ "capabilities": [cap["capability"] for cap in capabilities],
185
+ "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
186
+ "created_at": model["created_at"],
187
+ "updated_at": model["updated_at"]
188
+ }
189
+
190
+ return result
191
+
192
+ except Exception as e:
193
+ logger.error(f"Failed to get models by type {model_type}: {e}")
194
+ return {}
97
195
 
98
196
  def get_models_by_capability(self, capability: ModelCapability) -> Dict[str, Dict[str, Any]]:
99
197
  """Get all models with a specific capability"""
100
- return {
101
- model_id: info
102
- for model_id, info in self.registry.items()
103
- if capability.value in info["capabilities"]
104
- }
198
+ try:
199
+ with sqlite3.connect(self.db_path) as conn:
200
+ conn.row_factory = sqlite3.Row
201
+
202
+ models = conn.execute("""
203
+ SELECT DISTINCT m.model_id, m.model_type, m.metadata, m.created_at, m.updated_at
204
+ FROM models m
205
+ JOIN model_capabilities mc ON m.model_id = mc.model_id
206
+ WHERE mc.capability = ?
207
+ """, (capability.value,)).fetchall()
208
+
209
+ result = {}
210
+ for model in models:
211
+ model_id = model["model_id"]
212
+
213
+ # Get all capabilities for this model
214
+ capabilities = conn.execute("""
215
+ SELECT capability FROM model_capabilities WHERE model_id = ?
216
+ """, (model_id,)).fetchall()
217
+
218
+ result[model_id] = {
219
+ "type": model["model_type"],
220
+ "capabilities": [cap["capability"] for cap in capabilities],
221
+ "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
222
+ "created_at": model["created_at"],
223
+ "updated_at": model["updated_at"]
224
+ }
225
+
226
+ return result
227
+
228
+ except Exception as e:
229
+ logger.error(f"Failed to get models by capability {capability}: {e}")
230
+ return {}
105
231
 
106
232
  def has_capability(self, model_id: str, capability: ModelCapability) -> bool:
107
233
  """Check if a model has a specific capability"""
108
- model_info = self.get_model_info(model_id)
109
- if not model_info:
234
+ try:
235
+ with sqlite3.connect(self.db_path) as conn:
236
+ result = conn.execute("""
237
+ SELECT 1 FROM model_capabilities
238
+ WHERE model_id = ? AND capability = ?
239
+ """, (model_id, capability.value)).fetchone()
240
+
241
+ return result is not None
242
+
243
+ except Exception as e:
244
+ logger.error(f"Failed to check capability for {model_id}: {e}")
110
245
  return False
111
- return capability.value in model_info["capabilities"]
112
246
 
113
247
  def list_models(self) -> Dict[str, Dict[str, Any]]:
114
248
  """List all registered models"""
115
- return self.registry
249
+ try:
250
+ with sqlite3.connect(self.db_path) as conn:
251
+ conn.row_factory = sqlite3.Row
252
+
253
+ models = conn.execute("""
254
+ SELECT model_id, model_type, metadata, created_at, updated_at
255
+ FROM models ORDER BY created_at DESC
256
+ """).fetchall()
257
+
258
+ result = {}
259
+ for model in models:
260
+ model_id = model["model_id"]
261
+
262
+ # Get capabilities for this model
263
+ capabilities = conn.execute("""
264
+ SELECT capability FROM model_capabilities WHERE model_id = ?
265
+ """, (model_id,)).fetchall()
266
+
267
+ result[model_id] = {
268
+ "type": model["model_type"],
269
+ "capabilities": [cap["capability"] for cap in capabilities],
270
+ "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
271
+ "created_at": model["created_at"],
272
+ "updated_at": model["updated_at"]
273
+ }
274
+
275
+ return result
276
+
277
+ except Exception as e:
278
+ logger.error(f"Failed to list models: {e}")
279
+ return {}
280
+
281
+ def get_stats(self) -> Dict[str, Any]:
282
+ """Get registry statistics"""
283
+ try:
284
+ with sqlite3.connect(self.db_path) as conn:
285
+ # Count total models
286
+ total_models = conn.execute("SELECT COUNT(*) FROM models").fetchone()[0]
287
+
288
+ # Count by type
289
+ type_counts = dict(conn.execute("""
290
+ SELECT model_type, COUNT(*) FROM models GROUP BY model_type
291
+ """).fetchall())
292
+
293
+ # Count by capability
294
+ capability_counts = dict(conn.execute("""
295
+ SELECT capability, COUNT(*) FROM model_capabilities GROUP BY capability
296
+ """).fetchall())
297
+
298
+ return {
299
+ "total_models": total_models,
300
+ "models_by_type": type_counts,
301
+ "models_by_capability": capability_counts
302
+ }
303
+
304
+ except Exception as e:
305
+ logger.error(f"Failed to get stats: {e}")
306
+ return {}
307
+
308
+ def search_models(self, query: str) -> Dict[str, Dict[str, Any]]:
309
+ """Search models by name or metadata"""
310
+ try:
311
+ with sqlite3.connect(self.db_path) as conn:
312
+ conn.row_factory = sqlite3.Row
313
+
314
+ models = conn.execute("""
315
+ SELECT model_id, model_type, metadata, created_at, updated_at
316
+ FROM models
317
+ WHERE model_id LIKE ? OR metadata LIKE ?
318
+ ORDER BY created_at DESC
319
+ """, (f"%{query}%", f"%{query}%")).fetchall()
320
+
321
+ result = {}
322
+ for model in models:
323
+ model_id = model["model_id"]
324
+
325
+ # Get capabilities for this model
326
+ capabilities = conn.execute("""
327
+ SELECT capability FROM model_capabilities WHERE model_id = ?
328
+ """, (model_id,)).fetchall()
329
+
330
+ result[model_id] = {
331
+ "type": model["model_type"],
332
+ "capabilities": [cap["capability"] for cap in capabilities],
333
+ "metadata": json.loads(model["metadata"]) if model["metadata"] else {},
334
+ "created_at": model["created_at"],
335
+ "updated_at": model["updated_at"]
336
+ }
337
+
338
+ return result
339
+
340
+ except Exception as e:
341
+ logger.error(f"Failed to search models with query '{query}': {e}")
342
+ return {}