* feat(studio): save load settings in chat presets Presets previously stored only sampling params (temperature, top_p, etc.). Extend them with an optional loadConfig blob that captures context length, KV cache dtype, speculative decoding, and GPU layer knobs from the current runtime when saving. - Apply loadConfig when switching presets or hydrating on startup - Show a short summary under the preset controls - Prompt to reload when a model is already loaded Fixes #7347 * fix(studio): persist preset loadConfig and capture GGUF context Add ChatPresetLoadConfig to the chat settings API schema so presets with load settings no longer 400 on save. Capture effective GGUF context from ggufContextLength when customContextLength is cleared after auto-mode load. * fix(studio): address Codex review on preset load settings Coalesce default maxSeqLength/speculative/gpu knobs when capturing presets, no-op apply for legacy presets without loadConfig, preserve GPU pin on apply, and stop replaying stale loadConfig during settings hydration. * Remove unused getOrderedPresets import --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
parent
d17567af3e
commit
434fac6ffc
9 changed files with 467 additions and 15 deletions
70
tests/studio/test_chat_preset_load_config.py
Normal file
70
tests/studio/test_chat_preset_load_config.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
||||
|
||||
"""Contract coverage for preset load settings (#7347)."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _read(relative: str) -> str:
|
||||
path = ROOT / relative
|
||||
if not path.exists():
|
||||
path = ROOT / "unsloth_repo" / relative
|
||||
return path.read_text(encoding = "utf-8")
|
||||
|
||||
|
||||
def test_preset_interface_includes_load_config():
|
||||
policy = _read("studio/frontend/src/features/chat/presets/preset-policy.ts")
|
||||
assert "loadConfig?: PresetLoadConfig" in policy
|
||||
|
||||
|
||||
def test_preset_save_captures_load_config():
|
||||
sheet = _read("studio/frontend/src/features/chat/chat-settings-sheet.tsx")
|
||||
assert "capturePresetLoadConfig()" in sheet
|
||||
assert "applyPresetLoadConfig" in sheet
|
||||
|
||||
|
||||
def test_preset_apply_restores_load_config():
|
||||
sheet = _read("studio/frontend/src/features/chat/chat-settings-sheet.tsx")
|
||||
assert "if (p.loadConfig)" in sheet
|
||||
assert "applyPresetLoadConfig(p.loadConfig)" in sheet
|
||||
|
||||
|
||||
def test_persisted_preset_serializes_load_config():
|
||||
storage = _read("studio/frontend/src/features/chat/utils/chat-settings-storage.ts")
|
||||
assert "normalizePresetLoadConfig(item.loadConfig)" in storage
|
||||
api = _read("studio/frontend/src/features/chat/api/chat-settings-api.ts")
|
||||
assert "loadConfig?: Record<string, unknown>" in api
|
||||
|
||||
|
||||
def test_capture_reads_gguf_loaded_context():
|
||||
source = _read("studio/frontend/src/features/chat/presets/preset-load-config.ts")
|
||||
assert "store.ggufContextLength" in source
|
||||
assert "effectiveContextLength" in source
|
||||
|
||||
|
||||
def test_apply_skips_missing_load_config():
|
||||
source = _read("studio/frontend/src/features/chat/presets/preset-load-config.ts")
|
||||
assert "if (config == null)" in source
|
||||
assert "selectedGpuIds: store.selectedGpuIds" in source
|
||||
sheet = _read("studio/frontend/src/features/chat/chat-settings-sheet.tsx")
|
||||
assert "if (p.loadConfig)" in sheet
|
||||
|
||||
|
||||
def test_hydration_does_not_replay_preset_load_config():
|
||||
store = _read("studio/frontend/src/features/chat/stores/chat-runtime-store.ts")
|
||||
assert "applyPresetLoadConfig(activeDefinition.loadConfig)" not in store
|
||||
|
||||
|
||||
def test_capture_coalesces_default_load_knobs():
|
||||
source = _read("studio/frontend/src/features/chat/presets/preset-load-config.ts")
|
||||
assert "coalesceDefaultLoadKnobs" in source
|
||||
assert "DEFAULT_MAX_SEQ_LENGTH" in source
|
||||
|
||||
|
||||
def test_backend_chat_preset_accepts_load_config():
|
||||
routes = _read("studio/backend/routes/chat_history.py")
|
||||
assert "class ChatPresetLoadConfig" in routes
|
||||
assert "loadConfig: Optional[ChatPresetLoadConfig]" in routes
|
||||
Loading…
Add table
Add a link
Reference in a new issue