* checkpoint preview endpoint * harden new preview endpoints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * address review * Studio preview: pin adapter, guard streaming submit, robust copy-link Harden the public per-checkpoint preview surface: - Pin use_adapter=True in the preview payload sanitizer. Otherwise an unauthenticated /p caller can POST use_adapter=false, which calls disable_adapter_layers() on the shared in-memory model without restoring it; since load_model skips reloads for the same checkpoint, every later visitor (the page never sends the field) keeps getting base-model output instead of the fine-tuned checkpoint. Forcing it on also re-enables a previously disabled adapter and no-ops on merged checkpoints. - Ignore preview-page submits while a response is streaming. The send button was disabled but the Enter handler still called requestSubmit(), so a second request could start before the first reply landed in msgs and reorder the chat history. Both the keydown and submit handlers now honor the disabled button. - Keep the cloudflare-URL polling loop alive across transient startup fetch errors instead of letting one rejection halt it. - Build the copy-link from a backend preview_ref (output dir relative to outputs_root, gated on previewability and the two-segment /p route limit) so a nested output dir no longer copies a basename-only link that 404s. Expose preview_ref on training run summaries. Add route-level security tests (path traversal, payload sanitization, asset containment, CSP header, HTML title escaping, streaming lock held until drained) and preview_ref unit tests. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio preview: Safari-safe submit and adapter pin only for LoRA Follow-ups from cross-browser and route simulations: - Preview page: send the message from a shared send() helper called by both the form submit and the Enter key, instead of form.requestSubmit(). The latter throws on Safari < 16 and older iOS, which broke Enter-to-send there. Verified across Chromium, Firefox and WebKit with Playwright. - Only pin use_adapter=True when the resolved checkpoint is a LoRA adapter (adapter_config.json present); for a merged checkpoint strip it to None. A merged model has no adapter to toggle, so forcing it on only produced a per-request "not a PeftModel" warning. The cross-request base-model contamination fix still holds for LoRA previews. Add a merged-checkpoint test asserting use_adapter is stripped to None. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio preview: trim verbose comments Tighten comments across the preview routes, page, checkpoint helpers, and tests to short single-line notes; drop ones that just restate the code. No behavior change (verified comment/docstring-only with comment_tools.py check). * Harden preview routes for PR #6486 - Return a generic 400 detail on a rejected preview path so the public /p route never echoes the absolute install path (the real reason is logged server-side instead). - Strip confirm_tool_calls, session_id and rag_scope in the preview payload sanitizer so the public surface stays inert regardless of the tool gate. - Use Path.is_relative_to for the asset containment check, matching the rest of the codebase. - Add img-src 'self' and font-src 'self' to the preview page CSP. - Preview page: on a mid-stream error keep the streamed text, flag the break, and restore the prompt so the user can retry; drop the unused --font-sans var. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
50 lines
1.4 KiB
Python
50 lines
1.4 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
|
|
|
|
"""Process-level server-side tool policy.
|
|
|
|
Set by `unsloth run` at startup; consulted by the inference route gates.
|
|
|
|
None -> no CLI override (default). Per-request `enable_tools` is honored.
|
|
True -> CLI forced tools on for every request.
|
|
False -> CLI forced tools off for every request.
|
|
"""
|
|
|
|
import contextvars
|
|
from contextlib import contextmanager
|
|
from typing import Iterator, Optional
|
|
|
|
_tool_policy: Optional[bool] = None
|
|
|
|
# Per-request hard-off so public surfaces refuse tools even under a CLI `--enable-tools`.
|
|
_force_disabled: contextvars.ContextVar[bool] = contextvars.ContextVar(
|
|
"tool_policy_force_disabled", default = False
|
|
)
|
|
|
|
|
|
def get_tool_policy() -> Optional[bool]:
|
|
if _force_disabled.get():
|
|
return False
|
|
return _tool_policy
|
|
|
|
|
|
@contextmanager
|
|
def tools_force_disabled() -> Iterator[None]:
|
|
"""Hard-disable server-side tools for the current async context."""
|
|
token = _force_disabled.set(True)
|
|
try:
|
|
yield
|
|
finally:
|
|
_force_disabled.reset(token)
|
|
|
|
|
|
def set_tool_policy(value: Optional[bool]) -> None:
|
|
if value is not None and not isinstance(value, bool):
|
|
raise TypeError(f"tool_policy must be Optional[bool], got {type(value).__name__}")
|
|
global _tool_policy
|
|
_tool_policy = value
|
|
|
|
|
|
def reset_tool_policy() -> None:
|
|
global _tool_policy
|
|
_tool_policy = None
|