|
@@ -6,7 +6,7 @@ from fastapi.responses import RedirectResponse, StreamingResponse
|
|
|
from urllib.parse import urlencode
|
|
from urllib.parse import urlencode
|
|
|
from gpustack_runtime.detector import ManufacturerEnum
|
|
from gpustack_runtime.detector import ManufacturerEnum
|
|
|
from sqlalchemy.orm import selectinload
|
|
from sqlalchemy.orm import selectinload
|
|
|
-from sqlmodel import and_, or_
|
|
|
|
|
|
|
+from sqlmodel import select, and_, or_
|
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
|
from enum import Enum
|
|
from enum import Enum
|
|
|
|
|
|
|
@@ -423,6 +423,33 @@ async def create_model(
|
|
|
# Model & ModelRoute names are unique within their Org. Two Orgs
|
|
# Model & ModelRoute names are unique within their Org. Two Orgs
|
|
|
# can each have a "llama3" without colliding.
|
|
# can each have a "llama3" without colliding.
|
|
|
org_scope = ctx.current_principal_id
|
|
org_scope = ctx.current_principal_id
|
|
|
|
|
+
|
|
|
|
|
+ # Check for ANY existing record with the same name (including soft-deleted)
|
|
|
|
|
+ # to avoid unique constraint violations
|
|
|
|
|
+ statement = select(Model).where(Model.name == model_in.name)
|
|
|
|
|
+ result = await session.exec(statement)
|
|
|
|
|
+ any_existing = result.first()
|
|
|
|
|
+
|
|
|
|
|
+ if any_existing:
|
|
|
|
|
+ if any_existing.deleted_at is not None:
|
|
|
|
|
+ # Soft-deleted record found - permanently delete it to free up the name
|
|
|
|
|
+ await session.delete(any_existing)
|
|
|
|
|
+ await session.flush()
|
|
|
|
|
+ else:
|
|
|
|
|
+ # Active record found - check if it's in the same org scope
|
|
|
|
|
+ if any_existing.owner_principal_id == org_scope:
|
|
|
|
|
+ raise AlreadyExistsException(
|
|
|
|
|
+ message=f"Model '{model_in.name}' already exists. "
|
|
|
|
|
+ "Please choose a different name or check the existing model."
|
|
|
|
|
+ )
|
|
|
|
|
+ else:
|
|
|
|
|
+ # Different org - still a conflict due to unique constraint on name
|
|
|
|
|
+ raise AlreadyExistsException(
|
|
|
|
|
+ message=f"Model name '{model_in.name}' is already in use by another organization. "
|
|
|
|
|
+ "Please choose a different name."
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Double-check for the specific org scope (defensive programming)
|
|
|
existing = await Model.one_by_fields(
|
|
existing = await Model.one_by_fields(
|
|
|
session,
|
|
session,
|
|
|
{"name": model_in.name, "owner_principal_id": org_scope},
|
|
{"name": model_in.name, "owner_principal_id": org_scope},
|