Hebrew LoRA (step 2200) + atomic IPA tokens for S2-Pro
Browse files- .gitattributes +6 -0
- README.md +142 -1
- config.json +72 -0
- hebrew_lora_step2200.ckpt +3 -0
- hebrew_lora_step2200.safetensors +3 -0
- ipa_embeddings.pt +3 -0
- ipa_token_map.json +28 -0
- samples/01_podcast_2hosts_63s.wav +3 -0
- samples/03_longform_15s.wav +3 -0
- samples/04_yod_BASE.wav +3 -0
- samples/05_yod_LORA.wav +3 -0
- samples/07_clone_LORA_ranlevi.wav +3 -0
- tokenizer/chat_template.jinja +85 -0
- tokenizer/special_tokens_map.json +0 -0
- tokenizer/tokenizer.json +3 -0
- tokenizer/tokenizer_config.json +0 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
samples/01_podcast_2hosts_63s.wav filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
samples/03_longform_15s.wav filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
samples/04_yod_BASE.wav filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
samples/05_yod_LORA.wav filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
samples/07_clone_LORA_ranlevi.wav filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
tokenizer/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,5 +1,146 @@
|
|
| 1 |
---
|
| 2 |
-
license:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
| 4 |
|
| 5 |

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
license: cc-by-nc-sa-4.0
|
| 3 |
+
language:
|
| 4 |
+
- he
|
| 5 |
+
- en
|
| 6 |
+
tags:
|
| 7 |
+
- text-to-speech
|
| 8 |
+
- tts
|
| 9 |
+
- hebrew
|
| 10 |
+
- fish-speech
|
| 11 |
+
- s2-pro
|
| 12 |
+
- lora
|
| 13 |
+
- voice-cloning
|
| 14 |
+
base_model: fishaudio/s2-pro
|
| 15 |
+
library_name: fish-speech
|
| 16 |
+
pipeline_tag: text-to-speech
|
| 17 |
---
|
| 18 |
|
| 19 |

