unsloth/tests
Ricardo-M-L af6504f900
fix(chat_templates): check find() return value before slicing on placeholders (#5763)
* fix(chat_templates): check find() return value before slicing on placeholders

Two places in `construct_chat_template()` use `str.find()` for sentinel
placeholders (`{INPUT}` / `{OUTPUT}`) without checking the -1 return:

1. The `except:` fallback (around line 2464) computes
   `chat_template[chat_template.find("{OUTPUT}") + len("{OUTPUT}"):]`.
   If the template has no `{OUTPUT}` marker, `find()` returns -1 and the
   slice starts at offset 7 (`-1 + len("{OUTPUT}")`), producing garbage
   that's then `re.escape`-d and fed back into the template-recovery
   regex. The user sees a confusing `IndexError` on
   `response_part = response_part[0]` instead of the real problem.

2. The final trim before returning (`input_part[:input_part.find("{INPUT}")]`
   and the matching `{OUTPUT}` line) silently drops the last character
   when the placeholder is missing — `find()` returns -1, and `[:-1]`
   slices everything except the last character, returning a corrupted
   template prefix to the caller.

Replace both with an explicit `-1` check that raises a clear
`RuntimeError` naming the missing placeholder, matching the existing
guard pattern from #4923 (`try_fix_tokenizer`).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(chat_templates): also guard {INPUT} and fallback regex/separator paths

Builds on the {OUTPUT} / final-trim guards in this branch by closing
the three remaining ways the except-block fallback in
construct_chat_template() can still raise a confusing IndexError or
AttributeError on malformed templates:

1. Validate both {INPUT} and {OUTPUT} before deriving `ending`. The
   regex two lines later (`{INPUT} + ending + ...`) still produced an
   empty list and crashed on `response_part[0]` if {INPUT} was missing.
2. Guard the regex no-match case. Some templates contain both
   placeholders but not in a recoverable two-example shape, in which
   case `re.findall` returns an empty list and `[0]` raises.
3. Initialize `found = None` before the separator-search loop and
   raise if the loop never sets it. Previously, if the first
   iteration's `re.finditer` was empty the loop broke without binding
   `found`, and `found.group(1)` raised AttributeError on the stale
   int left over from the outer rfind loop.

Rephrase the final-trim error messages from internal variable names
("input_part") to user-facing wording ("instruction section") and
include a bounded (200-char) excerpt of the offending content so the
error is debuggable without being unbounded.

Add tests/python/test_construct_chat_template_validation.py covering
each failure mode with a fake tokenizer (no HF_TOKEN, no model
download, CPU-only).

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-05-25 06:19:01 -07:00
..
notebooks CI: scope GITHUB_TOKEN permissions, add MLX CI, unblock ~60 skipped tests (#5312) 2026-05-11 03:19:13 -07:00
python fix(chat_templates): check find() return value before slicing on placeholders (#5763) 2026-05-25 06:19:01 -07:00
qlora Revert "[pre-commit.ci] auto fixes from pre-commit.com hooks" 2025-12-01 07:24:58 -08:00
saving fix: preserve tokenizer eos token on merged saves (#5451) 2026-05-17 06:44:23 -07:00
security ci: advisory lockfile supply-chain audit (no install-script changes) (#5604) 2026-05-19 05:56:56 -07:00
sh fix(tests/sh): accept pinned tokenizers line after #5359 (#5361) 2026-05-11 02:58:20 -07:00
studio ci: broaden Linux + narrow Windows llama.cpp runtime patterns + trim #5741 comments (#5746) 2026-05-23 21:48:12 -07:00
utils feat: Add cactus QAT scheme support (#4679) 2026-04-15 07:40:03 -07:00
version_compat tests: pinned-symbol canary for unsloth-zoo save_pretrained_merged guards (#5410) (#5433) 2026-05-17 01:35:28 -07:00
vllm_compat CI: scope GITHUB_TOKEN permissions, add MLX CI, unblock ~60 skipped tests (#5312) 2026-05-11 03:19:13 -07:00
__init__.py Qwen 3, Bug Fixes (#2445) 2025-04-30 22:38:39 -07:00
_zoo_aggressive_cuda_spoof.py CI: scope GITHUB_TOKEN permissions, add MLX CI, unblock ~60 skipped tests (#5312) 2026-05-11 03:19:13 -07:00
conftest.py tests: drift detector parity with unsloth-zoo (#5421) 2026-05-14 04:50:30 -07:00
run_all.sh fix: add tokenizers to no-torch deps and TORCH_CONSTRAINT for arm64 macOS py313+ (#4748) 2026-04-01 06:12:17 -07:00
test_attention_implementation.py fix(gpt-oss): prefer flex attention over sdpa (#5701) 2026-05-22 08:38:38 -07:00
test_callback_signature_drift.py tests + CI: callback signature drift detector (#5498) 2026-05-18 04:42:37 -07:00
test_cli_export_unpacking.py studio: stream export worker output into the export dialog (#4897) 2026-04-14 08:55:43 -07:00
test_finetune_last_n_layers.py fix(peft): expose finetune_last_n_layers for parity with mlx-lm CLI (#5564) 2026-05-19 05:58:44 -07:00
test_gemma4_chat_template.py update gema4 chat templates (#5116) 2026-04-22 09:04:08 -07:00
test_get_model_name.py feat: Add support for OLMo-3 model (#4678) 2026-04-15 07:39:11 -07:00
test_import_fixes_drift.py Fix loss function not patched for Qwen3.5 models (#5442) 2026-05-19 03:57:50 -07:00
test_loader_glob_skip.py Add unit tests for HfFileSystem glob skip guard (#4854) 2026-04-06 08:54:36 -07:00
test_model_registry.py Revert "[FIX] Vllm guided decoding params (#3662)" 2025-12-01 05:43:45 -08:00
test_multi_image_grpo_chunking.py Multi Image GRPO (#5197) 2026-05-13 04:27:49 -07:00
test_peft_weight_converter_compat.py Patch checkpoint reload init functions to strip unsupported args (#5167) 2026-04-29 02:50:49 -07:00
test_public_api_surface.py tests: public-api surface drift detector (companion to test_import_fixes_drift.py) (#5428) 2026-05-14 19:56:21 -07:00
test_raw_text.py Add Studio PR-time CI: pin enforcement, frontend, backend, wheel smoke (#5298) 2026-05-06 04:41:57 -07:00
test_resolve_model_class.py fix: guard resolve_model_class fallback against unresolvable transformers AutoModel entries (#5155) 2026-04-24 05:59:17 -07:00
test_studio_install_workspace_guard.py studio: security and hardening pass (auth rate-limit, sandbox, path containment, schema validation, headers) (#5375) 2026-05-13 06:12:18 -07:00
test_studio_root_resilience.py install: support STUDIO_HOME / UNSLOTH_STUDIO_HOME for custom install paths (#5190) 2026-05-05 23:17:40 -07:00