yujiepan commited on
Commit
00a86bb
·
verified ·
1 Parent(s): 4649822

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ 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
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
.meta/create_model.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def create_model(source_model_id: str, source_revision: str, save_folder: str) -> str:
2
+ import json
3
+ from pathlib import Path
4
+
5
+ import torch
6
+ from huggingface_hub import hf_hub_download
7
+ from transformers import AutoConfig, AutoModel, set_seed
8
+
9
+ folder = Path(save_folder)
10
+ folder.mkdir(parents=True, exist_ok=True)
11
+ for name in (
12
+ "action_head.py",
13
+ "configuration_minicpm_vla.py",
14
+ "modeling_minicpm_vla.py",
15
+ "chat_template.jinja",
16
+ "generation_config.json",
17
+ "preprocessor_config.json",
18
+ "tokenizer.json",
19
+ "tokenizer_config.json",
20
+ ):
21
+ (folder / name).write_bytes(Path(hf_hub_download(source_model_id, name, revision=source_revision)).read_bytes())
22
+
23
+ config = json.loads(Path(hf_hub_download(source_model_id, "config.json", revision=source_revision)).read_text())
24
+ text = config["vlm_config"]["text_config"]
25
+ text.update(
26
+ hidden_size=32,
27
+ intermediate_size=64,
28
+ head_dim=32,
29
+ num_attention_heads=1,
30
+ num_key_value_heads=1,
31
+ num_hidden_layers=8,
32
+ layer_types=[
33
+ "linear_attention",
34
+ "linear_attention",
35
+ "linear_attention",
36
+ "full_attention",
37
+ ]
38
+ * 2,
39
+ linear_key_head_dim=32,
40
+ linear_value_head_dim=32,
41
+ linear_num_key_heads=1,
42
+ linear_num_value_heads=1,
43
+ )
44
+ vision = config["vlm_config"]["vision_config"]
45
+ vision.update(hidden_size=32, intermediate_size=64, num_attention_heads=1, num_hidden_layers=2)
46
+ config["vlm_config"]["image_size"] = 112
47
+ (folder / "config.json").write_text(json.dumps(config, indent=2) + "\n")
48
+
49
+ action_head = (folder / "action_head.py").read_text()
50
+ for old, new in (
51
+ ("self.input_embedding_dim = 768", "self.input_embedding_dim = 32"),
52
+ ("input_embedding_dim=768,", "input_embedding_dim=32,"),
53
+ ("attention_head_dim=64,", "attention_head_dim=32,"),
54
+ ("num_attention_heads=12,", "num_attention_heads=1,"),
55
+ ("cross_attention_dim=1024,", "cross_attention_dim=32,"),
56
+ ("num_layers=16,", "num_layers=2,"),
57
+ ("output_dim=1024,", "output_dim=32,"),
58
+ ("hidden_dim=hidden_size,", "hidden_dim=32,"),
59
+ ("input_dim=self.model.config.output_dim,", "input_dim=32,"),
60
+ ):
61
+ if old not in action_head:
62
+ raise ValueError(f"Expected RobotManip action-head fragment is missing: {old}")
63
+ action_head = action_head.replace(old, new)
64
+ (folder / "action_head.py").write_text(action_head)
65
+
66
+ set_seed(42)
67
+ model = AutoModel.from_config(AutoConfig.from_pretrained(folder, trust_remote_code=True), trust_remote_code=True)
68
+ model.save_pretrained(folder, safe_serialization=True)
69
+ return str(model)
70
+
71
+
72
+ create_model(
73
+ source_model_id="openbmb/MiniCPM-RobotManip",
74
+ source_revision="29cb257541e9f96f493abe21174f8892e5dffe3f",
75
+ save_folder="/tmp/minicpm-robot-manip",
76
+ )
.meta/env.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source_model": {
3
+ "name": "openbmb/MiniCPM-RobotManip",
4
+ "commit_id": "29cb257541e9f96f493abe21174f8892e5dffe3f"
5
+ },
6
+ "environments": {
7
+ "creation": {
8
+ "python": "3.12.13",
9
+ "packages": {
10
+ "diffusers": "0.42.0.dev0",
11
+ "huggingface_hub": "2.3.0",
12
+ "safetensors": "0.8.0",
13
+ "tokenizers": "0.23.3",
14
+ "torch": "2.13.0+cu126",
15
+ "transformers": "5.20.2"
16
+ },
17
+ "cuda": "12.6",
18
+ "hardware": {
19
+ "gpu_model": "mlx lab master节点不包含GPU。请使用mlx worker launch -- nvidia-smi 来检查GPU状态, 或通过mlx worker launch -- bash登入gpu worker使用gpu相关命令。也可以通过mlx worker launch -h 查看更多launch命令使用方法。 如果mlx worker命令使用遇到问题,可运行 mlx upgrade将命令行工具升级至最新版"
20
+ }
21
+ },
22
+ "transformers": {
23
+ "python": "3.12.13",
24
+ "packages": {
25
+ "Pillow": "12.3.0",
26
+ "diffusers": "0.42.0.dev0",
27
+ "huggingface_hub": "2.3.0",
28
+ "safetensors": "0.8.0",
29
+ "tokenizers": "0.23.3",
30
+ "torch": "2.13.0+cu126",
31
+ "transformers": "5.20.2"
32
+ },
33
+ "cuda": "12.6",
34
+ "hardware": {
35
+ "gpu_model": "NVIDIA H20"
36
+ }
37
+ }
38
+ }
39
+ }
.meta/model.log ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MiniCPMV_VLA(
2
+ (vlm): MiniCPMV4_6ForConditionalGeneration(
3
+ (model): MiniCPMV4_6Model(
4
+ (vision_tower): MiniCPMV4_6VisionModel(
5
+ (embeddings): MiniCPMV4_6VisionEmbeddings(
6
+ (patch_embedding): Conv2d(3, 32, kernel_size=(14, 14), stride=(14, 14), padding=valid)
7
+ (position_embedding): Embedding(4900, 32)
8
+ )
9
+ (encoder): MiniCPMV4_6VisionEncoder(
10
+ (layers): ModuleList(
11
+ (0-1): 2 x MiniCPMV4_6VisionEncoderLayer(
12
+ (layer_norm1): LayerNorm((32,), eps=1e-06, elementwise_affine=True, bias=True)
13
+ (self_attn): MiniCPMV4_6VisionAttention(
14
+ (k_proj): Linear(in_features=32, out_features=32, bias=True)
15
+ (v_proj): Linear(in_features=32, out_features=32, bias=True)
16
+ (q_proj): Linear(in_features=32, out_features=32, bias=True)
17
+ (out_proj): Linear(in_features=32, out_features=32, bias=True)
18
+ )
19
+ (layer_norm2): LayerNorm((32,), eps=1e-06, elementwise_affine=True, bias=True)
20
+ (mlp): MiniCPMV4_6VisionMLP(
21
+ (activation_fn): GELUTanh()
22
+ (fc1): Linear(in_features=32, out_features=64, bias=True)
23
+ (fc2): Linear(in_features=64, out_features=32, bias=True)
24
+ )
25
+ )
26
+ )
27
+ )
28
+ (post_layernorm): LayerNorm((32,), eps=1e-06, elementwise_affine=True, bias=True)
29
+ (vit_merger): MiniCPMV4_6ViTWindowAttentionMerger(
30
+ (self_attn): MiniCPMV4_6VisionAttention(
31
+ (k_proj): Linear(in_features=32, out_features=32, bias=True)
32
+ (v_proj): Linear(in_features=32, out_features=32, bias=True)
33
+ (q_proj): Linear(in_features=32, out_features=32, bias=True)
34
+ (out_proj): Linear(in_features=32, out_features=32, bias=True)
35
+ )
36
+ (layer_norm1): LayerNorm((32,), eps=1e-06, elementwise_affine=True, bias=True)
37
+ (pre_norm): LayerNorm((128,), eps=1e-06, elementwise_affine=True, bias=True)
38
+ (linear_1): Linear(in_features=128, out_features=256, bias=True)
39
+ (act): GELUTanh()
40
+ (linear_2): Linear(in_features=256, out_features=32, bias=True)
41
+ )
42
+ )
43
+ (language_model): Qwen3_5TextModel(
44
+ (embed_tokens): Embedding(248094, 32)
45
+ (layers): ModuleList(
46
+ (0-2): 3 x Qwen3_5DecoderLayer(
47
+ (linear_attn): Qwen3_5GatedDeltaNet(
48
+ (conv1d): Conv1d(96, 96, kernel_size=(4,), stride=(1,), padding=(3,), groups=96, bias=False)
49
+ (norm): Qwen3_5RMSNormGated()
50
+ (out_proj): Linear(in_features=32, out_features=32, bias=False)
51
+ (in_proj_qkv): Linear(in_features=32, out_features=96, bias=False)
52
+ (in_proj_z): Linear(in_features=32, out_features=32, bias=False)
53
+ (in_proj_b): Linear(in_features=32, out_features=1, bias=False)
54
+ (in_proj_a): Linear(in_features=32, out_features=1, bias=False)
55
+ )
56
+ (mlp): Qwen3_5MLP(
57
+ (gate_proj): Linear(in_features=32, out_features=64, bias=False)
58
+ (up_proj): Linear(in_features=32, out_features=64, bias=False)
59
+ (down_proj): Linear(in_features=64, out_features=32, bias=False)
60
+ (act_fn): SiLUActivation()
61
+ )
62
+ (input_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
63
+ (post_attention_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
64
+ )
65
+ (3): Qwen3_5DecoderLayer(
66
+ (self_attn): Qwen3_5Attention(
67
+ (q_proj): Linear(in_features=32, out_features=64, bias=False)
68
+ (k_proj): Linear(in_features=32, out_features=32, bias=False)
69
+ (v_proj): Linear(in_features=32, out_features=32, bias=False)
70
+ (o_proj): Linear(in_features=32, out_features=32, bias=False)
71
+ (q_norm): Qwen3_5RMSNorm((32,), eps=1e-06)
72
+ (k_norm): Qwen3_5RMSNorm((32,), eps=1e-06)
73
+ )
74
+ (mlp): Qwen3_5MLP(
75
+ (gate_proj): Linear(in_features=32, out_features=64, bias=False)
76
+ (up_proj): Linear(in_features=32, out_features=64, bias=False)
77
+ (down_proj): Linear(in_features=64, out_features=32, bias=False)
78
+ (act_fn): SiLUActivation()
79
+ )
80
+ (input_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
81
+ (post_attention_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
82
+ )
83
+ (4-6): 3 x Qwen3_5DecoderLayer(
84
+ (linear_attn): Qwen3_5GatedDeltaNet(
85
+ (conv1d): Conv1d(96, 96, kernel_size=(4,), stride=(1,), padding=(3,), groups=96, bias=False)
86
+ (norm): Qwen3_5RMSNormGated()
87
+ (out_proj): Linear(in_features=32, out_features=32, bias=False)
88
+ (in_proj_qkv): Linear(in_features=32, out_features=96, bias=False)
89
+ (in_proj_z): Linear(in_features=32, out_features=32, bias=False)
90
+ (in_proj_b): Linear(in_features=32, out_features=1, bias=False)
91
+ (in_proj_a): Linear(in_features=32, out_features=1, bias=False)
92
+ )
93
+ (mlp): Qwen3_5MLP(
94
+ (gate_proj): Linear(in_features=32, out_features=64, bias=False)
95
+ (up_proj): Linear(in_features=32, out_features=64, bias=False)
96
+ (down_proj): Linear(in_features=64, out_features=32, bias=False)
97
+ (act_fn): SiLUActivation()
98
+ )
99
+ (input_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
100
+ (post_attention_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
101
+ )
102
+ (7): Qwen3_5DecoderLayer(
103
+ (self_attn): Qwen3_5Attention(
104
+ (q_proj): Linear(in_features=32, out_features=64, bias=False)
105
+ (k_proj): Linear(in_features=32, out_features=32, bias=False)
106
+ (v_proj): Linear(in_features=32, out_features=32, bias=False)
107
+ (o_proj): Linear(in_features=32, out_features=32, bias=False)
108
+ (q_norm): Qwen3_5RMSNorm((32,), eps=1e-06)
109
+ (k_norm): Qwen3_5RMSNorm((32,), eps=1e-06)
110
+ )
111
+ (mlp): Qwen3_5MLP(
112
+ (gate_proj): Linear(in_features=32, out_features=64, bias=False)
113
+ (up_proj): Linear(in_features=32, out_features=64, bias=False)
114
+ (down_proj): Linear(in_features=64, out_features=32, bias=False)
115
+ (act_fn): SiLUActivation()
116
+ )
117
+ (input_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
118
+ (post_attention_layernorm): Qwen3_5RMSNorm((32,), eps=1e-06)
119
+ )
120
+ )
121
+ (norm): Qwen3_5RMSNorm((32,), eps=1e-06)
122
+ (rotary_emb): Qwen3_5TextRotaryEmbedding()
123
+ )
124
+ (merger): MiniCPMV4_6Merger(
125
+ (mlp): ModuleList(
126
+ (0): MiniCPMV4_6DownsampleMLP(
127
+ (pre_norm): LayerNorm((128,), eps=1e-06, elementwise_affine=True, bias=True)
128
+ (linear_1): Linear(in_features=128, out_features=128, bias=True)
129
+ (act): GELU(approximate='none')
130
+ (linear_2): Linear(in_features=128, out_features=32, bias=True)
131
+ )
132
+ )
133
+ )
134
+ )
135
+ (lm_head): Linear(in_features=32, out_features=248094, bias=False)
136
+ )
137
+ (action_head): MiniCPMV_VLA_ActionHead(
138
+ (model): DiT(
139
+ (timestep_encoder): TimestepEncoder(
140
+ (time_proj): Timesteps()
141
+ (timestep_embedder): TimestepEmbedding(
142
+ (linear_1): Linear(in_features=256, out_features=32, bias=True)
143
+ (act): SiLU()
144
+ (linear_2): Linear(in_features=32, out_features=32, bias=True)
145
+ )
146
+ )
147
+ (transformer_blocks): ModuleList(
148
+ (0-1): 2 x BasicTransformerBlock(
149
+ (norm1): AdaLayerNorm(
150
+ (silu): SiLU()
151
+ (linear): Linear(in_features=32, out_features=64, bias=True)
152
+ (norm): LayerNorm((32,), eps=1e-05, elementwise_affine=False, bias=False)
153
+ )
154
+ (attn1): Attention(
155
+ (to_q): Linear(in_features=32, out_features=32, bias=True)
156
+ (to_k): Linear(in_features=32, out_features=32, bias=True)
157
+ (to_v): Linear(in_features=32, out_features=32, bias=True)
158
+ (to_out): ModuleList(
159
+ (0): Linear(in_features=32, out_features=32, bias=True)
160
+ (1): Dropout(p=0.2, inplace=False)
161
+ )
162
+ )
163
+ (norm3): LayerNorm((32,), eps=1e-05, elementwise_affine=False, bias=False)
164
+ (ff): FeedForward(
165
+ (net): ModuleList(
166
+ (0): GELU(
167
+ (proj): Linear(in_features=32, out_features=128, bias=True)
168
+ )
169
+ (1): Dropout(p=0.2, inplace=False)
170
+ (2): Linear(in_features=128, out_features=32, bias=True)
171
+ (3): Dropout(p=0.2, inplace=False)
172
+ )
173
+ )
174
+ (final_dropout): Dropout(p=0.2, inplace=False)
175
+ )
176
+ )
177
+ (norm_out): LayerNorm((32,), eps=1e-06, elementwise_affine=False, bias=False)
178
+ (proj_out_1): Linear(in_features=32, out_features=64, bias=True)
179
+ (proj_out_2): Linear(in_features=32, out_features=32, bias=True)
180
+ )
181
+ (action_encoder): MultiEmbodimentActionEncoder(
182
+ (W1): CategorySpecificLinear()
183
+ (W2): CategorySpecificLinear()
184
+ (W3): CategorySpecificLinear()
185
+ (pos_encoding): SinusoidalPositionalEncoding()
186
+ )
187
+ (action_decoder): CategorySpecificMLP(
188
+ (layer1): CategorySpecificLinear()
189
+ (layer2): CategorySpecificLinear()
190
+ )
191
+ (future_tokens): Embedding(32, 32)
192
+ (position_embedding): Embedding(1024, 32)
193
+ )
194
+ )
README.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ pipeline_tag: robotics
4
+ base_model:
5
+ - openbmb/MiniCPM-RobotManip
6
+ ---
7
+
8
+ This is a randomly initialized tiny model based on [openbmb/MiniCPM-RobotManip](https://huggingface.co/openbmb/MiniCPM-RobotManip) for testing and debugging.
9
+
10
+ | Feature | Source | This repo |
11
+ |---|---|---|
12
+ | Checkpoint tensor elements | 1.30B BF16, 235.81M F32 | 8.29M BF16, 459.10K F32 |
13
+ | Safetensors weight files | 3.54 GB (model.safetensors) | 18.45 MB (model.safetensors) |
14
+ | VLM text layers / width / head dimension | 24 / 1024 / 256 | 8 / 32 / 32 |
15
+ | VLM attention cadence | 3 linear + 1 full, repeated | Two 3-linear + 1-full cycles |
16
+ | Vision layers / width | 27 / 1152 | 2 / 32 |
17
+ | Action diffusion blocks / width | 16 / 768 | 2 / 32 |
18
+ | Action horizon / action-state dimensions | 30 / 80-80 | Same |
19
+ | Embodiment categories | 32 | Same |
20
+ | Text vocabulary rows | 248094 | Same |
21
+ | Stored VLM / action-head precision | BF16 / FP32 | Same |
22
+ | Input / output | Image, instruction, state → action chunk | Same |
23
+
24
+ | File path | Size |
25
+ |------|------|
26
+ | model.safetensors | 18.4MB |
27
+
28
+ ### Example usage
29
+
30
+ #### Transformers (✅ verified)
31
+
32
+ ```python
33
+ def run_transformers(model_id: str):
34
+ import torch
35
+ from PIL import Image
36
+ from transformers import AutoModel, AutoProcessor
37
+
38
+ model = AutoModel.from_pretrained(model_id, trust_remote_code=True, dtype=torch.bfloat16).eval().cuda()
39
+ processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
40
+ image = Image.new("RGB", (112, 112), "blue")
41
+ inputs = processor.apply_chat_template(
42
+ [
43
+ {
44
+ "role": "user",
45
+ "content": [{"type": "image", "image": image}, {"type": "text", "text": "Pick up the blue object."}],
46
+ }
47
+ ],
48
+ tokenize=True,
49
+ add_generation_prompt=True,
50
+ return_dict=True,
51
+ return_tensors="pt",
52
+ processor_kwargs={"padding": False},
53
+ ).to(model.device)
54
+ action = model.predict_action(
55
+ state=torch.zeros(1, 1, 80, device=model.device),
56
+ embodiment_id=torch.zeros(1, dtype=torch.long, device=model.device),
57
+ **inputs,
58
+ )
59
+ assert action.shape == (1, 30, 80)
60
+ assert torch.isfinite(action).all()
61
+ print(action.shape)
62
+
63
+
64
+ if __name__ == "__main__":
65
+ run_transformers(model_id="tiny-random/minicpm-robot-manip")
66
+ ```
67
+
68
+ ### Creating this repo
69
+
70
+ - Code: [.meta/create_model.py](.meta/create_model.py)
71
+ - Model structure: [.meta/model.log](.meta/model.log)
72
+ - Environment: [.meta/env.json](.meta/env.json)
73
+
74
+ ### Change log
75
+
76
+ - 2026-10-27: Created a tiny MiniCPM RobotManip vision-language-action policy.
77
+
78
+ *This repo is generated by GPT-6.1-Sol.*
action_head.py ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ import torch
6
+ import torch.nn.functional as F
7
+ from diffusers import ConfigMixin, ModelMixin
8
+ from diffusers.configuration_utils import register_to_config
9
+ from diffusers.models.attention import Attention, FeedForward
10
+ from diffusers.models.embeddings import SinusoidalPositionalEmbedding, TimestepEmbedding, Timesteps
11
+ from torch import nn
12
+
13
+
14
+ class TimestepEncoder(nn.Module):
15
+ def __init__(self, embedding_dim: int):
16
+ super().__init__()
17
+ self.time_proj = Timesteps(num_channels=256, flip_sin_to_cos=True, downscale_freq_shift=1)
18
+ self.timestep_embedder = TimestepEmbedding(in_channels=256, time_embed_dim=embedding_dim)
19
+
20
+ def forward(self, timesteps: torch.Tensor) -> torch.Tensor:
21
+ return self.timestep_embedder(self.time_proj(timesteps).to(next(self.parameters()).dtype))
22
+
23
+
24
+ class AdaLayerNorm(nn.Module):
25
+ def __init__(
26
+ self,
27
+ embedding_dim: int,
28
+ norm_elementwise_affine: bool = False,
29
+ norm_eps: float = 1e-5,
30
+ ):
31
+ super().__init__()
32
+ self.silu = nn.SiLU()
33
+ self.linear = nn.Linear(embedding_dim, embedding_dim * 2)
34
+ self.norm = nn.LayerNorm(embedding_dim, norm_eps, norm_elementwise_affine)
35
+
36
+ def forward(self, x: torch.Tensor, temb: torch.Tensor) -> torch.Tensor:
37
+ scale, shift = self.linear(self.silu(temb)).chunk(2, dim=1)
38
+ return self.norm(x) * (1 + scale[:, None]) + shift[:, None]
39
+
40
+
41
+ class BasicTransformerBlock(nn.Module):
42
+ def __init__(
43
+ self,
44
+ dim: int,
45
+ num_attention_heads: int,
46
+ attention_head_dim: int,
47
+ dropout: float = 0.0,
48
+ cross_attention_dim: Optional[int] = None,
49
+ activation_fn: str = "geglu",
50
+ attention_bias: bool = False,
51
+ upcast_attention: bool = False,
52
+ norm_elementwise_affine: bool = True,
53
+ norm_type: str = "layer_norm",
54
+ norm_eps: float = 1e-5,
55
+ final_dropout: bool = False,
56
+ positional_embeddings: Optional[str] = None,
57
+ num_positional_embeddings: Optional[int] = None,
58
+ ff_inner_dim: Optional[int] = None,
59
+ ff_bias: bool = True,
60
+ attention_out_bias: bool = True,
61
+ ):
62
+ super().__init__()
63
+ if positional_embeddings and num_positional_embeddings is None:
64
+ raise ValueError("num_positional_embeddings is required for positional embeddings")
65
+ self.norm_type = norm_type
66
+ self.pos_embed = (
67
+ SinusoidalPositionalEmbedding(dim, max_seq_length=num_positional_embeddings)
68
+ if positional_embeddings == "sinusoidal"
69
+ else None
70
+ )
71
+ self.norm1 = (
72
+ AdaLayerNorm(dim)
73
+ if norm_type == "ada_norm"
74
+ else nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps)
75
+ )
76
+ self.attn1 = Attention(
77
+ query_dim=dim,
78
+ heads=num_attention_heads,
79
+ dim_head=attention_head_dim,
80
+ dropout=dropout,
81
+ bias=attention_bias,
82
+ cross_attention_dim=cross_attention_dim,
83
+ upcast_attention=upcast_attention,
84
+ out_bias=attention_out_bias,
85
+ )
86
+ self.norm3 = nn.LayerNorm(dim, norm_eps, norm_elementwise_affine)
87
+ self.ff = FeedForward(
88
+ dim,
89
+ dropout=dropout,
90
+ activation_fn=activation_fn,
91
+ final_dropout=final_dropout,
92
+ inner_dim=ff_inner_dim,
93
+ bias=ff_bias,
94
+ )
95
+ self.final_dropout = nn.Dropout(dropout) if final_dropout else None
96
+
97
+ def forward(
98
+ self,
99
+ hidden_states: torch.Tensor,
100
+ encoder_hidden_states: Optional[torch.Tensor] = None,
101
+ encoder_attention_mask: Optional[torch.Tensor] = None,
102
+ temb: Optional[torch.Tensor] = None,
103
+ ) -> torch.Tensor:
104
+ norm_hidden_states = (
105
+ self.norm1(hidden_states, temb) if self.norm_type == "ada_norm" else self.norm1(hidden_states)
106
+ )
107
+ if self.pos_embed is not None:
108
+ norm_hidden_states = self.pos_embed(norm_hidden_states)
109
+ attention_output = self.attn1(
110
+ norm_hidden_states,
111
+ encoder_hidden_states=encoder_hidden_states,
112
+ attention_mask=encoder_attention_mask,
113
+ )
114
+ if self.final_dropout is not None:
115
+ attention_output = self.final_dropout(attention_output)
116
+ hidden_states = attention_output + hidden_states
117
+ if hidden_states.ndim == 4:
118
+ hidden_states = hidden_states.squeeze(1)
119
+ hidden_states = self.ff(self.norm3(hidden_states)) + hidden_states
120
+ if hidden_states.ndim == 4:
121
+ hidden_states = hidden_states.squeeze(1)
122
+ return hidden_states
123
+
124
+
125
+ class DiT(ModelMixin, ConfigMixin):
126
+ _supports_gradient_checkpointing = True
127
+
128
+ @register_to_config
129
+ def __init__(
130
+ self,
131
+ num_attention_heads: int = 8,
132
+ attention_head_dim: int = 64,
133
+ output_dim: int = 26,
134
+ num_layers: int = 12,
135
+ dropout: float = 0.1,
136
+ attention_bias: bool = True,
137
+ activation_fn: str = "gelu-approximate",
138
+ num_embeds_ada_norm: Optional[int] = 1000,
139
+ upcast_attention: bool = False,
140
+ norm_type: str = "ada_norm",
141
+ norm_elementwise_affine: bool = False,
142
+ norm_eps: float = 1e-5,
143
+ max_num_positional_embeddings: int = 512,
144
+ compute_dtype: torch.dtype = torch.float32,
145
+ final_dropout: bool = True,
146
+ positional_embeddings: Optional[str] = "sinusoidal",
147
+ interleave_self_attention: bool = False,
148
+ cross_attention_dim: Optional[int] = None,
149
+ **kwargs,
150
+ ):
151
+ super().__init__()
152
+ self.attention_head_dim = attention_head_dim
153
+ self.inner_dim = self.config.num_attention_heads * self.config.attention_head_dim
154
+ self.gradient_checkpointing = False
155
+ self.timestep_encoder = TimestepEncoder(self.inner_dim)
156
+ self.transformer_blocks = nn.ModuleList(
157
+ [
158
+ BasicTransformerBlock(
159
+ self.inner_dim,
160
+ self.config.num_attention_heads,
161
+ self.config.attention_head_dim,
162
+ dropout=self.config.dropout,
163
+ activation_fn=self.config.activation_fn,
164
+ attention_bias=self.config.attention_bias,
165
+ upcast_attention=self.config.upcast_attention,
166
+ norm_type=norm_type,
167
+ norm_elementwise_affine=self.config.norm_elementwise_affine,
168
+ norm_eps=self.config.norm_eps,
169
+ positional_embeddings=positional_embeddings,
170
+ num_positional_embeddings=self.config.max_num_positional_embeddings,
171
+ final_dropout=final_dropout,
172
+ cross_attention_dim=(
173
+ None if index % 2 == 1 and interleave_self_attention else cross_attention_dim
174
+ ),
175
+ )
176
+ for index in range(self.config.num_layers)
177
+ ]
178
+ )
179
+ self.norm_out = nn.LayerNorm(self.inner_dim, elementwise_affine=False, eps=1e-6)
180
+ self.proj_out_1 = nn.Linear(self.inner_dim, 2 * self.inner_dim)
181
+ self.proj_out_2 = nn.Linear(self.inner_dim, self.config.output_dim)
182
+
183
+ def forward(
184
+ self,
185
+ hidden_states: torch.Tensor,
186
+ encoder_hidden_states: torch.Tensor,
187
+ timestep: Optional[torch.LongTensor] = None,
188
+ return_all_hidden_states: bool = False,
189
+ encoder_attention_mask: Optional[torch.Tensor] = None,
190
+ ):
191
+ time_embedding = self.timestep_encoder(timestep)
192
+ hidden_states = hidden_states.contiguous()
193
+ encoder_hidden_states = encoder_hidden_states.contiguous()
194
+ all_hidden_states = [hidden_states]
195
+ for index, block in enumerate(self.transformer_blocks):
196
+ self_attention = index % 2 == 1 and self.config.interleave_self_attention
197
+ hidden_states = block(
198
+ hidden_states,
199
+ encoder_hidden_states=None if self_attention else encoder_hidden_states,
200
+ encoder_attention_mask=None if self_attention else encoder_attention_mask,
201
+ temb=time_embedding,
202
+ )
203
+ all_hidden_states.append(hidden_states)
204
+ shift, scale = self.proj_out_1(F.silu(time_embedding)).chunk(2, dim=1)
205
+ hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None]
206
+ output = self.proj_out_2(hidden_states)
207
+ return (output, all_hidden_states) if return_all_hidden_states else output
208
+
209
+
210
+ class SinusoidalPositionalEncoding(nn.Module):
211
+ def __init__(self, embedding_dim: int):
212
+ super().__init__()
213
+ self.embedding_dim = embedding_dim
214
+
215
+ def forward(self, timesteps: torch.Tensor) -> torch.Tensor:
216
+ timesteps = timesteps.float()
217
+ half_dim = self.embedding_dim // 2
218
+ exponent = -torch.arange(half_dim, dtype=torch.float, device=timesteps.device) * (
219
+ torch.log(torch.tensor(10000.0, device=timesteps.device)) / half_dim
220
+ )
221
+ frequencies = timesteps.unsqueeze(-1) * exponent.exp()
222
+ return torch.cat([torch.sin(frequencies), torch.cos(frequencies)], dim=-1)
223
+
224
+
225
+ class CategorySpecificLinear(nn.Module):
226
+ def __init__(self, num_categories: int, input_dim: int, output_dim: int):
227
+ super().__init__()
228
+ self.num_categories = num_categories
229
+ self.W = nn.Parameter(0.02 * torch.randn(num_categories, input_dim, output_dim))
230
+ self.b = nn.Parameter(torch.zeros(num_categories, output_dim))
231
+
232
+ def forward(self, x: torch.Tensor, category_ids: torch.Tensor) -> torch.Tensor:
233
+ if category_ids is None:
234
+ raise ValueError("embodiment_id (B,) is required")
235
+ return torch.bmm(x, self.W[category_ids]) + self.b[category_ids].unsqueeze(1)
236
+
237
+
238
+ class CategorySpecificMLP(nn.Module):
239
+ def __init__(self, num_categories: int, input_dim: int, hidden_dim: int, output_dim: int):
240
+ super().__init__()
241
+ self.layer1 = CategorySpecificLinear(num_categories, input_dim, hidden_dim)
242
+ self.layer2 = CategorySpecificLinear(num_categories, hidden_dim, output_dim)
243
+
244
+ def forward(self, x: torch.Tensor, category_ids: torch.Tensor) -> torch.Tensor:
245
+ return self.layer2(F.relu(self.layer1(x, category_ids)), category_ids)
246
+
247
+
248
+ class MultiEmbodimentActionEncoder(nn.Module):
249
+ def __init__(self, action_dim: int, hidden_size: int, num_embodiments: int):
250
+ super().__init__()
251
+ self.W1 = CategorySpecificLinear(num_embodiments, action_dim, hidden_size)
252
+ self.W2 = CategorySpecificLinear(num_embodiments, 2 * hidden_size, hidden_size)
253
+ self.W3 = CategorySpecificLinear(num_embodiments, hidden_size, hidden_size)
254
+ self.pos_encoding = SinusoidalPositionalEncoding(hidden_size)
255
+
256
+ def forward(
257
+ self,
258
+ actions: torch.Tensor,
259
+ timesteps: torch.Tensor,
260
+ embodiment_id: torch.Tensor,
261
+ ) -> torch.Tensor:
262
+ batch_size, horizon, _ = actions.shape
263
+ if timesteps.dim() != 1 or timesteps.shape[0] != batch_size:
264
+ raise ValueError("timesteps must have shape (B,)")
265
+ timesteps = timesteps.unsqueeze(1).expand(-1, horizon)
266
+ action_embedding = self.W1(actions, embodiment_id)
267
+ time_embedding = self.pos_encoding(timesteps).to(dtype=action_embedding.dtype)
268
+ hidden = self.W2(torch.cat([action_embedding, time_embedding], dim=-1), embodiment_id)
269
+ return self.W3(hidden * torch.sigmoid(hidden), embodiment_id)
270
+
271
+
272
+ class MiniCPMV_VLA_ActionHead(nn.Module):
273
+ """80-D, 32-embodiment action head for MiniCPM-VLA."""
274
+
275
+ def __init__(
276
+ self,
277
+ hidden_size: int = 1024,
278
+ action_dim: int = 80,
279
+ state_dim: int = 80,
280
+ action_horizon: int = 30,
281
+ num_inference_timesteps: int = 4,
282
+ num_target_vision_tokens: int = 32,
283
+ max_seq_len: int = 1024,
284
+ num_timestep_buckets: int = 1000,
285
+ max_num_embodiments: int = 32,
286
+ ):
287
+ super().__init__()
288
+ self.hidden_size = hidden_size
289
+ self.input_embedding_dim = 32
290
+ self.model = DiT(
291
+ input_embedding_dim=32,
292
+ attention_head_dim=32,
293
+ num_attention_heads=1,
294
+ cross_attention_dim=32,
295
+ dropout=0.2,
296
+ final_dropout=True,
297
+ interleave_self_attention=True,
298
+ norm_type="ada_norm",
299
+ num_layers=2,
300
+ output_dim=32,
301
+ positional_embeddings=None,
302
+ )
303
+ self.action_dim = action_dim
304
+ self.state_dim = state_dim
305
+ self.action_horizon = action_horizon
306
+ self.num_inference_timesteps = num_inference_timesteps
307
+ self.max_num_embodiments = max_num_embodiments
308
+ self.multi_embodiment = True
309
+ self.proprio_inject = "concat"
310
+ self.state_encoder = None
311
+ self.action_encoder = MultiEmbodimentActionEncoder(
312
+ action_dim=action_dim + state_dim,
313
+ hidden_size=self.input_embedding_dim,
314
+ num_embodiments=max_num_embodiments,
315
+ )
316
+ self.action_decoder = CategorySpecificMLP(
317
+ num_categories=max_num_embodiments,
318
+ input_dim=32,
319
+ hidden_dim=32,
320
+ output_dim=action_dim,
321
+ )
322
+ self.future_tokens = nn.Embedding(num_target_vision_tokens, self.input_embedding_dim)
323
+ nn.init.normal_(self.future_tokens.weight, mean=0.0, std=0.02)
324
+ self.position_embedding = nn.Embedding(max_seq_len, self.input_embedding_dim)
325
+ nn.init.normal_(self.position_embedding.weight, mean=0.0, std=0.02)
326
+ self.num_timestep_buckets = num_timestep_buckets
327
+
328
+ def _encode_action_tokens(
329
+ self,
330
+ noisy_actions: torch.Tensor,
331
+ state: torch.Tensor,
332
+ timesteps: torch.Tensor,
333
+ embodiment_id: torch.Tensor,
334
+ ) -> torch.Tensor:
335
+ if state is None:
336
+ raise ValueError("state is required because PROPRIO_INJECT=concat")
337
+ state = state.expand(-1, noisy_actions.shape[1], -1)
338
+ inputs = torch.cat([noisy_actions, state], dim=-1)
339
+ return self.action_encoder(inputs, timesteps, embodiment_id)
340
+
341
+ def _build_sequence(self, action_features: torch.Tensor) -> torch.Tensor:
342
+ future_tokens = self.future_tokens.weight.unsqueeze(0).expand(action_features.shape[0], -1, -1)
343
+ return torch.cat((future_tokens, action_features), dim=1)
344
+
345
+ def _predict(
346
+ self,
347
+ noisy_actions: torch.Tensor,
348
+ vl_embs: torch.Tensor,
349
+ state: torch.Tensor,
350
+ timesteps: torch.Tensor,
351
+ embodiment_id: torch.Tensor,
352
+ ) -> torch.Tensor:
353
+ features = self._encode_action_tokens(noisy_actions, state, timesteps, embodiment_id)
354
+ position_ids = torch.arange(features.shape[1], dtype=torch.long, device=features.device)
355
+ features = features + self.position_embedding(position_ids).unsqueeze(0)
356
+ output = self.model(
357
+ hidden_states=self._build_sequence(features),
358
+ encoder_hidden_states=vl_embs,
359
+ timestep=timesteps,
360
+ )
361
+ return self.action_decoder(output, embodiment_id)[:, -self.action_horizon :]
362
+
363
+ @torch.no_grad()
364
+ def predict_action(
365
+ self,
366
+ vl_embs: torch.Tensor,
367
+ state: torch.Tensor,
368
+ embodiment_id: torch.Tensor,
369
+ ) -> torch.Tensor:
370
+ actions = torch.zeros(
371
+ (vl_embs.shape[0], self.action_horizon, self.action_dim),
372
+ dtype=vl_embs.dtype,
373
+ device=vl_embs.device,
374
+ )
375
+ noise = torch.randn_like(actions)
376
+ for step in range(self.num_inference_timesteps, 0, -1):
377
+ time = step / float(self.num_inference_timesteps)
378
+ timestep = min(int(time * self.num_timestep_buckets), self.num_timestep_buckets - 1)
379
+ timesteps = torch.full(
380
+ (vl_embs.shape[0],),
381
+ timestep,
382
+ device=vl_embs.device,
383
+ dtype=torch.long,
384
+ )
385
+ noisy_actions = time * noise + (1 - time) * actions
386
+ actions = self._predict(noisy_actions, vl_embs, state, timesteps, embodiment_id)
387
+ return actions
chat_template.jinja ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if enable_thinking is not defined -%}
2
+ {%- set enable_thinking = false -%}
3
+ {%- endif -%}
4
+ {%- macro render_content(content, is_system_content=false) -%}
5
+ {%- if content is string -%}
6
+ {{- content -}}
7
+ {%- elif content is iterable and content is not mapping -%}
8
+ {%- set ns = namespace(parts=[]) -%}
9
+ {%- for item in content -%}
10
+ {%- if 'image' in item or 'image_url' in item or item.type == 'image' -%}
11
+ {%- if is_system_content -%}
12
+ {{- raise_exception('System message cannot contain images.') -}}
13
+ {%- endif -%}
14
+ {%- set ns.parts = ns.parts + ['<|image_pad|>'] -%}
15
+ {%- elif 'video' in item or item.type == 'video' -%}
16
+ {%- if is_system_content -%}
17
+ {{- raise_exception('System message cannot contain videos.') -}}
18
+ {%- endif -%}
19
+ {%- set ns.parts = ns.parts + ['<|video_pad|>'] -%}
20
+ {%- elif 'text' in item -%}
21
+ {%- set ns.parts = ns.parts + [item.text] -%}
22
+ {%- else -%}
23
+ {{- raise_exception('Unexpected item type in content.') -}}
24
+ {%- endif -%}
25
+ {%- endfor -%}
26
+ {{- ns.parts | join('\n') -}}
27
+ {%- elif content is none or content is undefined -%}
28
+ {{- '' -}}
29
+ {%- else -%}
30
+ {{- raise_exception('Unexpected content type.') -}}
31
+ {%- endif -%}
32
+ {%- endmacro -%}
33
+ {%- if not messages %}
34
+ {{- raise_exception('No messages provided.') }}
35
+ {%- endif %}
36
+ {%- if tools and tools is iterable and tools is not mapping %}
37
+ {{- '<|im_start|>system\n' }}
38
+ {{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
39
+ {%- for tool in tools %}
40
+ {{- "\n" }}
41
+ {{- tool | tojson }}
42
+ {%- endfor %}
43
+ {{- "\n</tools>" }}
44
+ {{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
45
+ {%- if messages[0].role == 'system' %}
46
+ {%- set content = render_content(messages[0].content, true)|trim %}
47
+ {%- if content %}
48
+ {{- '\n\n' + content }}
49
+ {%- endif %}
50
+ {%- endif %}
51
+ {{- '<|im_end|>\n' }}
52
+ {%- else %}
53
+ {%- if messages[0].role == 'system' %}
54
+ {%- set content = render_content(messages[0].content, true)|trim %}
55
+ {{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
56
+ {%- endif %}
57
+ {%- endif %}
58
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
59
+ {%- for message in messages[::-1] %}
60
+ {%- set index = (messages|length - 1) - loop.index0 %}
61
+ {%- if ns.multi_step_tool and message.role == "user" %}
62
+ {%- set content = render_content(message.content)|trim %}
63
+ {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
64
+ {%- set ns.multi_step_tool = false %}
65
+ {%- set ns.last_query_index = index %}
66
+ {%- endif %}
67
+ {%- endif %}
68
+ {%- endfor %}
69
+ {%- if ns.multi_step_tool %}
70
+ {{- raise_exception('No user query found in messages.') }}
71
+ {%- endif %}
72
+ {%- for message in messages %}
73
+ {%- set content = render_content(message.content)|trim %}
74
+ {%- if message.role == "system" %}
75
+ {%- if not loop.first %}
76
+ {{- raise_exception('System message must be at the beginning.') }}
77
+ {%- endif %}
78
+ {%- elif message.role == "user" %}
79
+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
80
+ {%- elif message.role == "assistant" %}
81
+ {%- set reasoning_content = '' %}
82
+ {%- if message.reasoning_content is string %}
83
+ {%- set reasoning_content = message.reasoning_content %}
84
+ {%- else %}
85
+ {%- if '</think>' in content %}
86
+ {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
87
+ {%- set content = content.split('</think>')[-1].lstrip('\n') %}
88
+ {%- endif %}
89
+ {%- endif %}
90
+ {%- set reasoning_content = reasoning_content|trim %}
91
+ {%- if loop.index0 > ns.last_query_index %}
92
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
93
+ {%- else %}
94
+ {{- '<|im_start|>' + message.role + '\n' + content }}
95
+ {%- endif %}
96
+ {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
97
+ {%- for tool_call in message.tool_calls %}
98
+ {%- if tool_call.function is defined %}
99
+ {%- set tool_call = tool_call.function %}
100
+ {%- endif %}
101
+ {%- if loop.first %}
102
+ {%- if content|trim %}
103
+ {{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
104
+ {%- else %}
105
+ {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
106
+ {%- endif %}
107
+ {%- else %}
108
+ {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
109
+ {%- endif %}
110
+ {%- if tool_call.arguments is defined %}
111
+ {%- for args_name, args_value in tool_call.arguments|items %}
112
+ {{- '<parameter=' + args_name + '>\n' }}
113
+ {%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
114
+ {{- args_value }}
115
+ {{- '\n</parameter>\n' }}
116
+ {%- endfor %}
117
+ {%- endif %}
118
+ {{- '</function>\n</tool_call>' }}
119
+ {%- endfor %}
120
+ {%- endif %}
121
+ {{- '<|im_end|>\n' }}
122
+ {%- elif message.role == "tool" %}
123
+ {%- if loop.previtem and loop.previtem.role != "tool" %}
124
+ {{- '<|im_start|>user' }}
125
+ {%- endif %}
126
+ {{- '\n<tool_response>\n' }}
127
+ {{- content }}
128
+ {{- '\n</tool_response>' }}
129
+ {%- if not loop.last and loop.nextitem.role != "tool" %}
130
+ {{- '<|im_end|>\n' }}
131
+ {%- elif loop.last %}
132
+ {{- '<|im_end|>\n' }}
133
+ {%- endif %}
134
+ {%- else %}
135
+ {{- raise_exception('Unexpected message role.') }}
136
+ {%- endif %}
137
+ {%- endfor %}
138
+ {%- if add_generation_prompt %}
139
+ {{- '<|im_start|>assistant\n' }}
140
+ {%- if enable_thinking is defined and enable_thinking is false %}
141
+ {{- '<think>\n\n</think>\n\n' }}
142
+ {%- else %}
143
+ {{- '<think>\n' }}
144
+ {%- endif %}
145
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "action_dim": 80,
3
+ "action_head_dtype": "float32",
4
+ "action_horizon": 30,
5
+ "architectures": [
6
+ "MiniCPMV_VLA"
7
+ ],
8
+ "auto_map": {
9
+ "AutoConfig": "configuration_minicpm_vla.MiniCPMVLAConfig",
10
+ "AutoModel": "modeling_minicpm_vla.MiniCPMV_VLA"
11
+ },
12
+ "dtype": "bfloat16",
13
+ "max_num_embodiments": 32,
14
+ "model_type": "minicpm_vla",
15
+ "num_inference_timesteps": 4,
16
+ "state_dim": 80,
17
+ "transformers_version": "5.20.2",
18
+ "vlm_config": {
19
+ "architectures": [
20
+ "MiniCPMV4_6ForConditionalGeneration"
21
+ ],
22
+ "bos_token_id": null,
23
+ "chunk_size_feed_forward": 0,
24
+ "downsample_mode": "16x",
25
+ "drop_vision_last_layer": false,
26
+ "dtype": null,
27
+ "eos_token_id": 248044,
28
+ "id2label": {
29
+ "0": "LABEL_0",
30
+ "1": "LABEL_1"
31
+ },
32
+ "image_size": 112,
33
+ "image_token_id": 248056,
34
+ "insert_layer_id": 6,
35
+ "is_encoder_decoder": false,
36
+ "label2id": {
37
+ "LABEL_0": 0,
38
+ "LABEL_1": 1
39
+ },
40
+ "merge_kernel_size": [
41
+ 2,
42
+ 2
43
+ ],
44
+ "merger_times": 1,
45
+ "model_type": "minicpmv4_6",
46
+ "output_attentions": false,
47
+ "output_hidden_states": false,
48
+ "pad_token_id": null,
49
+ "patch_size": 14,
50
+ "problem_type": null,
51
+ "return_dict": true,
52
+ "text_config": {
53
+ "architectures": null,
54
+ "attention_bias": false,
55
+ "attention_dropout": 0.0,
56
+ "attn_output_gate": true,
57
+ "bos_token_id": null,
58
+ "chunk_size_feed_forward": 0,
59
+ "dtype": null,
60
+ "eos_token_id": null,
61
+ "full_attention_interval": 4,
62
+ "head_dim": 32,
63
+ "hidden_act": "silu",
64
+ "hidden_size": 32,
65
+ "id2label": {
66
+ "0": "LABEL_0",
67
+ "1": "LABEL_1"
68
+ },
69
+ "initializer_range": 0.02,
70
+ "intermediate_size": 64,
71
+ "is_encoder_decoder": false,
72
+ "label2id": {
73
+ "LABEL_0": 0,
74
+ "LABEL_1": 1
75
+ },
76
+ "layer_types": [
77
+ "linear_attention",
78
+ "linear_attention",
79
+ "linear_attention",
80
+ "full_attention",
81
+ "linear_attention",
82
+ "linear_attention",
83
+ "linear_attention",
84
+ "full_attention"
85
+ ],
86
+ "linear_conv_kernel_dim": 4,
87
+ "linear_key_head_dim": 32,
88
+ "linear_num_key_heads": 1,
89
+ "linear_num_value_heads": 1,
90
+ "linear_value_head_dim": 32,
91
+ "mamba_ssm_dtype": "float32",
92
+ "max_position_embeddings": 262144,
93
+ "mlp_only_layers": [],
94
+ "model_type": "qwen3_5_text",
95
+ "mtp_num_hidden_layers": 1,
96
+ "mtp_use_dedicated_embeddings": false,
97
+ "num_attention_heads": 1,
98
+ "num_hidden_layers": 8,
99
+ "num_key_value_heads": 1,
100
+ "output_attentions": false,
101
+ "output_hidden_states": false,
102
+ "pad_token_id": null,
103
+ "partial_rotary_factor": 0.25,
104
+ "problem_type": null,
105
+ "return_dict": true,
106
+ "rms_norm_eps": 1e-06,
107
+ "rope_parameters": {
108
+ "partial_rotary_factor": 0.25,
109
+ "rope_theta": 10000000,
110
+ "rope_type": "default"
111
+ },
112
+ "tie_word_embeddings": true,
113
+ "use_cache": true,
114
+ "vocab_size": 248094
115
+ },
116
+ "tie_word_embeddings": true,
117
+ "transformers_version": "5.7.0",
118
+ "video_token_id": 248057,
119
+ "vision_config": {
120
+ "architectures": null,
121
+ "attention_dropout": 0.0,
122
+ "chunk_size_feed_forward": 0,
123
+ "dtype": null,
124
+ "hidden_act": "gelu_pytorch_tanh",
125
+ "hidden_size": 32,
126
+ "id2label": {
127
+ "0": "LABEL_0",
128
+ "1": "LABEL_1"
129
+ },
130
+ "image_size": 980,
131
+ "insert_layer_id": 6,
132
+ "intermediate_size": 64,
133
+ "is_encoder_decoder": false,
134
+ "label2id": {
135
+ "LABEL_0": 0,
136
+ "LABEL_1": 1
137
+ },
138
+ "layer_norm_eps": 1e-06,
139
+ "num_attention_heads": 1,
140
+ "num_channels": 3,
141
+ "num_hidden_layers": 2,
142
+ "output_attentions": false,
143
+ "output_hidden_states": false,
144
+ "patch_size": 14,
145
+ "problem_type": null,
146
+ "return_dict": true,
147
+ "window_kernel_size": [
148
+ 2,
149
+ 2
150
+ ]
151
+ }
152
+ },
153
+ "vlm_dtype": "bfloat16"
154
+ }
configuration_minicpm_vla.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from transformers import PretrainedConfig
4
+
5
+
6
+ class MiniCPMVLAConfig(PretrainedConfig):
7
+ """Configuration for the complete VLM and action-head VLA model."""
8
+
9
+ model_type = "minicpm_vla"
10
+ is_composition = True
11
+
12
+ def __init__(
13
+ self,
14
+ vlm_config: dict | PretrainedConfig | None = None,
15
+ action_dim: int = 80,
16
+ state_dim: int = 80,
17
+ action_horizon: int = 30,
18
+ max_num_embodiments: int = 32,
19
+ num_inference_timesteps: int = 4,
20
+ vlm_dtype: str = "bfloat16",
21
+ action_head_dtype: str = "float32",
22
+ **kwargs,
23
+ ):
24
+ super().__init__(**kwargs)
25
+ if isinstance(vlm_config, PretrainedConfig):
26
+ vlm_config = vlm_config.to_dict()
27
+ self.vlm_config = vlm_config
28
+ self.action_dim = action_dim
29
+ self.state_dim = state_dim
30
+ self.action_horizon = action_horizon
31
+ self.max_num_embodiments = max_num_embodiments
32
+ self.num_inference_timesteps = num_inference_timesteps
33
+ self.vlm_dtype = vlm_dtype
34
+ self.action_head_dtype = action_head_dtype
35
+ self.architectures = ["MiniCPMV_VLA"]
generation_config.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 248045,
3
+ "do_sample": true,
4
+ "eos_token_id": [
5
+ 248044,
6
+ 248046
7
+ ],
8
+ "temperature": 0.7,
9
+ "top_k": 0,
10
+ "top_p": 1.0,
11
+ "repetition_penalty": 1.0,
12
+ "transformers_version": "5.7.0"
13
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c8076646005e1ef1748225910dacfe2760cb880d053e8c400fc589b29842325
3
+ size 18447424
modeling_minicpm_vla.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+
5
+ import torch
6
+ from transformers import AutoConfig, AutoModel, AutoModelForImageTextToText, PretrainedConfig, PreTrainedModel
7
+
8
+ try:
9
+ from .action_head import MiniCPMV_VLA_ActionHead
10
+ from .configuration_minicpm_vla import MiniCPMVLAConfig
11
+ except ImportError: # pragma: no cover - fallback for non-package loaders
12
+ from action_head import MiniCPMV_VLA_ActionHead
13
+ from configuration_minicpm_vla import MiniCPMVLAConfig
14
+
15
+
16
+ def _torch_dtype(name: str) -> torch.dtype:
17
+ try:
18
+ return getattr(torch, name)
19
+ except AttributeError as exc:
20
+ raise ValueError(f"Unsupported torch dtype: {name}") from exc
21
+
22
+
23
+ def _build_vlm_config(config_dict: dict) -> PretrainedConfig:
24
+ values = dict(config_dict)
25
+ model_type = values.pop("model_type")
26
+ return AutoConfig.for_model(model_type, **values)
27
+
28
+
29
+ class MiniCPMV_VLA(PreTrainedModel):
30
+ """Complete VLA model loadable with ``AutoModel.from_pretrained``."""
31
+
32
+ config_class = MiniCPMVLAConfig
33
+ base_model_prefix = ""
34
+ _tied_weights_keys = {
35
+ "vlm.lm_head.weight": "vlm.model.language_model.embed_tokens.weight",
36
+ }
37
+ _no_split_modules = ["BasicTransformerBlock"]
38
+
39
+ def __init__(self, config: MiniCPMVLAConfig):
40
+ super().__init__(config)
41
+ if config.vlm_config is None:
42
+ raise ValueError("config.vlm_config is required")
43
+ vlm_config = _build_vlm_config(config.vlm_config)
44
+ self.vlm_dtype = _torch_dtype(config.vlm_dtype)
45
+ self.action_head_dtype = _torch_dtype(config.action_head_dtype)
46
+ self.vlm = AutoModelForImageTextToText.from_config(vlm_config).to(self.vlm_dtype)
47
+ self.vlm.config.hidden_size = self.vlm.config.text_config.hidden_size
48
+ self.action_head = MiniCPMV_VLA_ActionHead(
49
+ action_dim=config.action_dim,
50
+ state_dim=config.state_dim,
51
+ action_horizon=config.action_horizon,
52
+ num_inference_timesteps=config.num_inference_timesteps,
53
+ max_num_embodiments=config.max_num_embodiments,
54
+ ).to(self.action_head_dtype)
55
+ self.post_init()
56
+
57
+ def get_input_embeddings(self):
58
+ return self.vlm.get_input_embeddings()
59
+
60
+ def set_input_embeddings(self, value):
61
+ return self.vlm.set_input_embeddings(value)
62
+
63
+ def _vlm_forward(self, data: Mapping[str, object]):
64
+ inputs = {key: value for key, value in data.items() if value is not None}
65
+ if "language_attention_mask" in inputs and "attention_mask" not in inputs:
66
+ inputs["attention_mask"] = inputs.pop("language_attention_mask")
67
+ inputs.setdefault("output_hidden_states", True)
68
+ inputs.setdefault("return_dict", True)
69
+ inputs.setdefault("use_cache", False)
70
+ inputs.setdefault("logits_to_keep", 1)
71
+ with torch.autocast("cuda", dtype=self.vlm_dtype, enabled=torch.cuda.is_available()):
72
+ try:
73
+ return self.vlm(**inputs)
74
+ except TypeError as exc:
75
+ if "logits_to_keep" not in str(exc):
76
+ raise
77
+ inputs.pop("logits_to_keep")
78
+ return self.vlm(**inputs)
79
+
80
+ @torch.no_grad()
81
+ def predict_action(
82
+ self,
83
+ state: torch.Tensor,
84
+ embodiment_id: torch.Tensor,
85
+ **vlm_inputs,
86
+ ) -> torch.Tensor:
87
+ vl_embs = self._vlm_forward(vlm_inputs).hidden_states[-1].to(self.action_head_dtype)
88
+ return self.action_head.predict_action(vl_embs, state, embodiment_id)
89
+
90
+
91
+ AutoConfig.register("minicpm_vla", MiniCPMVLAConfig)
92
+ AutoModel.register(MiniCPMVLAConfig, MiniCPMV_VLA)
preprocessor_config.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MiniCPMV4_6ImageProcessor",
3
+ "processor_class": "MiniCPMV4_6Processor",
4
+ "max_slice_nums": 9,
5
+ "scale_resolution": 448,
6
+ "patch_size": 14,
7
+ "use_image_id": true,
8
+ "slice_mode": true,
9
+ "image_mean": [
10
+ 0.5,
11
+ 0.5,
12
+ 0.5
13
+ ],
14
+ "image_std": [
15
+ 0.5,
16
+ 0.5,
17
+ 0.5
18
+ ]
19
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:33861e37bb955af1e3f3061182b820f347eba2b9c2c1011c82794bf0d6e77b54
3
+ size 19992481
tokenizer_config.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "audio_bos_token": "<|audio_start|>",
4
+ "audio_eos_token": "<|audio_end|>",
5
+ "audio_token": "<|audio_pad|>",
6
+ "backend": "tokenizers",
7
+ "bos_token": "<|im_start|>",
8
+ "clean_up_tokenization_spaces": false,
9
+ "eos_token": "<|im_end|>",
10
+ "errors": "replace",
11
+ "extra_special_tokens": {
12
+ "image_token": "<|image_pad|>",
13
+ "video_token": "<|video_pad|>",
14
+ "image_start_token": "<image>",
15
+ "image_end_token": "</image>",
16
+ "slice_start_token": "<slice>",
17
+ "slice_end_token": "</slice>",
18
+ "image_id_start_token": "<image_id>",
19
+ "image_id_end_token": "</image_id>"
20
+ },
21
+ "image_token": "<|image_pad|>",
22
+ "is_local": true,
23
+ "model_max_length": 262144,
24
+ "model_specific_special_tokens": {
25
+ "audio_bos_token": "<|audio_start|>",
26
+ "audio_eos_token": "<|audio_end|>",
27
+ "audio_token": "<|audio_pad|>",
28
+ "image_token": "<|image_pad|>",
29
+ "video_token": "<|video_pad|>",
30
+ "vision_bos_token": "<|vision_start|>",
31
+ "vision_eos_token": "<|vision_end|>"
32
+ },
33
+ "pad_token": "<|endoftext|>",
34
+ "pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
35
+ "split_special_tokens": false,
36
+ "unk_token": "<unk>",
37
+ "video_token": "<|video_pad|>",
38
+ "vision_bos_token": "<|vision_start|>",
39
+ "vision_eos_token": "<|vision_end|>",
40
+ "chat_template": "{%- if enable_thinking is not defined -%}\n {%- set enable_thinking = false -%}\n{%- endif -%}\n{%- macro render_content(content, is_system_content=false) -%}\n {%- if content is string -%}\n {{- content -}}\n {%- elif content is iterable and content is not mapping -%}\n {%- set ns = namespace(parts=[]) -%}\n {%- for item in content -%}\n {%- if 'image' in item or 'image_url' in item or item.type == 'image' -%}\n {%- if is_system_content -%}\n {{- raise_exception('System message cannot contain images.') -}}\n {%- endif -%}\n {%- set ns.parts = ns.parts + ['<|image_pad|>'] -%}\n {%- elif 'video' in item or item.type == 'video' -%}\n {%- if is_system_content -%}\n {{- raise_exception('System message cannot contain videos.') -}}\n {%- endif -%}\n {%- set ns.parts = ns.parts + ['<|video_pad|>'] -%}\n {%- elif 'text' in item -%}\n {%- set ns.parts = ns.parts + [item.text] -%}\n {%- else -%}\n {{- raise_exception('Unexpected item type in content.') -}}\n {%- endif -%}\n {%- endfor -%}\n {{- ns.parts | join('\\n') -}}\n {%- elif content is none or content is undefined -%}\n {{- '' -}}\n {%- else -%}\n {{- raise_exception('Unexpected content type.') -}}\n {%- endif -%}\n{%- endmacro -%}\n{%- if not messages %}\n {{- raise_exception('No messages provided.') }}\n{%- endif %}\n{%- if tools and tools is iterable and tools is not mapping %}\n {{- '<|im_start|>system\\n' }}\n {{- \"# Tools\\n\\nYou have access to the following functions:\\n\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\" }}\n {{- '\\n\\nIf you choose to call a function ONLY reply in the following format with NO suffix:\\n\\n<tool_call>\\n<function=example_function_name>\\n<parameter=example_parameter_1>\\nvalue_1\\n</parameter>\\n<parameter=example_parameter_2>\\nThis is the value for the second parameter\\nthat can span\\nmultiple lines\\n</parameter>\\n</function>\\n</tool_call>\\n\\n<IMPORTANT>\\nReminder:\\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\\n- Required parameters MUST be specified\\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\\n</IMPORTANT>' }}\n {%- if messages[0].role == 'system' %}\n {%- set content = render_content(messages[0].content, true)|trim %}\n {%- if content %}\n {{- '\\n\\n' + content }}\n {%- endif %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {%- set content = render_content(messages[0].content, true)|trim %}\n {{- '<|im_start|>system\\n' + content + '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}\n{%- for message in messages[::-1] %}\n {%- set index = (messages|length - 1) - loop.index0 %}\n {%- if ns.multi_step_tool and message.role == \"user\" %}\n {%- set content = render_content(message.content)|trim %}\n {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}\n {%- set ns.multi_step_tool = false %}\n {%- set ns.last_query_index = index %}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if ns.multi_step_tool %}\n {{- raise_exception('No user query found in messages.') }}\n{%- endif %}\n{%- for message in messages %}\n {%- set content = render_content(message.content)|trim %}\n {%- if message.role == \"system\" %}\n {%- if not loop.first %}\n {{- raise_exception('System message must be at the beginning.') }}\n {%- endif %}\n {%- elif message.role == \"user\" %}\n {{- '<|im_start|>' + message.role + '\\n' + content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {%- set reasoning_content = '' %}\n {%- if message.reasoning_content is string %}\n {%- set reasoning_content = message.reasoning_content %}\n {%- else %}\n {%- if '</think>' in content %}\n {%- set reasoning_content = content.split('</think>')[0].rstrip('\\n').split('<think>')[-1].lstrip('\\n') %}\n {%- set content = content.split('</think>')[-1].lstrip('\\n') %}\n {%- endif %}\n {%- endif %}\n {%- set reasoning_content = reasoning_content|trim %}\n {%- if loop.index0 > ns.last_query_index %}\n {{- '<|im_start|>' + message.role + '\\n<think>\\n' + reasoning_content + '\\n</think>\\n\\n' + content }}\n {%- else %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- endif %}\n {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {%- if loop.first %}\n {%- if content|trim %}\n {{- '\\n\\n<tool_call>\\n<function=' + tool_call.name + '>\\n' }}\n {%- else %}\n {{- '<tool_call>\\n<function=' + tool_call.name + '>\\n' }}\n {%- endif %}\n {%- else %}\n {{- '\\n<tool_call>\\n<function=' + tool_call.name + '>\\n' }}\n {%- endif %}\n {%- if tool_call.arguments is defined %}\n {%- for args_name, args_value in tool_call.arguments|items %}\n {{- '<parameter=' + args_name + '>\\n' }}\n {%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}\n {{- args_value }}\n {{- '\\n</parameter>\\n' }}\n {%- endfor %}\n {%- endif %}\n {{- '</function>\\n</tool_call>' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.previtem and loop.previtem.role != \"tool\" %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- content }}\n {{- '\\n</tool_response>' }}\n {%- if not loop.last and loop.nextitem.role != \"tool\" %}\n {{- '<|im_end|>\\n' }}\n {%- elif loop.last %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- else %}\n {{- raise_exception('Unexpected message role.') }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n {%- if enable_thinking is defined and enable_thinking is false %}\n {{- '<think>\\n\\n</think>\\n\\n' }}\n {%- else %}\n {{- '<think>\\n' }}\n {%- endif %}\n{%- endif %}\n",
41
+ "tokenizer_class": "Qwen2Tokenizer"
42
+ }