Studio: add option to disable the in-memory API monitor (#7156)
--------- Co-authored-by: oobabooga <112222186+oobabooga@users.noreply.github.com>
This commit is contained in:
parent
c8bc451d7e
commit
36e83de336
2 changed files with 78 additions and 2 deletions
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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) ────────────────────────────
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue