Skip to content

feat: add --sft-assistant-turns option for multi-turn SFT - #313

Open
Liu14159 wants to merge 1 commit into
inclusionAI:mainfrom
Liu14159:feat/sft-assistant-turns
Open

feat: add --sft-assistant-turns option for multi-turn SFT#313
Liu14159 wants to merge 1 commit into
inclusionAI:mainfrom
Liu14159:feat/sft-assistant-turns

Conversation

@Liu14159

@Liu14159 Liu14159 commented Jul 28, 2026

Copy link
Copy Markdown

Summary

Add --sft-assistant-turns [all|last] option for multi-turn SFT, allowing users to train on every assistant turn (default all) or only the final assistant turn (last). Earlier assistant turns in last mode are treated as context. User, system, and tool-result tokens are always excluded from the loss.

Usage

# Train on all assistant turns (default, backward compatible)
areno train \
  --algo sft \
  --ckpt Qwen/Qwen3-0.6B \
  --dataset-path /path/to/multiturn.jsonl \
  --dataset-loader-fn examples/sft/multiturn/dataset_loader.py \
  --sft-assistant-turns all \
  --tp-size 1 \
  --world-size 1 \
  --batch-size 2 \
  --mini-bs 1

# Train only on the final assistant turn
areno train \
  --algo sft \
  --ckpt Qwen/Qwen3-0.6B \
  --dataset-path /path/to/multiturn.jsonl \
  --dataset-loader-fn examples/sft/multiturn/dataset_loader.py \
  --sft-assistant-turns last \
  --tp-size 1 \
  --world-size 1 \
  --batch-size 2 \
  --mini-bs 1

Multi-turn data format (JSONL, one conversation per line):

{"messages": [{"role": "user", "content": "What is 2+2?"}, {"role": "assistant", "content": "4"}, {"role": "user", "content": "And 3+3?"}, {"role": "assistant", "content": "6"}]}

Related issue

Fixes #224

Type of change

  • ✨ New feature

What changed

File Change
areno/api/trainer_config.py +3: add sft_assistant_turns field with __post_init__ validation
areno/api/data_utils.py +74: new messages_to_tokens_and_mask() with last_assistant_only support
areno/api/trainers/sft.py +36/-16: integrate messages format + sft_assistant_turns passthrough into _record_to_train_sequence
areno/cli/train.py +10: expose --sft-assistant-turns CLI option, config summary, dashboard settings
tests/test_trainer_dataset_utils_cpu.py +210: 13 new CPU tests (MultiTurnSFTMaskTest)
docs/cli/training.rst +40: document option + runnable example
docs/cli/dataset_loaders.rst +18: multi-turn messages format documentation
examples/sft/multiturn/ New: README.md + dataset_loader.py

Edge cases handled

Scenario Behavior
{"messages": None} Returns None (filtered out)
{"messages": []} Returns None (empty, no trainable tokens)
Messages with no assistant turn Returns None (nothing to train)
Invalid sft_assistant_turns value Raises ValueError with clear message
Single-turn prompt/response rows Unchanged — sft_assistant_turns has no effect
Neither messages nor prompt/response Raises ValueError with schema hint
last mode with single assistant turn Identical to all mode

Design decisions

  • Mask convention: True = do not train (context), False = train (assistant response). Matches existing prompt_response_to_tokens_and_mask contract.
  • Default all: preserves backward compatibility — existing single-turn SFT runs are unaffected.
  • Chat-template path: encodes conversation incrementally (messages[:i+1]) to attribute tokens to individual turns. O(N) calls to apply_chat_template for N messages; acceptable for typical SFT data (2-10 turns).
  • Plain-text fallback: for tokenizers without chat_template, concatenates role: content per turn.
  • EOS handling: appended after last message if not already present, marked as trainable in both modes.
  • No new dependencies: reuses existing data_utils, tokenizer, and TrainerConfig contracts.

How was it tested?

Tested on Kaggle (Python 3.12, T4 GPU, ARENO_BUILD_EXT=0):

pytest tests/test_trainer_dataset_utils_cpu.py -v

25 tests collected, all passed. Config validation and CLI option verified separately:

TrainerConfig default: sft_assistant_turns='all'
TrainerConfig(sft_assistant_turns='last'): accepted
TrainerConfig(sft_assistant_turns='invalid'): raises ValueError
areno train --help | grep sft-assistant-turns: --sft-assistant-turns [all|last]

Checklist

  • The PR title summarizes the contribution.
  • Linked the related issue in the description (if any).
  • Existing tests pass (pytest tests/ -k cpu).
  • New behavior is covered by tests.
  • Described the test commands run and any hardware limitations.
  • Public API / CLI changes are additive and backward-compatible (see CONTRIBUTING.md).

@Liu14159 Liu14159 changed the title Feat/sft assistant turns feat: add --sft-assistant-turns option for multi-turn SFT Jul 29, 2026
@Liu14159
Liu14159 force-pushed the feat/sft-assistant-turns branch 5 times, most recently from 8519faf to c701277 Compare July 30, 2026 08:38
…sionAI#224)

Add the ability to train on every assistant turn (all, default) or only
the final assistant turn (last) in multi-turn chat data.

Changes:
- areno/api/trainer_config.py: add sft_assistant_turns field with validation
- areno/api/data_utils.py: add messages_to_tokens_and_mask with last_assistant_only,
  chat-template encoding with prefix-stability guard and plain-text fallback
- areno/api/trainers/sft.py: integrate multi-turn messages format and turn selection
- areno/cli/train.py: expose --sft-assistant-turns CLI option
- tests/test_trainer_dataset_utils_cpu.py: 13 new CPU tests
- docs/cli/training.rst, docs/cli/dataset_loaders.rst: document the option
- examples/sft/multiturn/: runnable example with dataset loader and README

Edge cases handled: messages=None, empty messages, no assistant turn,
invalid sft_assistant_turns value, single-turn backward compat,
tokenizer chat_template not prefix-stable (warn + fallback).

Default is 'all' to preserve backward compatibility.
@Liu14159
Liu14159 force-pushed the feat/sft-assistant-turns branch from c701277 to 5654897 Compare August 4, 2026 03:21
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.

Select all or only the final assistant turn for multi-turn SFT

1 participant