Pratham-math commited on
Commit
4e04091
·
verified ·
1 Parent(s): fffaeea

fix(grpo): tail-truncate context inside _to_prompt to satisfy vllm input-length gate

Browse files
Files changed (1) hide show
  1. train/grpo.py +27 -1
train/grpo.py CHANGED
@@ -184,13 +184,39 @@ def run_grpo(
184
  "respond with the shortest exact answer span."
185
  )
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  def _to_prompt(example: dict) -> dict:
 
 
188
  msgs = [
189
  {"role": "system", "content": sys_msg},
190
  {
191
  "role": "user",
192
  "content": (
193
- f"Context:\n{example.get('context','')}\n\n"
194
  f"{example.get('prompt','')}"
195
  ),
196
  },
 
184
  "respond with the shortest exact answer span."
185
  )
186
 
187
+ # vLLM hard-checks final prompt length against the model's max_position_embeddings
188
+ # (32 768 for Qwen 2.5 Coder 0.5B/1.5B). Our train.jsonl `context` field is the
189
+ # full long document (often >40K tokens). `max_prompt_length` in GRPOConfig is a
190
+ # post-tokenization cap that TRL applies AFTER vLLM has already rejected the
191
+ # request (`vllm.exceptions.VLLMValidationError: prompt contains 37220 tokens`).
192
+ # We must truncate inside `_to_prompt`, BEFORE chat templating.
193
+ #
194
+ # Budget: keep the prompt comfortably under cfg.train.max_prompt_length so the
195
+ # system message + chat-template overhead don't push us over. Tail-truncate the
196
+ # context (recent text usually contains the answer span in our synthetic data).
197
+ max_prompt_tok = int(cfg.train.max_prompt_length)
198
+ chat_overhead_tok = 256 # system msg + chat template wrappers + question
199
+ ctx_budget_tok = max(256, max_prompt_tok - chat_overhead_tok)
200
+
201
+ def _truncate_to_tokens(text: str, max_tokens: int) -> str:
202
+ if not text:
203
+ return ""
204
+ ids = tokenizer.encode(text, add_special_tokens=False)
205
+ if len(ids) <= max_tokens:
206
+ return text
207
+ # Keep the tail: synthetic gold answers are sampled across the doc, so tail
208
+ # is no worse than head and is cheaper to slice.
209
+ return tokenizer.decode(ids[-max_tokens:], skip_special_tokens=True)
210
+
211
  def _to_prompt(example: dict) -> dict:
212
+ ctx_full = example.get("context", "") or ""
213
+ ctx_truncated = _truncate_to_tokens(ctx_full, ctx_budget_tok)
214
  msgs = [
215
  {"role": "system", "content": sys_msg},
216
  {
217
  "role": "user",
218
  "content": (
219
+ f"Context:\n{ctx_truncated}\n\n"
220
  f"{example.get('prompt','')}"
221
  ),
222
  },