Reviewed against the official text-tuning data rules; fixes two confirmed
mismatches and fills enforcement gaps:
- thinking: exempt assistant messages carrying tool_calls from the
THINK_TAG_NOT_LAST check — the spec's tool+thinking combo example puts
<think> on a non-last assistant and was previously false-flagged
- DPO support matrix: reject image/video content items, tools, tool_calls
and role:tool (DPO_UNSUPPORTED_ELEMENT); also scan chosen/rejected
- DPO: messages not ending with user upgraded warning -> error
- OpenAI migration: name/weight upgraded warning -> error (spec: must not
carry); drop dead record-level name branch
- tool_call_id: unmatched tool response upgraded warning -> error
(one-to-one per spec); new TOOL_CALL_NO_RESPONSE warning for orphan calls
- loss_weight: validate range at message level too; warn when placed on
anything but the last assistant message (LOSS_WEIGHT_PLACEMENT)
- video params: fps/sample_fps must be within [0.1, 10]
(INVALID_VIDEO_FPS); mode-mismatched params warned
(VIDEO_PARAM_MODE_MISMATCH); video_start/video_end type-checked
- zip: skip macOS packaging metadata (__MACOSX/, .DS_Store, ._*) in
filename constraints and image counting to stop false failures on
Finder-created archives
Tests 45 -> 59 covering every new/changed rule, including a replica of the
spec's official tool+thinking example.
- Support content array format [{text/image/video}] alongside legacy string
- Add tool role support with tool_calls structure and tool_call_id validation
- Add thinking tag placement check (only in last assistant message)
- Add OpenAI migration guards: warn on unsupported name/weight fields
- Add loss_weight range validation (0.0–1.0)
- Fix size limits: SFT/DPO 200MB, CPT 300MB, media ZIP 2GB
- Enforce data.jsonl at ZIP root (reject nested wrapping folders)
- Add ZIP filename constraints: charset [a-zA-Z0-9_-], length ≤120, uniqueness
- Add .tif to accepted image extensions
- Add DPO_LAST_MSG_NOT_USER warning when messages don't end with user role
- CPT profile now uses dedicated 300MB cap instead of shared default
- Expand unit tests from 19 to 45 covering all new validation paths