Skip to content

Commit ff79285

Browse files
committed
Unify safetensors models
1 parent 0086578 commit ff79285

8 files changed

Lines changed: 994 additions & 258 deletions

File tree

‎backend/database.py‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from sqlalchemy.ext.declarative import declarative_base
1616
from sqlalchemy.orm import sessionmaker, relationship
1717
from datetime import datetime
18+
from typing import Dict, List
1819
import os
1920
from backend.logging_config import get_logger
2021

@@ -140,6 +141,12 @@ async def init_db():
140141

141142
# Migrate existing models to populate base_model_name
142143
migrate_existing_models()
144+
145+
# Migrate safetensors models: merge multiple rows per repo into single row
146+
try:
147+
migrate_safetensors_models_to_unified()
148+
except Exception as exc:
149+
logger.warning(f"Failed to migrate safetensors models: {exc}")
143150

144151

145152
def migrate_existing_models():
@@ -199,6 +206,75 @@ def ensure_running_instance_runtime_column():
199206
logger.info("Added runtime_type column to running_instances table")
200207

201208

209+
def migrate_safetensors_models_to_unified():
210+
"""Migrate safetensors models: merge multiple Model rows per repo into a single row."""
211+
db = SessionLocal()
212+
try:
213+
# Find all safetensors models grouped by huggingface_id
214+
safetensors_models = db.query(Model).filter(
215+
Model.model_format == "safetensors"
216+
).all()
217+
218+
# Group by huggingface_id
219+
by_repo: Dict[str, List[Model]] = {}
220+
for model in safetensors_models:
221+
hf_id = model.huggingface_id or "unknown"
222+
by_repo.setdefault(hf_id, []).append(model)
223+
224+
merged_count = 0
225+
for huggingface_id, models in by_repo.items():
226+
if len(models) <= 1:
227+
continue # Already unified
228+
229+
# Keep the first model, merge others into it
230+
primary = models[0]
231+
others = models[1:]
232+
233+
# Aggregate file_size
234+
total_size = sum(m.file_size or 0 for m in models)
235+
if total_size:
236+
primary.file_size = total_size
237+
238+
# Merge metadata: use most complete pipeline_tag, model_type, etc.
239+
for other in others:
240+
if not primary.pipeline_tag and other.pipeline_tag:
241+
primary.pipeline_tag = other.pipeline_tag
242+
if not primary.model_type and other.model_type:
243+
primary.model_type = other.model_type
244+
if not primary.base_model_name and other.base_model_name:
245+
primary.base_model_name = other.base_model_name
246+
# Use earliest downloaded_at
247+
if other.downloaded_at and (not primary.downloaded_at or other.downloaded_at < primary.downloaded_at):
248+
primary.downloaded_at = other.downloaded_at
249+
250+
# Update RunningInstance records to point to primary model
251+
for other in others:
252+
instances = db.query(RunningInstance).filter(
253+
RunningInstance.model_id == other.id
254+
).all()
255+
for instance in instances:
256+
instance.model_id = primary.id
257+
258+
# Delete duplicate models
259+
for other in others:
260+
db.delete(other)
261+
262+
merged_count += len(others)
263+
logger.info(f"Merged {len(others)} safetensors Model rows for {huggingface_id} into model_id={primary.id}")
264+
265+
if merged_count > 0:
266+
db.commit()
267+
logger.info(f"Migration complete: merged {merged_count} safetensors Model rows")
268+
else:
269+
logger.debug("No safetensors Model rows to merge")
270+
271+
except Exception as e:
272+
logger.error(f"Error migrating safetensors models: {e}")
273+
db.rollback()
274+
finally:
275+
db.close()
276+
277+
202278
def ensure_pipeline_tag_column():
203279
"""Ensure the models table stores pipeline tags."""
204280
inspector = inspect(engine)

0 commit comments

Comments
 (0)