Skip to content

Commit 1f29d58

Browse files
fix: require sampling_logprob per content[] entry, no fallback
Always read the per-token logprob from content[].sampling_logprob (the exact value the sampler drew with). Fail loud if any content[] entry lacks sampling_logprob instead of substituting the rounded content[].logprob or 0.0, so silently degraded logprobs never reach RL training. Removes the now-unused _extract_entry_logprob helper. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e583f85 commit 1f29d58

2 files changed

Lines changed: 37 additions & 15 deletions

File tree

eval_protocol/integrations/fireworks_v1_completions_client.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,6 @@ def _normalize_token_id_sequence(values: Any) -> List[int]:
8383
return [int(x) for x in list(values)]
8484

8585

86-
def _extract_entry_logprob(entry: Dict[str, Any]) -> float:
87-
"""Return the per-token logprob from a ``content[]`` logprobs entry.
88-
89-
Prefer ``sampling_logprob`` (the exact, full-precision value the sampler
90-
actually drew with) over ``logprob`` (a rounded/verification value). The
91-
sampler value is what RL training needs for accurate inference KLD.
92-
"""
93-
value = entry.get("sampling_logprob")
94-
if value is None:
95-
value = entry.get("logprob", 0.0)
96-
return float(value) if value is not None else 0.0
97-
98-
9986
def _coerce_message_content_to_text(content: Any) -> str:
10087
if content is None:
10188
return ""
@@ -437,8 +424,15 @@ async def create_completion_from_prompt_ids(
437424
f"at index {index}; cannot align per-token logprobs to token ids "
438425
"without re-encoding. Refusing to return corrupted data."
439426
)
427+
if entry.get("sampling_logprob") is None:
428+
raise RuntimeError(
429+
"Fireworks /v1/completions content[] entry is missing "
430+
f"sampling_logprob at index {index}. The sampling logprob is the "
431+
"exact value the sampler drew with and is required for correct "
432+
"inference KLD; refusing to substitute the rounded logprob or 0.0."
433+
)
440434
completion_token_ids.append(int(entry["token_id"]))
441-
completion_logprobs.append(_extract_entry_logprob(entry))
435+
completion_logprobs.append(float(entry["sampling_logprob"]))
442436

443437
completion_text = self.decode_token_ids(token_ids=completion_token_ids)
444438
if not completion_text:

tests/test_fireworks_v1_completions_client.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def _fail_tokenizer():
174174
"content": [
175175
{"token_id": 271, "sampling_logprob": -0.05483185, "logprob": -0.0548313},
176176
{"token_id": 248068, "sampling_logprob": -0.0014, "logprob": -0.0014},
177-
{"token_id": 26108, "logprob": -1.0},
177+
{"token_id": 26108, "sampling_logprob": -1.0, "logprob": -0.99},
178178
]
179179
},
180180
}
@@ -189,6 +189,34 @@ def _fail_tokenizer():
189189
asyncio.run(client.close())
190190

191191

192+
def test_raises_when_content_entry_missing_sampling_logprob(monkeypatch):
193+
client = FireworksV1CompletionsClient(
194+
model_id="test-model",
195+
tokenizer_name_or_path="Qwen/Qwen3-0.6B",
196+
)
197+
monkeypatch.setattr(client, "decode_token_ids", lambda token_ids: "text")
198+
_install_fake_completion(
199+
client,
200+
monkeypatch,
201+
{
202+
"choices": [
203+
{
204+
"finish_reason": "stop",
205+
"logprobs": {
206+
"content": [
207+
{"token_id": 1, "sampling_logprob": -0.1},
208+
{"token_id": 2, "logprob": -0.2},
209+
]
210+
},
211+
}
212+
],
213+
},
214+
)
215+
with pytest.raises(RuntimeError, match="missing .*sampling_logprob"):
216+
asyncio.run(client.create_completion_from_prompt_ids(prompt_token_ids=[1]))
217+
asyncio.run(client.close())
218+
219+
192220
def test_raises_when_no_content_logprobs(monkeypatch):
193221
client = FireworksV1CompletionsClient(
194222
model_id="test-model",

0 commit comments

Comments
 (0)