Skip to content

Commit 82a0266

Browse files
committed
feat(internals): add verbose logging to ReasoningBudgetSampler
- Add `verbose` parameter to ReasoningBudgetSampler to print high-level state transitions to stderr. - Log key events: initialization, reasoning_start matched, budget exhausted, forced end sequence, UTF-8 boundary waiting, manual force, natural end, reset. - Pass `verbose=getattr(model, "verbose", False)` from LlamaSamplingContext when building the sampler chain. - Preserve verbose flag when cloning the sampler. Signed-off-by: JamePeng <jame_peng@sina.com>
1 parent be123f1 commit 82a0266

1 file changed

Lines changed: 43 additions & 3 deletions

File tree

llama_cpp/_internals.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import ctypes
44
import enum
55
import os
6+
import sys
67

78
from typing import (
89
Callable,
@@ -1566,6 +1567,9 @@ def __init__(
15661567
sorted=False,
15671568
)
15681569

1570+
# Active Python reasoning-budget sampler for this sampling context.
1571+
self.reasoning_budget_sampler: Optional[ReasoningBudgetSampler] = None
1572+
15691573
# Sampler chain
15701574
if _existing_sampler:
15711575
self.sampler_chain = _existing_sampler
@@ -1583,9 +1587,6 @@ def __init__(
15831587
params.grammar_triggers,
15841588
)
15851589

1586-
# Active Python reasoning-budget sampler for this sampling context.
1587-
self.reasoning_budget_sampler: Optional[ReasoningBudgetSampler] = None
1588-
15891590
def _build_sampler_chain(self):
15901591
"""
15911592
Build sampler chain aligned with llama.cpp common_sampler_init
@@ -1682,6 +1683,7 @@ def _build_sampler_chain(self):
16821683
),
16831684
start_max_tokens=p.reasoning_start_max_tokens,
16841685
wait_utf8=True,
1686+
verbose=getattr(m, "verbose", False),
16851687
)
16861688

16871689
# Keep a direct Python reference so force_reasoning_budget() can
@@ -2131,6 +2133,7 @@ def __init__(
21312133
initial_state: ReasoningBudgetState = ReasoningBudgetState.IDLE,
21322134
start_max_tokens: Optional[int] = 32,
21332135
wait_utf8: bool = True,
2136+
verbose: bool = False,
21342137
):
21352138
"""
21362139
Initialize the reasoning budget sampler.
@@ -2182,6 +2185,11 @@ def __init__(
21822185
If True, when the budget is exhausted on an incomplete UTF-8
21832186
token piece, wait until a complete UTF-8 boundary before forcing
21842187
the end sequence.
2188+
2189+
verbose:
2190+
If True, print high-level reasoning-budget state transitions to
2191+
stderr. Logging is intentionally limited to transitions instead
2192+
of per-token events to avoid noisy generation output.
21852193
"""
21862194
if model is None:
21872195
raise ValueError("model must not be None")
@@ -2242,6 +2250,10 @@ def __init__(
22422250
# Whether to delay forcing until a complete UTF-8 boundary.
22432251
self.wait_utf8 = wait_utf8
22442252

2253+
# Whether to print high-level state transition logs.
2254+
# This follows the model/runtime verbose flag and avoids per-token spam.
2255+
self.verbose = verbose
2256+
22452257
# Keep cloned Python sampler objects alive when llama.cpp clones the
22462258
# sampler chain. Without this, cloned Python callbacks could be garbage
22472259
# collected while C still holds function pointers to them.
@@ -2258,6 +2270,19 @@ def __init__(
22582270
name="reasoning-budget",
22592271
)
22602272

2273+
if self.verbose:
2274+
print(
2275+
f"ReasoningBudgetSampler: initialized "
2276+
f"(state={self.state.name}, budget={self.reasoning_budget}, "
2277+
f"start_max_tokens={self.start_max_tokens}, wait_utf8={self.wait_utf8}).",
2278+
file=sys.stderr,
2279+
)
2280+
2281+
def _log(self, message: str) -> None:
2282+
"""Print a verbose reasoning-budget state transition message."""
2283+
if self.verbose:
2284+
print(f"ReasoningBudgetSampler: {message}", file=sys.stderr)
2285+
22612286
def force(self) -> bool:
22622287
"""
22632288
Manually transition the active reasoning block into forced ending.
@@ -2278,6 +2303,7 @@ def force(self) -> bool:
22782303
self.state = ReasoningBudgetState.FORCING
22792304
self.force_pos = 0
22802305
self.end_matcher.reset()
2306+
self._log("manual force requested; entering FORCING state.")
22812307
return True
22822308

22832309
def _token_utf8_complete(self, token: int) -> bool:
@@ -2313,9 +2339,11 @@ def _start_counting(self) -> None:
23132339
self.remaining = self.reasoning_budget
23142340
self.end_matcher.reset()
23152341
self.force_pos = 0
2342+
self._log(f"reasoning_start matched; entering COUNTING state (budget={self.reasoning_budget}).")
23162343

23172344
if self.remaining <= 0:
23182345
self.state = ReasoningBudgetState.FORCING
2346+
self._log("budget is 0; entering FORCING state immediately.")
23192347

23202348
def _accept(self, token: int) -> None:
23212349
"""
@@ -2345,6 +2373,10 @@ def _accept(self, token: int) -> None:
23452373
and self.generated_tokens >= self.start_max_tokens
23462374
):
23472375
self.state = ReasoningBudgetState.DONE
2376+
self._log(
2377+
f"reasoning_start not found within {self.start_max_tokens} generated tokens; "
2378+
"switching to DONE passthrough."
2379+
)
23482380
return
23492381

23502382
if self.state in (
@@ -2353,6 +2385,7 @@ def _accept(self, token: int) -> None:
23532385
):
23542386
if self.end_matcher.advance(token):
23552387
self.state = ReasoningBudgetState.DONE
2388+
self._log("reasoning_end matched naturally; switching to DONE passthrough.")
23562389
return
23572390

23582391
utf8_complete = self._token_utf8_complete(token)
@@ -2362,6 +2395,7 @@ def _accept(self, token: int) -> None:
23622395
self.state = ReasoningBudgetState.FORCING
23632396
self.force_pos = 0
23642397
self.end_matcher.reset()
2398+
self._log("UTF-8 boundary reached; entering FORCING state.")
23652399
return
23662400

23672401
self.remaining -= 1
@@ -2370,15 +2404,18 @@ def _accept(self, token: int) -> None:
23702404
self.state = ReasoningBudgetState.FORCING
23712405
self.force_pos = 0
23722406
self.end_matcher.reset()
2407+
self._log("reasoning budget exhausted; entering FORCING state.")
23732408
else:
23742409
self.state = ReasoningBudgetState.WAITING_UTF8
23752410
self.end_matcher.reset()
2411+
self._log("reasoning budget exhausted; waiting for UTF-8 boundary before forcing.")
23762412
return
23772413

23782414
if self.state == ReasoningBudgetState.FORCING:
23792415
self.force_pos += 1
23802416
if self.force_pos >= len(self.forced_tokens):
23812417
self.state = ReasoningBudgetState.DONE
2418+
self._log("forced end sequence completed; switching to DONE passthrough.")
23822419
return
23832420

23842421
if self.state == ReasoningBudgetState.DONE:
@@ -2448,6 +2485,8 @@ def _reset(self) -> None:
24482485
if self.state == ReasoningBudgetState.COUNTING and self.remaining <= 0:
24492486
self.state = ReasoningBudgetState.FORCING
24502487

2488+
self._log(f"reset to {self.state.name} state.")
2489+
24512490
def _clone(self):
24522491
"""
24532492
Clone the full runtime state.
@@ -2464,6 +2503,7 @@ def _clone(self):
24642503
initial_state=self.initial_state,
24652504
start_max_tokens=self.start_max_tokens,
24662505
wait_utf8=self.wait_utf8,
2506+
verbose=self.verbose,
24672507
)
24682508

24692509
cloned.remaining = self.remaining

0 commit comments

Comments
 (0)