|
| 20 |
+
|
| 21 |
+
# Fish Audio S2-Pro — Hebrew (LoRA + atomic IPA tokens)
|
| 22 |
+
|
| 23 |
+
A Hebrew adapter for [`fishaudio/s2-pro`](https://huggingface.co/fishaudio/s2-pro).
|
| 24 |
+
It keeps the base model's multilingual ability and voice cloning intact, and adds
|
| 25 |
+
native Hebrew synthesis driven by **IPA** rather than nikud.
|
| 26 |
+
|
| 27 |
+
This repo contains **only the adapter** (~67M parameters) plus the extended
|
| 28 |
+
tokenizer. You still need the S2-Pro base weights and codec.
|
| 29 |
+
|
| 30 |
+
## What's here
|
| 31 |
+
|
| 32 |
+
| File | What it is |
|
| 33 |
+
|---|---|
|
| 34 |
+
| `hebrew_lora_step2200.safetensors` | LoRA deltas + the trained `ipa_embeddings` table (67M params, bf16) |
|
| 35 |
+
| `hebrew_lora_step2200.ckpt` | Same weights as a Lightning checkpoint, with optimizer state — use this to resume training |
|
| 36 |
+
| `config.json` | S2-Pro config extended with `num_ipa_tokens: 26`, `ipa_token_start: 155774` |
|
| 37 |
+
| `ipa_token_map.json` | IPA symbol → atomic token (e.g. `ʃ` → `<ipa_u0283>`) |
|
| 38 |
+
| `ipa_embeddings.pt` | Initial IPA embedding table (mean of the symbol's BPE pieces); the trained one lives in the adapter |
|
| 39 |
+
| `tokenizer/` | S2-Pro tokenizer extended 155,774 → 155,800 tokens |
|
| 40 |
+
| `samples/` | Generated audio (see below) |
|
| 41 |
+
|
| 42 |
+
## Quick start
|
| 43 |
+
|
| 44 |
+
Code lives in the fork the adapter was trained with:
|
| 45 |
+
|
| 46 |
+
```bash
|
| 47 |
+
git clone https://github.com/maxmelichov/fish-speech
|
| 48 |
+
cd fish-speech && uv sync --python 3.12 --extra cu129
|
| 49 |
+
pip install renikud-plus # Hebrew grapheme-to-phoneme
|
| 50 |
+
|
| 51 |
+
hf download fishaudio/s2-pro --local-dir checkpoints/s2-pro
|
| 52 |
+
hf download notmax123/Fish-Audio-S2-Pro-He --local-dir checkpoints/he
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
Build the IPA-extended base checkpoint (symlinks the S2-Pro weights, drops the
|
| 56 |
+
extended tokenizer and config on top), then synthesize:
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
python tools/hebrew/build_ipa_checkpoint.py \
|
| 60 |
+
--base checkpoints/s2-pro --output checkpoints/s2-pro-he-ipa
|
| 61 |
+
|
| 62 |
+
python tools/hebrew/infer_hebrew.py \
|
| 63 |
+
--text "שלום, מה שלומך היום?" \
|
| 64 |
+
--base-checkpoint checkpoints/s2-pro-he-ipa \
|
| 65 |
+
--lora-checkpoint checkpoints/he/hebrew_lora_step2200.ckpt \
|
| 66 |
+
--lora-config r_32_alpha_16_core \
|
| 67 |
+
--ref-audio my_voice.wav --ref-text "..." \
|
| 68 |
+
--output out.wav
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
`infer_hebrew.py` runs plain unvocalized Hebrew through RenikudPlus G2P, maps the
|
| 72 |
+
IPA to the atomic tokens, and chunks long inputs on sentence boundaries.
|
| 73 |
+
`--lora-scale` scales the delta (0.0 = pure base model) if you want to dial the
|
| 74 |
+
adaptation down.
|
| 75 |
+
|
| 76 |
+
## How it works
|
| 77 |
+
|
| 78 |
+
**Atomic IPA tokens.** S2-Pro's BPE splits IPA into pieces that collide with
|
| 79 |
+
English orthography — Hebrew `י` phonemized as `j` was read as the English letter
|
| 80 |
+
*jay*. So each of the 26 Hebrew IPA symbols gets a dedicated input-only token
|
| 81 |
+
(`<ipa_j>`, `<ipa_u0283>`, …) in a separate trainable `nn.Embedding`, initialized
|
| 82 |
+
to the mean of the symbol's original BPE pieces. The output vocabulary is
|
| 83 |
+
untouched — these tokens are never predicted, only read.
|
| 84 |
+
|
| 85 |
+
**What trains.** LoRA r=32, α=16 on `attention` + `mlp` of the slow transformer,
|
| 86 |
+
plus the IPA embedding table. The acoustic/fast stack is frozen, so voice cloning
|
| 87 |
+
and the codec side are exactly the base model's. The residual-codebook loss is
|
| 88 |
+
down-weighted to 0.3 (Qwen3-TTS's sub-talker coefficient) so the gradient stays on
|
| 89 |
+
the text→semantic mapping.
|
| 90 |
+
|
| 91 |
+
**Training.** 279,476 Hebrew utterances (~10 speakers, WER ≤ 0.1), reference-
|
| 92 |
+
conditioned on a same-speaker utterance 80% of the time so training prompts match
|
| 93 |
+
the exact `generate_long()` inference format. bf16, lr 5e-5 constant with 100-step
|
| 94 |
+
warmup, effective batch 12, 2200 optimizer steps.
|
| 95 |
+
|
| 96 |
+
## Upstream bug fixed along the way
|
| 97 |
+
|
| 98 |
+
S2-Pro sets `scale_codebook_embeddings=True`. At inference, `forward_generate()`
|
| 99 |
+
divides semantic-position embeddings by `sqrt(num_codebooks + 1)` = 3.317; the
|
| 100 |
+
training path in `embed()` did **not**. Every fine-tune therefore learned against
|
| 101 |
+
embeddings 3.3× larger than the ones it would see at generation time. Teacher-
|
| 102 |
+
forced CE looked fine while free-running generation collapsed after the first
|
| 103 |
+
word — the classic symptom in fishaudio/fish-speech issues
|
| 104 |
+
[#1136](https://github.com/fishaudio/fish-speech/issues/1136) (Japanese gibberish),
|
| 105 |
+
[#682](https://github.com/fishaudio/fish-speech/issues/682) (Hindi noise) and
|
| 106 |
+
[#814](https://github.com/fishaudio/fish-speech/issues/814).
|
| 107 |
+
|
| 108 |
+
Five Hebrew runs collapsed the same way before this was found. After the fix
|
| 109 |
+
(train and inference embeddings verified bit-identical):
|
| 110 |
+
|
| 111 |
+
| | sample RMS | energy decay over the utterance |
|
| 112 |
+
|---|---|---|
|
| 113 |
+
| before | 0.008 – 0.022 | 0.07× |
|
| 114 |
+
| after | 0.171 – 0.205 | 1.02× |
|
| 115 |
+
| base model reference | 0.181 | — |
|
| 116 |
+
|
| 117 |
+
The fix is in `fish_speech/models/text2semantic/llama.py` in the fork above and
|
| 118 |
+
applies to any S2-Pro fine-tune, Hebrew or not.
|
| 119 |
+
|
| 120 |
+
## Samples
|
| 121 |
+
|
| 122 |
+
`samples/` contains, all generated with this adapter:
|
| 123 |
+
|
| 124 |
+
- `01_podcast_2hosts_63s.wav` — 63s two-host Hebrew conversation, cloned voices
|
| 125 |
+
- `03_longform_15s.wav` — multi-sentence long-form
|
| 126 |
+
- `04_yod_BASE.wav` / `05_yod_LORA.wav` — the `י` → English *jay* failure, before and after atomic IPA tokens
|
| 127 |
+
- `07_clone_LORA_ranlevi.wav` — voice clone from a real Hebrew speaker reference
|
| 128 |
+
|
| 129 |
+
## Known limitations
|
| 130 |
+
|
| 131 |
+
- **Emotion tags (`[whisper]`, `[excited]`, …) do not work** — and this is not a
|
| 132 |
+
regression from the LoRA. Measured on the *base* model in *English*: plain /
|
| 133 |
+
whisper / shouting produced RMS 0.0655 / 0.0652 / 0.0689, i.e. no response at
|
| 134 |
+
all. The released S2-Pro weights simply lack the tag alignment.
|
| 135 |
+
- **Pitch is not cloned.** Timbre transfers well (4/4 by ear), but neither base nor
|
| 136 |
+
LoRA reproduces the reference's F0 (base mean |err| 23 Hz, LoRA 20 Hz). The LoRA
|
| 137 |
+
homogenizes pitch somewhat: spread across speakers drops from 66 Hz to 26 Hz.
|
| 138 |
+
- Trained on read/broadcast-style Hebrew; conversational and heavily accented
|
| 139 |
+
speech are out of distribution.
|
| 140 |
+
- Hebrew input must go through G2P. Feeding nikud or bare Hebrew script directly
|
| 141 |
+
to the model is out of distribution — use `infer_hebrew.py`, which handles it.
|
| 142 |
+
|
| 143 |
+
## License
|
| 144 |
+
|
| 145 |
+
Inherits the base model's license (CC BY-NC-SA 4.0). Non-commercial.
|
| 146 |
+
Do not use it to clone a voice you do not have permission to clone.
|
config.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"audio_decoder_config": {
|
| 3 |
+
"attention_o_bias": false,
|
| 4 |
+
"attention_qk_norm": false,
|
| 5 |
+
"attention_qkv_bias": false,
|
| 6 |
+
"audio_hidden_dim": 5120,
|
| 7 |
+
"dim": 2560,
|
| 8 |
+
"dropout": 0.0,
|
| 9 |
+
"head_dim": 128,
|
| 10 |
+
"initializer_range": 0.01976423537605237,
|
| 11 |
+
"intermediate_size": 9728,
|
| 12 |
+
"max_seq_len": 11,
|
| 13 |
+
"model_type": "fish_qwen3_audio_decoder",
|
| 14 |
+
"moe_intermediate_size": 768,
|
| 15 |
+
"n_head": 32,
|
| 16 |
+
"n_layer": 4,
|
| 17 |
+
"n_local_heads": 8,
|
| 18 |
+
"norm_eps": 1e-06,
|
| 19 |
+
"norm_topk_prob": true,
|
| 20 |
+
"num_codebooks": 10,
|
| 21 |
+
"num_experts": 1,
|
| 22 |
+
"num_experts_per_tok": 1,
|
| 23 |
+
"rope_base": 1000000,
|
| 24 |
+
"router_gamma": 0.001,
|
| 25 |
+
"text_dim": 2560,
|
| 26 |
+
"tie_word_embeddings": false,
|
| 27 |
+
"use_aux_loss_free": false,
|
| 28 |
+
"use_bfloat16": false,
|
| 29 |
+
"use_gradient_checkpointing": true,
|
| 30 |
+
"use_moe": false,
|
| 31 |
+
"vocab_size": 4096
|
| 32 |
+
},
|
| 33 |
+
"audio_pad_token_id": 151677,
|
| 34 |
+
"dtype": "bfloat16",
|
| 35 |
+
"eos_token_id": 151645,
|
| 36 |
+
"model_type": "fish_qwen3_omni",
|
| 37 |
+
"pad_token_id": 151669,
|
| 38 |
+
"semantic_end_token_id": 155773,
|
| 39 |
+
"semantic_start_token_id": 151678,
|
| 40 |
+
"text_config": {
|
| 41 |
+
"attention_o_bias": false,
|
| 42 |
+
"attention_qk_norm": true,
|
| 43 |
+
"attention_qkv_bias": false,
|
| 44 |
+
"audio_hidden_dim": 5120,
|
| 45 |
+
"dim": 2560,
|
| 46 |
+
"dropout": 0.0,
|
| 47 |
+
"head_dim": 128,
|
| 48 |
+
"initializer_range": 0.01976423537605237,
|
| 49 |
+
"intermediate_size": 9728,
|
| 50 |
+
"max_seq_len": 32768,
|
| 51 |
+
"model_type": "fish_qwen3",
|
| 52 |
+
"moe_intermediate_size": 768,
|
| 53 |
+
"n_head": 32,
|
| 54 |
+
"n_layer": 36,
|
| 55 |
+
"n_local_heads": 8,
|
| 56 |
+
"norm_eps": 1e-06,
|
| 57 |
+
"norm_topk_prob": true,
|
| 58 |
+
"num_experts": 1,
|
| 59 |
+
"num_experts_per_tok": 1,
|
| 60 |
+
"rope_base": 1000000,
|
| 61 |
+
"router_gamma": 0.001,
|
| 62 |
+
"tie_word_embeddings": true,
|
| 63 |
+
"use_aux_loss_free": false,
|
| 64 |
+
"use_bfloat16": false,
|
| 65 |
+
"use_gradient_checkpointing": true,
|
| 66 |
+
"use_moe": false,
|
| 67 |
+
"vocab_size": 155776
|
| 68 |
+
},
|
| 69 |
+
"transformers_version": "4.57.1",
|
| 70 |
+
"num_ipa_tokens": 26,
|
| 71 |
+
"ipa_token_start": 155774
|
| 72 |
+
}
|
hebrew_lora_step2200.ckpt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6b2f69aa1b79c1a04613ead65c77995045efdef2ce417ce55c10df7b834777d9
|
| 3 |
+
size 402000398
|
hebrew_lora_step2200.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:dce55d0b3d998f0cf407ce7b2833a34738b7527664c10a2f3ba044e65ae95015
|
| 3 |
+
size 133871024
|
ipa_embeddings.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8af4a5ba9359e1ea6ff5d750e7274795c8d9974fe9bce9061ffc229ff7ab9534
|
| 3 |
+
size 267866
|
ipa_token_map.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"a": "<ipa_a>",
|
| 3 |
+
"b": "<ipa_b>",
|
| 4 |
+
"d": "<ipa_d>",
|
| 5 |
+
"e": "<ipa_e>",
|
| 6 |
+
"f": "<ipa_f>",
|
| 7 |
+
"h": "<ipa_h>",
|
| 8 |
+
"i": "<ipa_i>",
|
| 9 |
+
"j": "<ipa_j>",
|
| 10 |
+
"k": "<ipa_k>",
|
| 11 |
+
"l": "<ipa_l>",
|
| 12 |
+
"m": "<ipa_m>",
|
| 13 |
+
"n": "<ipa_n>",
|
| 14 |
+
"o": "<ipa_o>",
|
| 15 |
+
"p": "<ipa_p>",
|
| 16 |
+
"s": "<ipa_s>",
|
| 17 |
+
"t": "<ipa_t>",
|
| 18 |
+
"u": "<ipa_u>",
|
| 19 |
+
"v": "<ipa_v>",
|
| 20 |
+
"w": "<ipa_w>",
|
| 21 |
+
"z": "<ipa_z>",
|
| 22 |
+
"ɡ": "<ipa_u0261>",
|
| 23 |
+
"ʁ": "<ipa_u0281>",
|
| 24 |
+
"ʃ": "<ipa_u0283>",
|
| 25 |
+
"ʔ": "<ipa_u0294>",
|
| 26 |
+
"ˈ": "<ipa_u02C8>",
|
| 27 |
+
"χ": "<ipa_u03C7>"
|
| 28 |
+
}
|
samples/01_podcast_2hosts_63s.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4f27a0f3a57965085bc831289f6933081e9eebf351480e42ca6f4679588de704
|
| 3 |
+
size 5570220
|
samples/03_longform_15s.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c58aabf367ae1e6e0076d04b96d0ac94e7937faf615c061ef412c80a20f26d65
|
| 3 |
+
size 1335340
|
samples/04_yod_BASE.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9266d78bcb116f14cb97da0f6fb36b413bd12bcdcb088f317b5827c14579c415
|
| 3 |
+
size 426028
|
samples/05_yod_LORA.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f2046bca2cc8206bbc262a1ba5b43da75fdc846518f4a7a7cc06c5fdbb0eb547
|
| 3 |
+
size 430124
|
samples/07_clone_LORA_ranlevi.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:92da7b28077461ef5e630de658d4c7dd8be624cadaca685e6a746ae9a3c878a2
|
| 3 |
+
size 421932
|
tokenizer/chat_template.jinja
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- if tools %}
|
| 2 |
+
{{- '<|im_start|>system\n' }}
|
| 3 |
+
{%- if messages[0].role == 'system' %}
|
| 4 |
+
{{- messages[0].content + '\n\n' }}
|
| 5 |
+
{%- endif %}
|
| 6 |
+
{{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
|
| 7 |
+
{%- for tool in tools %}
|
| 8 |
+
{{- "\n" }}
|
| 9 |
+
{{- tool | tojson }}
|
| 10 |
+
{%- endfor %}
|
| 11 |
+
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
|
| 12 |
+
{%- else %}
|
| 13 |
+
{%- if messages[0].role == 'system' %}
|
| 14 |
+
{{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
|
| 15 |
+
{%- endif %}
|
| 16 |
+
{%- endif %}
|
| 17 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 18 |
+
{%- for message in messages[::-1] %}
|
| 19 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 20 |
+
{%- if ns.multi_step_tool and message.role == "user" and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
|
| 21 |
+
{%- set ns.multi_step_tool = false %}
|
| 22 |
+
{%- set ns.last_query_index = index %}
|
| 23 |
+
{%- endif %}
|
| 24 |
+
{%- endfor %}
|
| 25 |
+
{%- for message in messages %}
|
| 26 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 27 |
+
{{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
|
| 28 |
+
{%- elif message.role == "assistant" %}
|
| 29 |
+
{%- set content = message.content %}
|
| 30 |
+
{%- set reasoning_content = '' %}
|
| 31 |
+
{%- if message.reasoning_content is defined and message.reasoning_content is not none %}
|
| 32 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 33 |
+
{%- else %}
|
| 34 |
+
{%- if '</think>' in message.content %}
|
| 35 |
+
{%- set content = message.content.split('</think>')[-1].lstrip('\n') %}
|
| 36 |
+
{%- set reasoning_content = message.content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 37 |
+
{%- endif %}
|
| 38 |
+
{%- endif %}
|
| 39 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 40 |
+
{%- if loop.last or (not loop.last and reasoning_content) %}
|
| 41 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 42 |
+
{%- else %}
|
| 43 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 44 |
+
{%- endif %}
|
| 45 |
+
{%- else %}
|
| 46 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 47 |
+
{%- endif %}
|
| 48 |
+
{%- if message.tool_calls %}
|
| 49 |
+
{%- for tool_call in message.tool_calls %}
|
| 50 |
+
{%- if (loop.first and content) or (not loop.first) %}
|
| 51 |
+
{{- '\n' }}
|
| 52 |
+
{%- endif %}
|
| 53 |
+
{%- if tool_call.function %}
|
| 54 |
+
{%- set tool_call = tool_call.function %}
|
| 55 |
+
{%- endif %}
|
| 56 |
+
{{- '<tool_call>\n{"name": "' }}
|
| 57 |
+
{{- tool_call.name }}
|
| 58 |
+
{{- '", "arguments": ' }}
|
| 59 |
+
{%- if tool_call.arguments is string %}
|
| 60 |
+
{{- tool_call.arguments }}
|
| 61 |
+
{%- else %}
|
| 62 |
+
{{- tool_call.arguments | tojson }}
|
| 63 |
+
{%- endif %}
|
| 64 |
+
{{- '}\n</tool_call>' }}
|
| 65 |
+
{%- endfor %}
|
| 66 |
+
{%- endif %}
|
| 67 |
+
{{- '<|im_end|>\n' }}
|
| 68 |
+
{%- elif message.role == "tool" %}
|
| 69 |
+
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
|
| 70 |
+
{{- '<|im_start|>user' }}
|
| 71 |
+
{%- endif %}
|
| 72 |
+
{{- '\n<tool_response>\n' }}
|
| 73 |
+
{{- message.content }}
|
| 74 |
+
{{- '\n</tool_response>' }}
|
| 75 |
+
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
|
| 76 |
+
{{- '<|im_end|>\n' }}
|
| 77 |
+
{%- endif %}
|
| 78 |
+
{%- endif %}
|
| 79 |
+
{%- endfor %}
|
| 80 |
+
{%- if add_generation_prompt %}
|
| 81 |
+
{{- '<|im_start|>assistant\n' }}
|
| 82 |
+
{%- if enable_thinking is defined and enable_thinking is false %}
|
| 83 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 84 |
+
{%- endif %}
|
| 85 |
+
{%- endif %}
|
tokenizer/special_tokens_map.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer/tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31c2fb72254c63945fef9081a0e68a0ba752dc685cff5a44efabab28e23a9f29
|
| 3 |
+
size 12222680
|
tokenizer/tokenizer_config.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|