Studio: Show Run button for downloaded non-GGUF models in the Model Hub (#7001)
This commit is contained in:
parent
f60b982a09
commit
76d7088e0a
5 changed files with 57 additions and 13 deletions
|
|
@ -4,6 +4,7 @@
|
|||
import { TrainIcon } from "../components/train-icon";
|
||||
import {
|
||||
HUB_GGUF_RUN_ACTIONS_VISIBLE,
|
||||
HUB_NON_GGUF_RUN_ACTIONS_VISIBLE,
|
||||
HUB_POST_DOWNLOAD_ACTIONS_VISIBLE,
|
||||
} from "../lib/hub-feature-flags";
|
||||
import {
|
||||
|
|
@ -410,7 +411,7 @@ export function LocalOnDeviceCard({
|
|||
const showOldCacheHint = source === "hf_cache" && !!unsupportedReason;
|
||||
const runActionsVisible = isGguf
|
||||
? HUB_GGUF_RUN_ACTIONS_VISIBLE
|
||||
: HUB_POST_DOWNLOAD_ACTIONS_VISIBLE;
|
||||
: HUB_NON_GGUF_RUN_ACTIONS_VISIBLE;
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
|
|
|
|||
|
|
@ -263,17 +263,22 @@ type VramInfo = { est: number; status: "fits" | "tight" | "exceeds" } | null;
|
|||
function ModelStatusChips({
|
||||
isDataset,
|
||||
isGguf,
|
||||
chatOnly,
|
||||
unslothSupport,
|
||||
vramInfo,
|
||||
}: {
|
||||
isDataset: boolean;
|
||||
isGguf: boolean;
|
||||
chatOnly: boolean;
|
||||
unslothSupport: UnslothSupport;
|
||||
vramInfo: VramInfo;
|
||||
}) {
|
||||
const showUnsupported = !isDataset && unslothSupport.status === "unsupported";
|
||||
// The format-unsupported chip already explains itself; this one covers the
|
||||
// supported-format model a chat-only host still can't run.
|
||||
const showChatOnly = !isDataset && !isGguf && chatOnly && !showUnsupported;
|
||||
const showVram = !isDataset && vramInfo && !isGguf;
|
||||
if (!showUnsupported && !showVram) return null;
|
||||
if (!showUnsupported && !showChatOnly && !showVram) return null;
|
||||
|
||||
const vramTone = vramInfo
|
||||
? vramInfo.status === "exceeds"
|
||||
|
|
@ -323,6 +328,26 @@ function ModelStatusChips({
|
|||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
{showChatOnly && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild={true}>
|
||||
<span tabIndex={0} className="inline-flex outline-none">
|
||||
<StatusChip tone="warning" label="GGUF-only device" />
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="bottom"
|
||||
sideOffset={6}
|
||||
className="tooltip-compact max-w-xs"
|
||||
>
|
||||
This device has no supported GPU or usable MLX, so only GGUF models
|
||||
can run here.
|
||||
<span className="mt-1 block text-[10.5px] font-normal text-white/75">
|
||||
Still downloadable to your Hugging Face cache.
|
||||
</span>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
{showVram && vramInfo && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild={true}>
|
||||
|
|
@ -406,6 +431,7 @@ export const ModelInspector = memo(function ModelInspector({
|
|||
onSearchHub,
|
||||
} = actions;
|
||||
const deviceType = usePlatformStore((s) => s.deviceType);
|
||||
const chatOnly = usePlatformStore((s) => s.isChatOnly());
|
||||
const hfToken = useHfTokenStore((s) => s.token);
|
||||
const datasetRepoId = isDataset && model?.hubRepoId ? model.hubRepoId : null;
|
||||
const datasetSize = useDatasetSize(datasetRepoId, {
|
||||
|
|
@ -504,15 +530,19 @@ export const ModelInspector = memo(function ModelInspector({
|
|||
const paramsLabel = model.totalParams
|
||||
? formatCompact(model.totalParams)
|
||||
: "N/A";
|
||||
const trainingSupported = unslothSupport.status !== "unsupported";
|
||||
const unslothSupported = unslothSupport.status !== "unsupported";
|
||||
// Chat-only hosts (no supported GPU / usable MLX) run inference only through
|
||||
// llama.cpp, so only GGUF is loadable.
|
||||
const canRunModel =
|
||||
!isDataset && (model.runtimeCapabilities?.canChat ?? true);
|
||||
!isDataset &&
|
||||
(model.runtimeCapabilities?.canChat ?? true) &&
|
||||
(model.isGguf || (!chatOnly && unslothSupported));
|
||||
const canTrainModel =
|
||||
!isDataset &&
|
||||
(model.runtimeCapabilities?.canTrain ?? false) &&
|
||||
model.modelFormat !== "gguf" &&
|
||||
model.modelFormat !== "adapter" &&
|
||||
trainingSupported;
|
||||
unslothSupported;
|
||||
|
||||
const languages = parseLanguageTags(model.tags);
|
||||
const datasetSizeBytes =
|
||||
|
|
@ -765,6 +795,7 @@ export const ModelInspector = memo(function ModelInspector({
|
|||
<ModelStatusChips
|
||||
isDataset={isDataset}
|
||||
isGguf={model.isGguf}
|
||||
chatOnly={chatOnly}
|
||||
unslothSupport={unslothSupport}
|
||||
vramInfo={vramInfo}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ import {
|
|||
PlayIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { TrainIcon } from "../components/train-icon";
|
||||
import { HUB_POST_DOWNLOAD_ACTIONS_VISIBLE } from "../lib/hub-feature-flags";
|
||||
import {
|
||||
HUB_NON_GGUF_RUN_ACTIONS_VISIBLE,
|
||||
HUB_POST_DOWNLOAD_ACTIONS_VISIBLE,
|
||||
} from "../lib/hub-feature-flags";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useHfTokenStore } from "../stores/hf-token-store";
|
||||
|
|
@ -158,6 +161,7 @@ export function SafetensorsDownloadCard({
|
|||
const showActionPair = isDownloaded && !downloading && (canRun || !!onTrain);
|
||||
const showUnavailableAction =
|
||||
isDownloaded && !downloading && !canRun && !onTrain;
|
||||
const trainActionVisible = !!onTrain && HUB_POST_DOWNLOAD_ACTIONS_VISIBLE;
|
||||
const canDelete =
|
||||
(isDownloaded || isPartial) &&
|
||||
!downloading &&
|
||||
|
|
@ -238,17 +242,17 @@ export function SafetensorsDownloadCard({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* Divider sits above the Download CTA; in the action-pair state it hides with the pair. */}
|
||||
{(!showActionPair || HUB_POST_DOWNLOAD_ACTIONS_VISIBLE) && <CardDivider />}
|
||||
{/* Info/actions hairline; dropped for the run action row (no divider before
|
||||
Run, as in the GGUF card's Run CTA), restored when the Train pair ships. */}
|
||||
{(!showActionPair || trainActionVisible) && <CardDivider />}
|
||||
{showActionPair ? (
|
||||
<div
|
||||
className={cn(
|
||||
"group/pair flex h-9 shrink-0 items-stretch gap-1.5",
|
||||
// Run+Train pair hidden until Hub→chat / Hub→train pickers ship.
|
||||
!HUB_POST_DOWNLOAD_ACTIONS_VISIBLE && "hidden",
|
||||
!HUB_NON_GGUF_RUN_ACTIONS_VISIBLE && "hidden",
|
||||
)}
|
||||
>
|
||||
{onTrain && (
|
||||
{trainActionVisible && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onTrain}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,13 @@
|
|||
// Hub feature flags for staged rollout. JSX/wiring stay in place so flipping
|
||||
// a flag here is the only edit needed to re-enable.
|
||||
|
||||
// Post-download Run / New Chat / Use in chat / Train CTAs; hidden until the
|
||||
// Hub-aware chat and train pickers ship.
|
||||
// Post-download New Chat / Use in chat / Train CTAs; hidden until the
|
||||
// Hub-aware chat and train pickers ship. Run CTAs are gated separately below.
|
||||
export const HUB_POST_DOWNLOAD_ACTIONS_VISIBLE = false;
|
||||
|
||||
export const HUB_GGUF_RUN_ACTIONS_VISIBLE = true;
|
||||
|
||||
// Post-download Run CTA for non-GGUF models (MLX repos classify as safetensors).
|
||||
// Run has no dependency on the Hub-aware chat/train pickers, so it enables
|
||||
// independently of HUB_POST_DOWNLOAD_ACTIONS_VISIBLE, which still gates those.
|
||||
export const HUB_NON_GGUF_RUN_ACTIONS_VISIBLE = true;
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ function detectFormatKey(
|
|||
if (alias) return alias;
|
||||
}
|
||||
if (modelId) {
|
||||
// Owner implies format even when local metadata lacks tags; mirrors the
|
||||
// backend's _looks_like_mlx_repo heuristic.
|
||||
if (modelId.trim().toLowerCase().startsWith("mlx-community/")) return "mlx";
|
||||
const name = repoLeaf(modelId);
|
||||
for (const { key, pattern } of FORMAT_NAME_PATTERNS) {
|
||||
if (pattern.test(name)) return key;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue