Skip to content

[DistillationTrainer refactor] Support tool calling - #6723

Merged
qgallouedec merged 25 commits into
mainfrom
distillation-tool-calling
Aug 24, 2026
Merged

qgallouedec merged 25 commits into
mainfrom
distillation-tool-calling

Conversation

@qgallouedec

@qgallouedec qgallouedec commented Aug 12, 2026 •

Copy link
Copy Markdown
Member

What

Important

For reviewers: it's mostly copy-pasted from GRPO, nothing really new here!

Adds tool-calling support to DistillationTrainer, mirroring GRPO. The student generates multi-turn tool-calling trajectories on-policy; tool-result tokens are masked out of the JSD loss.

  • max_tool_calling_iterations config field (caps tool turns; None = unlimited)
  • tools constructor param + _tool_dict, supports_tool_calling / response-schema / training-chat-template setup
  • _tool_call_loop, parse_response, _get_tool_suffix_ids in the generation path
  • tool_mask loss masking: loss_mask = completion_mask * tool_mask
  • test_train_with_tools (mocked generate, asserts tools/call_frequency and tools/failure_frequency)

All mirrored blocks are reproduced verbatim from GRPO.

Deferred (future PR)

Async tools, environments, and per-token logprob capture — none exist on a stable base yet.

Stacking

Stacked on #6650 (VLM). Re-base to main once #6650 lands.

Part of #6449.


Note

Medium Risk
Touches core on-policy generation, loss masking, and VLM forward kwargs in DistillationTrainer; behavior is largely ported from GRPO but multimodal tool loops add integration risk.

Overview
Adds on-policy tool-calling to DistillationTrainer, aligned with GRPO: pass a tools list, run a multi-turn generate→execute→regenerate loop, and distill only on student-generated tokens by masking tool-result tokens out of the JSD loss (loss_mask = completion_mask * tool_mask).

DistillationConfig gains max_tool_calling_iterations to cap tool turns. Init wires response parsing, prefix-preserving chat templates, transformers ≥5.0 checks, and rejects async tools. Logging adds tools/call_frequency / tools/failure_frequency, and token/completion length metrics note they exclude tool tokens when tools are enabled.

VLM paths merge tool-returned images into generation and the forward pass (including mm_token_type_ids / truncation safety). Docs add an Agent Training section for distillation; GRPO docs document the same iteration cap.

Tests cover mocked multi-turn tool calls (success/failure metrics) and VLM tool responses (image and text).

Reviewed by Cursor Bugbot for commit 8ed5196. Bugbot is set up for automated code reviews on this repo. Configure here.

…rompts

Mirror GRPO verbatim (minus the tools/environments/chat_template it lacks): add self._is_vlm (canonical
if/elif/else on the processing_class), import prepare_multimodal_messages + transformers, and rewrite
_tokenize_prompts to normalize multimodal messages, extract images, and return the processor's multimodal fields.
Steps 2-4 (batch threading, loss threading, vLLM+tests+docs) follow.
…batch

_generate now returns multimodal_fields; _generate_and_score_completions stores the processor's multimodal keys
(pixel_values, image_grid_thw, pixel_attention_mask, spatial_shapes, image_sizes, token_type_ids,
mm_token_type_ids, image_position_ids) in the output batch, mirroring GRPO's block verbatim. Text path unaffected
(multimodal_fields is empty).
…repare_inputs

Mirror GRPO: split_pixel_values_by_grid before splitting the generation batch into micro-batches, and
unsplit_pixel_values_by_grid on each buffered micro-batch, so each training step gets the pixel_values for its
own prompts. No-op for the text path. _compute_loss threading (step 3b) still to come.
…essor auto-build

- Pass the multimodal fields to the student/teacher hidden-state forward in _compute_loss.
- Auto-build a processor (AutoProcessor) instead of a tokenizer, and resolve pad_token via
  self._tokenizer, so VLM processors work — mirrors GRPO.
Mirror GRPO's TestGRPOTrainerVLM: parametrize over the tiny VLM checkpoints and train on
zen-image. Uses self-distillation (no tiny+small VLM fixture pair) and asserts the loss stayed
finite rather than params-changed.
Mirror GRPO's skipped vLLM+images placeholder (server mode, pending a vLLM server mock). The
colocate vLLM path already threads images identically to GRPO.
Add a 'Training Vision Language Models' section: pass a VLM as student and teacher, use a
prompt-only dataset with an image/images column, tested architectures + the compatibility tip.
…vice-prep, token_type_ids

Mirror GRPO's multimodal handling in _generate_and_score_completions:
- Read the dataset image/images column and inject it into prompts via prepare_multimodal_messages
  (previously the image column was dropped, so VLM training ran text-only).
