agent: optional exact token-ID capture for token-level training - #77
Merged
Conversation
Adds `capture_token_ids` to LitellmModelConfig (default False). When set, requests
carry `extra_body={"return_token_ids": true}` and the ids the server actually used
are stored on each assistant message as `extra["token_capture"]`, so they persist
into the saved trajectory.
Motivation: token-level training (on-policy distillation, TITO-style SFT) needs the
tokens the model really saw. Re-tokenizing a finished trajectory is not equivalent —
BPE is non-injective, tool-call serialization can differ between inference and
training, and under context compaction the prompt a turn saw no longer exists in the
final message list. Capturing at request time makes the training input identical to
the inference input by construction.
- `_with_return_token_ids` merges into any existing `extra_body` rather than
replacing it.
- `_extract_token_capture` handles both placements seen in practice (vLLM puts
`prompt_token_ids` on the response and `token_ids` on the choice; some versions
nest both under the choice) and returns None rather than partial data.
- A server that ignores the flag logs a warning instead of silently yielding
trajectories with no ids — that failure otherwise only surfaces much later as a
training set with zero usable rows.
- The summarizer path is deliberately not captured; those turns are excluded from
training anyway.
Off by default, so existing runs are byte-identical. Bumps version to 0.0.20.
README.md formatting has been failing on main since 8bb229c; fixed here so the lint job is green again rather than carrying a known-red baseline.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an opt-in
capture_token_idsflag toLitellmModelConfig. Off by default — existing runs are byte-identical.Why
Token-level training (on-policy distillation, TITO-style SFT) needs the tokens the model actually saw. Reconstructing them afterwards by re-tokenizing a finished trajectory is not equivalent:
HAVINGcan tokenize asH+AVINGorHAV+ING)Capturing at request time makes the training input identical to the inference input by construction.
Concretely: a scan of a 10-pair
flash_10run found 1942 assistant turns and zero usable states, because there was no way to record ids.What it does
With
capture_token_ids: true, requests carryextra_body={"return_token_ids": true}and the returned ids land on each assistant message as:which persists into the saved trajectory.
Details worth noting:
_with_return_token_idsmerges into any existingextra_bodyinstead of replacing it._extract_token_capturehandles both placements seen in practice — vLLM putsprompt_token_idson the response andtoken_idson the choice; some versions nest both under the choice — and returnsNonerather than partial data.Testing
tests/agents: 121 passed. The 2 failures intest_team_wiring.pyare a missing optionalopenhands.tools.task_tracker.coop_definitionmodule and reproduce on cleanmain.Verified directly:
plus both response placements, missing ids, and empty-list ids (all correctly rejected).
Requires vLLM >= 0.10.2 or SGLang with
return_token_idssupport. Bumps version to 0.0.20.