From 36e83de33653b2e669fc6fed6b53ebcae85bf1b2 Mon Sep 17 00:00:00 2001 From: Gaurav Dubey Date: Tue, 28 Jul 2026 07:17:24 +0530 Subject: [PATCH] Studio: add option to disable the in-memory API monitor (#7156) --------- Co-authored-by: oobabooga <112222186+oobabooga@users.noreply.github.com> --- studio/backend/core/inference/api_monitor.py | 23 +++++++- studio/backend/tests/test_api_monitor.py | 57 ++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/api_monitor.py b/studio/backend/core/inference/api_monitor.py index ce32a6d3ef..b637ba56d1 100644 --- a/studio/backend/core/inference/api_monitor.py +++ b/studio/backend/core/inference/api_monitor.py @@ -5,6 +5,7 @@ from __future__ import annotations +import os import threading import time import uuid @@ -18,6 +19,14 @@ _MAX_PROMPT_CHARS = 12000 _MAX_REPLY_CHARS = 12000 _PREVIEW_CHARS = 360 +# Opt-in startup kill switch for Studio's in-memory API monitor. +_DISABLE_ENV = "UNSLOTH_STUDIO_DISABLE_API_MONITOR" +_TRUE_VALUES = frozenset({"1", "true", "yes", "on"}) + + +def _api_monitor_disabled() -> bool: + return os.environ.get(_DISABLE_ENV, "").strip().lower() in _TRUE_VALUES + def _trim(text: Optional[str], limit: int) -> str: if not text: @@ -104,10 +113,16 @@ class ApiMonitorEntry: class ApiMonitor: - def __init__(self, max_entries: int = _MAX_ENTRIES): + def __init__( + self, + max_entries: int = _MAX_ENTRIES, + *, + enabled: bool = True, + ): self._entries: deque[ApiMonitorEntry] = deque() self._max_entries = max(0, max_entries) self._lock = threading.Lock() + self._enabled = enabled def start( self, @@ -119,6 +134,8 @@ class ApiMonitor: context_length: Optional[int] = None, subject: Optional[str] = None, ) -> str: + if not self._enabled: + return "" now = time.time() entry = ApiMonitorEntry( id = f"apireq_{uuid.uuid4().hex[:12]}", @@ -152,6 +169,8 @@ class ApiMonitor: :meth:`fail`; an unload is terminal on arrival. Rows are shared (visible to every subject) and share the request retention budget. """ + if not self._enabled: + return "" now = time.time() entry = ApiMonitorEntry( id = f"apievt_{uuid.uuid4().hex[:12]}", @@ -392,4 +411,4 @@ class ApiMonitor: self._entries = kept -api_monitor = ApiMonitor() +api_monitor = ApiMonitor(enabled = not _api_monitor_disabled()) diff --git a/studio/backend/tests/test_api_monitor.py b/studio/backend/tests/test_api_monitor.py index 7dd4baa2dd..4602b5cf62 100644 --- a/studio/backend/tests/test_api_monitor.py +++ b/studio/backend/tests/test_api_monitor.py @@ -260,6 +260,63 @@ def test_api_monitor_append_reply_exact_cap_then_more_marks_truncated(): assert len(reply) == m._MAX_REPLY_CHARS and reply.endswith("...") +def test_api_monitor_disabled_is_noop(): + monitor = ApiMonitor(max_entries = 3, enabled = False) + + request_id = monitor.start( + endpoint = "/v1/chat/completions", + method = "POST", + model = "local-model", + prompt = "user: hello", + context_length = 100, + ) + load_id = monitor.record_lifecycle( + event = "load", + model = "local-model", + running = True, + ) + unload_id = monitor.record_lifecycle( + event = "unload", + model = "local-model", + ) + assert request_id == load_id == unload_id == "" + + # Every mutator must be a safe no-op on the falsy id. + monitor.append_reply(request_id, "hi") + monitor.set_reply(request_id, "hi") + monitor.set_usage(request_id, prompt_tokens = 4, completion_tokens = 6) + monitor.relabel(load_id, "renamed-model") + monitor.set_progress(load_id, 50) + monitor.finish(load_id) + monitor.fail_open(load_id, "boom") + monitor.fail(request_id, "boom") + monitor.discard(unload_id) + + assert monitor.snapshot() == [] + assert monitor.active_count() == 0 + assert monitor.get(request_id) is None + + +def test_api_monitor_disable_env_var_truthy(monkeypatch): + import core.inference.api_monitor as m + for value in ("1", "true", "yes", "on", "TRUE", "On", " yes "): + monkeypatch.setenv(m._DISABLE_ENV, value) + assert m._api_monitor_disabled() is True, value + + +def test_api_monitor_disable_env_var_falsy(monkeypatch): + import core.inference.api_monitor as m + for value in ("", "0", "false", "no", "off", "disabled"): + monkeypatch.setenv(m._DISABLE_ENV, value) + assert m._api_monitor_disabled() is False, value + + +def test_api_monitor_disable_env_var_unset(monkeypatch): + import core.inference.api_monitor as m + monkeypatch.delenv(m._DISABLE_ENV, raising = False) + assert m._api_monitor_disabled() is False + + # ── model lifecycle rows (load / unload) ────────────────────────────