From f20c7ca54ddd04b64caabfef938d70fbabd8a993 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Mon, 16 Mar 2026 06:09:38 +0000 Subject: [PATCH] Friendlier unsupported model errors, show estimated download size 1. Backend: When a model fails with "No config file found" or similar unsupported-model errors, wrap the message with "This model is not supported yet. Try a different model." instead of showing the raw Unsloth exception. 2. Frontend: Compute estimated download size from the HF search API's safetensors.parameters dtype breakdown (BF16=2B/param, I32=4B/param, F32=4B/param, etc.) and show it in the model picker instead of just the param count. For example, Kimi-K2.5 now shows "~554 GB" instead of "171B" (which was misleading since 171B params != 171GB download). --- studio/backend/routes/inference.py | 12 +++++++++- .../assistant-ui/model-selector/pickers.tsx | 9 +++++-- .../frontend/src/hooks/use-hf-model-search.ts | 24 ++++++++++++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/studio/backend/routes/inference.py b/studio/backend/routes/inference.py index c7fa9ed720..13062f8b29 100644 --- a/studio/backend/routes/inference.py +++ b/studio/backend/routes/inference.py @@ -288,7 +288,17 @@ async def load_model( raise except Exception as e: logger.error(f"Error loading model: {e}", exc_info = True) - raise HTTPException(status_code = 500, detail = f"Failed to load model: {str(e)}") + msg = str(e) + # Surface a friendlier message for models that Unsloth cannot load + not_supported_hints = [ + "No config file found", + "not yet supported", + "is not supported", + "does not support", + ] + if any(h.lower() in msg.lower() for h in not_supported_hints): + msg = f"This model is not supported yet. Try a different model. (Original error: {msg})" + raise HTTPException(status_code = 500, detail = f"Failed to load model: {msg}") @router.post("/validate", response_model = ValidateModelResponse) diff --git a/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx b/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx index b8b464c56b..22a1cb7e72 100644 --- a/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx +++ b/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx @@ -408,8 +408,13 @@ export function HubModelPicker({ () => new Map( results - .filter((result) => result.totalParams) - .map((result) => [result.id, formatCompact(result.totalParams!)]), + .filter((result) => result.totalParams || result.estimatedSizeBytes) + .map((result) => [ + result.id, + result.estimatedSizeBytes + ? `~${formatBytes(result.estimatedSizeBytes)}` + : formatCompact(result.totalParams!), + ]), ), [results], ); diff --git a/studio/frontend/src/hooks/use-hf-model-search.ts b/studio/frontend/src/hooks/use-hf-model-search.ts index a092a895a8..f7b06ab65a 100644 --- a/studio/frontend/src/hooks/use-hf-model-search.ts +++ b/studio/frontend/src/hooks/use-hf-model-search.ts @@ -11,6 +11,7 @@ export interface HfModelResult { downloads: number; likes: number; totalParams?: number; + estimatedSizeBytes?: number; } const EXCLUDED_TAGS = new Set([ @@ -54,13 +55,33 @@ function withPopularitySort( return fetch(url, init); } +/** Bytes per parameter for each dtype. */ +const DTYPE_BYTES: Record = { + F64: 8, F32: 4, F16: 2, BF16: 2, + I64: 8, I32: 4, I16: 2, I8: 1, U8: 1, + // Quantized types (4-bit) + NF4: 0.5, FP4: 0.5, INT4: 0.5, GPTQ: 0.5, +}; + +function estimateSizeFromDtypes( + params: Record | undefined, +): number | undefined { + if (!params) return undefined; + let total = 0; + for (const [dtype, count] of Object.entries(params)) { + const bpp = DTYPE_BYTES[dtype.toUpperCase()] ?? 2; // default BF16 + total += count * bpp; + } + return total > 0 ? total : undefined; +} + function makeMapModel(excludeGguf: boolean) { return (raw: unknown): HfModelResult | null => { const m = raw as { name: string; downloads: number; likes: number; - safetensors?: { total: number }; + safetensors?: { total: number; parameters?: Record }; tags?: string[]; }; const isEmbedding = m.tags?.some((t) => EMBEDDING_TAGS.has(t)); @@ -75,6 +96,7 @@ function makeMapModel(excludeGguf: boolean) { downloads: m.downloads, likes: m.likes, totalParams: m.safetensors?.total, + estimatedSizeBytes: estimateSizeFromDtypes(m.safetensors?.parameters), }; }; }