fix: request exact token ids and fail loud in FireworksV1CompletionsClient#457
fix: request exact token ids and fail loud in FireworksV1CompletionsClient#457Dranoxgithub wants to merge 1 commit into
Conversation
…lient The /v1/completions sampling path corrupted RL training via retokenization drift. The request never set return_token_ids, so choices[].token_ids came back empty and the client silently re-encoded decoded text. Retokenization drops the trailing end-of-turn/EOS token, making completion_ids one shorter than token_logprobs and misaligning every per-token logprob (inference KLD spiked to ~60 vs ~0.028 with exact ids). - Default return_token_ids=True in the request payload (overridable via request_params). - Remove the tokenizer.encode() re-encode fallback; raise when exact ids are absent instead of silently returning corrupted data. - Assert len(completion_token_ids) == len(completion_logprobs) at the boundary to catch any residual drift. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Background / how the root cause was pinned down (adding here since it wasn't captured in the PR): The two runs that matter — same GLM-5.1 multihop-QA RL config, 256 rows, identical code except the
That A/B is the whole proof: flipping only the flag moved every row from diff=+1 to diff=0 and collapsed the KLD spikes. How we got there (the misleading trail):
Live confirmation of the mechanism (Qwen 3.6 deployment, direct Monorepo counterpart (e2e vendored client + fail-loud guard): fw-ai/fireworks#33733. |
Summary
FireworksV1CompletionsClient.create_completion_from_prompt_idsis the sampling path for RL rollouts against Fireworks/v1/completions. Downstream, the trainer aligns each per-token inference logprob to its completion token id position-by-position, which requirescompletion_ids[i]andtoken_logprobs[i]to be the same length and same tokens. Two combining defects corrupted that alignment on every rollout:logprobs; it never setreturn_token_ids, sochoices[].token_idscame back empty by design.tokenizer.encode(decoded_text, add_special_tokens=False). Retokenization drops the trailing end-of-turn/EOS token (which decodes to empty text), socompletion_ids = Nwhiletoken_logprobs = N+1. The +1 shift misaligns every logprob against the wrong token.Observed impact on a GLM-family LoRA RFT deployment (MTP speculative decoding): without
return_token_ids,completion_idswas one shorter thantoken_logprobson all 120 rows (diff=+1) andinference_kldclimbed to ~60; requesting exact ids made diff=0 andinference_kldflat ~0.028.Changes
return_token_ids=Truein the request payload viasetdefault, so it stays overridable throughrequest_params.tokenizer.encode(...)re-encode fallback. If exact ids are absent after readingchoices[].token_ids/raw_output.completion_token_ids, raiseRuntimeError— a missing/contract failure must not silently reach training.len(completion_token_ids) == len(completion_logprobs)(when logprobs are present) so any residual drift is caught at the boundary rather than as a downstream KLD anomaly.This mirrors the upstream Fireworks monorepo fix (fw-ai/fireworks#33729), bringing the base
eval_protocolclient in line so unmodified callers (e.g.examples/multihop_qa/multihop_qa_rollout.py) are no longer silently corrupted.Test plan
choices[0].token_idspresent →completion_idsequals it (tokenizer not used to re-encode) andlen(completion_ids) == len(completion_logprobs).return_token_ids=Truesent by default; overridable viarequest_params.token_idsand noraw_output.completion_token_ids→ raises (no silent re-encode).pytest tests/test_fireworks_v1_completions_client.pypasses (13 tests).Made with Cursor