From 21cff233e58ca0c3e06ca650802fed2aadd3ac99 Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Tue, 10 Mar 2026 16:54:19 +0000 Subject: [PATCH] feat: add model_type field to backend /config and /list responses Derive a single model_type string ("text" | "vision" | "audio" | "embeddings") from existing is_vision and audio_type detection, so the frontend doesn't have to infer modality from scattered boolean flags. --- studio/backend/models/models.py | 3 +++ studio/backend/routes/models.py | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/studio/backend/models/models.py b/studio/backend/models/models.py index e69c48691f..67eaf3fb96 100644 --- a/studio/backend/models/models.py +++ b/studio/backend/models/models.py @@ -7,6 +7,8 @@ Pydantic schemas for Model Management API from pydantic import BaseModel, Field from typing import Optional, List, Dict, Any, Literal +ModelType = Literal["text", "vision", "audio", "embeddings"] + class CheckpointInfo(BaseModel): """Information about a discovered checkpoint directory.""" @@ -60,6 +62,7 @@ class ModelDetails(BaseModel): is_audio: bool = Field(False, description="Whether model is a TTS audio model") audio_type: Optional[str] = Field(None, description="Audio codec type: snac, csm, bicodec, dac") has_audio_input: bool = Field(False, description="Whether model accepts audio input (ASR)") + model_type: Optional[ModelType] = Field(None, description="Collapsed model modality: text, vision, audio, or embeddings") base_model: Optional[str] = Field(None, description="Base model if this is a LoRA adapter") diff --git a/studio/backend/routes/models.py b/studio/backend/routes/models.py index e217ff63d8..05b18aa606 100644 --- a/studio/backend/routes/models.py +++ b/studio/backend/routes/models.py @@ -60,12 +60,21 @@ from models import ( LoRAInfo, ModelListResponse, ) -from models.models import GgufVariantDetail, GgufVariantsResponse +from models.models import GgufVariantDetail, GgufVariantsResponse, ModelType from models.responses import LoRABaseModelResponse, VisionCheckResponse router = APIRouter() logger = logging.getLogger(__name__) + +def derive_model_type(is_vision: bool, audio_type: Optional[str]) -> ModelType: + """Collapse individual capability flags into a single model modality string.""" + if audio_type is not None: + return "audio" + if is_vision: + return "vision" + return "text" + # Configure logger if not logger.handlers: handler = logging.StreamHandler() @@ -224,14 +233,17 @@ async def list_models( # Get loaded models loaded_models = [] for model_name, model_data in inference_backend.models.items(): + _is_vision = model_data.get("is_vision", False) + _audio_type = model_data.get("audio_type") model_info = ModelDetails( id=model_name, name=model_name.split("/")[-1] if "/" in model_name else model_name, - is_vision=model_data.get("is_vision", False), + is_vision=_is_vision, is_lora=model_data.get("is_lora", False), is_audio=model_data.get("is_audio", False), - audio_type=model_data.get("audio_type"), + audio_type=_audio_type, has_audio_input=model_data.get("has_audio_input", False), + model_type=derive_model_type(_is_vision, _audio_type), ) loaded_models.append(model_info) @@ -309,6 +321,7 @@ async def get_model_config( is_audio=audio_type is not None, audio_type=audio_type, has_audio_input=is_audio_input_type(audio_type), + model_type=derive_model_type(is_vision, audio_type), base_model=base_model, )