Skip to content

Commit bdaadcb

Browse files
committed
address comments, pre-commit
Signed-off-by: Erin Ho <erinh@nvidia.com>
1 parent 36f96ad commit bdaadcb

6 files changed

Lines changed: 63 additions & 28 deletions

File tree

nemo_rl/data/llm_message_utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,8 @@ def batched_message_log_to_flat_message(
377377
try:
378378
_validate_tensor_consistency(tensors)
379379
except RuntimeError as e:
380-
raise RuntimeError(f"[key={key!r}] {e}") from e
380+
e.add_note(f"[key={key!r}]")
381+
raise
381382

382383
# Create zero tensors for None values
383384
filled_values: list[Tensor] = [

nemo_rl/environments/nemo_gym.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,10 @@ def _postprocess_nemo_gym_to_nemo_rl_result(
345345

346346
# Note that NeMo-Gym will only return token ids on "assistant" messages and not other message types.
347347
# Also skip if generation_token_ids is present but empty, e.g. all-EOS generation stripped to [] — torch.tensor([]) defaults to float32 and breaks batch dtype consistency.
348-
if "generation_token_ids" not in output_item_dict or not output_item_dict["generation_token_ids"]:
348+
if (
349+
"generation_token_ids" not in output_item_dict
350+
or not output_item_dict["generation_token_ids"]
351+
):
349352
continue
350353

351354
assert (

nemo_rl/models/generation/openai_server_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,6 @@ def replace_prefix_tokens(
120120
f"Template repr (detokenized): {repr(tokenizer.decode(template_token_ids))}"
121121
)
122122

123-
return model_prefix_token_ids[:model_cut_end] + template_token_ids[template_cut_start:]
123+
return (
124+
model_prefix_token_ids[:model_cut_end] + template_token_ids[template_cut_start:]
125+
)

nemo_rl/models/generation/trtllm/trtllm_http_server.py

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,19 @@
2424
import threading
2525
import time
2626
import uuid
27-
from typing import Any, Optional
27+
from typing import TYPE_CHECKING, Any
2828

2929
from nemo_rl.models.generation.openai_server_utils import (
3030
replace_prefix_tokens,
3131
)
3232

33+
if TYPE_CHECKING:
34+
from fastapi import FastAPI
35+
3336
logger = logging.getLogger(__name__)
3437

3538
_TOOL_BOT = "<tool_call>\n"
36-
_TOOL_CALL_RE = re.compile(
37-
r"<tool_call>\n(.*?)\n</tool_call>", re.DOTALL
38-
)
39+
_TOOL_CALL_RE = re.compile(r"<tool_call>\n(.*?)\n</tool_call>", re.DOTALL)
3940

4041

4142
def create_app(
@@ -59,6 +60,7 @@ def create_app(
5960
# so model output starts inside the reasoning block without a leading <think> tag.
6061
# parse() is stateless — safe for concurrent requests.
6162
from tensorrt_llm.llmapi.reasoning_parser import DeepSeekR1Parser
63+
6264
_reasoning_parser = DeepSeekR1Parser(reasoning_at_start=True)
6365

6466
# Stop tokens TRT-LLM appends to gen_token_ids but NOT reproduced by apply_chat_template.
@@ -87,7 +89,10 @@ async def chat_completions(request: Request):
8789
logprobs_requested = body.get("logprobs", False)
8890

8991
prompt_token_ids = _build_prompt_token_ids(
90-
messages, tokenizer, tools=tools, default_template_kwargs=_default_template_kwargs
92+
messages,
93+
tokenizer,
94+
tools=tools,
95+
default_template_kwargs=_default_template_kwargs,
9196
)
9297

9398
# Prefix splice: empty required_prefix_ids (no prior assistant turn) → returns template unchanged.
@@ -102,14 +107,18 @@ async def chat_completions(request: Request):
102107
template_token_ids=prompt_token_ids,
103108
)
104109

105-
max_tokens_requested = body.get("max_tokens") or body.get("max_completion_tokens") or max_seq_len
110+
max_tokens_requested = (
111+
body.get("max_tokens") or body.get("max_completion_tokens") or max_seq_len
112+
)
106113
remaining_ctx = max(0, max_seq_len - len(adj_prompt))
107114

108115
# Return HTTP 400 on context exhaustion.
109116
if remaining_ctx == 0:
110117
return JSONResponse(
111118
status_code=400,
112-
content={"error": f"context length exceeded: prompt ({len(adj_prompt)} tokens) exhausted context window ({max_seq_len})"},
119+
content={
120+
"error": f"context length exceeded: prompt ({len(adj_prompt)} tokens) exhausted context window ({max_seq_len})"
121+
},
113122
)
114123

115124
max_tokens = min(int(max_tokens_requested), remaining_ctx)
@@ -131,7 +140,11 @@ async def chat_completions(request: Request):
131140
)
132141
except Exception as e:
133142
err = str(e)
134-
if "prompt length" in err or "max_num_tokens" in err or "max_seq_len" in err:
143+
if (
144+
"prompt length" in err
145+
or "max_num_tokens" in err
146+
or "max_seq_len" in err
147+
):
135148
return JSONResponse(
136149
status_code=400,
137150
content={"error": f"context length exceeded: {err}"},
@@ -144,13 +157,14 @@ async def chat_completions(request: Request):
144157

145158
gen_logprobs: list[float] = []
146159
if gen.logprobs:
147-
for lp in gen.logprobs:
160+
# TRT-LLM returns floats in simple format and token-indexed dicts otherwise.
161+
for token_id, lp in zip(gen_token_ids, gen.logprobs, strict=True):
148162
if isinstance(lp, (int, float)):
149163
gen_logprobs.append(float(lp))
150164
elif isinstance(lp, dict):
151-
gen_logprobs.append(float(next(iter(lp.values())).logprob))
165+
gen_logprobs.append(float(lp[token_id].logprob))
152166
else:
153-
gen_logprobs.append(float(lp))
167+
raise TypeError(f"Unsupported TRT-LLM logprob type: {type(lp)}")
154168

155169
# Strip trailing stop tokens TRT-LLM appends — apply_chat_template doesn't reproduce
156170
# <|endoftext|>, so they'd break seen_token_ids contiguity. Trim logprobs in lockstep.
@@ -239,6 +253,7 @@ async def chat_completions(request: Request):
239253
# Tool-call parsing — mirrors Qwen3ToolParser.detect_and_parse() exactly
240254
# ---------------------------------------------------------------------------
241255

256+
242257
def _parse_tool_calls(text: str) -> list[dict[str, Any]]:
243258
"""Extract <tool_call> blocks; arg serialisation is byte-identical to Qwen3ToolParser."""
244259
results: list[dict[str, Any]] = []
@@ -253,14 +268,16 @@ def _parse_tool_calls(text: str) -> list[dict[str, Any]]:
253268
obj.get("parameters") or obj.get("arguments", {}),
254269
ensure_ascii=False,
255270
)
256-
results.append({
257-
"id": f"call_{uuid.uuid4().hex[:8]}",
258-
"type": "function",
259-
"function": {
260-
"name": func_name,
261-
"arguments": arguments,
262-
},
263-
})
271+
results.append(
272+
{
273+
"id": f"call_{uuid.uuid4().hex[:8]}",
274+
"type": "function",
275+
"function": {
276+
"name": func_name,
277+
"arguments": arguments,
278+
},
279+
}
280+
)
264281
return results
265282

266283

@@ -276,6 +293,7 @@ def _strip_tool_call_tags(text: str) -> str:
276293
# Prompt construction
277294
# ---------------------------------------------------------------------------
278295

296+
279297
def _to_int_ids(enc: Any) -> list[int]:
280298
"""Coerce apply_chat_template output to flat list[int] (handles transformers v5 BatchEncoding)."""
281299
if hasattr(enc, "input_ids"): # v5 BatchEncoding
@@ -329,17 +347,23 @@ def _compute_splice_inputs(
329347
required_prefix_ids: list[int] = []
330348
for _m in reversed(messages):
331349
if _m.get("role") == "assistant" and "prompt_token_ids" in _m:
332-
required_prefix_ids = (
333-
list(_m["prompt_token_ids"]) + list(_m.get("generation_token_ids") or [])
350+
required_prefix_ids = list(_m["prompt_token_ids"]) + list(
351+
_m.get("generation_token_ids") or []
334352
)
335353
break
336354

337355
_clean = [_strip_token_fields(m) for m in messages]
338356
_last_asst_idx = next(
339-
(i for i in reversed(range(len(_clean))) if _clean[i].get("role") == "assistant"),
357+
(
358+
i
359+
for i in reversed(range(len(_clean)))
360+
if _clean[i].get("role") == "assistant"
361+
),
340362
None,
341363
)
342-
_msgs_to_last_asst = _clean[:_last_asst_idx + 1] if _last_asst_idx is not None else _clean
364+
_msgs_to_last_asst = (
365+
_clean[: _last_asst_idx + 1] if _last_asst_idx is not None else _clean
366+
)
343367
_prefix_tkw: dict[str, Any] = {
344368
**default_template_kwargs,
345369
"tokenize": True,
@@ -357,6 +381,7 @@ def _compute_splice_inputs(
357381
# Server lifecycle
358382
# ---------------------------------------------------------------------------
359383

384+
360385
def start_server(
361386
llm: Any,
362387
tokenizer: Any,
@@ -368,6 +393,7 @@ def start_server(
368393
) -> "tuple[threading.Thread, str, Any]":
369394
"""Start the HTTP server in a daemon thread and return (thread, base_url, server)."""
370395
import uvicorn
396+
371397
from nemo_rl.distributed.virtual_cluster import (
372398
_get_free_port_local,
373399
_get_node_ip_local,

nemo_rl/models/generation/trtllm/trtllm_worker_async.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ def start_http_server(self, port: int = 0) -> str:
271271
from nemo_rl.models.generation.trtllm.trtllm_http_server import start_server
272272

273273
tokenizer = AutoTokenizer.from_pretrained(
274-
self.model_name, trust_remote_code=True,
274+
self.model_name,
275+
trust_remote_code=True,
275276
)
276277
self._http_thread, self._http_base_url, self._http_server = start_server(
277278
llm=self.llm,

tests/unit/models/generation/test_vllm_generation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
from nemo_rl.models.generation.vllm.vllm_worker import (
4141
_resolve_enable_prefix_caching,
4242
)
43-
from nemo_rl.models.generation.vllm.vllm_worker_async import VllmAsyncGenerationWorkerImpl
43+
from nemo_rl.models.generation.vllm.vllm_worker_async import (
44+
VllmAsyncGenerationWorkerImpl,
45+
)
4446
from nemo_rl.models.policy import LoRAConfig, PolicyConfig
4547
from nemo_rl.models.policy.lm_policy import Policy
4648

0 commit comments

Comments
 (0)