Spaces:
Running
Running
zero-gpu: skip the forced think block for triage labels
Browse files- BUILD.json +1 -1
- traceforge/config.py +1 -0
- traceforge/space/local_llm.py +7 -1
- traceforge/space/pipeline.py +3 -0
- traceforge/triage.py +4 -0
BUILD.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
{
|
| 2 |
-
"built_at": "2026-10-29T15:
|
| 3 |
"commit": "e9f5ce0",
|
| 4 |
"files": 53,
|
| 5 |
"with_store": false,
|
|
|
|
| 1 |
{
|
| 2 |
+
"built_at": "2026-10-29T15:10:15",
|
| 3 |
"commit": "e9f5ce0",
|
| 4 |
"files": 53,
|
| 5 |
"with_store": false,
|
traceforge/config.py
CHANGED
|
@@ -119,6 +119,7 @@ class Config:
|
|
| 119 |
triage_model: str = "" # empty = server default
|
| 120 |
triage_max_chars: int = 24_000 # per-window char budget (≈6k tokens)
|
| 121 |
triage_max_tokens: int = 4000 # per-window label emit ceiling
|
|
|
|
| 122 |
triage_overlap_messages: int = 4
|
| 123 |
triage_workers: int = 8 # parallel window requests
|
| 124 |
# Parallel traces in the pre-pass (2026-10-23). The pre-pass used to be
|
|
|
|
| 119 |
triage_model: str = "" # empty = server default
|
| 120 |
triage_max_chars: int = 24_000 # per-window char budget (≈6k tokens)
|
| 121 |
triage_max_tokens: int = 4000 # per-window label emit ceiling
|
| 122 |
+
triage_think: bool = True # False = skip the model's think block
|
| 123 |
triage_overlap_messages: int = 4
|
| 124 |
triage_workers: int = 8 # parallel window requests
|
| 125 |
# Parallel traces in the pre-pass (2026-10-23). The pre-pass used to be
|
traceforge/space/local_llm.py
CHANGED
|
@@ -288,7 +288,7 @@ def _prefetch_error() -> str:
|
|
| 288 |
|
| 289 |
@_gpu
|
| 290 |
def generate(messages: list[dict], *, max_tokens: int = 512,
|
| 291 |
-
temperature: float = 0.2) -> tuple[str, dict]:
|
| 292 |
"""One chat completion on the local model (inside a GPU slice)."""
|
| 293 |
if _stub():
|
| 294 |
return '{"ok": true}', {"stub": True}
|
|
@@ -303,6 +303,11 @@ def generate(messages: list[dict], *, max_tokens: int = 512,
|
|
| 303 |
model, tok = _load()
|
| 304 |
prompt = tok.apply_chat_template(messages, tokenize=False,
|
| 305 |
add_generation_prompt=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
enc = tok(prompt, return_tensors="pt", truncation=True,
|
| 307 |
max_length=LOCAL_MAX_INPUT_TOKENS).to(LOCAL_DEVICE)
|
| 308 |
n = max(1, min(int(max_tokens or 512), LOCAL_MAX_NEW_TOKENS))
|
|
@@ -406,6 +411,7 @@ class _Handler(BaseHTTPRequestHandler):
|
|
| 406 |
messages,
|
| 407 |
max_tokens=int(payload.get("max_tokens") or 512),
|
| 408 |
temperature=float(payload.get("temperature") or 0.2),
|
|
|
|
| 409 |
)
|
| 410 |
except Exception as e:
|
| 411 |
global LAST_ERROR
|
|
|
|
| 288 |
|
| 289 |
@_gpu
|
| 290 |
def generate(messages: list[dict], *, max_tokens: int = 512,
|
| 291 |
+
temperature: float = 0.2, think: bool = True) -> tuple[str, dict]:
|
| 292 |
"""One chat completion on the local model (inside a GPU slice)."""
|
| 293 |
if _stub():
|
| 294 |
return '{"ok": true}', {"stub": True}
|
|
|
|
| 303 |
model, tok = _load()
|
| 304 |
prompt = tok.apply_chat_template(messages, tokenize=False,
|
| 305 |
add_generation_prompt=True)
|
| 306 |
+
if not think and prompt.rstrip().endswith(" thinking"):
|
| 307 |
+
# The template opens a think block for us; closing it immediately
|
| 308 |
+
# skips the reasoning pass. At ~16 tok/s on the MIG slice the
|
| 309 |
+
# reasoning costs more than the answer it improves.
|
| 310 |
+
prompt = prompt.rstrip() + "\n\n\n\n"
|
| 311 |
enc = tok(prompt, return_tensors="pt", truncation=True,
|
| 312 |
max_length=LOCAL_MAX_INPUT_TOKENS).to(LOCAL_DEVICE)
|
| 313 |
n = max(1, min(int(max_tokens or 512), LOCAL_MAX_NEW_TOKENS))
|
|
|
|
| 411 |
messages,
|
| 412 |
max_tokens=int(payload.get("max_tokens") or 512),
|
| 413 |
temperature=float(payload.get("temperature") or 0.2),
|
| 414 |
+
think=bool(payload.get("think", True)),
|
| 415 |
)
|
| 416 |
except Exception as e:
|
| 417 |
global LAST_ERROR
|
traceforge/space/pipeline.py
CHANGED
|
@@ -89,6 +89,9 @@ def space_config(data_dir: Optional[str] = None, **overrides: Any) -> Config:
|
|
| 89 |
# Labels are compact JSON: a 4000-token ceiling let the model ramble
|
| 90 |
# for 3264 tokens (199 s) and blow the slice.
|
| 91 |
cfg.triage_max_tokens = _env_int("TRACEFORGE_LOCAL_TRIAGE_TOKENS", 600)
|
|
|
|
|
|
|
|
|
|
| 92 |
cfg.rewrite_max_tokens = _env_int("TRACEFORGE_LOCAL_REWRITE_TOKENS",
|
| 93 |
1000)
|
| 94 |
cfg.judge_max_tokens = _env_int("TRACEFORGE_LOCAL_JUDGE_TOKENS", 1200)
|
|
|
|
| 89 |
# Labels are compact JSON: a 4000-token ceiling let the model ramble
|
| 90 |
# for 3264 tokens (199 s) and blow the slice.
|
| 91 |
cfg.triage_max_tokens = _env_int("TRACEFORGE_LOCAL_TRIAGE_TOKENS", 600)
|
| 92 |
+
# The template forces a </think> block; closing it up front keeps the
|
| 93 |
+
# whole budget for the labels.
|
| 94 |
+
cfg.triage_think = False
|
| 95 |
cfg.rewrite_max_tokens = _env_int("TRACEFORGE_LOCAL_REWRITE_TOKENS",
|
| 96 |
1000)
|
| 97 |
cfg.judge_max_tokens = _env_int("TRACEFORGE_LOCAL_JUDGE_TOKENS", 1200)
|
traceforge/triage.py
CHANGED
|
@@ -361,6 +361,10 @@ def _classify_window(
|
|
| 361 |
"temperature": cfg.triage_temperature,
|
| 362 |
"max_tokens": cfg.triage_max_tokens,
|
| 363 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
if not model:
|
| 365 |
payload.pop("model")
|
| 366 |
payload.update(variant)
|
|
|
|
| 361 |
"temperature": cfg.triage_temperature,
|
| 362 |
"max_tokens": cfg.triage_max_tokens,
|
| 363 |
}
|
| 364 |
+
if not cfg.triage_think:
|
| 365 |
+
# Only the local server understands this; remote endpoints get
|
| 366 |
+
# the field stripped so their schema validation never sees it.
|
| 367 |
+
payload["think"] = False
|
| 368 |
if not model:
|
| 369 |
payload.pop("model")
|
| 370 |
payload.update(variant)
|