diff --git a/studio/backend/tests/test_openai_auto_switch.py b/studio/backend/tests/test_openai_auto_switch.py index d02a2a4f7e..8742b84ae7 100644 --- a/studio/backend/tests/test_openai_auto_switch.py +++ b/studio/backend/tests/test_openai_auto_switch.py @@ -1609,11 +1609,11 @@ def test_env_idle_ttl_standalone_when_no_stored_value(monkeypatch): def test_stored_idle_value_overrides_env_and_stays_gated(monkeypatch): # An explicit stored value wins over the env default and remains gated on the # auto-switch toggle. - store = {settings.AUTO_UNLOAD_IDLE_SETTING_KEY: 30} + store = {settings.AUTO_UNLOAD_IDLE_SETTING_KEY: 90} monkeypatch.setattr(settings, "_cached_setting", lambda k, d = None: store.get(k, d)) monkeypatch.setenv("UNSLOTH_MODEL_IDLE_TTL", "600") monkeypatch.setattr(settings, "get_openai_auto_switch_enabled", lambda: True) - assert settings.get_auto_unload_idle_seconds() == 30 # stored wins, not env + assert settings.get_auto_unload_idle_seconds() == 90 # stored wins, not env monkeypatch.setattr(settings, "get_openai_auto_switch_enabled", lambda: False) assert settings.get_auto_unload_idle_seconds() == 0 # explicit value still gated off @@ -3094,3 +3094,53 @@ def test_responses_stream_hint_matches_toggle_regardless_of_active_model(monkeyp monkeypatch, enabled = False, active_model_name = "unsloth/Llama-3.2-1B-Instruct" ) assert "Model auto-switch" in non_gguf_loaded + + +def test_setter_rejects_idle_below_floor(monkeypatch): + import storage.studio_db as db + + writes = [] + monkeypatch.setattr(db, "upsert_app_settings", lambda m: writes.append(dict(m))) + settings._cache.clear() + + with pytest.raises(ValueError, match = "at least 60"): + settings.set_openai_auto_switch(True, 30) + assert writes == [] # rejected before any persist + # 0 (off) and >= 60 pass through unchanged. + assert settings.set_openai_auto_switch(True, 0)[1] == 0 + assert settings.set_openai_auto_switch(True, 60)[1] == 60 + assert settings.set_openai_auto_switch(True, 3600)[1] == 3600 + + +def test_put_route_rejects_idle_below_floor(): + import routes.settings as settings_route + from fastapi import HTTPException + + payload = settings_route.OpenAIAutoSwitchPayload(enabled = True, auto_unload_idle_seconds = 30) + with pytest.raises(HTTPException) as excinfo: + settings_route.update_openai_auto_switch(payload, "tester") + assert excinfo.value.status_code == 400 + + +def test_stored_legacy_idle_below_floor_is_clamped(monkeypatch): + # Values persisted before the floor existed are raised to it on read, for + # both the effective TTL and the value the settings UI displays. + store = {settings.AUTO_UNLOAD_IDLE_SETTING_KEY: 5} + monkeypatch.setattr(settings, "_cached_setting", lambda k, d = None: store.get(k, d)) + monkeypatch.setattr(settings, "get_openai_auto_switch_enabled", lambda: True) + assert settings.get_auto_unload_idle_seconds() == 60 + assert settings.get_stored_auto_unload_idle_seconds() == 60 + store[settings.AUTO_UNLOAD_IDLE_SETTING_KEY] = 90 + assert settings.get_auto_unload_idle_seconds() == 90 + + +def test_env_idle_below_floor_is_clamped(monkeypatch): + monkeypatch.setattr(settings, "_cached_setting", lambda k, d = None: d) + monkeypatch.setenv(settings.MODEL_IDLE_TTL_ENV_VAR, "5") + assert settings.get_auto_unload_idle_seconds() == 60 + monkeypatch.setenv(settings.MODEL_IDLE_TTL_ENV_VAR, "0") + assert settings.get_auto_unload_idle_seconds() == 0 + monkeypatch.setenv(settings.MODEL_IDLE_TTL_ENV_VAR, "600") + assert settings.get_auto_unload_idle_seconds() == 600 + monkeypatch.delenv(settings.MODEL_IDLE_TTL_ENV_VAR) + assert settings.get_auto_unload_idle_seconds() == 0 diff --git a/studio/backend/utils/openai_auto_switch_settings.py b/studio/backend/utils/openai_auto_switch_settings.py index 1689395f40..462435e5d5 100644 --- a/studio/backend/utils/openai_auto_switch_settings.py +++ b/studio/backend/utils/openai_auto_switch_settings.py @@ -8,7 +8,9 @@ Two settings, both off by default so existing API behavior is unchanged: names a downloaded local GGUF different from the loaded one transparently loads it before serving (llama-swap-style). Unknown names pass through. - ``openai_api_auto_unload_idle_seconds``: when > 0, the loaded GGUF is - unloaded after this many idle seconds to free VRAM. + unloaded after this many idle seconds to free VRAM. Enabled values have a + 60s floor (0 stays "off"): a tiny TTL tears the model down between turns of + an active chat, forcing a full weight reload + prompt re-prefill per turn. The idle TTL can also be set at startup via the ``UNSLOTH_MODEL_IDLE_TTL`` env var. Unlike the stored setting (which stays gated on auto-switch), the env value @@ -33,6 +35,7 @@ MODEL_IDLE_TTL_ENV_VAR = "UNSLOTH_MODEL_IDLE_TTL" DEFAULT_OPENAI_AUTO_SWITCH_ENABLED = False DEFAULT_AUTO_UNLOAD_IDLE_SECONDS = 0 +MIN_AUTO_UNLOAD_IDLE_SECONDS = 60 _CACHE_TTL_S = 2.0 _cache_lock = threading.Lock() @@ -58,6 +61,10 @@ def _coerce_int(value: Any) -> int | None: return None +def _apply_idle_floor(seconds: int) -> int: + return 0 if seconds <= 0 else max(MIN_AUTO_UNLOAD_IDLE_SECONDS, seconds) + + def _cached_setting(key: str, default: Any) -> Any: """Read an app setting, memoized for _CACHE_TTL_S to spare the hot path.""" now = time.monotonic() @@ -91,12 +98,34 @@ def _stored_idle_seconds() -> Optional[int]: return _coerce_int(_cached_setting(AUTO_UNLOAD_IDLE_SETTING_KEY, None)) +_env_floor_warned = False + + def _env_idle_seconds() -> Optional[int]: - """UNSLOTH_MODEL_IDLE_TTL as a non-negative seconds value, or None if unset/invalid.""" + """UNSLOTH_MODEL_IDLE_TTL as a non-negative seconds value, or None if unset/invalid. + + Floored to MIN_AUTO_UNLOAD_IDLE_SECONDS here (with a one-time warning) since + headless/container deploys have no UI to surface a validation error.""" raw = os.environ.get(MODEL_IDLE_TTL_ENV_VAR) if raw is None or not raw.strip(): return None - return _coerce_int(raw) + parsed = _coerce_int(raw) + if parsed is None: + return None + floored = _apply_idle_floor(parsed) + if floored != parsed: + global _env_floor_warned + if not _env_floor_warned: + _env_floor_warned = True + from loggers import get_logger + get_logger(__name__).warning( + "%s=%s is below the %ss minimum; using %ss", + MODEL_IDLE_TTL_ENV_VAR, + parsed, + MIN_AUTO_UNLOAD_IDLE_SECONDS, + floored, + ) + return floored def get_stored_auto_unload_idle_seconds() -> int: @@ -108,7 +137,9 @@ def get_stored_auto_unload_idle_seconds() -> int: """ stored = _stored_idle_seconds() if stored is not None: - return stored + # Floor legacy values persisted before the minimum existed, so the UI + # displays the effective TTL and round-trips it cleanly. + return _apply_idle_floor(stored) env = _env_idle_seconds() return env if env is not None else DEFAULT_AUTO_UNLOAD_IDLE_SECONDS @@ -118,8 +149,9 @@ def get_auto_unload_idle_seconds() -> int: stored = _stored_idle_seconds() if stored is not None: # An explicit UI/API value stays gated on auto-switch: off reports 0 so the - # off state is identical to pre-feature. - return stored if get_openai_auto_switch_enabled() else 0 + # off state is identical to pre-feature. Floored to cover values persisted + # before the minimum existed. + return _apply_idle_floor(stored) if get_openai_auto_switch_enabled() else 0 # No stored value: UNSLOTH_MODEL_IDLE_TTL is a standalone startup default that # enables idle-unload even with auto-switch off (headless/container deploys). env = _env_idle_seconds() @@ -136,6 +168,11 @@ def set_openai_auto_switch(enabled: Any, idle_seconds: Any) -> tuple[bool, int]: parsed_idle = _coerce_int(idle_seconds) if parsed_idle is None: raise ValueError("Auto-unload idle seconds must be a non-negative integer.") + if 0 < parsed_idle < MIN_AUTO_UNLOAD_IDLE_SECONDS: + raise ValueError( + f"Auto-unload idle seconds must be 0 (off) or at least " + f"{MIN_AUTO_UNLOAD_IDLE_SECONDS}." + ) from storage.studio_db import upsert_app_settings upsert_app_settings( diff --git a/studio/frontend/src/features/settings/components/model-auto-switch-section.tsx b/studio/frontend/src/features/settings/components/model-auto-switch-section.tsx index 5bebefa84c..32b3e53a2c 100644 --- a/studio/frontend/src/features/settings/components/model-auto-switch-section.tsx +++ b/studio/frontend/src/features/settings/components/model-auto-switch-section.tsx @@ -14,6 +14,9 @@ import { import { SettingsRow } from "./settings-row"; import { SettingsSection } from "./settings-section"; +// Mirrors MIN_AUTO_UNLOAD_IDLE_SECONDS in the backend settings store. +const MIN_IDLE_SECONDS = 60; + export function ModelAutoSwitchSection() { const t = useT(); const [settings, setSettings] = useState( @@ -45,13 +48,16 @@ export function ModelAutoSwitchSection() { }; }, [t]); - // Parse the idle-seconds draft to a non-negative integer; empty/invalid -> null. + // Parse the idle-seconds draft: 0 (off) or >= MIN_IDLE_SECONDS; else null. const parseIdleSeconds = (): number | null => { if (!draftIdleSeconds.trim()) { return null; } const parsed = Number(draftIdleSeconds); - return Number.isInteger(parsed) && parsed >= 0 ? parsed : null; + if (!Number.isInteger(parsed)) { + return null; + } + return parsed === 0 || parsed >= MIN_IDLE_SECONDS ? parsed : null; }; const persist = async ( diff --git a/studio/frontend/src/i18n/locales/ar.ts b/studio/frontend/src/i18n/locales/ar.ts index 28f404a384..744a2002c9 100644 --- a/studio/frontend/src/i18n/locales/ar.ts +++ b/studio/frontend/src/i18n/locales/ar.ts @@ -155,14 +155,14 @@ export const ar = { "عندما يسمّي طلب متوافق مع OpenAI ملف GGUF مُنزّلاً مختلفًا، يتم تحميله قبل الخدمة. مُعطّل افتراضيًا؛ الأسماء غير المعروفة تُبقي على النموذج المُحمَّل.", idleUnload: "الإلغاء التلقائي عند الخمول", idleUnloadDescription: - "إلغاء تحميل النموذج بعد هذا العدد من ثواني الخمول لتحرير الـ VRAM؛ الطلب التالي يعيد تحميله. القيمة 0 تُبقيه محمَّلاً.", + "إلغاء تحميل النموذج بعد هذا العدد من ثواني الخمول لتحرير الـ VRAM؛ الطلب التالي يعيد تحميله. القيمة 0 تُبقيه محمَّلاً. الحد الأدنى 60 ثانية.", idleNeedsEnable: "فعّل تبديل النموذج حسب الطلب حتى يعاد تحميل النموذج غير المحمَّل عند الاستخدام التالي.", idleActiveViaEnv: "الإلغاء التلقائي عند الخمول مُفعَّل عبر متغير البيئة UNSLOTH_MODEL_IDLE_TTL.", loadError: "فشل تحميل إعدادات التبديل التلقائي للنموذج.", saveError: "فشل حفظ إعدادات التبديل التلقائي للنموذج.", - idleError: "أدخل عددًا صحيحًا من الثواني (0 أو أكثر).", + idleError: "أدخل 0 لإبقاء النموذج محمَّلاً، أو 60 ثانية على الأقل.", }, previewSharing: { sectionTitle: "مشاركة المعاينة", diff --git a/studio/frontend/src/i18n/locales/de.ts b/studio/frontend/src/i18n/locales/de.ts index e38cdbfa0e..7d94e7656e 100644 --- a/studio/frontend/src/i18n/locales/de.ts +++ b/studio/frontend/src/i18n/locales/de.ts @@ -158,7 +158,7 @@ export const de = { "Wenn eine OpenAI-kompatible Anfrage ein anderes heruntergeladenes GGUF nennt, wird dieses vor der Auslieferung geladen. Standardmäßig aus; unbekannte Namen liefern weiterhin das geladene Modell aus.", idleUnload: "Automatisches Entladen bei Inaktivität", idleUnloadDescription: - "Entlädt das Modell nach dieser Anzahl inaktiver Sekunden, um VRAM freizugeben; die nächste Anfrage lädt es erneut. 0 hält es geladen.", + "Entlädt das Modell nach dieser Anzahl inaktiver Sekunden, um VRAM freizugeben; die nächste Anfrage lädt es erneut. 0 hält es geladen. Minimum 60 Sekunden.", idleNeedsEnable: "Aktivieren Sie \"Modell je Anfrage wechseln\", damit ein entladenes Modell bei der nächsten Nutzung erneut geladen wird.", idleActiveViaEnv: @@ -167,7 +167,7 @@ export const de = { "Einstellungen für automatischen Modellwechsel konnten nicht geladen werden.", saveError: "Einstellungen für automatischen Modellwechsel konnten nicht gespeichert werden.", - idleError: "Geben Sie eine ganze Anzahl an Sekunden ein (0 oder mehr).", + idleError: "Geben Sie 0 ein, um das Modell geladen zu halten, oder mindestens 60 Sekunden.", }, previewSharing: { sectionTitle: "Vorschau-Freigabe", diff --git a/studio/frontend/src/i18n/locales/en.ts b/studio/frontend/src/i18n/locales/en.ts index fe3a6f8542..de8ac17c29 100644 --- a/studio/frontend/src/i18n/locales/en.ts +++ b/studio/frontend/src/i18n/locales/en.ts @@ -224,14 +224,14 @@ export const en = { "When an OpenAI-compatible request names a different downloaded GGUF, load it before serving. Off by default; unknown names keep serving the loaded model.", idleUnload: "Idle auto-unload", idleUnloadDescription: - "Unload the model after this many idle seconds to free VRAM; the next request reloads it. 0 keeps it loaded.", + "Unload the model after this many idle seconds to free VRAM; the next request reloads it. 0 keeps it loaded. Minimum 60 seconds.", idleNeedsEnable: "Turn on Switch model by request so an unloaded model reloads on next use.", idleActiveViaEnv: "Idle auto-unload is active via the UNSLOTH_MODEL_IDLE_TTL environment variable.", loadError: "Failed to load model auto-switch settings.", saveError: "Failed to save model auto-switch settings.", - idleError: "Enter a whole number of seconds (0 or more).", + idleError: "Enter 0 to keep the model loaded, or at least 60 seconds.", }, previewSharing: { sectionTitle: "Preview sharing", diff --git a/studio/frontend/src/i18n/locales/es.ts b/studio/frontend/src/i18n/locales/es.ts index e5f9650bef..988c109a3f 100644 --- a/studio/frontend/src/i18n/locales/es.ts +++ b/studio/frontend/src/i18n/locales/es.ts @@ -157,7 +157,7 @@ export const es = { "Cuando una solicitud compatible con OpenAI nombra un GGUF descargado distinto, se carga antes de responder. Desactivado por defecto; los nombres desconocidos siguen usando el modelo cargado.", idleUnload: "Descarga automática por inactividad", idleUnloadDescription: - "Descarga el modelo tras este número de segundos inactivo para liberar VRAM; la siguiente solicitud lo recarga. 0 lo mantiene cargado.", + "Descarga el modelo tras este número de segundos inactivo para liberar VRAM; la siguiente solicitud lo recarga. 0 lo mantiene cargado. Mínimo 60 segundos.", idleNeedsEnable: "Activa Cambiar de modelo según la solicitud para que un modelo descargado se recargue en el próximo uso.", idleActiveViaEnv: @@ -166,7 +166,7 @@ export const es = { "No se pudo cargar la configuración de cambio automático de modelo.", saveError: "No se pudo guardar la configuración de cambio automático de modelo.", - idleError: "Introduce un número entero de segundos (0 o más).", + idleError: "Introduce 0 para mantener el modelo cargado, o al menos 60 segundos.", }, previewSharing: { sectionTitle: "Compartir vista previa", diff --git a/studio/frontend/src/i18n/locales/fr.ts b/studio/frontend/src/i18n/locales/fr.ts index 190284175d..e1f2a0c5ec 100644 --- a/studio/frontend/src/i18n/locales/fr.ts +++ b/studio/frontend/src/i18n/locales/fr.ts @@ -157,7 +157,7 @@ export const fr = { "Lorsqu'une requête compatible OpenAI nomme un autre GGUF téléchargé, le charger avant de répondre. Désactivé par défaut ; les noms inconnus continuent de servir le modèle chargé.", idleUnload: "Déchargement automatique en cas d'inactivité", idleUnloadDescription: - "Décharger le modèle après ce nombre de secondes d'inactivité pour libérer la VRAM ; la requête suivante le recharge. 0 le maintient chargé.", + "Décharger le modèle après ce nombre de secondes d'inactivité pour libérer la VRAM ; la requête suivante le recharge. 0 le maintient chargé. Minimum 60 secondes.", idleNeedsEnable: "Activez Changer de modèle par requête pour qu'un modèle déchargé se recharge à la prochaine utilisation.", idleActiveViaEnv: @@ -166,7 +166,7 @@ export const fr = { "Échec du chargement des paramètres de changement automatique de modèle.", saveError: "Échec de l'enregistrement des paramètres de changement automatique de modèle.", - idleError: "Saisissez un nombre entier de secondes (0 ou plus).", + idleError: "Saisissez 0 pour garder le modèle chargé, ou au moins 60 secondes.", }, previewSharing: { sectionTitle: "Partage de l'aperçu", diff --git a/studio/frontend/src/i18n/locales/hi.ts b/studio/frontend/src/i18n/locales/hi.ts index 97f55251c5..77b6265e7b 100644 --- a/studio/frontend/src/i18n/locales/hi.ts +++ b/studio/frontend/src/i18n/locales/hi.ts @@ -154,14 +154,14 @@ export const hi = { "जब कोई OpenAI-संगत अनुरोध किसी अन्य डाउनलोड किए गए GGUF का नाम लेता है, तो सर्व करने से पहले उसे लोड करें। डिफ़ॉल्ट रूप से बंद; अज्ञात नाम लोड किए गए मॉडल को सर्व करते रहते हैं।", idleUnload: "निष्क्रिय ऑटो-अनलोड", idleUnloadDescription: - "VRAM मुक्त करने के लिए इतने निष्क्रिय सेकंड के बाद मॉडल को अनलोड करें; अगला अनुरोध इसे फिर से लोड करता है। 0 इसे लोड रखता है।", + "VRAM मुक्त करने के लिए इतने निष्क्रिय सेकंड के बाद मॉडल को अनलोड करें; अगला अनुरोध इसे फिर से लोड करता है। 0 इसे लोड रखता है। न्यूनतम 60 सेकंड।", idleNeedsEnable: "अनुरोध के अनुसार मॉडल बदलें चालू करें ताकि अनलोड किया गया मॉडल अगले उपयोग पर फिर से लोड हो।", idleActiveViaEnv: "निष्क्रिय ऑटो-अनलोड UNSLOTH_MODEL_IDLE_TTL एनवायरनमेंट वेरिएबल के माध्यम से सक्रिय है।", loadError: "मॉडल ऑटो-स्विच सेटिंग्स लोड करने में विफल।", saveError: "मॉडल ऑटो-स्विच सेटिंग्स सहेजने में विफल।", - idleError: "सेकंड की पूरी संख्या दर्ज करें (0 या अधिक)।", + idleError: "मॉडल को लोड रखने के लिए 0 दर्ज करें, या कम से कम 60 सेकंड।", }, previewSharing: { sectionTitle: "पूर्वावलोकन साझाकरण", diff --git a/studio/frontend/src/i18n/locales/ja.ts b/studio/frontend/src/i18n/locales/ja.ts index 23752b6c26..a261994f03 100644 --- a/studio/frontend/src/i18n/locales/ja.ts +++ b/studio/frontend/src/i18n/locales/ja.ts @@ -149,12 +149,12 @@ export const ja = { enable: "リクエストごとにモデルを切り替え", enableDescription: "OpenAI互換のリクエストが別のダウンロード済み GGUF を指定した場合、応答する前にそのモデルを読み込みます。デフォルトはオフです。不明な名前の場合は、読み込み済みのモデルで応答を続けます。", idleUnload: "アイドル時の自動アンロード", - idleUnloadDescription: "指定した秒数だけアイドル状態が続くとモデルをアンロードして VRAM を解放します。次のリクエストで再読み込みされます。0 にすると読み込んだままにします。", + idleUnloadDescription: "指定した秒数だけアイドル状態が続くとモデルをアンロードして VRAM を解放します。次のリクエストで再読み込みされます。0 にすると読み込んだままにします。最小 60 秒。", idleNeedsEnable: "アンロードされたモデルが次回使用時に再読み込みされるように、「リクエストごとにモデルを切り替え」をオンにしてください。", idleActiveViaEnv: "アイドル時の自動アンロードは UNSLOTH_MODEL_IDLE_TTL 環境変数によって有効になっています。", loadError: "モデル自動切り替え設定の読み込みに失敗しました。", saveError: "モデル自動切り替え設定の保存に失敗しました。", - idleError: "秒数を整数(0 以上)で入力してください。", + idleError: "モデルを読み込んだままにするには 0 を、それ以外は 60 秒以上を入力してください。", }, previewSharing: { sectionTitle: "プレビュー共有", diff --git a/studio/frontend/src/i18n/locales/ko.ts b/studio/frontend/src/i18n/locales/ko.ts index a11a94faf2..7c5691925e 100644 --- a/studio/frontend/src/i18n/locales/ko.ts +++ b/studio/frontend/src/i18n/locales/ko.ts @@ -153,14 +153,14 @@ export const ko = { "OpenAI 호환 요청이 다운로드된 다른 GGUF를 지정하면, 응답하기 전에 해당 모델을 불러옵니다. 기본값은 꺼짐이며, 알 수 없는 이름은 불러온 모델을 계속 제공합니다.", idleUnload: "유휴 시 자동 해제", idleUnloadDescription: - "지정한 유휴 시간(초)이 지나면 모델을 해제하여 VRAM을 확보합니다. 다음 요청 시 다시 불러옵니다. 0으로 설정하면 계속 로드된 상태로 유지됩니다.", + "지정한 유휴 시간(초)이 지나면 모델을 해제하여 VRAM을 확보합니다. 다음 요청 시 다시 불러옵니다. 0으로 설정하면 계속 로드된 상태로 유지됩니다. 최소 60초입니다.", idleNeedsEnable: "해제된 모델이 다음 사용 시 다시 로드되도록 하려면 요청에 따라 모델 전환을 켜세요.", idleActiveViaEnv: "유휴 시 자동 해제가 UNSLOTH_MODEL_IDLE_TTL 환경 변수를 통해 활성화되어 있습니다.", loadError: "모델 자동 전환 설정을 불러오지 못했습니다.", saveError: "모델 자동 전환 설정을 저장하지 못했습니다.", - idleError: "정수(초)를 입력하세요(0 이상).", + idleError: "모델을 로드 상태로 유지하려면 0을, 그렇지 않으면 60초 이상을 입력하세요.", }, previewSharing: { sectionTitle: "미리보기 공유", diff --git a/studio/frontend/src/i18n/locales/pt-br.ts b/studio/frontend/src/i18n/locales/pt-br.ts index 07832ef1d0..e6d2347c10 100644 --- a/studio/frontend/src/i18n/locales/pt-br.ts +++ b/studio/frontend/src/i18n/locales/pt-br.ts @@ -157,14 +157,14 @@ export const ptBR = { "Quando uma requisição compatível com OpenAI nomear um GGUF baixado diferente, carrega-o antes de responder. Desativado por padrão; nomes desconhecidos continuam usando o modelo carregado.", idleUnload: "Descarregamento automático por inatividade", idleUnloadDescription: - "Descarrega o modelo após esta quantidade de segundos de inatividade para liberar VRAM; a próxima requisição o recarrega. 0 mantém o modelo carregado.", + "Descarrega o modelo após esta quantidade de segundos de inatividade para liberar VRAM; a próxima requisição o recarrega. 0 mantém o modelo carregado. Mínimo de 60 segundos.", idleNeedsEnable: "Ative Trocar de modelo por requisição para que um modelo descarregado seja recarregado no próximo uso.", idleActiveViaEnv: "O descarregamento automático por inatividade está ativo por meio da variável de ambiente UNSLOTH_MODEL_IDLE_TTL.", loadError: "Falha ao carregar as configurações de troca automática de modelo.", saveError: "Falha ao salvar as configurações de troca automática de modelo.", - idleError: "Insira um número inteiro de segundos (0 ou mais).", + idleError: "Insira 0 para manter o modelo carregado, ou pelo menos 60 segundos.", }, previewSharing: { sectionTitle: "Compartilhamento de pré-visualização", diff --git a/studio/frontend/src/i18n/locales/ru.ts b/studio/frontend/src/i18n/locales/ru.ts index 81d20cc2ea..c7464a3b44 100644 --- a/studio/frontend/src/i18n/locales/ru.ts +++ b/studio/frontend/src/i18n/locales/ru.ts @@ -154,14 +154,14 @@ export const ru = { "Когда OpenAI-совместимый запрос указывает другую загруженную GGUF, загружать её перед обслуживанием. По умолчанию выключено; неизвестные имена продолжают обслуживать загруженную модель.", idleUnload: "Автовыгрузка при простое", idleUnloadDescription: - "Выгружать модель после указанного числа секунд простоя, чтобы освободить VRAM; следующий запрос загрузит её снова. 0 оставляет модель загруженной.", + "Выгружать модель после указанного числа секунд простоя, чтобы освободить VRAM; следующий запрос загрузит её снова. 0 оставляет модель загруженной. Минимум 60 секунд.", idleNeedsEnable: "Включите «Переключать модель по запросу», чтобы выгруженная модель загружалась при следующем использовании.", idleActiveViaEnv: "Автовыгрузка при простое активна через переменную окружения UNSLOTH_MODEL_IDLE_TTL.", loadError: "Не удалось загрузить настройки автопереключения модели.", saveError: "Не удалось сохранить настройки автопереключения модели.", - idleError: "Введите целое число секунд (0 или больше).", + idleError: "Введите 0, чтобы модель оставалась загруженной, или не менее 60 секунд.", }, previewSharing: { sectionTitle: "Публикация предпросмотра", diff --git a/studio/frontend/src/i18n/locales/zh-CN.ts b/studio/frontend/src/i18n/locales/zh-CN.ts index 4c51755244..ff218adad2 100644 --- a/studio/frontend/src/i18n/locales/zh-CN.ts +++ b/studio/frontend/src/i18n/locales/zh-CN.ts @@ -152,14 +152,14 @@ export const zhCN = { "当兼容 OpenAI 的请求指定了另一个已下载的 GGUF 时,先加载它再提供服务。默认关闭;未知名称将继续使用已加载的模型。", idleUnload: "空闲自动卸载", idleUnloadDescription: - "空闲达到该秒数后卸载模型以释放 VRAM;下次请求会重新加载。设为 0 则保持加载。", + "空闲达到该秒数后卸载模型以释放 VRAM;下次请求会重新加载。设为 0 则保持加载。最小 60 秒。", idleNeedsEnable: "开启“按请求切换模型”,以便已卸载的模型在下次使用时重新加载。", idleActiveViaEnv: "空闲自动卸载已通过 UNSLOTH_MODEL_IDLE_TTL 环境变量启用。", loadError: "加载模型自动切换设置失败。", saveError: "保存模型自动切换设置失败。", - idleError: "请输入整数秒数(0 或以上)。", + idleError: "输入 0 保持模型加载,或输入至少 60 秒。", }, previewSharing: { sectionTitle: "预览分享",