studio: GGUF unlimited context, auto-load, settings UX, recommended list

- GGUF: use -c 0 for model's native context size (no 4096 cap)
- GGUF: hide Max Seq Length slider (irrelevant), set Max Tokens to Max
- Non-GGUF: default Max Tokens to 4096
- Max Tokens slider shows "Max" label when at ceiling for GGUFs
- Run non-GGUF load_model in asyncio.to_thread for progress polling
- Auto-load smallest downloaded model when chatting without selection
- Wait for in-progress model load before inference (modelLoading store flag)
- Recommended list: 4 GGUFs + 4 hub models after case-insensitive dedup
- Model selector waits for cached data before rendering
- Toast close button repositioned, Sampling section open by default
- Add logging to _get_repo_size_cached exception handler
This commit is contained in:
Daniel Han 2026-03-16 09:40:11 +00:00
commit ec9a0906eb
3 changed files with 23 additions and 11 deletions

View file

@ -770,7 +770,8 @@ def _get_repo_size_cached(repo_id: str) -> int:
total = sum(s.size for s in info.siblings if s.size)
_repo_size_cache[repo_id] = total
return total
except Exception:
except Exception as e:
logger.warning(f"Failed to get repo size for {repo_id}: {e}")
return 0

View file

@ -26,6 +26,7 @@ import {
DEFAULT_INFERENCE_PARAMS,
type InferenceParams,
} from "./types/runtime";
import { useChatRuntimeStore } from "./stores/chat-runtime-store";
import { Switch } from "@/components/ui/switch";
export const defaultInferenceParams = DEFAULT_INFERENCE_PARAMS;
@ -67,6 +68,7 @@ function ParamSlider({
max,
step,
onChange,
displayValue,
}: {
label: string;
value: number;
@ -74,13 +76,14 @@ function ParamSlider({
max: number;
step: number;
onChange: (v: number) => void;
displayValue?: string;
}) {
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs font-medium">{label}</span>
<span className="text-xs tabular-nums text-muted-foreground">
{value}
{displayValue ?? value}
</span>
</div>
<Slider
@ -158,6 +161,7 @@ export function ChatSettingsPanel({
autoTitle,
onAutoTitleChange,
}: ChatSettingsPanelProps) {
const isGguf = useChatRuntimeStore((s) => s.activeGgufVariant) != null;
const [presets, setPresets] = useState<Preset[]>(BUILTIN_PRESETS);
const [activePreset, setActivePreset] = useState("Default");
const isBuiltinPreset = BUILTIN_PRESETS.some((p) => p.name === activePreset);
@ -322,21 +326,24 @@ export function ChatSettingsPanel({
step={0.05}
onChange={set("repetitionPenalty")}
/>
<ParamSlider
label="Max Seq Length"
value={params.maxSeqLength}
min={128}
max={32768}
step={128}
onChange={set("maxSeqLength")}
/>
{!isGguf && (
<ParamSlider
label="Max Seq Length"
value={params.maxSeqLength}
min={128}
max={32768}
step={128}
onChange={set("maxSeqLength")}
/>
)}
<ParamSlider
label="Max Tokens"
value={params.maxTokens}
min={64}
max={4096}
max={isGguf ? 131072 : 32768}
step={64}
onChange={set("maxTokens")}
displayValue={isGguf && params.maxTokens >= 131072 ? "Max" : undefined}
/>
</div>
</CollapsibleSection>

View file

@ -132,9 +132,13 @@ function mergeRecommendedInference(
modelId: string,
): InferenceParams {
const inference = response.inference;
// GGUF: max tokens = 131072 (effectively unlimited, model decides)
// Non-GGUF: max tokens = 4096
const defaultMaxTokens = response.is_gguf ? 131072 : 4096;
return {
...current,
checkpoint: modelId,
maxTokens: defaultMaxTokens,
temperature:
toFiniteNumber(inference?.temperature) ?? current.temperature,
topP: toFiniteNumber(inference?.top_p) ?? current.topP,