- Rebuild forward_kwargs from the images with the processor (batched, device-placed tensors),
  emit num_images / num_tiles so split_pixel_values_by_grid regroups packed pixel_values under
  gradient accumulation, and extend token_type_ids / mm_token_type_ids over the completion.
- Thread token_type_ids / mm_token_type_ids into _get_last_hidden_state (required by Qwen-VL
  M-RoPE); restore GRPO's pre-5.0 VLM backbone fallback.
- Add a gradient-accumulation VLM test exercising the num_images split.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0fae09aca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread trl/trainer/distillation_trainer.py Resolved
Comment thread trl/trainer/distillation_trainer.py Resolved
Comment thread trl/trainer/distillation_trainer.py Resolved
Comment thread trl/trainer/distillation_trainer.py Resolved
@qgallouedec
qgallouedec force-pushed the distillation-tool-calling branch from c0fae09 to d3bb76e Compare August 12, 2026 21:34
@qgallouedec
qgallouedec changed the base branch from 59-vlm to 61-whatsnew-distillation August 12, 2026 21:34
@bot-ci-comment

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Comment thread trl/trainer/distillation_trainer.py Resolved
Replace the What's New section (GRPO multi-environment RL + KTO stable) with the DistillationTrainer
graduation to the stable API, in README.md and docs/source/index.md.
Add the tools ctor param + guards (transformers>=5.0, jmespath/response_template, supports_tool_calling),
build the tool dict, wire the response schema + training-safe chat template, and set
max_tool_calling_iterations. Async tools rejected (deferred to a future PR); environments dropped
(no reward path). Mirrors GRPO.
… masking

- _tokenize_prompts: pass tools + chat_template.
- _generate: parse_response decode + _tool_call_loop; return tool_mask/images + tool metrics.
- _get_tool_suffix_ids + _tool_call_loop ported from GRPO, dropping async + logprobs (justified;
  every other line diff-verified against GRPO).
- _generate_and_score_completions: pad tool_mask, loss_mask = completion_mask * tool_mask, store
  tool_mask in the batch. _compute_loss masks the JSD by loss_mask (tool-result tokens stay
  attended but are not distilled).
Mirror GRPO's tool test — mock generation to emit a valid call, an invalid call, and a non-call,
and assert tools/call_frequency==2/3 and tools/failure_frequency==1/2 (async variant dropped).
@qgallouedec
qgallouedec force-pushed the 61-whatsnew-distillation branch from d9a1e9e to a2ec0f8 Compare August 13, 2026 00:42
@qgallouedec
qgallouedec force-pushed the distillation-tool-calling branch from d3bb76e to 083be80 Compare August 13, 2026 00:42
Base automatically changed from 61-whatsnew-distillation to main August 13, 2026 01:23
qgallouedec and others added 4 commits August 18, 2026 14:54
…ling

# Conflicts:
#	tests/test_distillation_trainer.py
#	trl/trainer/distillation_trainer.py
…h GRPO

- Document tool calling: add an Agent Training section (Tools + Multimodal
  Tool Responses) mirroring grpo_trainer.md, and list tools/call_frequency
  and tools/failure_frequency in Logged metrics (plus the non-tool token
  qualifier GRPO carries on num_tokens and completions/*_length).
- Move max_tool_calling_iterations to the training section of
  DistillationConfig, matching GRPOConfig.
- Port test_train_with_tools_multimodal_response and
  test_train_with_tools_text_response_multimodal_prompt from
  test_grpo_trainer.py; both fail without the VLM x tools fixes in 8f11ec3.
- Restore the is-not-None tool metric assertions in test_train_with_tools.
break
tid = self._tokenizer.convert_tokens_to_ids("<|video_pad|>")
if tid != self._tokenizer.unk_token_id:
self._video_pad_token_id = tid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tool-image token IDs miss models

Medium Severity

Vision type IDs for tool-returned images are rebuilt by matching only <|image_pad|> and <|image|>. Advertised VLMs such as Gemma 3 (<start_of_image>) and LLaVA never match, so mm_ids stay zeros and image placeholders in the completion are not marked for generate or the JSD forward.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 49747ae. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 740ef37. Configure here.

Comment thread trl/trainer/distillation_trainer.py Resolved
@qgallouedec

Copy link
Copy Markdown
Member Author

Deep review done before merging: cursor's vllm_mode catch was real (AttributeError with use_vllm + tools), fixed in 8ed5196 along with gating the decode on tools and doc alignment; the other bot findings were fixed earlier or exist verbatim in GRPO.
Remaining findings are GRPO-shared (multi-GPU tool-loop collectives, VLM chat_template re-render, tools or None guard), filing as separate issues. Review request open 12 days, self-merging.

@qgallouedec
qgallouedec merged commit 964868b into main Aug 24, 2026
9 checks passed
@qgallouedec
qgallouedec deleted the distillation-tool-calling branch August 24, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant