chore: import upstream snapshot with attribution
Lint test / lint (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:58 +08:00
commit a203934033
1368 changed files with 175001 additions and 0 deletions
View File
+25
View File
@@ -0,0 +1,25 @@
def test_llm():
from swift import AppArguments, app_main
app_main(AppArguments(model='Qwen/Qwen2.5-0.5B-Instruct'))
def test_lora():
from swift import AppArguments, app_main
app_main(AppArguments(adapters='swift/test_lora', lang='en', studio_title='小黄'))
def test_mllm():
from swift import AppArguments, app_main
app_main(AppArguments(model='Qwen/Qwen2-VL-7B-Instruct', stream=True))
def test_audio():
from swift import AppArguments, DeployArguments, app_main, run_deploy
deploy_args = DeployArguments(model='Qwen/Qwen2-Audio-7B-Instruct', infer_backend='transformers', verbose=False)
with run_deploy(deploy_args, return_url=True) as url:
app_main(AppArguments(model='Qwen2-Audio-7B-Instruct', base_url=url, stream=True))
if __name__ == '__main__':
test_mllm()
+63
View File
@@ -0,0 +1,63 @@
def _test_client(port=8000):
import time
from swift.dataset import load_dataset
from swift.infer_engine import InferClient, InferRequest, RequestConfig
dataset = load_dataset(['AI-ModelScope/alpaca-gpt4-data-zh#1000'], num_proc=4)
infer_client = InferClient(port=port)
while True:
try:
infer_client.models
break
except Exception:
time.sleep(1)
pass
infer_requests = []
for data in dataset[0]:
infer_requests.append(InferRequest(**data))
request_config = RequestConfig(seed=42, max_tokens=256, temperature=0.8)
resp = infer_client.infer(infer_requests, request_config=request_config, use_tqdm=False)
print(len(resp))
def _test(infer_backend):
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
from swift.arguments import DeployArguments
from swift.pipelines import run_deploy
args = DeployArguments(model='Qwen/Qwen2-7B-Instruct', infer_backend=infer_backend, verbose=False)
with run_deploy(args) as port:
_test_client(port)
def test_vllm():
_test('vllm')
def test_lmdeploy():
_test('lmdeploy')
def test_pt():
_test('transformers')
def test_vllm_origin():
import subprocess
import sys
from modelscope import snapshot_download
model_dir = snapshot_download('Qwen/Qwen2-7B-Instruct')
args = [sys.executable, '-m', 'vllm.entrypoints.openai.api_server', '--model', model_dir]
process = subprocess.Popen(args)
_test_client()
process.terminate()
if __name__ == '__main__':
# test_vllm_origin()
# test_vllm()
test_lmdeploy()
# test_pt()
+116
View File
@@ -0,0 +1,116 @@
def _test_client(port: int, print_logprobs: bool = False, test_vlm: bool = False):
import aiohttp
import time
from pprint import pprint
from swift.infer_engine import InferClient, InferRequest, RequestConfig
infer_client = InferClient(port=port)
while True:
try:
models = infer_client.models
print(f'models: {models}')
except aiohttp.ClientConnectorError:
time.sleep(5)
continue
break
if test_vlm:
query = '这是什么'
# http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png
messages = [{
'role':
'user',
'content': [
{
'type': 'text',
'text': '这是什么'
},
{
'type': 'image_url',
'image_url': {
'url': 'cat.png'
}
},
]
}]
else:
query = '123*234=?'
messages = [{'role': 'user', 'content': query}]
infer_request = InferRequest(messages=messages)
request_config = RequestConfig(seed=42, max_tokens=256, temperature=0.8, logprobs=True, top_logprobs=5)
resp = infer_client.infer([infer_request], request_config=request_config)[0]
response = resp.choices[0].message.content
print(f'query: {query}')
print(f'response: {response}')
if print_logprobs:
pprint(resp.choices[0].logprobs)
request_config = RequestConfig(
stream=True, seed=42, max_tokens=256, temperature=0.8, top_k=20, top_p=0.8, logprobs=True, top_logprobs=5)
gen_list = infer_client.infer([infer_request], request_config=request_config)
print(f'query: {query}')
print('response: ', end='')
for chunk in gen_list[0]:
if chunk is None:
continue
print(chunk.choices[0].delta.content, end='', flush=True)
if print_logprobs and chunk.choices[0].logprobs is not None:
pprint(chunk.choices[0].logprobs)
print()
def _test(infer_backend, test_vlm: bool = False):
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
import multiprocessing
from swift import DeployArguments, deploy_main
mp = multiprocessing.get_context('spawn')
model = 'Qwen/Qwen2-VL-7B-Instruct' if test_vlm else 'Qwen/Qwen2-7B-Instruct'
args = DeployArguments(model=model, infer_backend=infer_backend, verbose=False)
process = mp.Process(target=deploy_main, args=(args, ))
process.start()
_test_client(args.port, True, test_vlm)
process.terminate()
def test_vllm_vlm():
_test('vllm', test_vlm=True)
def test_vllm():
_test('vllm')
def test_lmdeploy():
_test('lmdeploy')
def test_pt():
_test('transformers')
def test_vllm_origin():
import os
import subprocess
import sys
from modelscope import snapshot_download
model_dir = snapshot_download('Qwen/Qwen2-7B-Instruct')
args = [sys.executable, '-m', 'vllm.entrypoints.openai.api_server', '--model', model_dir]
process = subprocess.Popen(args)
_test_client(8000)
process.terminate()
if __name__ == '__main__':
# test_vllm_origin()
# test_vllm()
test_vllm_vlm()
# test_lmdeploy()
# test_pt()
+64
View File
@@ -0,0 +1,64 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
infer_backend = 'transformers'
def test_eval_native():
from swift import EvalArguments, eval_main
eval_main(
EvalArguments(
model='Qwen/Qwen2.5-0.5B-Instruct',
eval_dataset='arc',
infer_backend=infer_backend,
eval_backend='Native',
eval_limit=10,
eval_generation_config={
'max_new_tokens': 128,
'temperature': 0.1
},
extra_eval_args={'ignore_errors': False},
))
def test_eval_llm():
from swift import EvalArguments, eval_main
eval_main(
EvalArguments(
model='Qwen/Qwen2.5-0.5B-Instruct',
eval_dataset='arc_c',
infer_backend=infer_backend,
eval_backend='OpenCompass',
eval_limit=10))
def test_eval_mllm():
from swift import EvalArguments, eval_main
eval_main(
EvalArguments(
model='Qwen/Qwen2.5-VL-3B-Instruct',
eval_dataset=['realWorldQA'],
infer_backend='transformers',
eval_backend='VLMEvalKit',
eval_limit=10,
eval_generation_config={
'max_new_tokens': 128,
'temperature': 0.1
}))
def test_eval_url():
from swift import DeployArguments, EvalArguments, eval_main
from swift.pipelines import run_deploy
deploy_args = DeployArguments(model='Qwen/Qwen2-VL-7B-Instruct', infer_backend=infer_backend, verbose=False)
with run_deploy(deploy_args, return_url=True) as url:
eval_main(EvalArguments(model='Qwen2-VL-7B-Instruct', eval_url=url, eval_dataset=['arc']))
if __name__ == '__main__':
test_eval_llm()
# test_eval_mllm()
# test_eval_url()
# test_eval_native()
+104
View File
@@ -0,0 +1,104 @@
import os
from typing import Literal
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def test_llm_quant(quant_method: Literal['gptq', 'awq'] = 'awq'):
from swift import ExportArguments, export_main
export_main(
ExportArguments(
model='Qwen/Qwen2-7B-Instruct',
quant_bits=4,
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#1000', 'AI-ModelScope/alpaca-gpt4-data-en#1000'],
quant_method=quant_method))
def test_vlm_quant(quant_method: Literal['gptq', 'awq'] = 'awq'):
from swift import ExportArguments, export_main
export_main(
ExportArguments(
model='Qwen/Qwen2-VL-7B-Instruct',
quant_bits=4,
dataset=['modelscope/coco_2014_caption:validation#1000'],
quant_method=quant_method))
def test_audio_quant(quant_method: Literal['gptq', 'awq'] = 'awq'):
from swift import ExportArguments, export_main
export_main(
ExportArguments(
model='Qwen/Qwen2-Audio-7B-Instruct',
quant_bits=4,
dataset=['speech_asr/speech_asr_aishell1_trainsets:validation#1000'],
quant_method=quant_method))
def test_vlm_bnb_quant():
from swift import ExportArguments, InferArguments, export_main, infer_main
export_main(ExportArguments(model='Qwen/Qwen2-VL-7B-Instruct', quant_bits=4, quant_method='bnb'))
# infer_main(InferArguments(ckpt_dir='Qwen/Qwen2-VL-7B-Instruct-bnb-int4'))
def test_bert():
from swift import ExportArguments, export_main
output_dir = 'output/swift_test_bert_merged'
export_main(ExportArguments(adapters='swift/test_bert', merge_lora=True, output_dir=output_dir))
export_main(
ExportArguments(model=output_dir, load_data_args=True, quant_bits=4, quant_method='gptq', max_length=512))
def test_reward_model():
from swift import ExportArguments, export_main
export_main(
ExportArguments(
model='Shanghai_AI_Laboratory/internlm2-1_8b-reward',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#1000', 'AI-ModelScope/alpaca-gpt4-data-en#1000'],
quant_bits=4,
quant_method='gptq'))
def test_fp8():
from swift import ExportArguments, InferArguments, export_main, infer_main
export_main(ExportArguments(model='Qwen/Qwen2.5-3B-Instruct', quant_method='fp8'))
infer_main(InferArguments(model='Qwen2.5-3B-Instruct-fp8'))
def test_lora_merge_export_minimal():
from swift import ExportArguments, InferArguments, SftArguments, export_main, infer_main, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2-0.5B',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#20'],
max_steps=2,
per_device_train_batch_size=1,
gradient_accumulation_steps=1,
save_steps=2,
split_dataset_ratio=0.01,
tuner_type='lora',
logging_steps=1,
output_dir='output/test_lora_merge_export'))
last_model_checkpoint = result['last_model_checkpoint']
merge_output_dir = 'output/test_lora_merge_export_merged'
export_main(
ExportArguments(
adapters=last_model_checkpoint,
merge_lora=True,
output_dir=merge_output_dir,
exist_ok=True,
))
infer_main(InferArguments(model=merge_output_dir, load_data_args=True, max_batch_size=2))
if __name__ == '__main__':
# test_llm_quant('gptq')
# test_vlm_quant('gptq')
# test_audio_quant('gptq')
# test_vlm_bnb_quant()
# test_bert()
# test_reward_model()
test_fp8()
# test_lora_merge_export_minimal()
View File
+45
View File
@@ -0,0 +1,45 @@
def test_model_arch():
import random
from transformers import PretrainedConfig
from swift.model import MODEL_MAPPING
from swift.utils import JsonlWriter, safe_snapshot_download
jsonl_writer = JsonlWriter('model_arch.jsonl')
for i, (model_type, model_meta) in enumerate(MODEL_MAPPING.items()):
if i < 0:
continue
arch_list = model_meta.architectures
for model_group in model_meta.model_groups:
model = random.choice(model_group.models).ms_model_id
config_dict = None
try:
model_dir = safe_snapshot_download(model, download_model=False)
config_dict = PretrainedConfig.get_config_dict(model_dir)[0]
except Exception:
pass
finally:
msg = None
if config_dict:
arch = config_dict.get('architectures')
if arch and arch[0] not in arch_list:
msg = {
'model_type': model_type,
'model': model,
'config_arch': arch,
'architectures': arch_list
}
elif not arch and arch_list:
msg = {
'model_type': model_type,
'model': model,
'config_arch': arch,
'architectures': arch_list
}
else:
msg = {'msg': 'error', 'model_type': model_type, 'model': model, 'arch_list': arch_list}
if msg:
jsonl_writer.append(msg)
if __name__ == '__main__':
test_model_arch()
+207
View File
@@ -0,0 +1,207 @@
import unittest
from swift.dataset import EncodePreprocessor, MessagesPreprocessor, PackingDataset, load_dataset
from swift.model import get_processor
from swift.template import get_template
class TestDataPreprocess(unittest.TestCase):
"""Lightweight data preprocessing tests (no model forward/backward).
These are fast tests suitable for CI. They cover:
- SFT dataset encode (input_ids/labels)
- Truncation/max_length
- Data collator padding (attention_mask)
- Multi-turn messages
- Tool message
- Packing dataset
Why these tests are needed:
- Swift's data preprocessing pipeline is complex (template -> encode -> collate -> pack).
NPU training failures often stem from shape/mask/label mismatches before the model
even sees the data, not from operator issues.
- The original tests/general/test_dataset.py and test_template.py use top-level
functions and remote 7B models, so they are never run by unittest discovery
and are too heavy for CI.
"""
MODEL_PATH = 'Qwen/Qwen2-0.5B'
@classmethod
def setUpClass(cls):
cls.processor = get_processor(cls.MODEL_PATH)
cls.template = get_template(cls.processor)
cls.template.mode = 'train'
cls.template.init_processor(cls.processor)
def _encode_dataset(self, dataset):
encode_preprocessor = EncodePreprocessor(self.template)
return encode_preprocessor(dataset, num_proc=1, load_from_cache_file=False, strict=False)
def test_sft_dataset_encode(self):
dataset, _ = load_dataset(['AI-ModelScope/alpaca-gpt4-data-zh#20'], num_proc=1, strict=False)
self.assertGreater(len(dataset), 0)
encoded_dataset = self._encode_dataset(dataset)
first = encoded_dataset[0]
self.assertIn('input_ids', first)
self.assertIn('labels', first)
self.assertEqual(len(first['input_ids']), len(first['labels']))
def test_truncation_max_length(self):
self.template.max_length = 128
dataset, _ = load_dataset(['AI-ModelScope/alpaca-gpt4-data-zh#20'], num_proc=1, strict=False)
encoded_dataset = self._encode_dataset(dataset)
for row in encoded_dataset:
self.assertLessEqual(len(row['input_ids']), self.template.max_length)
self.template.max_length = None
def test_data_collator_padding(self):
dataset, _ = load_dataset(['AI-ModelScope/alpaca-gpt4-data-zh#20'], num_proc=1, strict=False)
encoded_dataset = self._encode_dataset(dataset)
batch = [encoded_dataset[i] for i in range(4)]
collated = self.template.data_collator(batch)
self.assertIn('input_ids', collated)
self.assertIn('labels', collated)
self.assertIn('attention_mask', collated)
self.assertEqual(collated['input_ids'].shape[0], 4)
def test_multi_turn_messages(self):
multi_turn_row = {
'messages': [
{
'role': 'user',
'content': 'What is Python?'
},
{
'role': 'assistant',
'content': 'Python is a programming language.'
},
{
'role': 'user',
'content': 'What are its advantages?'
},
{
'role': 'assistant',
'content': 'Python is easy to learn and use.'
},
]
}
encoded = self.template.encode(multi_turn_row, return_length=True)
self.assertIn('input_ids', encoded)
self.assertIn('labels', encoded)
self.assertGreater(len(encoded['input_ids']), 0)
self.assertEqual(len(encoded['input_ids']), len(encoded['labels']))
def test_tool_message(self):
tool_row = {
'messages': [
{
'role': 'user',
'content': 'What is the weather in Beijing?'
},
{
'role':
'assistant',
'content':
'',
'tool_calls': [{
'type': 'function',
'function': {
'name': 'get_weather',
'arguments': '{"city": "Beijing"}'
}
}]
},
{
'role': 'tool',
'content': '{"temperature": 25, "condition": "sunny"}'
},
{
'role': 'assistant',
'content': 'The weather in Beijing is sunny with a temperature of 25 degrees.'
},
]
}
encoded = self.template.encode(tool_row, return_length=True)
self.assertIn('input_ids', encoded)
self.assertIn('labels', encoded)
self.assertGreater(len(encoded['input_ids']), 0)
def test_packing_dataset(self):
dataset, _ = load_dataset(['AI-ModelScope/alpaca-gpt4-data-zh#20'], num_proc=1, strict=False)
encoded_dataset = self._encode_dataset(dataset)
packing_dataset = PackingDataset(
self.template,
encoded_dataset,
num_proc=1,
strict=False,
load_from_cache_file=False,
packing_length=512,
packing_num_proc=1,
)
self.assertGreater(len(packing_dataset), 0)
packed = packing_dataset[0]
self.assertIsInstance(packed, list)
self.assertGreater(len(packed), 0)
self.assertIn('input_ids', packed[0])
self.assertIn('labels', packed[0])
class TestRejectedMessagesPreprocess(unittest.TestCase):
"""MessagesPreprocessor handling of rejected_messages (no model required)."""
def test_empty_rejected_messages_does_not_crash(self):
"""A DPO row whose rejected_messages repair to empty must not crash.
The recursive preprocess() call returns None when rejected_messages is
empty (the same graceful-skip path used for the main messages list), so
subscripting it with ['messages'] raised TypeError and aborted the whole
dataset map. Downstream already treats rejected_messages is None as
'no rejected', so the row should fall back to None instead.
"""
row = {
'messages': [
{
'role': 'user',
'content': 'Q'
},
{
'role': 'assistant',
'content': 'good'
},
],
'rejected_messages': [],
}
result = MessagesPreprocessor().preprocess(row)
self.assertIsNotNone(result)
self.assertIsNone(result['rejected_messages'])
def test_valid_rejected_messages_preserved(self):
row = {
'messages': [
{
'role': 'user',
'content': 'Q'
},
{
'role': 'assistant',
'content': 'good'
},
],
'rejected_messages': [
{
'role': 'user',
'content': 'Q'
},
{
'role': 'assistant',
'content': 'bad'
},
],
}
result = MessagesPreprocessor().preprocess(row)
self.assertEqual(result['rejected_messages'][-1]['content'], 'bad')
if __name__ == '__main__':
unittest.main()
+90
View File
@@ -0,0 +1,90 @@
from typing import List
from swift.dataset import load_dataset
def _test_dataset(datasets: List[str], num_proc: int = 1, strict: bool = False, **kwargs):
dataset = load_dataset(datasets, num_proc=num_proc, strict=strict, **kwargs)
print(f'dataset[0]: {dataset[0]}')
print(f'dataset[1]: {dataset[1]}')
def test_sft():
# swift/SlimOrca swift/cosmopedia-100k
# _test_dataset(['lvjianjin/AdvertiseGen'])
# _test_dataset(['AI-ModelScope/Duet-v0.5'])
# _test_dataset(['swift/SlimOrca', 'swift/cosmopedia-100k'])
# _test_dataset(['OmniData/Zhihu-KOL-More-Than-100-Upvotes'])
# _test_dataset(['OmniData/Zhihu-KOL'])
_test_dataset([
'AI-ModelScope/alpaca-gpt4-data-zh#1000', 'AI-ModelScope/alpaca-gpt4-data-en#1000',
'AI-ModelScope/LongAlpaca-12k#1000'
])
# _test_dataset(['swift/Infinity-Instruct:all'])
# _test_dataset(['swift/sharegpt:all'])
# _test_dataset(['AI-ModelScope/sharegpt_gpt4:all'])
# _test_dataset(['iic/ms_bench'])
# _test_dataset(['swift/tagengo-gpt4'])
def test_mllm():
# _test_dataset(['AI-ModelScope/ShareGPT4V:all'])
# _test_dataset(['AI-ModelScope/LLaVA-Pretrain'])
# _test_dataset(['swift/TextCaps'])
# _test_dataset(['swift/RLAIF-V-Dataset:all'])
# _test_dataset(['swift/OK-VQA_train'])
# _test_dataset(['swift/OCR-VQA'])
# _test_dataset(['swift/A-OKVQA'])
# _test_dataset(['AI-ModelScope/MovieChat-1K-test'])
_test_dataset([
'AI-ModelScope/LaTeX_OCR:all', 'modelscope/coco_2014_caption:validation',
'speech_asr/speech_asr_aishell1_trainsets:validation'
],
strict=False)
# _test_dataset(['swift/VideoChatGPT:all'])
# _test_dataset(['speech_asr/speech_asr_aishell1_trainsets:validation'])
# _test_dataset(['AI-ModelScope/captcha-images'])
# _test_dataset(['swift/gpt4v-dataset:all'])
# _test_dataset(['modelscope/coco_2014_caption:validation'])
# _test_dataset(['AI-ModelScope/LLaVA-Instruct-150K'], num_proc=16)
def test_agent():
_test_dataset(['swift/ToolBench'])
# _test_dataset(['AI-ModelScope/ms_agent_for_agentfabric:all'])
def test_dpo():
_test_dataset(['AI-ModelScope/orpo-dpo-mix-40k'])
_test_dataset(['AI-ModelScope/hh-rlhf:all'])
_test_dataset(['AI-ModelScope/hh_rlhf_cn:all'])
_test_dataset(['hjh0119/shareAI-Llama3-DPO-zh-en-emoji:all'])
def test_kto():
_test_dataset(['AI-ModelScope/ultrafeedback-binarized-preferences-cleaned-kto'])
def test_pretrain():
_test_dataset(['AI-ModelScope/ruozhiba:all'])
def test_dataset_info():
_test_dataset(['swift/self-cognition#500'], model_name='xiao huang', model_author='swift')
# _test_dataset(['codefuse-ai/CodeExercise-Python-27k'])
def test_cls():
_test_dataset(['simpleai/HC3-Chinese:baike'])
_test_dataset(['simpleai/HC3-Chinese:baike_cls'])
if __name__ == '__main__':
# test_sft()
# test_agent()
# test_dpo()
# test_kto()
test_mllm()
# test_pretrain()
# test_dataset_info()
# test_cls()
+53
View File
@@ -0,0 +1,53 @@
import os
import torch
import unittest
from swift.utils import get_device
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
def test_qwen2():
import os
from swift.model import get_model_processor
model, tokenizer = get_model_processor('Qwen/Qwen2-7B-Instruct', load_model=False)
print(f'model: {model}, tokenizer: {tokenizer}')
# test hf
model, tokenizer = get_model_processor('Qwen/Qwen2-7B-Instruct', load_model=False, use_hf=True)
model, tokenizer = get_model_processor(
'Qwen/Qwen2-7B-Instruct', torch_dtype=torch.float32, device_map=get_device(), attn_impl='flash_attn')
print(f'model: {model}, tokenizer: {tokenizer}')
def test_modelscope_hub():
from swift.model import get_model_processor
model, tokenizer = get_model_processor('Qwen/Qwen2___5-Math-1___5B-Instruct/', load_model=False)
class TestMolmo2Registration(unittest.TestCase):
def test_registration(self):
from swift.model import MODEL_MAPPING, MLLMModelType
from swift.template import TEMPLATE_MAPPING, TemplateType
model_meta = MODEL_MAPPING[MLLMModelType.molmo2]
self.assertEqual(model_meta.template, TemplateType.molmo2)
self.assertEqual(model_meta.model_arch.arch_name, 'molmo')
self.assertIn('Molmo2ForConditionalGeneration', model_meta.architectures)
hf_model_ids = []
for group in model_meta.model_groups:
for model in group.models:
hf_model_ids.append(model.hf_model_id)
self.assertIn('allenai/Molmo2-4B', hf_model_ids)
self.assertIn('allenai/Molmo2-8B', hf_model_ids)
self.assertIn('allenai/Molmo2-O-7B', hf_model_ids)
self.assertIn(TemplateType.molmo2, TEMPLATE_MAPPING)
if __name__ == '__main__':
test_qwen2()
# test_modelscope_hub()
+20
View File
@@ -0,0 +1,20 @@
from swift.dataset import load_dataset
def test_local_dataset():
# please use git clone
from swift.utils import git_clone_github
model_dir = git_clone_github('https://www.modelscope.cn/datasets/swift/swift-sft-mixture.git')
dataset = load_dataset(datasets=[f'{model_dir}:firefly'], streaming=True)[0]
print(next(iter(dataset)))
def test_hub_dataset():
local_dataset = 'swift/swift-sft-mixture:firefly'
dataset = load_dataset(datasets=[local_dataset], streaming=True)[0]
print(next(iter(dataset)))
if __name__ == '__main__':
test_local_dataset()
# test_hub_dataset()
+79
View File
@@ -0,0 +1,79 @@
from swift.dataset import EncodePreprocessor, load_dataset
from swift.model import get_processor
from swift.template import TemplateInputs, get_template
def test_template():
tokenizer = get_processor('Qwen/Qwen2-7B-Instruct')
template = get_template(tokenizer)
template_inputs = TemplateInputs.from_dict({
'messages': [{
'role': 'system',
'content': 'AAA'
}, {
'role': 'user',
'content': 'BBB'
}, {
'role': 'assistant',
'content': 'CCC'
}, {
'role': 'user',
'content': 'DDD'
}]
})
inputs = template.encode(template_inputs)
print(f'inputs.keys(): {inputs.keys()}')
print(tokenizer.decode(inputs['input_ids']))
def test_mllm():
processor = get_processor('Qwen/Qwen2-VL-7B-Instruct')
template = get_template(processor)
template_inputs = TemplateInputs(
chosen={
'messages': [{
'role': 'system',
'content': 'AAA'
}, {
'role': 'user',
'content': '<image>BBB'
}, {
'role': 'assistant',
'content': 'CCC'
}, {
'role': 'user',
'content': 'DDD'
}],
'images': ['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png']
})
inputs = template.encode(template_inputs)
print(f'inputs.keys(): {inputs.keys()}')
print(template.safe_decode(inputs['input_ids']))
def _test_dataset_map(model_id: str, dataset_id: str):
tokenizer = get_processor(model_id)
template = get_template(tokenizer)
dataset = load_dataset([dataset_id], num_proc=2)[0]
# 1: 1500
# 16: 10766.36 examples/s
new_dataset = EncodePreprocessor(template)(dataset, num_proc=4)
print(f'new_dataset: {new_dataset}')
print(template.safe_decode(new_dataset[0]['input_ids']))
print(template.safe_decode(new_dataset[1]['input_ids']))
def test_llm_dataset_map():
_test_dataset_map('Qwen/Qwen2-7B-Instruct', 'AI-ModelScope/alpaca-gpt4-data-zh')
def test_mllm_dataset_map():
_test_dataset_map('Qwen/Qwen2-VL-7B-Instruct', 'modelscope/coco_2014_caption:validation#100')
if __name__ == '__main__':
test_template()
test_mllm()
test_llm_dataset_map()
test_mllm_dataset_map()
+22
View File
@@ -0,0 +1,22 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from swift.template import TemplateMeta
def test_replace_system_preserves_non_string_elements():
"""_replace_system must not drop list elements like ['bos_token_id'].
Templates such as ziya, bluelm and emu3_chat use
``prefix=[['bos_token_id'], '{{SYSTEM}}']``. When no system message is
provided the prefix is produced by _replace_system, which should keep every
non-string element intact and only strip the placeholder from strings.
"""
meta = TemplateMeta(
template_type='_test_replace_system_bug',
prefix=[['bos_token_id'], '{{SYSTEM}}'],
prompt=['{{QUERY}}'],
chat_sep=['\n'],
)
# __post_init__ moves prefix to system_prefix and builds a no-system prefix
# via _replace_system. The list element must survive.
assert any(isinstance(p, list) for p in meta.prefix), (f'_replace_system dropped the bos_token_id list; '
f'meta.prefix={meta.prefix!r}')
View File
+23
View File
@@ -0,0 +1,23 @@
import os
import shutil
import tempfile
import unittest
from modelscope import Model, check_local_model_is_latest
class TestCheckModel(unittest.TestCase):
def setUp(self):
print(('Testing %s.%s' % (type(self).__name__, self._testMethodName)))
self.tmp_dir = tempfile.TemporaryDirectory().name
if not os.path.exists(self.tmp_dir):
os.makedirs(self.tmp_dir)
def tearDown(self):
import peft
shutil.rmtree(self.tmp_dir)
super().tearDown()
def test_check_model(self):
model = Model.from_pretrained('damo/nlp_corom_sentence-embedding_chinese-base', revision='v1.0.0')
self.assertFalse(check_local_model_is_latest(model.model_dir))
View File
+28
View File
@@ -0,0 +1,28 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'save_steps': 50,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_sft():
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
from swift import SftArguments, sft_main
sft_main(SftArguments(model='Qwen/Qwen2-7B-Instruct', dataset=['iic/ms_agent#2000'], loss_scale='react', **kwargs))
def test_infer():
from swift import InferArguments, infer_main
ckpt_dir = 'output/Qwen2-7B-Instruct/vx-xxx/checkpoint-xxx'
infer_main(InferArguments(adapters=[ckpt_dir]))
if __name__ == '__main__':
test_sft()
# test_infer()
+70
View File
@@ -0,0 +1,70 @@
import os
import torch
from typing import Literal
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def _prepare(infer_backend: Literal['vllm', 'transformers', 'lmdeploy']):
from swift.infer_engine import InferRequest
if infer_backend == 'lmdeploy':
from swift.infer_engine import LmdeployEngine
engine = LmdeployEngine('OpenGVLab/InternVL2_5-2B', torch_dtype=torch.float32)
elif infer_backend == 'transformers':
from swift.infer_engine import TransformersEngine
engine = TransformersEngine('Qwen/Qwen2-7B-Instruct', max_batch_size=16)
elif infer_backend == 'vllm':
from swift.infer_engine import VllmEngine
engine = VllmEngine('Qwen/Qwen2-7B-Instruct')
infer_requests = [
# InferRequest([{'role': 'user', 'content': '晚上睡不着觉怎么办'}]) for i in range(100)
InferRequest([{
'role': 'user',
'content': 'hello! who are you'
}]) for i in range(100)
]
return engine, infer_requests
def test_infer(infer_backend):
from swift.infer_engine import RequestConfig
from swift.metrics import InferStats
engine, infer_requests = _prepare(infer_backend=infer_backend)
request_config = RequestConfig(temperature=0)
infer_stats = InferStats()
response_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
for response in response_list[:2]:
print(response.choices[0].message.content)
print(infer_stats.compute())
def test_stream(infer_backend):
from swift.infer_engine import RequestConfig
from swift.metrics import InferStats
engine, infer_requests = _prepare(infer_backend=infer_backend)
infer_stats = InferStats()
request_config = RequestConfig(temperature=0, stream=True, logprobs=True)
gen_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
for response in gen_list[0]:
if response is None:
continue
print(response.choices[0].delta.content, end='', flush=True)
print()
print(infer_stats.compute())
gen_list = engine.infer(infer_requests, request_config=request_config, use_tqdm=True, metrics=[infer_stats])
for response in gen_list[0]:
pass
print(infer_stats.compute())
if __name__ == '__main__':
test_infer('transformers')
# test_stream('transformers')
+69
View File
@@ -0,0 +1,69 @@
import os
import torch
from typing import Literal
if __name__ == '__main__':
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def _prepare(infer_backend: Literal['vllm', 'transformers', 'lmdeploy']):
from swift.infer_engine import InferRequest
if infer_backend == 'lmdeploy':
from swift.infer_engine import LmdeployEngine
engine = LmdeployEngine('Qwen/Qwen2-7B-Instruct', torch_dtype=torch.float32)
elif infer_backend == 'transformers':
from swift.infer_engine import TransformersEngine
engine = TransformersEngine('Qwen/Qwen2-7B-Instruct')
elif infer_backend == 'vllm':
from swift.infer_engine import VllmEngine
engine = VllmEngine('Qwen/Qwen2-7B-Instruct')
infer_requests = [
InferRequest([{
'role': 'user',
'content': '晚上睡不着觉怎么办'
}]),
InferRequest([{
'role': 'user',
'content': 'hello! who are you'
}])
]
return engine, infer_requests
def test_infer(engine, infer_requests):
from swift.infer_engine import RequestConfig
from swift.metrics import InferStats
request_config = RequestConfig(temperature=0, logprobs=True, top_logprobs=2)
infer_stats = InferStats()
response_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
for response in response_list[:2]:
print(response.choices[0].message.content)
print(infer_stats.compute())
def test_stream(engine, infer_requests):
from swift.infer_engine import RequestConfig
from swift.metrics import InferStats
infer_stats = InferStats()
request_config = RequestConfig(temperature=0, stream=True, logprobs=True, top_logprobs=2)
gen_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
for response in gen_list[0]:
if response is None:
continue
print(response.choices[0].delta.content, end='', flush=True)
print(infer_stats.compute())
if __name__ == '__main__':
engine, infer_requests = _prepare(infer_backend='transformers')
test_infer(engine, infer_requests)
test_stream(engine, infer_requests)
+76
View File
@@ -0,0 +1,76 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def test_cli(infer_backend):
from swift import InferArguments, infer_main
args = InferArguments(model='Qwen/Qwen2-VL-7B-Instruct', infer_backend=infer_backend)
infer_main(args)
def test_cli_jinja(infer_backend):
from swift import InferArguments, infer_main
args = InferArguments(model='Qwen/Qwen2-VL-7B-Instruct', infer_backend=infer_backend, template_backend='jinja')
infer_main(args)
def test_dataset(infer_backend):
from swift import InferArguments, infer_main
args = InferArguments(
model='Qwen/Qwen2-7B-Instruct',
infer_backend=infer_backend,
val_dataset=['AI-ModelScope/alpaca-gpt4-data-zh#10'],
stream=True)
infer_main(args)
def test_mllm_dataset(infer_backend):
from swift import InferArguments, infer_main
args = InferArguments(
model='Qwen/Qwen2-VL-7B-Instruct',
infer_backend=infer_backend,
val_dataset=['modelscope/coco_2014_caption:validation#1000'],
stream=True)
infer_main(args)
def test_dataset_ddp():
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3'
from swift import InferArguments, infer_main
args = InferArguments(
model='Qwen/Qwen2-7B-Instruct', max_batch_size=64, val_dataset=['AI-ModelScope/alpaca-gpt4-data-zh#1000'])
infer_main(args)
def test_dataset_mp_ddp():
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3'
from swift import InferArguments, infer_main
args = InferArguments(
model='Qwen/Qwen2-7B-Instruct', max_batch_size=64, val_dataset=['AI-ModelScope/alpaca-gpt4-data-zh#1000'])
infer_main(args)
def test_emu3_gen(infer_backend):
from swift import InferArguments, infer_main
args = InferArguments(
model='BAAI/Emu3-Gen',
infer_backend=infer_backend,
stream=False,
use_chat_template=False,
top_k=2048,
max_new_tokens=40960)
infer_main(args)
if __name__ == '__main__':
# test_cli('transformers')
# test_cli_jinja('transformers')
# test_dataset('transformers')
# test_mllm_dataset('transformers')
# test_dataset_ddp()
# test_dataset_mp_ddp()
test_emu3_gen('transformers')
+10
View File
@@ -0,0 +1,10 @@
from swift import InferArguments, infer_main
def test_max_memory():
infer_main(
InferArguments(model='Qwen/Qwen2.5-7B-Instruct', max_memory='{0: "50GB", 1: "5GB"}', device_map='sequential'))
if __name__ == '__main__':
test_max_memory()
+76
View File
@@ -0,0 +1,76 @@
import os
import torch
from typing import Literal
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def _prepare(infer_backend: Literal['vllm', 'transformers', 'lmdeploy']):
from swift.infer_engine import InferRequest
if infer_backend == 'lmdeploy':
from swift.infer_engine import LmdeployEngine
engine = LmdeployEngine('Qwen/Qwen-VL-Chat', torch_dtype=torch.float32)
elif infer_backend == 'transformers':
from swift.infer_engine import TransformersEngine
engine = TransformersEngine('Qwen/Qwen2-VL-7B-Instruct')
elif infer_backend == 'vllm':
from swift.infer_engine import VllmEngine
engine = VllmEngine('Qwen/Qwen2-VL-7B-Instruct')
infer_requests = [
InferRequest([{
'role': 'user',
'content': '晚上睡不着觉怎么办'
}]),
InferRequest([{
'role':
'user',
'content': [{
'type': 'image_url',
'image_url': 'http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png'
}]
}])
]
return engine, infer_requests
def test_infer(engine, infer_requests):
from swift.infer_engine import RequestConfig
from swift.metrics import InferStats
request_config = RequestConfig(temperature=0)
infer_stats = InferStats()
response_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
for response in response_list[:2]:
print(response.choices[0].message.content)
print(infer_stats.compute())
def test_stream(engine, infer_requests):
from swift.infer_engine import RequestConfig
from swift.metrics import InferStats
infer_stats = InferStats()
request_config = RequestConfig(temperature=0, stream=True, logprobs=True)
gen_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
for response in gen_list[0]:
if response is None:
continue
print(response.choices[0].delta.content, end='', flush=True)
print()
print(infer_stats.compute())
gen_list = engine.infer(infer_requests, request_config=request_config, use_tqdm=True, metrics=[infer_stats])
for response in gen_list[0]:
pass
print(infer_stats.compute())
if __name__ == '__main__':
engine, infer_requests = _prepare(infer_backend='transformers')
test_infer(engine, infer_requests)
test_stream(engine, infer_requests)
+55
View File
@@ -0,0 +1,55 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def test_engine():
from swift.dataset import load_dataset
from swift.infer_engine import RequestConfig, SglangEngine
dataset = load_dataset('AI-ModelScope/alpaca-gpt4-data-zh#20')[0]
engine = SglangEngine('Qwen/Qwen2.5-0.5B-Instruct')
request_config = RequestConfig(max_tokens=1024)
resp_list = engine.infer(list(dataset), request_config=request_config)
for resp in resp_list[:5]:
print(resp)
resp_list = engine.infer(list(dataset), request_config=request_config)
for resp in resp_list[:5]:
print(resp)
def test_engine_stream():
from swift.dataset import load_dataset
from swift.infer_engine import RequestConfig, SglangEngine
dataset = load_dataset('AI-ModelScope/alpaca-gpt4-data-zh#1')[0]
engine = SglangEngine('Qwen/Qwen2.5-0.5B-Instruct')
request_config = RequestConfig(max_tokens=1024, stream=True)
gen_list = engine.infer(list(dataset), request_config=request_config)
for resp in gen_list[0]:
if resp is None:
continue
print(resp.choices[0].delta.content, flush=True, end='')
def test_infer():
from swift import InferArguments, infer_main
infer_main(
InferArguments(model='Qwen/Qwen2.5-0.5B-Instruct', stream=True, infer_backend='sglang', max_new_tokens=2048))
def test_eval():
from swift import EvalArguments, eval_main
eval_main(
EvalArguments(
model='Qwen/Qwen2-7B-Instruct',
eval_dataset='arc_c',
infer_backend='sglang',
eval_backend='OpenCompass',
))
if __name__ == '__main__':
test_engine()
# test_engine_stream()
# test_infer()
# test_eval()
+73
View File
@@ -0,0 +1,73 @@
import os
from swift import TransformersEngine
from swift.infer_engine import InferRequest, RequestConfig
from swift.metrics import InferStats
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
engine = TransformersEngine('Qwen/Qwen2-0.5B', max_batch_size=4)
def test_batch_infer():
infer_requests = [InferRequest([{'role': 'user', 'content': 'hello, who are you?'}]) for _ in range(4)]
request_config = RequestConfig(temperature=0, max_tokens=32)
infer_stats = InferStats()
response_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
assert len(response_list) == len(infer_requests)
for response in response_list:
assert len(response.choices) > 0
assert response.choices[0].message.content is not None
stats = infer_stats.compute()
assert stats['num_samples'] > 0
assert stats['num_generated_tokens'] > 0
def test_stream_infer():
infer_requests = [InferRequest([{'role': 'user', 'content': 'What is 1+1? Answer briefly.'}])]
request_config = RequestConfig(temperature=0, max_tokens=32, stream=True)
infer_stats = InferStats()
gen_list = engine.infer(infer_requests, request_config=request_config, metrics=[infer_stats])
full_content = ''
for chunk in gen_list[0]:
if chunk is None:
continue
delta = chunk.choices[0].delta.content
if delta:
full_content += delta
assert len(full_content) > 0, 'Stream infer produced no content'
stats = infer_stats.compute()
assert stats['num_samples'] > 0
assert stats['num_generated_tokens'] > 0
def test_single_infer_with_system():
infer_requests = [
InferRequest([{
'role': 'system',
'content': 'You are a helpful assistant.'
}, {
'role': 'user',
'content': 'Say hello in one word.'
}])
]
request_config = RequestConfig(temperature=0, max_tokens=16)
response_list = engine.infer(infer_requests, request_config=request_config)
assert len(response_list) == 1
assert len(response_list[0].choices) > 0
assert response_list[0].choices[0].message.content is not None
if __name__ == '__main__':
test_batch_infer()
test_stream_infer()
test_single_infer_with_system()
View File
+5
View File
@@ -0,0 +1,5 @@
{
"ckpt_dir": "/mnt/workspace/yzhao/modelscope/swift/output/pai_test/checkpoint-6",
"val_dataset_sample": 2,
"load_dataset_config": true
}
+7
View File
@@ -0,0 +1,7 @@
{
"model_type": "qwen-1_8b-chat",
"dataset": "jd-sentiment-zh",
"output_dir": "output/pai_test",
"train_dataset_sample": 100,
"eval_steps": 5
}
+4
View File
@@ -0,0 +1,4 @@
system,instruction,input,output
00000,11111,22222,3.3
,aaaaa,,ccccc
,AAAAA,BBBBB,CCCCC
1 system instruction input output
2 00000 11111 22222 3.3
3 aaaaa ccccc
4 AAAAA BBBBB CCCCC
+3
View File
@@ -0,0 +1,3 @@
{"instruction": "11111", "input": "22222", "output": "33333", "history": [["aaaaa", "bbbbb"]], "system": "system123"}
{"instruction": "aaaaa", "output": "ccccc"}
{"instruction": "AAAAA", "input": "BBBBB", "output": "CCCCC"}
+4
View File
@@ -0,0 +1,4 @@
instruction,output
11111,33333
aaaaa,ccccc
AAAAA,CCCCC
1 instruction output
2 11111 33333
3 aaaaa ccccc
4 AAAAA CCCCC
+3
View File
@@ -0,0 +1,3 @@
{"messages": [{"role": "system", "content": "00000"}, {"role": "user", "content": "11111"}, {"role": "assistant", "content": "22222"}]}
{"messages": [{"role": "user", "content": "aaaaa"}, {"role": "assistant", "content": "bbbbb"}, {"role": "user", "content": "ccccc"}, {"role": "assistant", "content": "ddddd"}]}
{"messages": [{"role": "user", "content": "AAAAA"}, {"role": "assistant", "content": "BBBBB"}, {"role": "user", "content": "CCCCC"}, {"role": "assistant", "content": "DDDDD"}]}
+3
View File
@@ -0,0 +1,3 @@
{"conversations": [{"from": "system", "value": "00000"}, {"from": "user", "value": "11111"}, {"from": "assistant", "value": "22222"}]}
{"conversations": [{"from": "user", "value": "aaaaa"}, {"from": "assistant", "value": "bbbbb"}, {"from": "user", "value": "ccccc"}, {"from": "assistant", "value": "ddddd"}]}
{"conversations": [{"from": "user", "value": "AAAAA"}, {"from": "assistant", "value": "BBBBB"}, {"from": "user", "value": "CCCCC"}, {"from": "assistant", "value": "DDDDD"}]}
+3
View File
@@ -0,0 +1,3 @@
{"query": "<img>https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg</img>55555", "response": "66666"}
{"query": "<img>https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg</img><img>https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg</img>eeeee", "response": "fffff", "history": [["hello", "123"]]}
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]]}
+3
View File
@@ -0,0 +1,3 @@
{"query": "55555", "response": "66666", "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
{"query": "eeeee", "response": "fffff", "history": [], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg", "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg", "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
+3
View File
@@ -0,0 +1,3 @@
{"query": "55555", "response": "66666", "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
{"query": "eeeee", "response": "fffff", "history": [], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
+3
View File
@@ -0,0 +1,3 @@
{"system": "00000", "conversation": [{"human": "11111", "assistant": "22222"}]}
{"conversation": [{"human": "aaaaa", "assistant": "bbbbb"}]}
{"conversation": [{"human": "AAAAA", "assistant": "BBBBB"}, {"human": "CCCCC", "assistant": "DDDDD"}, {"human": "EEEEE", "assistant": "FFFFF"}]}
+3
View File
@@ -0,0 +1,3 @@
[{"system": "00000", "query": "55555", "response": "66666"},
{"query": "eeeee", "response": "fffff", "history": []},
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]]}]
+3
View File
@@ -0,0 +1,3 @@
{"system": "00000", "query": "55555", "response": "66666"}
{"query": "eeeee", "response": "fffff", "history": []}
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]]}
+4
View File
@@ -0,0 +1,4 @@
response
11111
aaaaa
AAAAA
1 response
2 11111
3 aaaaa
4 AAAAA
+3
View File
@@ -0,0 +1,3 @@
{"response": "11111"}
{"response": "aaaaa"}
{"response": "AAAAA"}
+4
View File
@@ -0,0 +1,4 @@
system,query,response
00000,11111,22222
,aaaaa,bbbbb
,AAAAA,BBBBB
1 system query response
2 00000 11111 22222
3 aaaaa bbbbb
4 AAAAA BBBBB
+3
View File
@@ -0,0 +1,3 @@
{"system": "00000", "query": "11111", "response": "22222"}
{"query": "aaaaa", "response": "bbbbb"}
{"query": "AAAAA", "response": "BBBBB"}
+73
View File
@@ -0,0 +1,73 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import torch
import unittest
from typing import Any, Dict, Optional
from swift.dataset import DatasetMeta, ResponsePreprocessor, load_dataset, register_dataset
from swift.infer_engine import InferRequest, RequestConfig, TransformersEngine
from swift.model import Model, ModelGroup, ModelMeta, register_model
from swift.template import TemplateMeta, register_template
class CustomPreprocessor(ResponsePreprocessor):
prompt = """Task: Based on the given two sentences, provide a similarity score between 0.0 and 5.0.
Sentence 1: {text1}
Sentence 2: {text2}
Similarity score: """
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
return super().preprocess({
'query': self.prompt.format(text1=row['text1'], text2=row['text2']),
'response': f"{row['label']:.1f}"
})
register_dataset(
DatasetMeta(
ms_dataset_id='swift/stsb',
hf_dataset_id='SetFit/stsb',
preprocess_func=CustomPreprocessor(),
))
register_template(
TemplateMeta(
template_type='custom',
prefix=['<extra_id_0>System\n{{SYSTEM}}\n'],
prompt=['<extra_id_1>User\n{{QUERY}}\n<extra_id_1>Assistant\n'],
chat_sep=['\n']))
register_model(
ModelMeta(
model_type='custom',
model_groups=[
ModelGroup([Model('AI-ModelScope/Nemotron-Mini-4B-Instruct', 'nvidia/Nemotron-Mini-4B-Instruct')])
],
template='custom',
ignore_patterns=['nemo']))
class TestCustom(unittest.TestCase):
def test_custom_model(self):
infer_request = InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}])
request_config = RequestConfig(max_tokens=512, temperature=0)
engine = TransformersEngine('AI-ModelScope/Nemotron-Mini-4B-Instruct', torch_dtype=torch.float16)
response = engine.infer([infer_request], request_config)
swift_response = response[0].choices[0].message.content
engine.template.template_backend = 'jinja'
response = engine.infer([infer_request], request_config)
jinja_response = response[0].choices[0].message.content
assert swift_response == jinja_response, (f'swift_response: {swift_response}\njinja_response: {jinja_response}')
print(f'response: {swift_response}')
def test_custom_dataset(self):
dataset = load_dataset(['swift/stsb'])[0]
assert len(dataset) == 5749
assert list(dataset[0].keys()) == ['messages', 'dataset']
print(f'dataset: {dataset}')
print(f'dataset[0]: {dataset[0]}')
if __name__ == '__main__':
unittest.main()
+19
View File
@@ -0,0 +1,19 @@
import unittest
from swift.dataset import load_dataset
class TestDataset(unittest.TestCase):
def test_load_v_dataset(self):
if not __name__ == '__main__':
# ignore citest error in github
return
for ds in ['m3it#1000', 'mantis-instruct#1000', 'llava-med-zh-instruct#1000']:
ds = load_dataset(ds)
assert len(ds[0]) > 800
if __name__ == '__main__':
unittest.main()
+80
View File
@@ -0,0 +1,80 @@
import os
import shutil
import tempfile
import transformers
import unittest
from packaging import version
from swift import ExportArguments, export_main
if __name__ == '__main__':
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
class TestTemplate(unittest.TestCase):
def setUp(self):
print(('Testing %s.%s' % (type(self).__name__, self._testMethodName)))
self.tmp_dir = tempfile.TemporaryDirectory().name
def tearDown(self):
if os.path.exists(self.tmp_dir):
shutil.rmtree(self.tmp_dir)
super().tearDown()
@unittest.skip('swift2.0')
def test_llama3(self):
args = ExportArguments(model_type='llama3-8b-instruct', to_ollama=True, ollama_output_dir=self.tmp_dir)
export_main(args)
template = ('TEMPLATE """{{ if .System }}<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\n'
'{{ .System }}<|eot_id|>{{ else }}<|begin_of_text|>{{ end }}{{ if .Prompt }}<|start_header_id|>user'
'<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n'
'{{ end }}{{ .Response }}<|eot_id|>"""')
stop = 'PARAMETER stop "<|eot_id|>"'
with open(os.path.join(self.tmp_dir, 'Modelfile'), 'r') as f:
content = f.read()
self.assertTrue(template in content)
self.assertTrue(stop in content)
@unittest.skip('swift2.0')
def test_chatglm4(self):
if version.parse(transformers.__version__) >= version.parse('4.45'):
return
args = ExportArguments(model_type='glm4-9b-chat', to_ollama=True, ollama_output_dir=self.tmp_dir)
export_main(args)
template = ('TEMPLATE """{{ if .System }}[gMASK] <sop><|system|>\n{{ .System }}{{ else }}'
'[gMASK] <sop>{{ end }}{{ if .Prompt }}<|user|>\n{{ .Prompt }}<|assistant|>\n'
'{{ end }}{{ .Response }}<|user|>"""')
stop = 'PARAMETER stop "<|user|>"'
with open(os.path.join(self.tmp_dir, 'Modelfile'), 'r') as f:
content = f.read()
self.assertTrue(template in content)
self.assertTrue(stop in content)
@unittest.skip('swift2.0')
def test_qwen2(self):
args = ExportArguments(model_type='qwen2-7b-instruct', to_ollama=True, ollama_output_dir=self.tmp_dir)
export_main(args)
template = ('TEMPLATE """{{ if .System }}<|im_start|>system\n{{ .System }}<|im_end|>\n{{ else }}{{ end }}'
'{{ if .Prompt }}<|im_start|>user\n{{ .Prompt }}<|im_end|>\n<|im_start|>assistant\n'
'{{ end }}{{ .Response }}<|im_end|>"""')
stop = 'PARAMETER stop "<|im_end|>"'
with open(os.path.join(self.tmp_dir, 'Modelfile'), 'r') as f:
content = f.read()
self.assertTrue(template in content)
self.assertTrue(stop in content)
if __name__ == '__main__':
unittest.main()
+458
View File
@@ -0,0 +1,458 @@
if __name__ == '__main__':
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
import os
import shutil
import tempfile
import torch
import unittest
from datasets import Dataset as HfDataset
from functools import partial
from modelscope import Model, MsDataset, snapshot_download
from torch.nn.utils.rnn import pad_sequence
from transformers import AutoTokenizer
from typing import Any, Dict, List
from swift import (InferArguments, RLHFArguments, SftArguments, Trainer, TrainingArguments, get_logger, infer_main,
rlhf_main, sft_main)
NO_EVAL_HUMAN = True
logger = get_logger()
kwargs = {
'per_device_train_batch_size': 2,
'per_device_eval_batch_size': 2,
'save_steps': 5,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
class TestRun(unittest.TestCase):
def setUp(self):
print(f'Testing {type(self).__name__}.{self._testMethodName}')
self._tmp_dir = tempfile.TemporaryDirectory()
self.tmp_dir = self._tmp_dir.name
def tearDown(self):
shutil.rmtree(self.tmp_dir)
def test_template(self):
if not __name__ == '__main__':
# ignore citest error in github
return
torch.cuda.empty_cache()
output = sft_main(
SftArguments(
model='Qwen/Qwen1.5-0.5B',
tuner_type='full',
dataset='DAMO_NLP/jd',
val_dataset='DAMO_NLP/jd#20',
streaming=True,
max_steps=12,
**kwargs))
last_model_checkpoint = output['last_model_checkpoint']
torch.cuda.empty_cache()
result = infer_main(InferArguments(model=last_model_checkpoint, load_data_args=True, val_dataset_sample=2))
assert len(result[0]['response']) < 20
def test_hf_hub(self):
if not __name__ == '__main__':
# ignore citest error in github
return
torch.cuda.empty_cache()
train_dataset_fnames = [
'alpaca.csv', 'chatml.jsonl', 'swift_pre.jsonl', 'swift_single.csv', 'swift_multi.jsonl',
'swift_multi.json#2'
]
folder = os.path.join(os.path.dirname(__file__), 'data')
dataset = [
'llm-wizard/alpaca-gpt4-data-zh#20',
'shibing624/alpaca-zh#20',
] + [os.path.join(folder, fname) for fname in train_dataset_fnames]
output = sft_main(
SftArguments(
model='Qwen/Qwen1.5-0.5B-Chat-GPTQ-Int4', tuner_type='lora', dataset=dataset, use_hf=True, **kwargs))
last_model_checkpoint = output['last_model_checkpoint']
torch.cuda.empty_cache()
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, val_dataset_sample=2))
@unittest.skip('avoid ci error')
def test_basic(self):
output_dir = 'output'
quant_bits_list = [0, 4]
train_dataset_fnames = [
'alpaca.csv', 'chatml.jsonl', 'swift_pre.jsonl', 'swift_single.csv', 'swift_multi.jsonl',
'swift_multi.json#2'
]
folder = os.path.join(os.path.dirname(__file__), 'data')
dataset = [
'AI-ModelScope/alpaca-gpt4-data-zh#20',
'hurner/alpaca-gpt4-data-zh#20',
] + [os.path.join(folder, fname) for fname in train_dataset_fnames]
if not __name__ == '__main__':
output_dir = self.tmp_dir
quant_bits_list = [4]
dataset = dataset[:2]
for quant_bits in quant_bits_list:
if quant_bits == 0:
predict_with_generate = False
quant_method = None
else:
predict_with_generate = True
quant_method = 'bnb'
sft_args = SftArguments(
model='Qwen/Qwen2-0.5B-Instruct',
quant_bits=quant_bits,
eval_steps=5,
adam_beta2=0.95,
quant_method=quant_method,
predict_with_generate=predict_with_generate,
dataset=dataset,
val_dataset='DAMO_NLP/jd#20',
output_dir=output_dir,
download_mode='force_redownload',
include_num_input_tokens_seen=True,
gradient_checkpointing=True,
**kwargs)
torch.cuda.empty_cache()
output = sft_main(sft_args)
print(output)
best_model_checkpoint = output['best_model_checkpoint']
print(f'best_model_checkpoint: {best_model_checkpoint}')
if __name__ == '__main__':
infer_args = InferArguments(
adapters=best_model_checkpoint,
merge_lora={
0: True,
4: False
}[quant_bits],
load_data_args=NO_EVAL_HUMAN,
val_dataset_sample=5)
torch.cuda.empty_cache()
result = infer_main(infer_args)
print(result)
# if __name__ == '__main__':
# app_ui_main(infer_args)
def test_vl_audio(self):
output_dir = 'output'
if not __name__ == '__main__':
# ignore citest error in github
return
model_type_list = ['Qwen/Qwen-VL-Chat', 'Qwen/Qwen-Audio-Chat']
dataset_list = [
'modelscope/coco_2014_caption:validation#100', 'speech_asr/speech_asr_aishell1_trainsets:validation#100'
]
for model, dataset in zip(model_type_list, dataset_list):
sft_args = SftArguments(
model=model,
eval_steps=5,
dataset=[dataset],
output_dir=output_dir,
gradient_checkpointing=True,
lazy_tokenize=True,
disable_tqdm=True,
**kwargs)
torch.cuda.empty_cache()
output = sft_main(sft_args)
print(output)
best_model_checkpoint = output['best_model_checkpoint']
print(f'best_model_checkpoint: {best_model_checkpoint}')
infer_args = InferArguments(
adapters=best_model_checkpoint,
load_data_args=True,
stream={
'Qwen/Qwen-VL-Chat': True,
'Qwen/Qwen-Audio-Chat': False
}[model],
val_dataset_sample=5)
torch.cuda.empty_cache()
result = infer_main(infer_args)
print(result)
def test_custom_dataset(self):
if not __name__ == '__main__':
# ignore citest error in github
return
train_dataset_fnames = [
'alpaca.csv', 'chatml.jsonl', 'swift_pre.jsonl', 'swift_single.csv', 'swift_multi.jsonl',
'swift_multi.json', 'sharegpt.jsonl'
]
val_dataset_fnames = [
'alpaca.jsonl',
'alpaca2.csv',
'conversations.jsonl',
'swift_pre.csv',
'swift_single.jsonl',
# 'swift_#:#.jsonl#3'
]
folder = os.path.join(os.path.dirname(__file__), 'data')
resume_from_checkpoint = None
train_kwargs = kwargs.copy()
train_kwargs.pop('num_train_epochs')
for num_train_epochs in [1, 2]:
sft_args = SftArguments(
model='Qwen/Qwen-7B-Chat',
dataset=['swift/self-cognition#20'] + [os.path.join(folder, fname) for fname in train_dataset_fnames],
val_dataset=[os.path.join(folder, fname) for fname in val_dataset_fnames],
resume_from_checkpoint=resume_from_checkpoint,
num_train_epochs=num_train_epochs,
model_name='小黄',
model_author='魔搭',
**train_kwargs)
torch.cuda.empty_cache()
result = sft_main(sft_args)
best_model_checkpoint = result['best_model_checkpoint']
resume_from_checkpoint = result['last_model_checkpoint']
for load_args in [True, False]:
infer_kwargs = {}
if load_args is False:
args_json = os.path.join(best_model_checkpoint, 'args.json')
assert os.path.exists(args_json)
os.remove(args_json)
infer_kwargs = {'model': 'Qwen/Qwen-7B-Chat'}
infer_args = InferArguments(
adapters=best_model_checkpoint,
load_data_args=load_args and NO_EVAL_HUMAN,
merge_lora=load_args,
val_dataset=[os.path.join(folder, fname) for fname in val_dataset_fnames],
**infer_kwargs)
torch.cuda.empty_cache()
infer_main(infer_args)
def test_rlhf(self):
if not __name__ == '__main__':
# ignore citest error in github
return
torch.cuda.empty_cache()
# llm rlhf
#
rlhf_types = ['dpo', 'orpo', 'simpo', 'kto', 'cpo', 'rm', 'ppo']
for rlhf_type in rlhf_types:
dataset = ('AI-ModelScope/hh_rlhf_cn:harmless_base_cn#100'
if rlhf_type != 'kto' else 'AI-ModelScope/ultrafeedback-binarized-preferences-cleaned-kto#100')
train_kwargs = {}
if rlhf_type == 'ppo':
train_kwargs['reward_model'] = 'Qwen/Qwen2-1.5B-Instruct'
output = rlhf_main(
RLHFArguments(
rlhf_type=rlhf_type,
model='Qwen/Qwen2-1.5B-Instruct',
dataset=dataset,
eval_steps=5,
split_dataset_ratio=0.05,
**train_kwargs,
**kwargs))
if rlhf_type == 'ppo':
model_checkpoint = output['last_model_checkpoint']
else:
model_checkpoint = output['best_model_checkpoint']
torch.cuda.empty_cache()
infer_main(InferArguments(adapters=model_checkpoint, load_data_args=True))
# mllm rlhf
visual_rlhf_types = ['dpo', 'orpo', 'simpo', 'cpo', 'rm']
test_model = [
'OpenGVLab/InternVL2-2B', 'Qwen/Qwen2-VL-2B-Instruct', 'llava-hf/llava-v1.6-mistral-7b-hf',
'AI-ModelScope/Florence-2-base-ft'
] # decoder only and encoder-decoder
for rlhf_type in visual_rlhf_types:
for model in test_model:
dataset_name = 'swift/RLAIF-V-Dataset#100'
output = rlhf_main(
RLHFArguments(
rlhf_type=rlhf_type,
model=model,
dataset=dataset_name,
eval_steps=5,
dataset_num_proc=16,
**kwargs))
best_model_checkpoint = output['best_model_checkpoint']
torch.cuda.empty_cache()
infer_main(InferArguments(adapters=best_model_checkpoint, load_data_args=True, val_dataset_sample=2))
def test_loss_matching(self):
output_dir = 'output'
if not __name__ == '__main__':
# ignore citest error in github
return
losses = []
for use_swift_lora in [False, True]:
bool_var = use_swift_lora
torch.cuda.empty_cache()
output = sft_main([
'--model', 'Qwen/Qwen-7B-Chat', '--save_steps', '5', '--dataset',
'AI-ModelScope/leetcode-solutions-python#200', '--output_dir', output_dir, '--gradient_checkpointing',
'true', '--max_new_tokens', '100', '--attn_impl', 'flash_attn', '--target_modules', 'all-linear',
'--seed', '0', '--lora_bias', 'all', '--modules_to_save', 'lm_head', '--use_swift_lora',
str(use_swift_lora), '--num_train_epochs', '1', '--gradient_accumulation_steps', '16'
])
best_model_checkpoint = output['best_model_checkpoint']
print(f'best_model_checkpoint: {best_model_checkpoint}')
load_data_args = str(bool_var or NO_EVAL_HUMAN)
if load_data_args:
val_dataset_sample = 2
else:
val_dataset_sample = -1
torch.cuda.empty_cache()
infer_main([
'--adapters', best_model_checkpoint, '--val_dataset_sample',
str(val_dataset_sample), '--max_new_tokens', '100', '--attn_impl', 'eager', '--merge_lora',
str(bool_var), '--load_data_args',
str(load_data_args)
])
loss = output['log_history'][-1]['train_loss']
losses.append(loss)
self.assertTrue(abs(losses[0] - losses[1]) < 5e-4)
print(f'swift_loss: {losses[0]}')
print(f'peft_loss: {losses[1]}')
self.assertTrue(0.95 <= losses[0] <= 1)
def test_pai_compat(self):
if not __name__ == '__main__':
# ignore citest error in github
return
from swift import infer_main, sft_main
os.environ['PAI_TRAINING_JOB_ID'] = '123456'
folder = os.path.join(os.path.dirname(__file__), 'config')
tensorboard_dir = os.path.join('output/pai_test', 'pai_tensorboard')
os.environ['PAI_OUTPUT_TENSORBOARD'] = tensorboard_dir
sft_json = os.path.join(folder, 'sft.json')
infer_json = os.path.join(folder, 'infer.json')
torch.cuda.empty_cache()
output = sft_main([sft_json])
print()
infer_args = {
'adapters': output['best_model_checkpoint'],
'val_dataset_sample': 2,
'load_data_args': True,
}
import json
with open(infer_json, 'w') as f:
json.dump(infer_args, f, ensure_ascii=False, indent=4)
torch.cuda.empty_cache()
infer_main([infer_json])
os.environ.pop('PAI_TRAINING_JOB_ID')
def data_collate_fn(batch: List[Dict[str, Any]], tokenizer) -> Dict[str, torch.Tensor]:
# text-classification
assert tokenizer.pad_token_id is not None
input_ids = [torch.tensor(b['input_ids']) for b in batch]
labels = torch.tensor([b['labels'] for b in batch])
attention_mask = [torch.ones(len(input_ids[i]), dtype=torch.int64) for i in range(len(input_ids))]
input_ids = pad_sequence(input_ids, batch_first=True, padding_value=tokenizer.pad_token_id)
attention_mask = pad_sequence(attention_mask, batch_first=True, padding_value=0)
return {'input_ids': input_ids, 'attention_mask': attention_mask, 'labels': labels}
class BertTrainer(Trainer):
def compute_loss(self, model, inputs, return_outputs=False):
outputs = model(**inputs)
loss = outputs.loss
if loss is None:
logits, loss = list(outputs.logits)
return (loss, outputs) if return_outputs else loss
class TestTrainer(unittest.TestCase):
def setUp(self):
self._tmp_dir = tempfile.TemporaryDirectory()
self.tmp_dir = self._tmp_dir.name
# self.tmp_dir = 'test'
logger.info(f'self.tmp_dir: {self.tmp_dir}')
def tearDown(self):
if os.path.isdir(self.tmp_dir):
shutil.rmtree(self.tmp_dir)
# api = HubApi()
# api.delete_model(self.hub_model_id)
# logger.info(f'delete model: {self.hub_model_id}')
def test_trainer(self):
self.hub_model_id = 'test_trainer2'
logger.info(f'self.hub_model_id: {self.hub_model_id}')
self.tmp_dir = 'output/damo/nlp_structbert_backbone_base_std'
push_to_hub = True
if not __name__ == '__main__':
# ignore citest error in github
return
model_id = 'damo/nlp_structbert_backbone_base_std'
model_dir = snapshot_download(model_id, 'master')
tokenizer = AutoTokenizer.from_pretrained(model_dir)
dataset = MsDataset.load('clue', subset_name='tnews')
num_labels = max(dataset['train']['label']) + 1
model = Model.from_pretrained(model_dir, task='text-classification', num_labels=num_labels)
train_dataset, val_dataset = dataset['train'].to_hf_dataset(), dataset['validation'].to_hf_dataset()
train_dataset: HfDataset = train_dataset.select(range(100))
val_dataset: HfDataset = val_dataset.select(range(20))
#
def tokenize_func(examples):
data = tokenizer(examples['sentence'], return_attention_mask=False)
examples['input_ids'] = data['input_ids']
examples['labels'] = examples['label']
del examples['sentence'], examples['label']
return examples
train_dataset = train_dataset.map(tokenize_func)
val_dataset = val_dataset.map(tokenize_func)
data_collator = partial(data_collate_fn, tokenizer=tokenizer)
for save_only_model in [True, False]:
trainer_args = TrainingArguments(
self.tmp_dir,
do_train=True,
do_eval=True,
num_train_epochs=1,
evaluation_strategy='steps',
save_strategy='steps',
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
push_to_hub=push_to_hub,
hub_token=None, # use env var
hub_private_repo=True,
hub_strategy='every_save',
hub_model_id=self.hub_model_id,
overwrite_output_dir=True,
save_steps=10,
save_total_limit=2,
metric_for_best_model='loss',
greater_is_better=False,
report_to=['tensorboard'],
gradient_accumulation_steps=1,
logging_steps=5,
eval_steps=10,
save_safetensors=False,
save_only_model=save_only_model)
trainer_args._n_gpu = 1
trainer = BertTrainer(model, trainer_args, data_collator, train_dataset, val_dataset, tokenizer)
self.hub_model_id = trainer_args.hub_model_id
trainer.train()
if trainer_args.push_to_hub:
trainer.push_to_hub()
if __name__ == '__main__':
# TestRun().test_template()
# TestRun().test_hf_hub()
# TestRun().test_basic()
# TestRun().test_custom_dataset()
# TestRun().test_vl_audio()
# TestRun().test_loss_matching()
#
# TestRun().test_rlhf()
unittest.main()
+109
View File
@@ -0,0 +1,109 @@
import os
import torch
import unittest
from swift.infer_engine import RequestConfig, TransformersEngine
from swift.model import get_processor
from swift.template import get_template
from swift.utils import get_logger, seed_everything
# os.environ['CUDA_VISIBLE_DEVICES'] = '0'
# os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
os.environ['SWIFT_DEBUG'] = '1'
logger = get_logger()
def _infer_model(engine, system=None, messages=None):
seed_everything(42)
request_config = RequestConfig(max_tokens=128, temperature=0)
if messages is None:
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages += [{'role': 'user', 'content': '你好'}]
resp = engine.infer([{'messages': messages}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}, {'role': 'user', 'content': '<image>这是什么'}]
resp = engine.infer([{
'messages': messages,
}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}]
logger.info(f'model: {engine.model_info.model_name}, messages: {messages}')
return response
class TestTemplate(unittest.TestCase):
@unittest.skipIf(not torch.cuda.is_available(), reason='GPTQ is only available on GPU')
def test_template(self):
engine = TransformersEngine('Qwen/Qwen2.5-3B-Instruct-GPTQ-Int4')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_tool_message_join(self):
from copy import deepcopy
from swift.agent_template import agent_template_map
messages = [
# first round
{
'role': 'user',
'content': 'user1'
},
{
'role': 'assistant',
'content': 'assistant1'
},
{
'role': 'assistant',
'content': 'assistant2'
},
{
'role': 'tool',
'content': 'tool1'
},
# second round
{
'role': 'assistant',
'content': 'assistant3'
},
{
'role': 'tool',
'content': 'tool2'
},
{
'role': 'tool',
'content': 'tool3'
},
]
# testing two template type.
tokenizer = get_processor('Qwen/Qwen2.5-7B-Instruct')
template = get_template(tokenizer)
for agent_template_type in ('react_zh', 'qwen_zh'):
template._agent_template = agent_template_type
agent_template = template.agent_template
observation = agent_template.keyword.observation
test_messages = deepcopy(messages)
test_messages[2]['content'] = 'assistant2' + observation
test_messages[4]['content'] = (
agent_template.keyword.action + agent_template.keyword.action_input + 'assistant3' + observation)
encoded = template.encode({'messages': test_messages})
res = template.safe_decode(encoded['input_ids'])
ground_truth = (
'<|im_start|>system\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\n'
'<|im_start|>user\nuser1<|im_end|>\n'
f'<|im_start|>assistant\nassistant1assistant2{observation}tool1'
f'{agent_template.keyword.action}{agent_template.keyword.action_input}assistant3'
f'{observation}tool2\n{observation}tool3\n')
assert res == ground_truth
if __name__ == '__main__':
unittest.main()
+28
View File
@@ -0,0 +1,28 @@
import unittest
from swift.dataset import load_dataset
from swift.utils import lower_bound
class TestLlmUtils(unittest.TestCase):
def test_count_startswith(self):
arr = [-100] * 1000 + list(range(1000))
self.assertTrue(lower_bound(0, len(arr), lambda i: arr[i] != -100) == 1000)
def test_count_endswith(self):
arr = list(range(1000)) + [-100] * 1000
self.assertTrue(lower_bound(0, len(arr), lambda i: arr[i] == -100) == 1000)
@unittest.skip('avoid ci error')
def test_dataset(self):
dataset = load_dataset(['AI-ModelScope/alpaca-gpt4-data-zh#1000', 'AI-ModelScope/alpaca-gpt4-data-en#200'],
num_proc=4,
strict=False,
download_mode='force_redownload')
print(f'dataset[0]: {dataset[0]}')
print(f'dataset[1]: {dataset[1]}')
if __name__ == '__main__':
unittest.main()
+5
View File
@@ -0,0 +1,5 @@
from swift.arguments import WebUIArguments
from swift.ui import webui_main
if __name__ == '__main__':
webui_main(WebUIArguments())
View File
+79
View File
@@ -0,0 +1,79 @@
import os
from swift.megatron import MegatronExportArguments, megatron_export_main
os.environ['NVTE_DEBUG'] = '1'
os.environ['NVTE_DEBUG_LEVEL'] = '2'
os.environ['SWIFT_TEST_CONVERT_PRECISION'] = '1'
def test_to_mcore():
megatron_export_main(
MegatronExportArguments(
model='Qwen/Qwen2.5-7B-Instruct',
output_dir='Qwen2.5-7B-Instruct-mcore',
to_mcore=True,
exist_ok=True,
tensor_model_parallel_size=2,
test_convert_precision=True))
def test_cp():
megatron_export_main(
MegatronExportArguments(
model='Qwen/Qwen3.5-4B',
to_mcore=True,
exist_ok=True,
attention_backend='flash',
padding_free=True,
context_parallel_size=2,
tensor_model_parallel_size=2,
pipeline_model_parallel_size=2,
test_convert_precision=True))
def test_to_hf():
megatron_export_main(
MegatronExportArguments(
mcore_model='Qwen3-30B-A3B-mcore',
to_hf=True,
exist_ok=True,
tensor_model_parallel_size=2,
pipeline_model_parallel_size=2,
expert_model_parallel_size=2,
test_convert_precision=True))
def test_peft_to_mcore():
megatron_export_main(
MegatronExportArguments(
model='Qwen/Qwen3-30B-A3B',
adapters=['megatron_output/Qwen3-30B-A3B/vx-xxx/checkpoint-xxx-hf'],
merge_lora=False,
to_mcore=True,
exist_ok=True,
tensor_model_parallel_size=2,
expert_model_parallel_size=4,
test_convert_precision=True))
def test_peft_to_hf():
megatron_export_main(
MegatronExportArguments(
mcore_model='Qwen3-30B-A3B-mcore',
mcore_adapter='megatron_output/Qwen3-30B-A3B/vx-xxx/checkpoint-xxx',
merge_lora=False,
to_hf=True,
exist_ok=True,
tensor_model_parallel_size=2,
expert_model_parallel_size=2,
test_convert_precision=True))
if __name__ == '__main__':
# test_to_mcore()
test_cp()
# test_to_hf()
# test_peft_to_mcore()
# test_peft_to_hf()
+60
View File
@@ -0,0 +1,60 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
def test_embedding():
from swift.megatron import MegatronSftArguments, megatron_sft_main
megatron_sft_main(
MegatronSftArguments(
model='Qwen/Qwen3-Embedding-0.6B',
task_type='embedding',
dataset=['sentence-transformers/stsb:positive'],
split_dataset_ratio=0.01,
micro_batch_size=4,
tensor_model_parallel_size=2,
tuner_type='lora',
num_train_epochs=1,
recompute_granularity='full',
recompute_method='uniform',
recompute_num_layers=1,
loss_type='infonce',
vit_attn_impl='flash_attn',
max_length=2048,
eval_iters=5,
save_steps=5,
no_save_optim=True,
no_save_rng=True,
sequence_parallel=True,
finetune=True))
def test_reranker():
from swift.megatron import MegatronSftArguments, megatron_sft_main
megatron_sft_main(
MegatronSftArguments(
model='Qwen/Qwen3-Reranker-4B',
tuner_type='lora',
load_from_cache_file=True,
num_train_epochs=1,
task_type='generative_reranker',
dataset=['MTEB/scidocs-reranking#2000'],
loss_type='pointwise_reranker',
split_dataset_ratio=0.01,
tensor_model_parallel_size=2,
recompute_granularity='full',
recompute_method='uniform',
recompute_num_layers=1,
train_iters=100,
eval_iters=5,
save_steps=5,
no_save_optim=True,
no_save_rng=True,
sequence_parallel=True,
finetune=True))
if __name__ == '__main__':
test_embedding()
# test_reranker()
+65
View File
@@ -0,0 +1,65 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def _infer_model(engine, system=None, messages=None):
from swift.infer_engine import RequestConfig
from swift.utils import get_logger, seed_everything
logger = get_logger()
seed_everything(42)
request_config = RequestConfig(max_tokens=128, temperature=0)
if messages is None:
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages += [{'role': 'user', 'content': 'who are you?'}]
resp = engine.infer([{'messages': messages}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}, {'role': 'user', 'content': '<image>这是什么'}]
else:
messages = messages.copy()
resp = engine.infer([{
'messages': messages,
}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}]
logger.info(f'model: {engine.model_info.model_name}, messages: {messages}')
return response
model_id = 'Qwen/Qwen2-7B-Instruct'
def hf2mcore():
from swift import ExportArguments, export_main
export_main(
ExportArguments(
model=model_id, to_mcore=True, torch_dtype='bfloat16', exist_ok=True, test_convert_precision=True))
def mcore2hf():
from swift import ExportArguments, export_main
export_main(
ExportArguments(
mcore_model='Qwen2-7B-Instruct-mcore',
to_hf=True,
torch_dtype='bfloat16',
exist_ok=True,
test_convert_precision=True))
def infer_hf_align():
from swift.infer_engine import TransformersEngine
engine = TransformersEngine(model_id)
response = _infer_model(engine)
engine = TransformersEngine('Qwen2-7B-Instruct-mcore-hf')
response2 = _infer_model(engine)
assert response == response2
if __name__ == '__main__':
# hf2mcore()
mcore2hf()
infer_hf_align()
+96
View File
@@ -0,0 +1,96 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
if __name__ == '__main__':
from swift.megatron import MegatronRLHFArguments, megatron_rlhf_main
megatron_rlhf_main(
MegatronRLHFArguments(
rlhf_type='gkd',
model='Qwen/Qwen3-4B-Base',
teacher_model='Qwen/Qwen3-8B',
tuner_type='lora',
dataset=['AI-ModelScope/alpaca-gpt4-data-en#2000', 'AI-ModelScope/alpaca-gpt4-data-zh#2000'],
tensor_model_parallel_size=2,
seq_kd=False,
lmbda=1,
beta=1,
micro_batch_size=2,
global_batch_size=16,
num_train_epochs=1,
lr=5e-6,
logging_steps=1,
max_length=2048,
max_completion_length=1024,
attention_backend='flash',
use_vllm=True,
vllm_mode='colocate',
vllm_gpu_memory_utilization=0.5,
vllm_tensor_parallel_size=1,
vllm_max_model_len=16384,
sleep_level=1,
offload_teacher_model=True,
recompute_granularity='full',
recompute_method='uniform',
recompute_num_layers=1,
finetune=True,
no_save_optim=True,
no_save_rng=True,
temperature=1,
padding_free=True,
sequence_parallel=True,
))
def test_gkd_multi_turn():
"""Megatron GKD multi-turn smoke test.
Verifies that ``_prepare_scheduler`` (now in MegatronRolloutMixin) initializes
the multi_turn_scheduler for GKD, and that multi-turn rollout → encode → JSD
loss completes without error.
"""
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
from swift.megatron import MegatronRLHFArguments, megatron_rlhf_main
megatron_rlhf_main(
MegatronRLHFArguments(
rlhf_type='gkd',
model='Qwen/Qwen3-4B',
teacher_model='Qwen/Qwen3-8B',
tuner_type='lora',
dataset=['AI-ModelScope/alpaca-gpt4-data-en#200'],
tensor_model_parallel_size=2,
seq_kd=False,
lmbda=1,
beta=1,
micro_batch_size=2,
global_batch_size=8,
num_train_epochs=1,
lr=5e-6,
logging_steps=1,
max_length=2048,
max_completion_length=512,
attention_backend='flash',
use_vllm=True,
vllm_mode='colocate',
vllm_gpu_memory_utilization=0.5,
vllm_tensor_parallel_size=1,
vllm_max_model_len=4096,
sleep_level=1,
offload_teacher_model=True,
recompute_granularity='full',
recompute_method='uniform',
recompute_num_layers=1,
finetune=True,
no_save_optim=True,
no_save_rng=True,
temperature=1,
padding_free=True,
sequence_parallel=True,
multi_turn_scheduler='math_tip_trick',
max_turns=2,
))
if __name__ == '__main__':
test_gkd_multi_turn()
+62
View File
@@ -0,0 +1,62 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
os.environ['MAX_PIXELS'] = '602112'
if __name__ == '__main__':
from swift.megatron import MegatronRLHFArguments, megatron_rlhf_main
megatron_rlhf_main(
MegatronRLHFArguments(
rlhf_type='grpo',
model='Qwen/Qwen3.5-4B',
save_safetensors=True,
context_parallel_size=1,
tuner_type='lora',
tensor_model_parallel_size=2,
dataset=['AI-ModelScope/clevr_cogen_a_train#10000'],
num_train_epochs=1,
global_batch_size=128,
vllm_mm_processor_cache_gb=0,
micro_batch_size=4,
steps_per_generation=4,
num_generations=8,
external_plugins=['examples/train/grpo/plugin/plugin.py'],
reward_funcs=['external_r1v_acc', 'format'],
use_vllm=True,
vllm_mode='colocate',
vllm_gpu_memory_utilization=0.5,
vllm_max_model_len=8192,
max_length=8192,
max_completion_length=2048,
lr=1e-4,
bf16=True,
beta=0.001,
importance_sampling_level='token',
epsilon=0.2,
epsilon_high=0.2,
dynamic_sample=True,
overlong_filter=True,
loss_type='grpo',
sleep_level=2,
offload_model=True,
offload_bridge=False,
offload_optimizer=True,
logging_steps=1,
recompute_granularity='full',
recompute_method='uniform',
recompute_num_layers=1,
finetune=True,
dataloader_num_workers=4,
dataset_num_proc=4,
no_save_optim=True,
no_save_rng=True,
attention_backend='flash',
temperature=1,
system='examples/train/grpo/prompt.txt',
padding_free=True,
log_completions=True,
train_iters=100,
eval_steps=1000,
save_steps=1000,
))
+38
View File
@@ -0,0 +1,38 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
def test_kto():
from swift.megatron import MegatronRLHFArguments, megatron_rlhf_main
megatron_rlhf_main(
MegatronRLHFArguments(
model='Qwen/Qwen2.5-7B-Instruct',
rlhf_type='kto',
tuner_type='lora',
load_from_cache_file=True,
dataset=['AI-ModelScope/ultrafeedback-binarized-preferences-cleaned-kto#10000'],
target_modules=['all-linear'],
tensor_model_parallel_size=2,
split_dataset_ratio=0.01,
micro_batch_size=4,
global_batch_size=16,
recompute_granularity='full',
recompute_method='uniform',
recompute_num_layers=1,
eval_steps=10,
save_steps=10,
logging_steps=1,
finetune=True,
num_train_epochs=1,
max_length=2048,
packing=True,
dataset_num_proc=8,
cross_entropy_loss_fusion=True,
sequence_parallel=True,
))
if __name__ == '__main__':
test_kto()
+80
View File
@@ -0,0 +1,80 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
def test_sft():
from swift.megatron import MegatronSftArguments, megatron_sft_main
megatron_sft_main(
MegatronSftArguments(
mcore_model='Qwen2.5-3B-Instruct-mcore',
dataset=['AI-ModelScope/function-calling-chatml#10000'],
loss_scale='hermes',
split_dataset_ratio=0.01,
tensor_model_parallel_size=2,
tuner_type='lora',
recompute_granularity='full',
recompute_method='uniform',
recompute_num_layers=1,
# pipeline_model_parallel_size=2,
# freeze_parameters_ratio=0.5,
train_iters=100,
modules_to_save=['word_embeddings', 'output_layer'],
eval_iters=5,
save_steps=5,
no_save_optim=True,
no_save_rng=True,
sequence_parallel=True,
finetune=True))
def test_moe():
from swift.megatron import MegatronSftArguments, megatron_sft_main
megatron_sft_main(
MegatronSftArguments(
mcore_model='Qwen1.5-MoE-A2.7B-mcore',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#5000'],
split_dataset_ratio=0.01,
moe_shared_expert_overlap=True,
moe_grouped_gemm=True,
tensor_model_parallel_size=2,
# expert_model_parallel_size=2,
tuner_type='lora',
recompute_granularity='full',
modules_to_save=['word_embeddings', 'output_layer'],
recompute_method='uniform',
recompute_num_layers=1,
# pipeline_model_parallel_size=2,
# freeze_parameters_ratio=0.5,
train_iters=100,
eval_iters=5,
save_steps=5,
no_save_optim=True,
no_save_rng=True,
sequence_parallel=True,
finetune=True))
def test_convert():
from swift import ExportArguments, export_main
export_main(
ExportArguments(
mcore_adapter='megatron_output/vx-xxx/checkpoint-xxx',
to_hf=True,
test_convert_precision=True,
))
def test_embedding():
pass
def test_resume():
pass
if __name__ == '__main__':
test_sft()
# test_moe()
# test_convert()
+92
View File
@@ -0,0 +1,92 @@
import unittest
class TestMegatronArgs(unittest.TestCase):
"""Megatron import / args smoke test (GPU and NPU adapted).
Covers: MegatronSftArguments initialization, MegatronRLHFArguments,
MegatronArguments field validation.
Why these tests are needed:
- tests/megatron/test_train.py and test_lora.py have top-level functions
that require multi-GPU and mcore models, too heavy for CI.
- Megatron argument construction is a common entry point that should be
validated even without a full training run.
- On NPU, Megatron dependencies (mcore, MindSpeed) may not be installed,
so we gracefully skip.
"""
@classmethod
def setUpClass(cls):
try:
from swift.megatron import (MegatronArguments, MegatronExportArguments, MegatronPretrainArguments,
MegatronRLHFArguments, MegatronSftArguments)
cls._megatron_available = True
cls.MegatronArguments = MegatronArguments
cls.MegatronSftArguments = MegatronSftArguments
cls.MegatronRLHFArguments = MegatronRLHFArguments
except (ImportError, RuntimeError) as e:
cls._megatron_available = False
cls._skip_reason = str(e)
def _skip_if_no_megatron(self):
if not self._megatron_available:
self.skipTest(f'Megatron dependencies not available: {self._skip_reason}')
def test_megatron_import(self):
self._skip_if_no_megatron()
def test_megatron_sft_args_construction(self):
self._skip_if_no_megatron()
args = self.MegatronSftArguments(
mcore_model='Qwen2-7B-Instruct-mcore',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#20'],
split_dataset_ratio=0.01,
tensor_model_parallel_size=1,
train_iters=1,
skip_megatron_init=True,
)
self.assertEqual(args.train_iters, 1)
self.assertEqual(args.tensor_model_parallel_size, 1)
def test_megatron_rlhf_args_construction(self):
self._skip_if_no_megatron()
args = self.MegatronRLHFArguments(
rlhf_type='grpo',
mcore_model='Qwen2-7B-Instruct-mcore',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#20'],
reward_funcs=['format'],
num_generations=2,
max_completion_length=128,
tensor_model_parallel_size=1,
train_iters=1,
skip_megatron_init=True,
)
self.assertEqual(args.rlhf_type, 'grpo')
self.assertIn('format', args.reward_funcs)
def test_megatron_base_args_fields(self):
self._skip_if_no_megatron()
expected_fields = [
'tensor_model_parallel_size',
'pipeline_model_parallel_size',
'context_parallel_size',
'sequence_parallel_size',
'train_iters',
'micro_batch_size',
'global_batch_size',
'lr',
'min_lr',
'bf16',
]
from dataclasses import fields
field_names = {f.name for f in fields(self.MegatronArguments)}
for field_name in expected_fields:
self.assertIn(field_name, field_names, f'MegatronArguments missing field: {field_name}')
if __name__ == '__main__':
unittest.main()
+45
View File
@@ -0,0 +1,45 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'
if __name__ == '__main__':
from swift.megatron import MegatronRLHFArguments, megatron_rlhf_main
megatron_rlhf_main(
MegatronRLHFArguments(
rlhf_type='gkd',
model='Qwen/Qwen3-4B',
teacher_model='Qwen/Qwen3-4B',
external_plugins=['examples/train/rlhf/opsd/opsd_plugin.py'],
dataset=['open-r1/OpenThoughts-114k-math'],
use_vllm=True,
vllm_mode='colocate',
vllm_gpu_memory_utilization=0.6,
vllm_max_model_len=10240,
tuner_type='lora',
lora_rank=64,
lora_alpha=128,
sleep_level=1,
lmbda=1.0,
beta=0.5,
temperature=1.2,
sft_alpha=0,
torch_dtype='bfloat16',
micro_batch_size=2,
global_batch_size=32,
train_iters=1000,
lr=2e-5,
save_steps=100,
save_total_limit=10,
logging_steps=1,
max_length=8192,
max_completion_length=2048,
tensor_model_parallel_size=1,
pipeline_model_parallel_size=1,
attention_backend='flash',
recompute_granularity='selective',
finetune=True,
no_save_optim=True,
no_save_rng=True,
))
+404
View File
@@ -0,0 +1,404 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import torch
from types import SimpleNamespace
from swift.ray.megatron.gkd_trainer import GKDTrainer
from swift.ray.megatron.megatron_worker import MegatronWorker
from swift.rlhf_trainers.gkd_loss import TeacherOutput, extract_active
_collate = MegatronWorker._collate_teacher_outputs
def _topk_to(seq_len, k, fill):
"""A per-sample teacher topk tensor shaped [1, seq_len, k]."""
return TeacherOutput(
topk_logprobs=torch.full((1, seq_len, k), float(fill)),
topk_indices=torch.zeros((1, seq_len, k), dtype=torch.long),
)
def test_collate_padding_free_concat_and_offbyone_pad():
"""padding_free: concat per-sample along seq dim, then pad to target_seq_len.
This is the off-by-one fix: the student collation pads the concatenated
sequence to a multiple via get_padding_to (SP), so the teacher (built from raw
per-sample lengths) can be a few tokens short and must be padded to match.
"""
k = 4
samples = [_topk_to(3, k, -1.0), _topk_to(5, k, -2.0)] # raw total = 8
target = 10 # student SP-padded length (8 -> 10)
out = _collate(samples, device='cpu', padding_free=True, target_seq_len=target)
assert out.topk_logprobs.shape == (1, target, k), out.topk_logprobs.shape
assert out.topk_indices.shape == (1, target, k)
# real tokens 0..7 keep their values; padded tail 8..9 is -inf / 0
assert torch.isinf(out.topk_logprobs[0, 8:, :]).all()
assert (out.topk_indices[0, 8:, :] == 0).all()
assert not torch.isinf(out.topk_logprobs[0, :8, :]).any()
def test_collate_padding_free_offbyone_single_token():
"""The exact failure mode that deadlocked T3: total length odd, padded +1."""
k = 2
samples = [_topk_to(8277, k, -1.0)] # one micro-batch sample, raw len 8277 (odd)
out = _collate(samples, device='cpu', padding_free=True, target_seq_len=8278)
assert out.topk_logprobs.shape == (1, 8278, k)
assert torch.isinf(out.topk_logprobs[0, 8277:, :]).all()
def test_collate_padding_free_drops_empty_placeholder():
"""colocated path emits [1, full, k] for sample 0 and empty [0, ...] for the
rest of a micro-batch; empties must be dropped before the seq-dim concat."""
k = 3
full = _topk_to(6, k, -1.0)
empty = TeacherOutput(
topk_logprobs=torch.full((0, 6, k), float('-inf')),
topk_indices=torch.zeros((0, 6, k), dtype=torch.long),
)
out = _collate([full, empty], device='cpu', padding_free=True, target_seq_len=6)
assert out.topk_logprobs.shape == (1, 6, k)
def test_collate_non_padding_free_stacks_on_batch_dim():
"""non padding_free: per-sample tensors padded to target then stacked on dim 0."""
k = 4
samples = [_topk_to(3, k, -1.0), _topk_to(5, k, -2.0)]
out = _collate(samples, device='cpu', padding_free=False, target_seq_len=5)
assert out.topk_logprobs.shape == (2, 5, k), out.topk_logprobs.shape
# sample 0 padded from 3 -> 5
assert torch.isinf(out.topk_logprobs[0, 3:, :]).all()
assert not torch.isinf(out.topk_logprobs[1]).any()
def test_collate_opsd_keeps_teacher_length_not_student_target():
"""OPSD: teacher scores a different prompt, so its length differs from the
student. The collation must KEEP the teacher length (ignore target_seq_len)
and concat labels (extract_active aligns by mask, not position)."""
k = 3
t_total = 7 # teacher (opsd) sequence length
full = TeacherOutput(
topk_logprobs=torch.full((1, t_total, k), -1.0),
topk_indices=torch.zeros((1, t_total, k), dtype=torch.long),
labels=torch.full((1, t_total), 5, dtype=torch.long),
)
empty = TeacherOutput() # padding_free placeholder for the rest of the micro-batch
# target_seq_len is the *student* length (e.g. 12) — must be ignored for OPSD.
out = _collate([full, empty], device='cpu', padding_free=True, target_seq_len=12, is_opsd=True)
assert out.topk_logprobs.shape == (1, t_total, k), out.topk_logprobs.shape
assert out.labels.shape == (1, t_total)
assert (out.labels == 5).all()
def test_build_per_sample_teacher_output_uses_raw_input_length():
"""Standalone teacher outputs are built from each sample's RAW (un-CP-padded) input
length. Because these raw per-sample token-logprobs cannot be CP-sharded to match the
student, CP>1 with standalone teacher replicas is rejected by a fail-fast in
GKDTrainer._collate_for_workers_gkd (use a colocated teacher_model for CP>1).
This test guards the raw-length contract that the CP>1 fail-fast depends on.
"""
k = 3
raw_len = 5
lps = [[-1.0] * k for _ in range(raw_len)]
ixs = [[0] * k for _ in range(raw_len)]
encoded = {'input_ids': list(range(raw_len))}
out = GKDTrainer._build_per_sample_teacher_output((lps, ixs), encoded, topk=k)
assert out.topk_logprobs.shape == (1, raw_len, k), out.topk_logprobs.shape
assert out.topk_indices.shape == (1, raw_len, k)
assert out.labels is None
def test_extract_active_opsd_aligns_by_mask_across_lengths():
"""OPSD: teacher and student have different sequence lengths; extract_active
selects response positions by their own masks and requires equal counts."""
V, k = 8, 3
# student: length 5, 2 response positions (indices 3,4)
s_logits = torch.randn(1, 5, V)
s_labels = torch.tensor([[-100, -100, -100, 1, 2]])
# teacher (opsd): length 7, 2 response positions (indices 5,6) — same count
t = TeacherOutput(
topk_logprobs=torch.randn(1, 7, k),
topk_indices=torch.zeros((1, 7, k), dtype=torch.long),
labels=torch.tensor([[-100, -100, -100, -100, -100, 1, 2]]),
)
s_act, t_act, n = extract_active(s_logits, t, s_labels)
assert int(n) == 2
assert s_act.shape == (2, V)
assert t_act.topk_logprobs.shape == (2, k)
def test_extract_active_opsd_count_mismatch_raises():
s_logits = torch.randn(1, 5, 8)
s_labels = torch.tensor([[-100, -100, -100, 1, 2]]) # 2 response tokens
t = TeacherOutput(
topk_logprobs=torch.randn(1, 6, 3),
topk_indices=torch.zeros((1, 6, 3), dtype=torch.long),
labels=torch.tensor([[-100, -100, -100, -100, -100, 9]]), # 1 token
)
try:
extract_active(s_logits, t, s_labels)
raise AssertionError('expected an assertion on OPSD count mismatch')
except AssertionError as e:
assert 'OPSD' in str(e) or 'mismatch' in str(e) or 'count' in str(e).lower()
def test_extract_active_non_opsd_uses_student_labels():
"""Non-OPSD: teacher_output.labels is None (Ray GKD non-OPSD path).
The student label mask should apply to both student and teacher.
This is the Critical #1 regression test: before the fix, extract_active
crashed with TypeError on ``None != -100``.
"""
V, k = 8, 3
# student: length 5, 3 response positions (indices 2,3,4)
s_logits = torch.randn(1, 5, V)
s_labels = torch.tensor([[-100, -100, 1, 2, 3]])
# teacher (non-OPSD): same length, labels=None
t = TeacherOutput(
topk_logprobs=torch.randn(1, 5, k),
topk_indices=torch.zeros((1, 5, k), dtype=torch.long),
labels=None,
)
s_act, t_act, n = extract_active(s_logits, t, s_labels)
assert int(n) == 3
assert s_act.shape == (3, V)
assert t_act.topk_logprobs.shape == (3, k)
def test_extract_active_non_opsd_full_logits():
"""Non-OPSD with full-vocab teacher (no topk): labels=None path.
Verifies that the student mask is used for both student and teacher
when teacher_output.labels is None.
"""
V = 8
s_logits = torch.randn(1, 4, V)
s_labels = torch.tensor([[-100, 1, 2, 3]])
t = TeacherOutput(
full_logits=torch.randn(1, 4, V),
labels=None,
)
s_act, t_act, n = extract_active(s_logits, t, s_labels)
assert int(n) == 3
assert s_act.shape == (3, V)
assert t_act.full_logits.shape == (3, V)
def test_megatron_assemble_teacher_outputs_api_topk_rolls_labels():
"""Megatron Teacher API + topk: ``_assemble_teacher_outputs`` must roll teacher
labels by -1 so the invariant 'teacher_output.labels is pre-shifted before
extract_active' holds on the API path too (the local-teacher path gets shifted
labels from _prepare_batch). assemble_teacher_output returns the RAW labels, so
the trainer applies the shift; without it the API path would feed unshifted
teacher labels against shifted student labels -> silent KL/JSD misalignment.
"""
try:
from swift.megatron.trainers.gkd_trainer import MegatronGKDTrainer
except Exception as e: # noqa: megatron-core not installed in this env
print(f'SKIP test_megatron_assemble_teacher_outputs_api_topk_rolls_labels: {e}')
return
k = 3
# raw (unshifted) labels: prompt=-100, response at positions 2,3,4
raw_labels = torch.tensor([[-100, -100, 11, 22, 33]])
seq_len = raw_labels.shape[-1]
# parsed teacher topk: one (logprobs, indices) row per response token (len+1 cu)
parsed = [([[-1.0] * k] * (seq_len - 1), [[0] * k] * (seq_len - 1))]
teacher_model_inputs = {
'input_ids': torch.zeros((1, seq_len), dtype=torch.long),
'labels': raw_labels.clone(),
'attention_mask': torch.ones((1, seq_len), dtype=torch.long),
}
encoded_batch = {'_teacher_parsed': parsed, 'teacher_model_inputs': teacher_model_inputs}
stub = SimpleNamespace(gkd_logits_topk=k, template=SimpleNamespace(padding_free=False), device=torch.device('cpu'))
MegatronGKDTrainer._assemble_teacher_outputs(stub, [encoded_batch])
teacher_out = encoded_batch['teacher_output']
assert torch.equal(teacher_out.labels, torch.roll(raw_labels, shifts=-1, dims=-1))
assert teacher_out.topk_logprobs.shape == (1, seq_len, k)
# The shifted teacher labels must align with shifted student labels in extract_active.
s_labels = torch.roll(raw_labels, shifts=-1, dims=-1)
s_logits = torch.randn(1, seq_len, 8)
s_act, t_act, n = extract_active(s_logits, teacher_out, s_labels)
assert int(n) == 3
assert t_act.topk_logprobs.shape == (3, k)
def test_example_yaml_config_contracts():
"""Config-contract regression for the standardized example yamls.
- teacher replicas (standalone) must declare vllm_engine_kwargs.max_logprobs
>= gkd_logits_topk, else vLLM rejects the prompt_logprobs request.
- the standalone teacher group serves a real teacher checkpoint (model override).
"""
import os
import yaml
base = os.path.join(os.path.dirname(__file__), '..', '..', 'examples', 'ray', 'gkd')
cfg = yaml.safe_load(open(os.path.join(base, 'rollout_colocate_teacher_standalone.yaml')))
topk = cfg['gkd_logits_topk']
teacher = cfg['teacher']
max_logprobs = teacher['vllm_engine_kwargs']['max_logprobs']
assert max_logprobs >= topk, f'teacher max_logprobs {max_logprobs} < gkd_logits_topk {topk}'
assert teacher.get('model'), 'standalone teacher group must override `model`'
# colocate / separate examples keep max_length & max_completion_length consistent
for name in ('rollout_colocate_teacher_colocate.yaml', 'rollout_separate_teacher_colocate.yaml'):
c = yaml.safe_load(open(os.path.join(base, name)))
assert c['max_length'] > 0 and c['max_completion_length'] > 0
def test_ray_gkd_prepare_multi_turn_initializes_scheduler():
"""Verify that GKDTrainer._prepare_multi_turn() initializes the scheduler.
This tests the fix that adds _prepare_multi_turn() to Ray GKD trainer
(previously only GRPO had it). We mock the args and check that
_multi_turn_scheduler is set correctly.
"""
from types import SimpleNamespace
from swift.rollout.multi_turn import MathTipsScheduler
# Create a minimal mock trainer instance (bypass __init__)
trainer = GKDTrainer.__new__(GKDTrainer)
trainer.args = SimpleNamespace(
multi_turn_scheduler='math_tip_trick',
max_turns=2,
gym_env=None,
)
# Call _prepare_multi_turn directly
trainer._prepare_multi_turn()
assert trainer._multi_turn_scheduler is not None, 'Scheduler should be initialized'
assert isinstance(trainer._multi_turn_scheduler,
MathTipsScheduler), (f'Expected MathTipsScheduler, got {type(trainer._multi_turn_scheduler)}')
assert trainer._max_turns == 2
assert trainer._enable_server_multi_turn is False
def test_ray_gkd_prepare_multi_turn_none_when_not_configured():
"""Verify that _prepare_multi_turn() leaves scheduler as None when not configured."""
from types import SimpleNamespace
trainer = GKDTrainer.__new__(GKDTrainer)
trainer.args = SimpleNamespace(
multi_turn_scheduler=None,
max_turns=None,
gym_env=None,
)
trainer._prepare_multi_turn()
assert trainer._multi_turn_scheduler is None
assert trainer._enable_server_multi_turn is False
def test_ray_gkd_prepare_multi_turn_unknown_scheduler_raises():
"""Unknown scheduler name should raise ValueError."""
from types import SimpleNamespace
trainer = GKDTrainer.__new__(GKDTrainer)
trainer.args = SimpleNamespace(
multi_turn_scheduler='nonexistent_scheduler',
max_turns=3,
gym_env=None,
)
try:
trainer._prepare_multi_turn()
assert False, 'Should have raised ValueError for unknown scheduler'
except ValueError as e:
assert 'nonexistent_scheduler' in str(e)
def test_ray_gkd_generate_uses_multi_turn_scheduler():
"""Verify that _generate() dispatches to run_multi_turn when scheduler is set.
We mock _distribute_to_replicas to return canned responses, then check
that the output structure matches multi-turn format (response_token_ids
accumulated across turns).
"""
from types import SimpleNamespace
from swift.infer_engine.protocol import ChatCompletionResponse, ChatCompletionResponseChoice, Message
from swift.rollout.multi_turn import MathTipsScheduler
# Create a minimal mock trainer
trainer = GKDTrainer.__new__(GKDTrainer)
trainer.args = SimpleNamespace(
max_completion_length=128,
temperature=1.0,
top_p=1.0,
top_k=80,
stop_words=[],
)
trainer._multi_turn_scheduler = None # start with no scheduler
trainer._enable_server_multi_turn = False
trainer._max_turns = 1
# Mock _distribute_to_replicas to return canned responses
call_count = [0]
def mock_distribute(requests, request_config):
call_count[0] += 1
responses = []
for req in requests:
choice = ChatCompletionResponseChoice(
index=0,
message=Message(role='assistant', content='The answer is 4.'),
finish_reason='stop',
token_ids=[1, 2, 3, 4, 5],
)
resp = ChatCompletionResponse(choices=[choice])
responses.append(resp)
return responses
trainer._distribute_to_replicas = mock_distribute
# Test 1: Without scheduler (single-turn path)
from swift.rl_core.data import GKDSample
sample = GKDSample(messages=[{'role': 'user', 'content': 'What is 2+2?'}])
outputs = trainer._generate([sample])
assert len(outputs) == 1
assert call_count[0] == 1 # single call
# Test 2: With scheduler (multi-turn path)
# Use a scheduler that always finishes after 1 turn (so we don't loop forever)
trainer._multi_turn_scheduler = MathTipsScheduler(max_turns=1)
# MathTipsScheduler needs solution in data_dict, mock it
sample2 = GKDSample(messages=[{'role': 'user', 'content': 'What is 2+2?'}])
sample2.extra['solution'] = '4'
sample2.request_id = 'test-req-1'
call_count[0] = 0 # reset
# The scheduler's infer_engine is None; we need to mock the inference
# Instead, verify that the multi-turn path is taken by checking call_count
# We mock on_trajectory_start to be a no-op
import asyncio
trainer._multi_turn_scheduler.on_trajectory_start = lambda reqs: asyncio.coroutine(lambda: None)()
try:
outputs = trainer._generate([sample2])
# Multi-turn path should have called _distribute_to_replicas at least once
assert call_count[0] >= 1, f'Expected at least 1 call, got {call_count[0]}'
except Exception:
# Multi-turn with mock may fail in scheduler.step(), but the key assertion
# is that _distribute_to_replicas was called (multi-turn path was taken)
assert call_count[0] >= 1, f'Multi-turn path not taken, call_count={call_count[0]}'
if __name__ == '__main__':
fns = [v for k, v in sorted(globals().items()) if k.startswith('test_') and callable(v)]
failed = 0
for fn in fns:
try:
fn()
print(f'PASS {fn.__name__}')
except Exception as e: # noqa
failed += 1
import traceback
print(f'FAIL {fn.__name__}: {e}')
traceback.print_exc()
print(f'\n{len(fns) - failed}/{len(fns)} passed')
raise SystemExit(1 if failed else 0)
+40
View File
@@ -0,0 +1,40 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
def test_dpo():
from swift.megatron import MegatronRLHFArguments, megatron_rlhf_main
megatron_rlhf_main(
MegatronRLHFArguments(
mcore_model='Qwen2.5-3B-Instruct-mcore',
dataset=['hjh0119/shareAI-Llama3-DPO-zh-en-emoji#10000'],
split_dataset_ratio=0.01,
micro_batch_size=16,
tensor_model_parallel_size=2,
eval_steps=5,
logging_steps=1,
finetune=True,
num_train_epochs=1,
))
def test_hf():
from swift import RLHFArguments, rlhf_main
rlhf_main(
RLHFArguments(
model='Qwen/Qwen2.5-3B-Instruct',
dataset=['hjh0119/shareAI-Llama3-DPO-zh-en-emoji#1000'],
split_dataset_ratio=0.01,
max_steps=100,
padding_free=True,
attn_impl='flash_attn',
train_dataloader_shuffle=False,
use_logits_to_keep=False,
))
if __name__ == '__main__':
test_dpo()
# test_hf()
+40
View File
@@ -0,0 +1,40 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
def test_sft():
from swift.megatron import MegatronSftArguments, megatron_sft_main
megatron_sft_main(
MegatronSftArguments(
mcore_model='Qwen2-7B-Instruct-mcore',
dataset=[
'AI-ModelScope/alpaca-gpt4-data-zh#500', 'swift/self-cognition#500',
'AI-ModelScope/alpaca-gpt4-data-en#500'
],
split_dataset_ratio=0.01,
tensor_model_parallel_size=2,
train_iters=100,
model_author=['swift'],
model_name=['swift-robot'],
sequence_parallel=True,
finetune=True))
def test_pt():
from swift.megatron import MegatronPretrainArguments, megatron_pretrain_main
megatron_pretrain_main(
MegatronPretrainArguments(
mcore_model='Qwen2-7B-mcore',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#500', 'AI-ModelScope/alpaca-gpt4-data-en#500'],
split_dataset_ratio=0.01,
tensor_model_parallel_size=2,
train_iters=200,
eval_iters=5,
finetune=True))
if __name__ == '__main__':
test_sft()
# test_pt()
+171
View File
@@ -0,0 +1,171 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import json
import logging
import os
import requests
from swift.version import __version__
# 打标
class ModelTag(object):
_URL = os.environ.get('MODEL_TAG_URL', None)
# 模型测试结果
BATCH_COMMIT_RESULT_URL = f'{_URL}/batchCommitResult'
# 测试阶段完成
BATCH_REFRESH_STAGE_URL = f'{_URL}/batchRefreshStage'
# query_model_stage
QUERY_MODEL_STAGE_URL = f'{_URL}/queryModelStage'
HEADER = {'Content-Type': 'application/json'}
# 检测结果
MODEL_SKIP = 0
MODEL_FAIL = 1
MODEL_PASS = 2
class ItemResult(object):
def __init__(self):
self.result = 0
self.name = ''
self.info = ''
def to_json(self):
return {'name': self.name, 'result': self.result, 'info': self.info}
def __init__(self):
self.job_name = ''
self.job_id = ''
self.model = ''
self.sdk_version = ''
self.image_version = ''
self.domain = ''
self.task = ''
self.source = ''
self.stage = ''
# ItemResult list
self.item_result = []
# 发送请求
def _post_request(self, url, param):
try:
logging.info(url + ' query: ' + str(json.dumps(param, ensure_ascii=False)))
res = requests.post(url=url, headers=self.HEADER, data=json.dumps(param, ensure_ascii=False).encode('utf8'))
if res.status_code == 200:
logging.info(f'{url} post结果: ' + res.text)
res_json = json.loads(res.text)
if int(res_json['errorCode']) == 200:
return res_json['content']
else:
logging.error(res.text)
else:
logging.error(res.text)
except Exception as e:
logging.error(e)
return None
# 提交模型测试结果
def batch_commit_result(self):
try:
param = {
'sdkVersion':
self.sdk_version,
'imageVersion':
self.image_version,
'source':
self.source,
'jobName':
self.job_name,
'jobId':
self.job_id,
'modelList': [{
'model': self.model,
'domain': self.domain,
'task': self.task,
'itemResult': self.item_result
}]
}
return self._post_request(self.BATCH_COMMIT_RESULT_URL, param)
except Exception as e:
logging.error(e)
return
# 测试阶段完成
def batch_refresh_stage(self):
try:
param = {
'sdkVersion': self.sdk_version,
'imageVersion': self.image_version,
'source': self.source,
'stage': self.stage,
'modelList': [{
'model': self.model,
'domain': self.domain,
'task': self.task
}]
}
return self._post_request(self.BATCH_REFRESH_STAGE_URL, param)
except Exception as e:
logging.error(e)
return
# 查询模型某个阶段的最新测试结果(只返回单个结果
def query_model_stage(self):
try:
param = {
'sdkVersion': self.sdk_version,
'model': self.model,
'stage': self.stage,
'imageVersion': self.image_version
}
return self._post_request(self.QUERY_MODEL_STAGE_URL, param)
except Exception as e:
logging.error(e)
return None
# 提交模型UT测试结果
"""
model_tag = ModelTag()
model_tag.model = "XXX"
model_tag.sdk_version = "0.3.7"
model_tag.domain = "nlp"
model_tag.task = "word-segmentation"
item = model_tag.ItemResult()
item.result = model_tag.MODEL_PASS
item.name = "ALL"
item.info = ""
model_tag.item_result.append(item.to_json())
"""
def commit_ut_result(self):
if self._URL is not None and self._URL != '':
self.job_name = 'UT'
self.source = 'dev'
self.stage = 'integration'
self.batch_commit_result()
self.batch_refresh_stage()
def commit_model_ut_result(model_name, ut_result):
model_tag = ModelTag()
model_tag.model = model_name.replace('damo/', '')
model_tag.sdk_version = __version__
# model_tag.domain = ""
# model_tag.task = ""
item = model_tag.ItemResult()
item.result = ut_result
item.name = 'ALL'
item.info = ''
model_tag.item_result.append(item.to_json())
model_tag.commit_ut_result()
+8
View File
@@ -0,0 +1,8 @@
from swift.model import get_model_processor
if __name__ == '__main__':
# model, tokenizer = get_model_processor('Qwen/Qwen2-7B-Instruct', attn_impl='flash_attn')
# model, tokenizer = get_model_processor('AIDC-AI/Ovis2-2B', attn_impl='flash_attn')
# model, tokenizer = get_model_processor('OpenGVLab/InternVL2-2B', attn_impl='flash_attn')
model, tokenizer = get_model_processor('Shanghai_AI_Laboratory/internlm3-8b-instruct', attn_impl='flash_attn')
print(model)
+17
View File
@@ -0,0 +1,17 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
def test_llama3():
from swift import InferArguments, infer_main
infer_main(
InferArguments(
model='LLM-Research/Meta-Llama-3.1-8B-Instruct',
max_batch_size=2,
val_dataset='AI-ModelScope/alpaca-gpt4-data-en#2'))
if __name__ == '__main__':
test_llama3()
+19
View File
@@ -0,0 +1,19 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def test_cogvlm():
from swift import InferArguments, SftArguments, infer_main, sft_main
# infer_main(InferArguments(model='ZhipuAI/cogvlm2-video-llama3-chat'))
sft_main(
SftArguments(
model='ZhipuAI/cogvlm2-video-llama3-chat',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#200', 'swift/VideoChatGPT:Generic#200'],
split_dataset_ratio=0.01))
if __name__ == '__main__':
test_cogvlm()
+566
View File
@@ -0,0 +1,566 @@
#!/usr/bin/env python
# Copyright (c) ModelScope Contributors. All rights reserved.
import argparse
import datetime
import math
import os
import pandas
import subprocess
import sys
import tempfile
import time
import torch
import unittest
import yaml
from fnmatch import fnmatch
from model_tag import ModelTag, commit_model_ut_result
from pathlib import Path
from test_utils import get_case_model_info
from unittest import TextTestResult
from swift.utils import get_logger
logger = get_logger()
def deduplicate_preserve_order(items):
return list(dict.fromkeys(items))
def get_available_npu_devices(visible_npus):
npu_devices = [device.strip() for device in visible_npus.split(',') if device.strip()]
if not npu_devices:
return []
try:
import torch_npu # noqa: F401
npu_count = torch.npu.device_count() if hasattr(torch, 'npu') and torch.npu.is_available() else 0
except Exception as e:
logger.warning('Failed to query torch.npu.device_count(): %s' % e)
return []
if npu_count <= 0:
logger.warning('ASCEND_RT_VISIBLE_DEVICES=%s, but torch.npu.device_count()=%s' % (visible_npus, npu_count))
return []
if npu_count < len(npu_devices):
logger.warning('ASCEND_RT_VISIBLE_DEVICES=%s, but torch.npu.device_count()=%s; using %s' %
(visible_npus, npu_count, ','.join(npu_devices[:npu_count])))
return npu_devices[:npu_count]
def test_cases_result_to_df(result_list):
table_header = ['Name', 'Result', 'Info', 'Start time', 'Stop time', 'Time cost(seconds)']
df = pandas.DataFrame(result_list, columns=table_header).sort_values(by=['Start time'], ascending=True)
return df
def statistics_test_result(df):
total_cases = df.shape[0]
# yapf: disable
success_cases = df.loc[df['Result'] == 'Success'].shape[0]
error_cases = df.loc[df['Result'] == 'Error'].shape[0]
failures_cases = df.loc[df['Result'] == 'Failures'].shape[0]
expected_failure_cases = df.loc[df['Result'] == 'ExpectedFailures'].shape[0]
unexpected_success_cases = df.loc[df['Result'] == 'UnexpectedSuccesses'].shape[0]
skipped_cases = df.loc[df['Result'] == 'Skipped'].shape[0]
# yapf: enable
if failures_cases > 0 or \
error_cases > 0 or \
unexpected_success_cases > 0:
final_result = 'FAILED'
else:
final_result = 'SUCCESS'
result_msg = '%s (Runs=%s,success=%s,failures=%s,errors=%s,\
skipped=%s,expected failures=%s,unexpected successes=%s)' % (final_result, total_cases, success_cases,
failures_cases, error_cases, skipped_cases,
expected_failure_cases, unexpected_success_cases)
model_cases = get_case_model_info()
for model_name, case_info in model_cases.items():
cases = df.loc[df['Name'].str.contains('|'.join(list(case_info)))]
results = cases['Result']
result = None
if any(results == 'Error') or any(results == 'Failures') or any(results == 'UnexpectedSuccesses'):
result = ModelTag.MODEL_FAIL
elif any(results == 'Success'):
result = ModelTag.MODEL_PASS
elif all(results == 'Skipped'):
result = ModelTag.MODEL_SKIP
else:
print(f'invalid results for {model_name} \n{result}')
if result is not None:
commit_model_ut_result(model_name, result)
print('Testing result summary.')
print(result_msg)
if final_result == 'FAILED':
sys.exit(1)
def gather_test_suites_in_files(test_dir, case_file_list, list_tests):
test_suite = unittest.TestSuite()
for case in deduplicate_preserve_order(case_file_list):
test_case = unittest.defaultTestLoader.discover(start_dir=test_dir, pattern=case)
test_suite.addTest(test_case)
if hasattr(test_case, '__iter__'):
for subcase in test_case:
if list_tests:
print(subcase)
else:
if list_tests:
print(test_case)
return test_suite
def gather_test_suites_files(test_dir, pattern):
case_file_list = []
for dirpath, dirnames, filenames in os.walk(test_dir):
for file in filenames:
if fnmatch(file, pattern):
case_file_list.append(file)
return deduplicate_preserve_order(case_file_list)
def collect_test_results(case_results):
result_list = [] # each item is Case, Result, Start time, Stop time, Time cost
for case_result in case_results.successes:
result_list.append((case_result.test_full_name, 'Success', '', case_result.start_time, case_result.stop_time,
case_result.time_cost))
for case_result in case_results.errors:
result_list.append((case_result[0].test_full_name, 'Error', case_result[1], case_result[0].start_time,
case_result[0].stop_time, case_result[0].time_cost))
for case_result in case_results.skipped:
result_list.append((case_result[0].test_full_name, 'Skipped', case_result[1], case_result[0].start_time,
case_result[0].stop_time, case_result[0].time_cost))
for case_result in case_results.expectedFailures:
result_list.append((case_result[0].test_full_name, 'ExpectedFailures', case_result[1],
case_result[0].start_time, case_result[0].stop_time, case_result[0].time_cost))
for case_result in case_results.failures:
result_list.append((case_result[0].test_full_name, 'Failures', case_result[1], case_result[0].start_time,
case_result[0].stop_time, case_result[0].time_cost))
for case_result in case_results.unexpectedSuccesses:
result_list.append((case_result.test_full_name, 'UnexpectedSuccesses', '', case_result.start_time,
case_result.stop_time, case_result.time_cost))
return result_list
def run_command_with_popen(cmd):
with subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, encoding='utf8') as sub_process:
for line in iter(sub_process.stdout.readline, ''):
sys.stdout.write(line)
def async_run_command_with_popen(cmd, device_id):
logger.info('Worker id: %s args: %s' % (device_id, cmd))
env = os.environ.copy()
visible_npus = env.get('ASCEND_RT_VISIBLE_DEVICES')
if visible_npus:
npu_devices = get_available_npu_devices(visible_npus)
if npu_devices:
env['ASCEND_RT_VISIBLE_DEVICES'] = npu_devices[device_id % len(npu_devices)]
logger.info('Worker id: %s ASCEND_RT_VISIBLE_DEVICES: %s' % (device_id, env['ASCEND_RT_VISIBLE_DEVICES']))
else:
env['CUDA_VISIBLE_DEVICES'] = '%s' % device_id
sub_process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
universal_newlines=True,
env=env,
encoding='utf8')
return sub_process
def save_test_result(df, args):
if args.result_dir is not None:
file_name = str(int(datetime.datetime.now().timestamp() * 1000))
os.umask(0)
Path(args.result_dir).mkdir(mode=0o777, parents=True, exist_ok=True)
Path(os.path.join(args.result_dir, file_name)).touch(mode=0o666, exist_ok=True)
df.to_pickle(os.path.join(args.result_dir, file_name))
def run_command(cmd):
logger.info('Running command: %s' % ' '.join(cmd))
response = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
response.check_returncode()
logger.info(response.stdout.decode('utf8'))
except subprocess.CalledProcessError as error:
logger.error('stdout: %s, stderr: %s' % (response.stdout.decode('utf8'), error.stderr.decode('utf8')))
def install_packages(pkgs):
if pkgs is None:
return
cmd = [sys.executable, '-m', 'pip', 'install']
for pkg in pkgs:
cmd.append(pkg)
run_command(cmd)
def install_requirements(requirements):
for req in requirements:
cmd = [
sys.executable, '-m', 'pip', 'install', '-r',
'requirements/%s' % req, '-f', 'https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html'
]
run_command(cmd)
def wait_for_free_worker(workers):
while True:
for idx, worker in enumerate(workers):
if worker is None:
logger.info('return free worker: %s' % (idx))
return idx
if worker.poll() is None: # running, get output
for line in iter(worker.stdout.readline, ''):
if line != '':
sys.stdout.write(line)
else:
break
else: # worker process completed.
logger.info('Process end: %s' % (idx))
workers[idx] = None
return idx
time.sleep(0.001)
def wait_for_workers(workers):
while True:
for idx, worker in enumerate(workers):
if worker is None:
continue
# check worker is completed.
if worker.poll() is None:
for line in iter(worker.stdout.readline, ''):
if line != '':
sys.stdout.write(line)
else:
break
else:
logger.info('Process idx: %s end!' % (idx))
workers[idx] = None
is_all_completed = True
for idx, worker in enumerate(workers):
if worker is not None:
is_all_completed = False
break
if is_all_completed:
logger.info('All sub process is completed!')
break
time.sleep(0.001)
def parallel_run_case_in_env(env_name, env, test_suite_env_map, isolated_cases, result_dir, parallel):
logger.info('Running case in env: %s' % env_name)
# install requirements and deps # run_config['envs'][env]
if 'requirements' in env:
install_requirements(env['requirements'])
if 'dependencies' in env:
install_packages(env['dependencies'])
# case worker processes
worker_processes = [None] * parallel
for test_suite_file in isolated_cases: # run case in subprocess
if test_suite_file in test_suite_env_map and test_suite_env_map[test_suite_file] == env_name:
cmd = [
'python',
'tests/run.py',
'--pattern',
test_suite_file,
'--result_dir',
result_dir,
]
worker_idx = wait_for_free_worker(worker_processes)
worker_process = async_run_command_with_popen(cmd, worker_idx)
os.set_blocking(worker_process.stdout.fileno(), False)
worker_processes[worker_idx] = worker_process
else:
pass # case not in run list.
# run remain cases in a process.
remain_suite_files = []
for k, v in test_suite_env_map.items():
if k not in isolated_cases and v == env_name:
remain_suite_files.append(k)
if len(remain_suite_files) == 0:
wait_for_workers(worker_processes)
return
# roughly split case in parallel
part_count = math.ceil(len(remain_suite_files) / parallel)
suites_chunks = [remain_suite_files[x:x + part_count] for x in range(0, len(remain_suite_files), part_count)]
for suites_chunk in suites_chunks:
worker_idx = wait_for_free_worker(worker_processes)
cmd = ['python', 'tests/run.py', '--result_dir', result_dir, '--suites']
for suite in suites_chunk:
cmd.append(suite)
worker_process = async_run_command_with_popen(cmd, worker_idx)
os.set_blocking(worker_process.stdout.fileno(), False)
worker_processes[worker_idx] = worker_process
wait_for_workers(worker_processes)
def run_case_in_env(env_name, env, test_suite_env_map, isolated_cases, result_dir):
# install requirements and deps # run_config['envs'][env]
if 'requirements' in env:
install_requirements(env['requirements'])
if 'dependencies' in env:
install_packages(env['dependencies'])
for test_suite_file in isolated_cases: # run case in subprocess
if test_suite_file in test_suite_env_map and test_suite_env_map[test_suite_file] == env_name:
cmd = [
'python',
'tests/run.py',
'--pattern',
test_suite_file,
'--result_dir',
result_dir,
]
run_command_with_popen(cmd)
else:
pass # case not in run list.
# run remain cases in a process.
remain_suite_files = []
for k, v in test_suite_env_map.items():
if k not in isolated_cases and v == env_name:
remain_suite_files.append(k)
if len(remain_suite_files) == 0:
return
cmd = ['python', 'tests/run.py', '--result_dir', result_dir, '--suites']
for suite in remain_suite_files:
cmd.append(suite)
run_command_with_popen(cmd)
def run_non_parallelizable_test_suites(suites, result_dir):
if len(suites) == 0:
return
cmd = ['python', 'tests/run.py', '--result_dir', result_dir, '--suites']
for suite in suites:
cmd.append(suite)
run_command_with_popen(cmd)
# Selected cases:
def get_selected_cases():
cmd = ['python', '-u', 'tests/run_analysis.py']
selected_cases = []
with subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, encoding='utf8') as sub_process:
for line in iter(sub_process.stdout.readline, ''):
sys.stdout.write(line)
if line.startswith('Selected cases:'):
line = line.replace('Selected cases:', '').strip()
selected_cases = line.split(',')
sub_process.wait()
if sub_process.returncode != 0:
msg = 'Run analysis exception, returncode: %s!' % sub_process.returncode
logger.error(msg)
raise Exception(msg)
return selected_cases
def run_in_subprocess(args):
# only case args.isolated_cases run in subprocess, all other run in a subprocess
if not args.no_diff: # run based on git diff
try:
test_suite_files = get_selected_cases()
logger.info('Tests suite to run: ')
for f in test_suite_files:
logger.info(f)
except Exception:
logger.error('Get test suite based diff exception!, will run all cases.')
test_suite_files = gather_test_suites_files(os.path.abspath(args.test_dir), args.pattern)
if len(test_suite_files) == 0:
logger.error('Get no test suite based on diff, run all the cases.')
test_suite_files = gather_test_suites_files(os.path.abspath(args.test_dir), args.pattern)
else:
test_suite_files = gather_test_suites_files(os.path.abspath(args.test_dir), args.pattern)
test_suite_files = deduplicate_preserve_order(test_suite_files)
non_parallelizable_suites = []
test_suite_files = [x for x in test_suite_files if x not in non_parallelizable_suites]
run_config = None
isolated_cases = []
test_suite_env_map = {}
# put all the case in default env.
for test_suite_file in test_suite_files:
test_suite_env_map[test_suite_file] = 'default'
if args.run_config is not None and Path(args.run_config).exists():
with open(args.run_config, encoding='utf-8') as f:
run_config = yaml.safe_load(f)
if 'isolated' in run_config:
isolated_cases = run_config['isolated']
if 'envs' in run_config:
for env in run_config['envs']:
if env != 'default':
for test_suite in run_config['envs'][env]['tests']:
if test_suite in test_suite_env_map:
test_suite_env_map[test_suite] = env
if args.subprocess: # run all case in subprocess
isolated_cases = test_suite_files
with tempfile.TemporaryDirectory() as temp_result_dir:
# first run cases that nonparallelizable
run_non_parallelizable_test_suites(non_parallelizable_suites, temp_result_dir)
# run case parallel in envs
for env in set(test_suite_env_map.values()):
parallel_run_case_in_env(env, run_config['envs'][env], test_suite_env_map, isolated_cases, temp_result_dir,
args.parallel)
result_dfs = []
result_path = Path(temp_result_dir)
for result in result_path.iterdir():
if Path.is_file(result):
df = pandas.read_pickle(result)
result_dfs.append(df)
result_pd = pandas.concat(result_dfs) # merge result of every test suite.
print_table_result(result_pd)
print_abnormal_case_info(result_pd)
statistics_test_result(result_pd)
def get_object_full_name(obj):
klass = obj.__class__
module = klass.__module__
if module == 'builtins':
return klass.__qualname__
return module + '.' + klass.__qualname__
class TimeCostTextTestResult(TextTestResult):
"""Record test case time used!"""
def __init__(self, stream, descriptions, verbosity):
self.successes = []
super(TimeCostTextTestResult, self).__init__(stream, descriptions, verbosity)
def startTest(self, test):
test.start_time = datetime.datetime.now()
test.test_full_name = get_object_full_name(test) + '.' + test._testMethodName
self.stream.writeln('Test case: %s start at: %s' % (test.test_full_name, test.start_time))
return super(TimeCostTextTestResult, self).startTest(test)
def stopTest(self, test):
TextTestResult.stopTest(self, test)
test.stop_time = datetime.datetime.now()
test.time_cost = (test.stop_time - test.start_time).total_seconds()
self.stream.writeln('Test case: %s stop at: %s, cost time: %s(seconds)' %
(test.test_full_name, test.stop_time, test.time_cost))
if torch.cuda.is_available() and test.time_cost > 5.0: # print nvidia-smi
cmd = ['nvidia-smi']
run_command_with_popen(cmd)
super(TimeCostTextTestResult, self).stopTest(test)
def addSuccess(self, test):
self.successes.append(test)
super(TextTestResult, self).addSuccess(test)
class TimeCostTextTestRunner(unittest.runner.TextTestRunner):
resultclass = TimeCostTextTestResult
def run(self, test):
return super(TimeCostTextTestRunner, self).run(test)
def _makeResult(self):
result = super(TimeCostTextTestRunner, self)._makeResult()
return result
def gather_test_cases(test_dir, pattern, list_tests):
case_list = gather_test_suites_files(test_dir, pattern)
test_suite = unittest.TestSuite()
for case in case_list:
test_case = unittest.defaultTestLoader.discover(start_dir=test_dir, pattern=case)
test_suite.addTest(test_case)
if hasattr(test_case, '__iter__'):
for subcase in test_case:
if list_tests:
print(subcase)
else:
if list_tests:
print(test_case)
return test_suite
def print_abnormal_case_info(df):
df = df.loc[(df['Result'] == 'Error') | (df['Result'] == 'Failures')]
for _, row in df.iterrows():
print('Case %s run result: %s, msg:\n%s' % (row['Name'], row['Result'], row['Info']))
def print_table_result(df):
df = df.loc[df['Result'] != 'Skipped']
df = df.drop('Info', axis=1)
formatters = {
'Name': '{{:<{}s}}'.format(df['Name'].str.len().max()).format,
'Result': '{{:<{}s}}'.format(df['Result'].str.len().max()).format,
}
with pandas.option_context('display.max_rows', None, 'display.max_columns', None, 'display.width', None):
print(df.to_string(justify='left', formatters=formatters, index=False))
def main(args):
runner = TimeCostTextTestRunner()
if args.suites is not None and len(args.suites) > 0:
logger.info('Running: %s' % ' '.join(args.suites))
test_suite = gather_test_suites_in_files(args.test_dir, args.suites, args.list_tests)
else:
test_suite = gather_test_cases(os.path.abspath(args.test_dir), args.pattern, args.list_tests)
if not args.list_tests:
result = runner.run(test_suite)
logger.info('Running case completed, pid: %s, suites: %s' % (os.getpid(), args.suites))
result = collect_test_results(result)
df = test_cases_result_to_df(result)
if args.result_dir is not None:
save_test_result(df, args)
else:
print_table_result(df)
print_abnormal_case_info(df)
statistics_test_result(df)
if __name__ == '__main__':
parser = argparse.ArgumentParser('test runner')
parser.add_argument('--list_tests', action='store_true', help='list all tests')
parser.add_argument('--pattern', default='test_*.py', help='test file pattern')
parser.add_argument('--test_dir', default='tests', help='directory to be tested')
parser.add_argument('--level', default=0, type=int, help='2 -- all, 1 -- p1, 0 -- p0')
parser.add_argument('--profile', action='store_true', help='enable profiling')
parser.add_argument('--run_config', default=None, help='specified case run config file(yaml file)')
parser.add_argument('--subprocess', action='store_true', help='run all test suite in subprocess')
parser.add_argument('--result_dir', default=None, help='Save result to directory, internal use only')
parser.add_argument(
'--parallel', default=1, type=int, help='Set case parallels, default single process, set with gpu number.')
parser.add_argument(
'--no-diff',
action='store_true',
help='Default running case based on git diff(with master), disable with --no-diff)')
parser.add_argument('--suites', nargs='*', help='Run specified test suites(test suite files list split by space)')
args = parser.parse_args()
print(args)
if args.run_config is not None or args.subprocess:
run_in_subprocess(args)
else:
main(args)
+8
View File
@@ -0,0 +1,8 @@
# isolate cases in env, we can install different dependencies in each env.
isolated: # test cases that may require excessive amount of GPU memory or run long time, which will be executed in dedicated process.
envs:
default: # default env, case not in other env will in default, pytorch.
dependencies: # requirement packagespip install before test case run.
# - numpy>=1.20,<=1.22.0
# - protobuf<4,>=3.20.2
+36
View File
@@ -0,0 +1,36 @@
import os
def test_client():
import json
from swift import SamplingArguments, sampling_main
base_url = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
api_key = os.environ.get('OPENAI_API_KEY')
engine_kwargs = json.dumps({
'base_url': base_url,
'api_key': api_key,
})
dataset = 'tastelikefeet/competition_math#5'
system = """A conversation between User and Assistant. The user asks a question, and the Assistant solves it.
The assistant first thinks about the reasoning process in the mind and then provides the user
with the answer. The reasoning process and answer are enclosed
within <think> </think> and <answer> </answer> tags, respectively,
i.e., <think> reasoning process here </think> <answer> answer here </answer>."""
args = SamplingArguments(
sampler_type='distill',
sampler_engine='client',
model='deepseek-r1',
dataset=dataset,
num_return_sequences=1,
stream=True,
system=system,
temperature=0.6,
top_p=0.95,
engine_kwargs=engine_kwargs,
)
sampling_main(args)
if __name__ == '__main__':
test_client()
+59
View File
@@ -0,0 +1,59 @@
import os
from pprint import pprint
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 4,
'per_device_eval_batch_size': 4,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
'save_steps': 100,
'max_length': 512,
'task_type': 'seq_cls',
'num_labels': 2,
}
def calc_acc(infer_result):
n_correct = 0
for res in infer_result:
if res['response'] == res['labels']:
n_correct += 1
return f'acc: {n_correct / len(infer_result)}, n_correct: {n_correct}, len(res): {len(infer_result)}'
def test_llm():
from swift import InferArguments, SftArguments, infer_main, sft_main
res = []
for model in ['Qwen/Qwen2.5-0.5B-Instruct', 'Qwen/Qwen2.5-0.5B', 'AI-ModelScope/bert-base-chinese']:
dataset = ['DAMO_NLP/jd:cls#2000']
result = sft_main(SftArguments(model=model, dataset=dataset, split_dataset_ratio=0.1, **kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_result = infer_main(
InferArguments(adapters=[last_model_checkpoint], load_data_args=True, truncation_strategy='right'))
res.append(calc_acc(infer_result))
infer_result2 = infer_main(
InferArguments(
adapters=[last_model_checkpoint], load_data_args=True, max_batch_size=16, truncation_strategy='right'))
res.append(calc_acc(infer_result2))
model = 'Qwen/Qwen2.5-0.5B-Instruct'
dataset = ['DAMO_NLP/jd#2000']
train_kwargs = kwargs.copy()
train_kwargs.pop('task_type')
train_kwargs.pop('num_labels')
result = sft_main(SftArguments(model=model, dataset=dataset, split_dataset_ratio=0.1, **train_kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_result = infer_main(
InferArguments(adapters=[last_model_checkpoint], load_data_args=True, truncation_strategy='right'))
res.append(calc_acc(infer_result))
infer_result2 = infer_main(
InferArguments(
adapters=[last_model_checkpoint], load_data_args=True, max_batch_size=16, truncation_strategy='right'))
res.append(calc_acc(infer_result2))
pprint(res)
if __name__ == '__main__':
test_llm()
+83
View File
@@ -0,0 +1,83 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def _infer_image(model, system=None, images=None):
engine = LmdeployEngine(model)
if images is None:
images = ['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png']
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages.append({'role': 'user', 'content': 'describe the image.'})
resp_list = engine.infer([InferRequest(messages=messages, images=images)],
RequestConfig(temperature=0, max_tokens=64, repetition_penalty=1.))
return resp_list[0].choices[0].message.content
def _infer_image_pipeline(model, images=None, prefix='<IMAGE_TOKEN>\n'):
from lmdeploy import GenerationConfig, pipeline
from lmdeploy.vl import load_image
from swift.utils import safe_snapshot_download
gen_config = GenerationConfig(temperature=0., repetition_penalty=1., max_new_tokens=64)
pipe = pipeline(safe_snapshot_download(model))
image = load_image('http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png')
response = pipe((f'{prefix}describe the image.', image), gen_config=gen_config)
return response.text
def test_internvl2_5():
model = 'OpenGVLab/InternVL2_5-4B'
response = _infer_image(model)
response2 = _infer_image_pipeline(model)
assert response == response2
def test_internvl2():
model = 'OpenGVLab/InternVL2-2B'
response = _infer_image(model)
response2 = _infer_image_pipeline(model) # Missing '\n' after '<|im_end|>'
assert response == response2
def test_deepseek_vl():
model = 'deepseek-ai/deepseek-vl-1.3b-chat'
response = _infer_image(model)
response2 = _infer_image_pipeline(model, prefix='<IMAGE_TOKEN>')
assert response == response2
def test_qwen_vl():
model = 'Qwen/Qwen-VL-Chat'
response = _infer_image_pipeline(model) # Missing: 'Picture 1: '
response2 = _infer_image(model)
assert response == response2
def test_qwen2_vl():
model = 'Qwen/Qwen2-VL-2B-Instruct'
response = _infer_image_pipeline(model, prefix='<IMAGE_TOKEN>')
response2 = _infer_image(model)
assert response == response2
def test_qwen2_5_vl():
model = 'Qwen/Qwen2.5-VL-3B-Instruct'
response = _infer_image(model)
response2 = _infer_image_pipeline(model, prefix='<IMAGE_TOKEN>')
assert response == response2
if __name__ == '__main__':
from swift.infer_engine import InferRequest, LmdeployEngine, RequestConfig
# test_internvl2()
# test_internvl2_5()
# test_deepseek_vl()
# test_qwen_vl()
# test_qwen2_vl()
test_qwen2_5_vl()
+568
View File
@@ -0,0 +1,568 @@
import copy
import os
import pytest
import torch
from contextlib import contextmanager, nullcontext
from typing import Any, Dict
from swift.model import get_processor
from swift.template import get_template
try:
from vllm.config import ModelConfig
from vllm.multimodal import MULTIMODAL_REGISTRY
from vllm.multimodal.inputs import nested_tensors_equal
except ImportError:
ModelConfig = None
MULTIMODAL_REGISTRY = None
nested_tensors_equal = None
pytestmark = pytest.mark.skipif(ModelConfig is None, reason='vLLM not available')
WEATHER_AUDIO = 'http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/weather.wav'
BABY_VIDEO = 'https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4'
DRAW_VIDEO = 'https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2.5-Omni/draw.mp4'
CAT_IMAGE = 'http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png'
_SKIP_TRAIN_KEYS = frozenset({'input_ids', 'labels', 'loss_scale', 'mm_token_type_ids'})
_QWEN_VL_VIDEO_ALIASES = {'video_second_per_grid': 'second_per_grid_ts'}
_GEMMA4_IMAGE_ALIASES = {'image_position_ids': 'pixel_position_ids'}
_QWEN3_OMNI_AUDIO_ALIASES = {
'input_features': 'input_audio_features',
'feature_attention_mask': 'feature_attention_mask',
}
_XFAIL_TESTS = frozenset({
'test_qwen2_5_vl_video',
'test_qwen3_vl_video',
'test_qwen3_5_video',
'test_gemma4_video',
})
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def as_list_ids(x):
if isinstance(x, torch.Tensor):
return x.reshape(-1).tolist()
return list(x)
def tensors_aligned(a, b) -> bool:
if isinstance(a, list) and isinstance(b, list):
return len(a) == len(b) and all(tensors_aligned(x, y) for x, y in zip(a, b))
if isinstance(a, list) and len(a) == 1:
a = a[0]
if isinstance(b, list) and len(b) == 1:
b = b[0]
if isinstance(a, torch.Tensor) and isinstance(b, torch.Tensor):
a, b = a.detach().cpu(), b.detach().cpu()
if a.ndim == b.ndim + 1 and a.shape[0] == 1 and a.shape[1:] == b.shape:
a = a.squeeze(0)
elif b.ndim == a.ndim + 1 and b.shape[0] == 1 and b.shape[1:] == a.shape:
b = b.squeeze(0)
if a.shape != b.shape:
return False
if a.dtype.is_floating_point or b.dtype.is_floating_point:
a = a.to(torch.bfloat16).float()
b = b.to(torch.bfloat16).float()
return torch.allclose(a, b, rtol=0, atol=0)
return torch.equal(a, b)
if nested_tensors_equal is None:
return a == b
return nested_tensors_equal(a, b)
def build_vllm_mm_data(vllm_encoded: Dict[str, Any]) -> Dict[str, Any]:
mm_data = {}
for plural, singular in [('images', 'image'), ('videos', 'video'), ('audios', 'audio')]:
data = vllm_encoded.get(plural)
if not data:
continue
if len(data) == 1 and not isinstance(data[0], tuple):
mm_data[singular] = data[0]
else:
mm_data[singular] = data
return mm_data
def swift_train_encode(template, sample: dict) -> Dict[str, Any]:
train_template = copy.deepcopy(template)
train_template.set_mode('train')
return train_template.encode(sample)
def vllm_forward_kwargs(model_id: str, template, sample: dict) -> Dict[str, Any]:
if ModelConfig is None:
raise RuntimeError('vLLM is not available')
vllm_template = copy.deepcopy(template)
vllm_template.set_mode('vllm')
encoded = vllm_template.encode(sample)
mm_data = build_vllm_mm_data(encoded)
if not mm_data:
return {'input_ids': encoded['input_ids'], 'mm_tensors': {}}
model_config = ModelConfig(model_id, trust_remote_code=True, dtype='auto', seed=0)
processor = MULTIMODAL_REGISTRY.create_processor(model_config)
mm_items = processor.info.parse_mm_data(mm_data)
result = processor(
encoded['input_ids'],
mm_items=mm_items,
hf_processor_mm_kwargs=encoded.get('mm_processor_kwargs') or {},
)
return {
'input_ids': result['prompt_token_ids'],
'mm_tensors': result['mm_kwargs'].get_data(),
}
@contextmanager
def audio_backend(backend: str):
prev = os.environ.get('SWIFT_AUDIO_LOAD_BACKEND')
os.environ['SWIFT_AUDIO_LOAD_BACKEND'] = backend
try:
yield
finally:
if prev is None:
os.environ.pop('SWIFT_AUDIO_LOAD_BACKEND', None)
else:
os.environ['SWIFT_AUDIO_LOAD_BACKEND'] = prev
@pytest.fixture(autouse=True)
def _soundfile_pyav_for_align_tests():
with audio_backend('soundfile_pyav'):
yield
def _vllm_audio_feature_lengths(train: dict, vllm_tensors: dict) -> None:
"""vLLM sets audio_feature_lengths; Swift derives the same value from mask.sum()."""
vllm_afl = vllm_tensors.get('audio_feature_lengths')
mask = train.get('feature_attention_mask')
if vllm_afl is None or mask is None:
return
derived = mask.sum(-1)
if derived.ndim == 0:
derived = derived.unsqueeze(0)
assert tensors_aligned(derived, vllm_afl), 'mask.sum() != vLLM audio_feature_lengths'
@contextmanager
def use_audio_in_video(enabled: bool = True):
prev = os.environ.get('USE_AUDIO_IN_VIDEO')
if enabled:
os.environ['USE_AUDIO_IN_VIDEO'] = 'true'
else:
os.environ.pop('USE_AUDIO_IN_VIDEO', None)
try:
yield
finally:
if prev is None:
os.environ.pop('USE_AUDIO_IN_VIDEO', None)
else:
os.environ['USE_AUDIO_IN_VIDEO'] = prev
def _assert_mm_align(
model_id,
sample,
*,
tensor_key_aliases=None,
check_input_ids=True,
check_vllm_audio_feature_lengths=False,
use_audio_in_video_flag=False,
):
tensor_key_aliases = tensor_key_aliases or {}
ctx = use_audio_in_video() if use_audio_in_video_flag else nullcontext()
with ctx:
processor = get_processor(model_id)
template = get_template(processor)
train = swift_train_encode(template, sample)
vllm = vllm_forward_kwargs(model_id, template, sample)
if check_input_ids:
assert as_list_ids(train['input_ids']) == as_list_ids(vllm['input_ids'])
vllm_tensors = dict(vllm['mm_tensors'])
compared = [(tk, tensor_key_aliases.get(tk, tk))
for tk in sorted(k for k, v in train.items() if v is not None and k not in _SKIP_TRAIN_KEYS)
if tensor_key_aliases.get(tk, tk) in vllm_tensors]
for train_key, vllm_key in compared:
assert tensors_aligned(train[train_key], vllm_tensors[vllm_key]), f'{train_key}!={vllm_key}'
if check_vllm_audio_feature_lengths:
_vllm_audio_feature_lengths(train, vllm_tensors)
# ---------------------------------------------------------------------------
# Qwen2.5-VL
# ---------------------------------------------------------------------------
def test_qwen2_5_vl_image():
_assert_mm_align(
'Qwen/Qwen2.5-VL-3B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the image.'
}],
'images': [CAT_IMAGE]
},
)
@pytest.mark.xfail(reason='vLLM Qwen2_5_VLProcessor rejects fps list in mm_processor_kwargs (expects scalar)')
def test_qwen2_5_vl_video():
_assert_mm_align(
'Qwen/Qwen2.5-VL-3B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the video.'
}],
'videos': [BABY_VIDEO]
},
tensor_key_aliases=_QWEN_VL_VIDEO_ALIASES,
)
# ---------------------------------------------------------------------------
# Qwen3-VL
# ---------------------------------------------------------------------------
def test_qwen3_vl_image():
_assert_mm_align(
'Qwen/Qwen3-VL-2B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the image.'
}],
'images': [CAT_IMAGE]
},
)
@pytest.mark.xfail(reason='vLLM get_video_repl drops outer vision_start/end wrapper (2-token diff vs HF)')
def test_qwen3_vl_video():
_assert_mm_align(
'Qwen/Qwen3-VL-2B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the video.'
}],
'videos': [BABY_VIDEO]
},
tensor_key_aliases=_QWEN_VL_VIDEO_ALIASES,
)
# ---------------------------------------------------------------------------
# Qwen3.5
# ---------------------------------------------------------------------------
def test_qwen3_5_image():
_assert_mm_align(
'Qwen/Qwen3.5-2B',
{
'messages': [{
'role': 'user',
'content': 'describe the image.'
}],
'images': [CAT_IMAGE]
},
)
@pytest.mark.xfail(reason='vLLM get_video_repl drops outer vision_start/end wrapper (2-token diff vs HF)')
def test_qwen3_5_video():
_assert_mm_align(
'Qwen/Qwen3.5-2B',
{
'messages': [{
'role': 'user',
'content': 'describe the video.'
}],
'videos': [BABY_VIDEO]
},
tensor_key_aliases=_QWEN_VL_VIDEO_ALIASES,
)
# ---------------------------------------------------------------------------
# Qwen2.5-Omni
# ---------------------------------------------------------------------------
def test_qwen2_5_omni_image():
_assert_mm_align(
'Qwen/Qwen2.5-Omni-7B',
{
'messages': [{
'role': 'user',
'content': 'describe the image.'
}],
'images': [CAT_IMAGE]
},
)
def test_qwen2_5_omni_video():
_assert_mm_align(
'Qwen/Qwen2.5-Omni-7B',
{
'messages': [{
'role': 'user',
'content': 'describe the video.'
}],
'videos': [BABY_VIDEO]
},
tensor_key_aliases={'video_second_per_grid': 'second_per_grid_ts'},
)
def test_qwen2_5_omni_audio():
# Standalone audio: sample has `audios` field (not extracted from video).
# vLLM path loads as (wav, sr) in _preprocess_inputs; train path uses ndarray.
_assert_mm_align(
'Qwen/Qwen2.5-Omni-7B',
{
'messages': [{
'role': 'user',
'content': 'describe the audio.'
}],
'audios': [WEATHER_AUDIO]
},
check_vllm_audio_feature_lengths=True,
)
def test_qwen2_5_omni_video_use_audio_in_video():
# Video track extracted in replace_tag; vLLM uses different audio/video token layout.
_assert_mm_align(
'Qwen/Qwen2.5-Omni-7B',
{
'messages': [{
'role': 'user',
'content': 'describe the video.'
}],
'videos': [DRAW_VIDEO]
},
tensor_key_aliases={'video_second_per_grid': 'second_per_grid_ts'},
check_input_ids=False,
check_vllm_audio_feature_lengths=True,
use_audio_in_video_flag=True,
)
# ---------------------------------------------------------------------------
# Qwen3-Omni
# ---------------------------------------------------------------------------
def test_qwen3_omni_image():
_assert_mm_align(
'Qwen/Qwen3-Omni-30B-A3B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the image.'
}],
'images': [CAT_IMAGE]
},
)
def test_qwen3_omni_video():
_assert_mm_align(
'Qwen/Qwen3-Omni-30B-A3B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the video.'
}],
'videos': [BABY_VIDEO]
},
tensor_key_aliases=_QWEN_VL_VIDEO_ALIASES,
)
def test_qwen3_omni_audio():
_assert_mm_align(
'Qwen/Qwen3-Omni-30B-A3B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the audio.'
}],
'audios': [WEATHER_AUDIO]
},
tensor_key_aliases=_QWEN3_OMNI_AUDIO_ALIASES,
check_vllm_audio_feature_lengths=True,
)
def test_qwen3_omni_audio_non_hop_aligned(tmp_path):
"""Verify hop-length floor trim when waveform length is not hop-aligned."""
import soundfile as sf
from swift.template.vision_utils import load_audio
hop = 160
wav = load_audio(WEATHER_AUDIO, 16000)
n = len(wav)
rem = n % hop
cut = rem if rem else hop // 2 + 1
wav = wav[:max(n - cut, hop + 1)]
assert len(wav) % hop != 0, 'test fixture must be non hop-aligned'
wav_path = tmp_path / 'non_hop_aligned.wav'
sf.write(str(wav_path), wav, 16000)
_assert_mm_align(
'Qwen/Qwen3-Omni-30B-A3B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the audio.'
}],
'audios': [str(wav_path)]
},
tensor_key_aliases=_QWEN3_OMNI_AUDIO_ALIASES,
check_vllm_audio_feature_lengths=True,
)
def test_qwen3_omni_video_use_audio_in_video():
_assert_mm_align(
'Qwen/Qwen3-Omni-30B-A3B-Instruct',
{
'messages': [{
'role': 'user',
'content': 'describe the video.'
}],
'videos': [DRAW_VIDEO]
},
tensor_key_aliases={
**_QWEN3_OMNI_AUDIO_ALIASES,
**_QWEN_VL_VIDEO_ALIASES
},
check_input_ids=False,
check_vllm_audio_feature_lengths=True,
use_audio_in_video_flag=True,
)
# ---------------------------------------------------------------------------
# Gemma4
# ---------------------------------------------------------------------------
def test_gemma4_image():
_assert_mm_align(
'google/gemma-4-E2B-it',
{
'messages': [{
'role': 'user',
'content': 'describe the image.'
}],
'images': [CAT_IMAGE]
},
tensor_key_aliases=_GEMMA4_IMAGE_ALIASES,
)
def test_gemma4_audio():
_assert_mm_align(
'google/gemma-4-E2B-it',
{
'messages': [{
'role': 'user',
'content': 'describe the audio.'
}],
'audios': [WEATHER_AUDIO]
},
tensor_key_aliases={
'input_features': 'input_features_padded',
'input_features_mask': 'input_features_mask',
},
)
def test_gemma4_audio_collator_3d():
"""Collator must batch audio as (N, max_len, feat_dim), not concat along time dim."""
sample = {
'messages': [{
'role': 'user',
'content': 'describe the audio.'
}],
'audios': [WEATHER_AUDIO],
}
processor = get_processor('google/gemma-4-E2B-it')
template = get_template(processor)
template.set_mode('train')
batch = [template.encode(sample), template.encode(sample)]
collated = template._data_collator_mm_data(batch)
assert collated['input_features'].ndim == 3
assert collated['input_features'].shape[0] == 2
assert collated['input_features_mask'].ndim == 2
assert collated['input_features_mask'].shape[0] == 2
@pytest.mark.xfail(reason='vLLM gemma4 video timestamp/soft-token path differs from HF Gemma4VideoProcessor')
def test_gemma4_video():
_assert_mm_align(
'google/gemma-4-E2B-it',
{
'messages': [{
'role': 'user',
'content': '<video>describe the video.'
}],
'videos': [BABY_VIDEO]
},
tensor_key_aliases={
'pixel_values_videos': 'pixel_values_videos',
'video_position_ids': 'video_position_ids',
},
)
if __name__ == '__main__':
tests = [
test_qwen2_5_vl_image,
test_qwen2_5_vl_video,
test_qwen3_vl_image,
test_qwen3_vl_video,
test_qwen3_5_image,
test_qwen3_5_video,
test_qwen2_5_omni_image,
test_qwen2_5_omni_video,
test_qwen2_5_omni_audio,
test_qwen2_5_omni_video_use_audio_in_video,
test_qwen3_omni_image,
test_qwen3_omni_video,
test_qwen3_omni_audio,
test_qwen3_omni_audio_non_hop_aligned,
test_qwen3_omni_video_use_audio_in_video,
test_gemma4_image,
test_gemma4_audio,
test_gemma4_audio_collator_3d,
test_gemma4_video,
]
passed = xfailed = failed = 0
for fn in tests:
name = fn.__name__
try:
fn()
print(f'{name}: PASS')
passed += 1
except Exception:
if name in _XFAIL_TESTS:
print(f'{name}: XFAIL (expected upstream vLLM mismatch)')
xfailed += 1
else:
print(f'{name}: FAIL')
failed += 1
raise
print(f'all mm processor align tests finished: {passed} passed, {xfailed} xfailed, {failed} failed')
+73
View File
@@ -0,0 +1,73 @@
import os
from pprint import pprint
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 4,
'per_device_eval_batch_size': 4,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
'save_steps': 100,
'max_length': 8192,
}
def calc_acc(infer_result):
n_correct = 0
for res in infer_result:
if res['response'] == res['labels']:
n_correct += 1
return f'acc: {n_correct / len(infer_result)}, n_correct: {n_correct}, len(res): {len(infer_result)}'
def calc_diff(infer_result, infer_result2):
n_correct = 0
for x1, x2 in zip(infer_result, infer_result2):
if x1['response'] == x2['response']:
n_correct += 1
return f'acc: {n_correct / len(infer_result)}, n_correct: {n_correct}, len(res): {len(infer_result)}'
def test_llm():
from swift import InferArguments, SftArguments, infer_main, sft_main
res = []
for padding_side in ['left', 'right']:
model = 'Qwen/Qwen2.5-0.5B-Instruct'
dataset = ['damo/zh_cls_fudan-news#2000']
result = sft_main(
SftArguments(model=model, dataset=dataset, split_dataset_ratio=0.1, padding_side=padding_side, **kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_result = infer_main(InferArguments(adapters=[last_model_checkpoint], load_data_args=True))
res.append(calc_acc(infer_result))
infer_result2 = infer_main(
InferArguments(adapters=[last_model_checkpoint], load_data_args=True, max_batch_size=16))
res.append(calc_acc(infer_result2))
pprint(res)
def test_mllm():
from swift import InferArguments, SftArguments, infer_main, sft_main
from swift.template import Template
res = []
for padding_side in ['left', 'right']:
model = 'Qwen/Qwen2-VL-2B-Instruct'
dataset = ['AI-ModelScope/LaTeX_OCR#2000']
result = sft_main(
SftArguments(model=model, dataset=dataset, split_dataset_ratio=0.01, padding_side=padding_side, **kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_result = infer_main(InferArguments(adapters=[last_model_checkpoint], load_data_args=True))
res.append(infer_result)
infer_result2 = infer_main(
InferArguments(adapters=[last_model_checkpoint], load_data_args=True, max_batch_size=16))
res.append(infer_result2)
print(calc_diff(res[0], res[1]))
print(calc_diff(res[2], res[3]))
print(calc_diff(res[0], res[2]))
print(calc_diff(res[0], res[3]))
print(calc_diff(res[2], res[1]))
if __name__ == '__main__':
test_llm()
test_mllm()
View File
@@ -0,0 +1,748 @@
import os
os.environ['SWIFT_DEBUG'] = '1'
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3'
system = 'You are a helpful assistant.'
tools = [{
'type': 'function',
'function': {
'name': 'get_current_weather',
'description': 'Get the current weather in a given location',
'parameters': {
'type': 'object',
'properties': {
'location': {
'type': 'string',
'description': 'The city and state, e.g. San Francisco, CA'
},
'unit': {
'type': 'string',
'enum': ['celsius', 'fahrenheit']
}
},
'required': ['location']
}
}
}, {
'name_for_model': 'tool2',
'name_for_human': '工具2',
'description': 'Tool2的描述',
}]
glm4_tools = [{
'type': 'function',
'function': {
'name': 'realtime_aqi',
'description': '天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息',
'parameters': {
'type': 'object',
'properties': {
'city': {
'description': '城市名'
}
},
'required': ['city']
}
}
}]
glm4_tool_messasges = [
{
'role': 'tool',
'content': '{"city": "北京", "aqi": "10", "unit": "celsius"}'
},
{
'role': 'tool',
'content': '{"city": "上海", "aqi": "72", "unit": "fahrenheit"}'
},
]
glm4_query = '北京和上海今天的天气情况'
def _infer(engine, num_tools: int = 1, agent_tools=None, tool_messages=None, query=None):
if agent_tools is None:
agent_tools = tools
if tool_messages is None:
tool_messages = []
for _ in range(num_tools):
tool_messages.append({
'role': 'tool',
'content': '{"temperature": 32, "condition": "Sunny", "humidity": 50}'
})
stop = [engine.template.agent_template.keyword.observation]
query = query or "How's the weather in Beijing today?"
infer_request = InferRequest([{'role': 'user', 'content': query}], tools=agent_tools)
request_config = RequestConfig(max_tokens=512, stop=stop, temperature=0)
resp_list = engine.infer([infer_request], request_config=request_config)
response = resp_list[0].choices[0].message.content
toolcall = resp_list[0].choices[0].message.tool_calls[0].function
print(f'response: {response}')
print(f'toolcall: {toolcall}')
assert toolcall is not None
infer_request.messages.append({'role': 'assistant', 'content': response})
infer_request.messages += tool_messages
resp_list = engine.infer([infer_request], request_config=request_config)
response2 = resp_list[0].choices[0].message.content
print(f'response2: {response2}')
infer_request.messages.append({'role': 'assistant', 'content': response2})
return infer_request.messages
def test_react_en():
agent_template = agent_template_map['react_en']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 1144
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'react_en'
messages = _infer(engine)
assert messages[-1]['content'] == (
'Thought: The current temperature in Beijing is 32 degrees Celsius, and the condition is sunny '
'with a humidity of 50%.\nFinal Answer: The current temperature in Beijing is 32 degrees Celsius,'
' and the condition is sunny with a humidity of 50%.')
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
def test_react_zh():
agent_template = agent_template_map['react_zh']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 712
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'react_zh'
_infer(engine)
def test_qwen_en():
agent_template = agent_template_map['qwen_en']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 879
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'qwen_en'
messages = _infer(engine)
assert messages[-1]['content'] == (
'✿RETURN✿: Today in Beijing, the temperature is 32°C with sunny conditions and the humidity '
'is at 50%. Enjoy the nice weather!')
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
def test_qwen_zh():
agent_template = agent_template_map['qwen_zh']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 577
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'qwen_zh'
_infer(engine)
def test_qwen_en_parallel():
agent_template = agent_template_map['qwen_en_parallel']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 1012
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'qwen_en_parallel'
messages = _infer(engine, num_tools=2)
assert messages[-1]['content'] == (
'✿RETURN✿: Today in Beijing, the temperature is 32 degrees Celsius with sunny conditions '
'and the humidity is at 50%. Enjoy the nice weather!')
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
def test_qwen_zh_parallel():
agent_template = agent_template_map['qwen_zh_parallel']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 688
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'qwen_zh_parallel'
_infer(engine, num_tools=2)
def test_hermes():
agent_template = agent_template_map['hermes']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 875
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'hermes'
messages = _infer(engine, num_tools=2)
template.template_backend = 'jinja'
messages2 = _infer(engine, num_tools=2)
assert messages[-1]['content'] == messages2[-1]['content'] == (
'Today in Beijing, the temperature is 32 degrees Celsius with sunny conditions '
'and the humidity is at 50%. Enjoy the nice weather!')
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert encoded['input_ids'] == encoded2['input_ids']
def test_toolbench():
agent_template = agent_template_map['toolbench']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 1833
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
template = engine.template
template._agent_template = 'toolbench'
_infer(engine)
def test_chatglm4():
agent_template = agent_template_map['chatglm4']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 846
engine = TransformersEngine('ZhipuAI/glm-4-9b-chat')
template = engine.template
template._agent_template = 'chatglm4'
_infer(engine, agent_tools=glm4_tools, tool_messages=glm4_tool_messasges, query=glm4_query)
def test_glm4():
agent_template = agent_template_map['glm4']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 769
engine = TransformersEngine('ZhipuAI/GLM-4-9B-0414')
template = engine.template
template._agent_template = 'glm4'
messages = _infer(engine, agent_tools=glm4_tools, tool_messages=glm4_tool_messasges, query=glm4_query)
assert messages[-1]['content'] == '根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。'
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
def test_llama3():
engine = TransformersEngine('LLM-Research/Llama-3.2-3B-Instruct')
template = engine.template
template._agent_template = 'llama3'
messages = _infer(engine)
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
def test_llama4():
engine = TransformersEngine('LLM-Research/Llama-4-Scout-17B-16E-Instruct')
template = engine.template
messages = _infer(engine)
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
def test_hunyuan():
engine = TransformersEngine('Tencent-Hunyuan/Hunyuan-1.8B-Instruct')
template = engine.template
template.template_backend = 'jinja'
_infer(engine, num_tools=2)
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert encoded['input_ids'][:-1] == encoded2['input_ids']
def test_glm4_5():
engine = TransformersEngine('ZhipuAI/GLM-4.5-Air')
template = engine.template
template.template_backend = 'jinja'
_infer(engine, num_tools=2)
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert encoded['input_ids'][:-1] == encoded2['input_ids']
def test_glm4_7():
engine = TransformersEngine('ZhipuAI/GLM-4.7-FP8', load_model=False)
template = engine.template
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert encoded['input_ids'][:-1] == encoded2['input_ids']
def test_qwen3_coder():
engine = TransformersEngine('Qwen/Qwen3-Coder-30B-A3B-Instruct')
template = engine.template
template.template_backend = 'jinja'
_infer(engine, num_tools=2)
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert encoded['input_ids'] == encoded2['input_ids']
def test_qwen3_5():
engine = TransformersEngine('Qwen/Qwen3.5-35B-A3B')
template = engine.template
template.template_backend = 'jinja'
_infer(engine, num_tools=2)
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
data['messages'].insert(0, {'role': 'system', 'content': 'You are a helpful assistant.'})
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert encoded['input_ids'] == encoded2['input_ids']
def test_deepseek_v3_1():
engine = TransformersEngine('deepseek-ai/DeepSeek-V3.1', load_model=False)
template = engine.template
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
# To test multiple tool calls and responses, we duplicate some messages.
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
expected_input_ids = (
'<begin▁of▁sentence>\n\n## Tools\n'
'You have access to the following tools:\n\n'
'### convert_temperature\n'
'Description: Convert temperature from one unit to another\n\n'
"Parameters: {\"type\": \"object\", \"properties\": {\"temperature\": {\"type\": \"number\", "
"\"description\": \"The temperature value\"}, \"from_unit\": {\"type\": \"string\", \"description\": "
"\"The unit to convert from\"}, \"to_unit\": {\"type\": \"string\", \"description\": \"The unit "
"to convert to\"}}, \"required\": [\"temperature\", \"from_unit\", \"to_unit\"]}\n\n"
'### get_current_date\n'
'Description: Get the current date\n\n'
'Parameters: {}\n\n'
'IMPORTANT: ALWAYS adhere to this exact format for tool use:\n'
'<tool▁calls▁begin><tool▁call▁begin>tool_call_name<tool▁sep>tool_call_arguments<tool▁call▁end>'
'{additional_tool_calls}<tool▁calls▁end>\n\n'
'Where:\n'
'- `tool_call_name` must be an exact match to one of the available tools\n'
"- `tool_call_arguments` must be valid JSON that strictly follows the tool's Parameters Schema\n"
'- For multiple tool calls, chain them directly without separators or spaces<User>'
'Hi, I need to convert a temperature from Celsius to Fahrenheit. The temperature is 30 degrees Celsius.'
'<Assistant></think><tool▁calls▁begin><tool▁call▁begin>convert_temperature<tool▁sep>'
"{\"temperature\": 30, \"from_unit\": \"Celsius\", \"to_unit\": \"Fahrenheit\"}<tool▁call▁end>"
'<tool▁call▁begin>convert_temperature<tool▁sep>'
"{\"temperature\": 30, \"from_unit\": \"Celsius\", \"to_unit\": \"Fahrenheit\"}<tool▁call▁end>"
'<tool▁calls▁end><end▁of▁sentence>'
"<tool▁output▁begin>{\"converted_temperature\": 86}<tool▁output▁end>"
"<tool▁output▁begin>{\"converted_temperature\": 86}<tool▁output▁end>"
'The converted temperature from 30 degrees Celsius to Fahrenheit is 86 degrees Fahrenheit.<end▁of▁sentence>')
# Expected labels string
expected_labels = (
'[-100 * 239]</think><tool▁calls▁begin><tool▁call▁begin>convert_temperature<tool▁sep>'
"{\"temperature\": 30, \"from_unit\": \"Celsius\", \"to_unit\": \"Fahrenheit\"}<tool▁call▁end>"
'<tool▁call▁begin>convert_temperature<tool▁sep>'
"{\"temperature\": 30, \"from_unit\": \"Celsius\", \"to_unit\": \"Fahrenheit\"}<tool▁call▁end>"
'<tool▁calls▁end><end▁of▁sentence>[-100 * 22]'
'The converted temperature from 30 degrees Celsius to Fahrenheit is 86 degrees Fahrenheit.<end▁of▁sentence>')
assert template.safe_decode(encoded['input_ids']) == expected_input_ids
assert template.safe_decode(encoded['labels']) == expected_labels
assert encoded['input_ids'][-122:] == encoded2['input_ids'][1:]
def test_youtu():
agent_template = agent_template_map['youtu']()
new_system = agent_template._format_tools(tools, system)
assert len(new_system) == 883
engine = TransformersEngine('Tencent-YouTu-Research/Youtu-LLM-2B')
template = engine.template
template._agent_template = 'youtu'
stop = [template.agent_template.keyword.observation]
query = "How's the weather in Beijing today?"
tool_messages = [{'role': 'tool', 'content': '{"temperature": 32, "condition": "Sunny", "humidity": 50}'}]
infer_request = InferRequest([{'role': 'user', 'content': query}], tools=tools)
request_config = RequestConfig(max_tokens=2048, stop=stop, temperature=0)
# First inference: get tool call
resp_list = engine.infer([infer_request], request_config=request_config)
response = resp_list[0].choices[0].message.content
toolcall = resp_list[0].choices[0].message.tool_calls
print(f'response: {response}')
print(f'toolcall: {toolcall}')
assert toolcall is not None, 'No tool_call generated'
infer_request.messages.append({'role': 'assistant', 'content': response})
infer_request.messages += tool_messages
# Second inference: get final response
resp_list = engine.infer([infer_request], request_config=request_config)
response2 = resp_list[0].choices[0].message.content
print(f'response2: {response2}')
infer_request.messages.append({'role': 'assistant', 'content': response2})
messages = infer_request.messages
template.set_mode('train')
encoded = template.encode({'messages': messages})
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
template.template_backend = 'swift'
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert encoded['input_ids'] == encoded2['input_ids']
def test_deepseek_v4():
engine = TransformersEngine('deepseek-ai/DeepSeek-V4-Flash', load_model=False)
template = engine.template
tools = [{
'type': 'function',
'function': {
'name': 'get_weather',
'description': 'Get the weather for a specific location',
'parameters': {
'type': 'object',
'properties': {
'location': {
'type': 'string',
'description': 'The city name'
},
'unit': {
'type': 'string',
'enum': ['celsius', 'fahrenheit'],
'description': 'Temperature unit'
}
},
'required': ['location']
}
}
}, {
'type': 'function',
'function': {
'name': 'search',
'description': 'Search the web for information',
'parameters': {
'type': 'object',
'properties': {
'query': {
'type': 'string',
'description': 'Search query'
},
'num_results': {
'type': 'integer',
'description': 'Number of results to return'
}
},
'required': ['query']
}
}
}]
data = {
'tools':
tools,
'messages': [{
'role': 'system',
'content': 'You are a helpful assistant.'
}, {
'role': 'user',
'content': "What's the weather in Beijing?"
}, {
'role':
'assistant',
'content':
'<think>The user wants to know the weather in Beijing. I should use the get_weather tool.</think>\n\n'
}, {
'role':
'tool_call',
'content':
'{"name": "get_weather", "arguments": "{\\"location\\": \\"Beijing\\", \\"unit\\": \\"celsius\\"}"}'
}, {
'role': 'tool_response',
'content': '{"temperature": 22, "condition": "sunny", "humidity": 45}'
}, {
'role':
'assistant',
'content': ('<think>Got the weather data. Let me format a nice response.</think>'
'The weather in Beijing is currently sunny with a temperature of 22°C and 45% humidity.')
}]
}
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
expected_input_ids = (
'<begin▁of▁sentence>You are a helpful assistant.\n\n## Tools\n\n'
'You have access to a set of tools to help answer the user\'s question. '
'You can invoke tools by writing a "<DSMLtool_calls>" block like the following:\n\n'
'<DSMLtool_calls>\n'
'<DSMLinvoke name="$TOOL_NAME">\n'
'<DSMLparameter name="$PARAMETER_NAME" string="true|false">$PARAMETER_VALUE</DSMLparameter>\n'
'...\n'
'</DSMLinvoke>\n'
'<DSMLinvoke name="$TOOL_NAME2">\n'
'...\n'
'</DSMLinvoke>\n'
'</DSMLtool_calls>\n\n'
'String parameters should be specified as is and set `string="true"`. '
'For all other types (numbers, booleans, arrays, objects), '
'pass the value in JSON format and set `string="false"`.\n\n'
'If thinking_mode is enabled (triggered by <think>), '
'you MUST output your complete reasoning inside <think>...</think> BEFORE any tool calls or final response.'
'\n\nOtherwise, output directly after </think> with tool calls or final response.\n\n'
'### Available Tool Schemas\n\n'
'{"name": "get_weather", "description": "Get the weather for a specific location", '
'"parameters": {"type": "object", "properties": {"location": {"type": "string", '
'"description": "The city name"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], '
'"description": "Temperature unit"}}, "required": ["location"]}}\n'
'{"name": "search", "description": "Search the web for information", '
'"parameters": {"type": "object", "properties": {"query": {"type": "string", '
'"description": "Search query"}, "num_results": {"type": "integer", '
'"description": "Number of results to return"}}, "required": ["query"]}}\n\n'
'You MUST strictly follow the above defined tool name and parameter schemas to invoke tool calls.\n'
'<User>What\'s the weather in Beijing?<Assistant>'
'<think>The user wants to know the weather in Beijing. I should use the get_weather tool.</think>\n\n'
'<DSMLtool_calls>\n'
'<DSMLinvoke name="get_weather">\n'
'<DSMLparameter name="location" string="true">Beijing</DSMLparameter>\n'
'<DSMLparameter name="unit" string="true">celsius</DSMLparameter>\n'
'</DSMLinvoke>\n'
'</DSMLtool_calls>'
'<end▁of▁sentence>'
'<User><tool_result>{"temperature": 22, "condition": "sunny", "humidity": 45}</tool_result>'
'<Assistant>'
'<think>Got the weather data. Let me format a nice response.</think>'
'The weather in Beijing is currently sunny with a temperature of 22°C and 45% humidity.'
'<end▁of▁sentence>')
assert template.safe_decode(encoded['input_ids']) == expected_input_ids
def test_seed_oss():
engine = TransformersEngine('ByteDance-Seed/Seed-OSS-36B-Instruct', load_model=False)
template = engine.template
dataset = load_dataset('AI-ModelScope/function-calling-chatml')[0]
data = dataset[6]
# To test multiple tool calls and responses, we duplicate some messages.
data['messages'].insert(1, data['messages'][1])
data['messages'].insert(3, data['messages'][3])
# Incomplete tool function will cause seed template to throw an error.
data['tools'] = [('{\n'
' "name": "convert_temperature",\n'
' "description": "Convert temperature from one unit to another",\n'
' "parameters": {\n'
' "type": "object",\n'
' "properties": {\n'
' "temperature": {\n'
' "type": "number",\n'
' "description": "The temperature value"\n'
' },\n'
' "from_unit": {\n'
' "type": "string",\n'
' "description": "The unit to convert from"\n'
' },\n'
' "to_unit": {\n'
' "type": "string",\n'
' "description": "The unit to convert to"\n'
' }\n'
' },\n'
' "required": [\n'
' "temperature",\n'
' "from_unit",\n'
' "to_unit"\n'
' ]\n'
' }\n'
'}'),
('{\n'
' "name": "get_current_date",\n'
' "description": "Get the current date",\n'
' "parameters": {\n'
' "type": "object",\n'
' "properties": {\n'
' "date": {\n'
' "type": "number",\n'
' "description": "The date value"}}}\n'
'}')]
data['thinking_budget'] = 0
template.template_backend = 'swift'
template.set_mode('train')
encoded = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded["input_ids"])}')
print(f'labels: {template.safe_decode(encoded["labels"])}')
import re
expected_input_ids = re.sub(
r'<seed:think>.*?</seed:think>', '', template.safe_decode(encoded['input_ids']), flags=re.DOTALL)
template.template_backend = 'jinja'
encoded2 = template.encode(data)
print(f'input_ids: {template.safe_decode(encoded2["input_ids"])}')
print(f'labels: {template.safe_decode(encoded2["labels"])}')
assert template.safe_decode(encoded2['input_ids']) == expected_input_ids
if __name__ == '__main__':
from swift import InferRequest, RequestConfig, TransformersEngine, agent_template_map, load_dataset
# test_react_en()
# test_react_zh()
# test_qwen_en()
# test_qwen_zh()
# test_qwen_en_parallel()
# test_qwen_zh_parallel()
# test_hermes()
# test_toolbench()
# test_chatglm4()
# test_glm4()
# test_llama3()
# test_llama4()
# test_hunyuan()
# test_glm4_5()
# test_glm4_7()
# test_qwen3_coder()
# test_qwen3_5()
# test_deepseek_v3_1()
test_deepseek_v4()
# test_seed_oss()
# test_youtu()
@@ -0,0 +1,116 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3'
def _infer_model(engine, system=None, messages=None, audios=None):
seed_everything(42)
request_config = RequestConfig(max_tokens=128, temperature=0)
if messages is None:
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages += [{'role': 'user', 'content': '你好'}]
resp = engine.infer([{'messages': messages}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}]
messages += [{'role': 'user', 'content': '<audio>这段语音说了什么'}]
else:
messages = messages.copy()
if audios is None:
audios = ['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/weather.wav']
resp = engine.infer([{'messages': messages, 'audios': audios}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}]
logger.info(f'model: {engine.model_info.model_name}, messages: {messages}')
return response
def test_qwen_audio():
engine = TransformersEngine('Qwen/Qwen-Audio-Chat')
_infer_model(engine)
def test_qwen2_audio():
# transformers==4.48.3
engine = TransformersEngine('Qwen/Qwen2-Audio-7B-Instruct')
messages = [{'role': 'user', 'content': '<audio>'}]
audios = ['https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/guess_age_gender.wav']
response = _infer_model(engine, messages=messages, audios=audios)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, audios=audios)
assert response == response2 == 'Yes, the speaker is female and in her twenties.'
def test_xcomposer2d5_ol():
engine = TransformersEngine('Shanghai_AI_Laboratory/internlm-xcomposer2d5-ol-7b:audio')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_step_audio_chat():
engine = TransformersEngine('stepfun-ai/Step-Audio-Chat')
response = _infer_model(engine, messages=[{'role': 'user', 'content': '<audio>'}])
assert response == ('是的呢,今天天气晴朗,阳光明媚,微风和煦,非常适合外出活动。天空湛蓝,白云朵朵,让人心情愉悦。希望你能好好享受这美好的一天!')
def test_qwen2_5_omni():
USE_AUDIO_IN_VIDEO = True
os.environ['USE_AUDIO_IN_VIDEO'] = str(USE_AUDIO_IN_VIDEO)
engine = TransformersEngine('Qwen/Qwen2.5-Omni-7B')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_gemma3n():
engine = TransformersEngine('google/gemma-3n-E4B-it')
messages = [{'role': 'user', 'content': '<audio>Transcribe this audio and complete the statement'}]
audios = ['https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/guess_age_gender.wav']
response = _infer_model(engine, messages=messages, audios=audios)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, audios=audios)
assert response == response2
def test_midashenglm():
engine = TransformersEngine('mispeech/midashenglm-7b')
messages = [{'role': 'user', 'content': '<audio>Caption the audio.'}]
response = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages)
assert response == response2 == "The audio contains a male voice speaking the phrase '今天天气真好呀' in Mandarin."
def test_step_audio2_mini():
engine = TransformersEngine('stepfun-ai/Step-Audio-2-mini')
messages = [{'role': 'user', 'content': '<audio>Caption the audio'}]
response = _infer_model(engine, messages=messages)
assert response == 'A woman says "今天天气真好呀" in Mandarin.'
def test_qwen3_asr():
messages = [{'role': 'user', 'content': '<audio>'}]
audios = ['https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-ASR-Repo/asr_zh.wav']
engine = TransformersEngine('Qwen/Qwen3-ASR-1.7B')
engine.template._response_prefix = 'language Chinese<asr_text>'
response = _infer_model(engine, messages=messages, audios=audios)
assert response == 'language Chinese<asr_text>甚至出现交易几乎停滞的情况。'
if __name__ == '__main__':
from swift.infer_engine import RequestConfig, TransformersEngine
from swift.utils import get_logger, seed_everything
logger = get_logger()
# test_qwen_audio()
# test_qwen2_audio()
# test_xcomposer2d5_ol()
# test_step_audio_chat()
# test_qwen2_5_omni()
# test_gemma3n()
# test_midashenglm()
# test_step_audio2_mini()
test_qwen3_asr()
@@ -0,0 +1,28 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
os.environ['SWIFT_DEBUG'] = '1'
def test_deepseek_janus_pro_gene():
from swift import InferArguments, infer_main
args = InferArguments(model='deepseek-ai/Janus-Pro-1B', infer_backend='transformers')
infer_main(args)
def test_emu3_gen(infer_backend):
from swift import InferArguments, infer_main
args = InferArguments(
model='BAAI/Emu3-Gen',
infer_backend=infer_backend,
stream=False,
use_chat_template=False,
top_k=2048,
max_new_tokens=40960)
infer_main(args)
if __name__ == '__main__':
# test_emu3_gen('transformers')
test_deepseek_janus_pro_gene()
+795
View File
@@ -0,0 +1,795 @@
import json
import os
import torch
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['SWIFT_DEBUG'] = '1'
def _infer_model(engine, system=None, messages=None):
seed_everything(42)
request_config = RequestConfig(max_tokens=128, temperature=0)
if messages is None:
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages += [{'role': 'user', 'content': '你好'}]
resp = engine.infer([{'messages': messages}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}, {'role': 'user', 'content': '<image>这是什么'}]
else:
messages = messages.copy()
resp = engine.infer([{
'messages': messages,
}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}]
logger.info(f'model: {engine.model_info.model_name}, messages: {messages}')
return response
def test_baichuan_m1():
engine = TransformersEngine('baichuan-inc/Baichuan-M1-14B-Instruct')
messages = [{'role': 'user', 'content': '你是谁'}]
response = _infer_model(engine, messages=messages)
assert response == '我是一个人工智能助手,可以回答你的问题并提供帮助。'
def test_qwen2_5():
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct-1M')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_qwen3():
engine = TransformersEngine('Qwen/Qwen3-4B')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_qwen3_guard():
engine = TransformersEngine('Qwen/Qwen3Guard-Gen-0.6B')
messages = [{'role': 'user', 'content': 'How can I make a bomb?'}]
response = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages)
assert response == response2
def test_yufeng_xguard():
engine = TransformersEngine('Alibaba-AAIG/YuFeng-XGuard-Reason-0.6B')
messages = [{'role': 'user', 'content': 'How can I make a bomb?'}]
response = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages)
assert response == response2
def test_phi4():
engine = TransformersEngine('LLM-Research/phi-4')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_phi4_mini():
engine = TransformersEngine('LLM-Research/Phi-4-mini-instruct')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_qwen1_5():
engine = TransformersEngine('Qwen/Qwen1.5-0.5B-Chat-GPTQ-Int4')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_chatglm4():
engine = TransformersEngine('ZhipuAI/glm-4-9b-chat')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_glm4():
models = ['ZhipuAI/GLM-4-9B-0414', 'ZhipuAI/GLM-Z1-9B-0414', 'ZhipuAI/GLM-Z1-Rumination-32B-0414']
for model in models:
engine = TransformersEngine(model)
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_qwq():
engine = TransformersEngine('Qwen/QwQ-32B-Preview')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_internlm():
engine = TransformersEngine('Shanghai_AI_Laboratory/internlm-chat-7b')
_infer_model(engine)
def test_internlm2():
engine = TransformersEngine('Shanghai_AI_Laboratory/internlm2_5-1_8b-chat')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_internlm3():
engine = TransformersEngine('Shanghai_AI_Laboratory/internlm3-8b-instruct')
response = _infer_model(engine, system='')
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_yi_coder():
engine = TransformersEngine('01ai/Yi-Coder-1.5B-Chat')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_yi():
engine = TransformersEngine('01ai/Yi-6B-Chat')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_deepseek_moe():
engine = TransformersEngine('deepseek-ai/deepseek-moe-16b-chat')
_infer_model(engine)
def test_codegeex4():
# jinja is missing a prefix.
engine = TransformersEngine('ZhipuAI/codegeex4-all-9b')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_telechat():
engine = TransformersEngine('TeleAI/TeleChat-12B', torch_dtype=torch.float16)
messages = [{'role': 'user', 'content': '你是谁'}]
response = _infer_model(engine, messages=messages)
assert response == ('我是中国电信星辰语义大模型,英文名TeleChat,是由中国电信自主研发的生成式大语言模型。\n\n'
'我基于Transformer-decoder结构,学习了海量知识,包括百科、书籍、论坛、党政媒体、GitHub代码、专业领域知识等,'
'具备自然语言处理、语义理解、内容创作和逻辑推理等能力,可以与人类进行对话互动和情感交流,还能提供知识问答、创作写作、'
'代码生成等服务,希望能为人类带来更加智能、高效和便捷的工作与生活体验。')
def test_telechat2():
engine = TransformersEngine('TeleAI/TeleChat2-7B-32K', torch_dtype=torch.float16)
messages = [{'role': 'system', 'content': '你是一个乐于助人的智能助手,请使用用户提问的语言进行有帮助的问答'}, {'role': 'user', 'content': '你好'}]
response = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages)
assert response == response2
def test_glm_edge():
engine = TransformersEngine('ZhipuAI/glm-edge-1.5b-chat')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_llama():
from swift.infer_engine import VllmEngine
# engine = TransformersEngine('LLM-Research/Meta-Llama-3.1-8B-Instruct-BNB-NF4')
# engine = TransformersEngine('LLM-Research/Meta-Llama-3.1-8B-Instruct')
# engine = TransformersEngine('LLM-Research/Meta-Llama-3-8B-Instruct')
engine = VllmEngine('LLM-Research/Llama-3.2-1B-Instruct')
# engine = TransformersEngine('AI-ModelScope/Llama-3.1-Nemotron-70B-Instruct-HF')
# engine = TransformersEngine('unsloth/Llama-3.3-70B-Instruct-bnb-4bit')
res = _infer_model(engine, system='')
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, system='')
assert res == res2, f'res: {res}, res2: {res2}'
def test_openbuddy():
# engine = TransformersEngine('OpenBuddy/openbuddy-yi1.5-34b-v21.3-32k')
engine = TransformersEngine('OpenBuddy/openbuddy-nemotron-70b-v23.2-131k')
# engine = TransformersEngine('OpenBuddy/openbuddy-llama3.3-70b-v24.3-131k')
res = _infer_model(engine, system='')
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_megrez():
engine = TransformersEngine('InfiniAI/Megrez-3b-Instruct')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_skywork_o1():
engine = TransformersEngine('AI-ModelScope/Skywork-o1-Open-Llama-3.1-8B')
res = _infer_model(
engine,
messages=[{
'role':
'user',
'content':
('Jane has 12 apples. She gives 4 apples to her friend Mark, then buys 1 more apple, and finally splits '
'all her apples equally among herself and her 2 siblings. How many apples does each person get?')
}])
assert res == ("To solve the problem, let's break it down into a series of logical steps:\n\n1. **Initial Number "
'of Apples**: Jane starts with 12 apples.\n2. **Apples Given Away**: Jane gives 4 apples to her '
'friend Mark. So, the number of apples she has now is:\n \\[\n 12 - 4 = 8\n \\]\n3. **Apples '
'Bought**: Jane then buys 1 more apple. So, the number of apples she has now is:\n \\[\n '
'8 + 1 = 9\n \\]\n4. **Apples Split Equally')
def test_internlm2_reward():
engine = TransformersEngine('Shanghai_AI_Laboratory/internlm2-1_8b-reward')
messages = [{
'role': 'user',
'content': "Hello! What's your name?"
}, {
'role': 'assistant',
'content': 'My name is InternLM2! A helpful AI assistant. What can I do for you?'
}]
res = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, messages=messages)
assert res == res2 == '0.48681640625'
def test_qwen2_reward():
engine = TransformersEngine('Qwen/Qwen2-Math-RM-72B')
messages = [{
'role':
'user',
'content': ('Suppose that a certain software product has a mean time between failures of 10,000 hours '
'and has a mean time to repair of 20 hours. If the product is used by 100 customers, '
'what is its availability?\nAnswer Choices: (A) 80% (B) 90% (C) 98% (D) 99.80%\nPlease '
'reason step by step, and put your final answer within \\boxed{}.')
}, {
'role':
'assistant',
'content': ("To find the availability of the software product, we'll use the formula:\n\n\\[ \\text{ "
'availability} = \\frac{\\text{Mean Time Between Failures (MTBF)}}{\\text{Mean Time Between '
'Failures (MTBF) + Mean Time To Repair (MTTR)}} \\]\n\nGiven:\n- MTBF = 10,000 hours\n- MTTR '
"= 20 hours\n\nLet's plug these values into the formula:\n\n\\[ \\text{availability} = "
'\\frac{10,000}{10,000 + 20} = \\frac{10,000}{10,020} \\]\n\nTo simplify this fraction, '
'we can divide both the numerator and the denominator by 10,000:\n\n\\[ \\text{availability} ='
' \\frac{10,000 \\div 10,000}{10,020 \\div 10,000} = \\frac{1}{1.002} \\]\n\nTo express this as'
' a percentage, we can calculate the decimal value of the fraction and then multiply by '
'100:\n\n\\[ \\text{availability} \\approx 0.998002 \\times 100 = 99.80\\% \\]\n\nTherefore, '
'the availability of the software product is approximately 99.80%.\n\nThe correct answer is '
'\\boxed{D}')
}]
res = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, messages=messages)
assert res == '1.84375' and res2 == '1.390625' # \n diff
def test_qwen2_5_math():
engine = TransformersEngine('Qwen/Qwen2.5-Math-1.5B-Instruct')
messages = [{'role': 'user', 'content': 'Find the value of $x$ that satisfies the equation $4x+5 = 6x+7$.'}]
res = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, messages=messages)
assert res == res2
def test_skywork_reward():
prompt = ('Jane has 12 apples. She gives 4 apples to her friend Mark, then buys 1 more apple, and finally splits '
'all her apples equally among herself and her 2 siblings. How many apples does each person get?')
response = ('1. Jane starts with 12 apples and gives 4 to Mark. 12 - 4 = 8. Jane now has 8 apples.\n2. Jane buys '
'1 more apple. 8 + 1 = 9. Jane now has 9 apples.\n3. Jane splits the 9 apples equally among herself '
'and her 2 siblings (3 people in total). 9 ÷ 3 = 3 apples each. Each person gets 3 apples.')
engine = TransformersEngine('AI-ModelScope/Skywork-Reward-Llama-3.1-8B-v0.2')
messages = [{'role': 'user', 'content': prompt}, {'role': 'assistant', 'content': response}]
res = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, messages=messages)
assert res == '14.25'
assert res2 == '13.8125'
def test_deepseek_r1_distill():
engine = TransformersEngine('deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_deepseek_prover_v2():
engine = TransformersEngine('deepseek-ai/DeepSeek-Prover-V2-7B')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_qwen2_5_prm():
engine = TransformersEngine('Qwen/Qwen2.5-Math-7B-PRM800K')
data = {
'system':
'Please reason step by step, and put your final answer within \\boxed{}.',
'query': ('Sue lives in a fun neighborhood. One weekend, the neighbors decided to play a prank on Sue. '
"On Friday morning, the neighbors placed 18 pink plastic flamingos out on Sue's front yard. "
'On Saturday morning, the neighbors took back one third of the flamingos, painted them white, and '
"put these newly painted white flamingos back out on Sue's front yard. Then, on Sunday morning, "
'they added another 18 pink plastic flamingos to the collection. At noon on Sunday, how many more '
'pink plastic flamingos were out than white plastic flamingos?'),
'response':
[('To find out how many more pink plastic flamingos were out than white plastic flamingos at noon on Sunday, '
'we can break down the problem into steps. First, on Friday, the neighbors start with 18 pink '
'plastic flamingos.'),
('On Saturday, they take back one third of the flamingos. Since there were 18 flamingos, (1/3 \\times 18 = 6) '
'flamingos are taken back. So, they have (18 - 6 = 12) flamingos left in their possession. Then, they paint '
"these 6 flamingos white and put them back out on Sue's front yard. Now, Sue has the original 12 pink "
'flamingos plus the 6 new white ones. Thus, by the end of Saturday, Sue has (12 + 6 = 18) pink flamingos '
'and 6 white flamingos.'),
("On Sunday, the neighbors add another 18 pink plastic flamingos to Sue's front yard. By the end of Sunday "
'morning, Sue has (18 + 18 = 36) pink flamingos and still 6 white flamingos.'),
('To find the difference, subtract the number of white flamingos from the number of pink '
'flamingos: (36 - 6 = 30). Therefore, at noon on Sunday, there were 30 more pink plastic flamingos out '
'than white plastic flamingos. The answer is (\\boxed{30}).')]
}
messages = [
{
'role': 'system',
'content': data['system']
},
{
'role': 'user',
'content': data['query']
},
{
'role': 'assistant',
'content': '<extra_0>'.join(data['response']) + '<extra_0>'
},
]
res = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, messages=messages)
assert res == res2 == json.dumps([0.9921875, 0.2490234375, 0.70703125, 0.9375]), f'res: {res}, res2: {res2}'
def test_mistral_small():
engine = TransformersEngine('mistralai/Mistral-Small-24B-Instruct-2501')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_moonlight():
engine = TransformersEngine('moonshotai/Moonlight-16B-A3B-Instruct')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_ling():
engine = TransformersEngine('inclusionAI/Ling-lite')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_gemma3():
engine = TransformersEngine('LLM-Research/gemma-3-1b-it')
res = _infer_model(engine, system='You are a helpful assistant')
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, system='You are a helpful assistant')
assert res == res2, f'res: {res}, res2: {res2}'
def test_mimo():
engine = TransformersEngine('XiaomiMiMo/MiMo-7B-RL-0530')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_minicpm():
engine = TransformersEngine('OpenBMB/MiniCPM4-0.5B')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_minimax():
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3,4,5,6,7'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3,4,5,6,7'
from transformers import QuantoConfig
quantization_config = QuantoConfig(weights='int8')
messages = [{
'role': 'system',
'content': 'You are a helpful assistant.'
}, {
'role': 'user',
'content': 'who are you?'
}]
engine = TransformersEngine('MiniMax/MiniMax-M1-40k', quantization_config=quantization_config)
res = _infer_model(engine, messages=messages)
print(f'res: {res}')
def test_kimi_dev():
engine = TransformersEngine('moonshotai/Kimi-Dev-72B')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_hunyuan():
# engine = TransformersEngine('Tencent-Hunyuan/Hunyuan-A13B-Instruct')
engine = TransformersEngine('Tencent-Hunyuan/Hunyuan-4B-Instruct')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_ernie():
engine = TransformersEngine('PaddlePaddle/ERNIE-4.5-0.3B-PT')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_devstral():
from swift.template.templates.mistral import devstral_small_2505_system
engine = TransformersEngine('mistralai/Devstral-Small-2505')
res = _infer_model(engine, system=devstral_small_2505_system)
engine.template.template_backend = 'jinja'
# taken from: https://github.com/vllm-project/vllm/blob/main/examples/tool_chat_template_mistral3.jinja
chat_template = (
'{%- set today = strftime_now("%Y-%m-%d") %}\n'
'{%- set default_system_message = "You are Mistral Small 3, a Large Language Model (LLM) '
'created by Mistral AI, a French startup headquartered in Paris.\\nYour knowledge base was '
'last updated on 2023-10-01. The current date is " + today + ".\\n\\nWhen you\'re not sure '
'about some information, you say that you don\'t have the information and don\'t make up '
'anything.\\nIf the user\'s question is not clear, ambiguous, or does not provide enough '
'context for you to accurately answer the question, you do not try to answer it right away '
'and you rather ask the user to clarify their request (e.g. \\"What are some good restaurants '
'around me?\\" => \\"Where are you?\\" or \\"When is the next flight to Tokyo\\" => '
'\\"Where do you travel from?\\")" %}\n\n'
'{{- bos_token }}\n\n'
'{%- if messages[0][\'role\'] == \'system\' %}\n'
' {%- if messages[0][\'content\'] is string %}\n'
' {%- set system_message = messages[0][\'content\'] %}\n'
' {%- set loop_messages = messages[1:] %}\n'
' {%- else %}\n'
' {%- set system_message = messages[0][\'content\'][0][\'text\'] %}\n'
' {%- set loop_messages = messages[1:] %}\n'
' {%- endif %}\n'
'{%- else %}\n'
' {%- set system_message = default_system_message %}\n'
' {%- set loop_messages = messages %}\n'
'{%- endif %}\n'
'{%- if not tools is defined %}\n'
' {%- set tools = none %}\n'
'{%- elif tools is not none %}\n'
' {%- set parallel_tool_prompt = "You are a helpful assistant that can call tools. '
'If you call one or more tools, format them in a single JSON array or objects, where each '
'object is a tool call, not as separate objects outside of an array or multiple arrays. '
'Use the format [{\\"name\\": tool call name, \\"arguments\\": tool call arguments}, '
'additional tool calls] if you call more than one tool. If you call tools, do not attempt '
'to interpret them or otherwise provide a response until you receive a tool call result '
'that you can interpret for the user." %}\n'
' {%- if system_message is defined %}\n'
' {%- set system_message = parallel_tool_prompt + "\\n\\n" + system_message %}\n'
' {%- else %}\n'
' {%- set system_message = parallel_tool_prompt %}\n'
' {%- endif %}\n'
'{%- endif %}\n'
'{{- \'[SYSTEM_PROMPT]\' + system_message + \'[/SYSTEM_PROMPT]\' }}\n\n'
'{%- set user_messages = loop_messages | selectattr("role", "equalto", "user") | list %}\n\n'
'{%- set filtered_messages = [] %}\n'
'{%- for message in loop_messages %}\n'
' {%- if message["role"] not in ["tool", "tool_results"] and not message.get("tool_calls") %}\n'
' {%- set filtered_messages = filtered_messages + [message] %}\n'
' {%- endif %}\n'
'{%- endfor %}\n\n'
'{%- for message in filtered_messages %}\n'
' {%- if (message["role"] == "user") != (loop.index0 % 2 == 0) %}\n'
' {{- raise_exception("After the optional system message, conversation roles must '
'alternate user/assistant/user/assistant/...") }}\n'
' {%- endif %}\n'
'{%- endfor %}\n\n'
'{%- for message in loop_messages %}\n'
' {%- if message["role"] == "user" %}\n'
' {%- if tools is not none and (message == user_messages[-1]) %}\n'
' {{- "[AVAILABLE_TOOLS] [" }}\n'
' {%- for tool in tools %}\n'
' {%- set tool = tool.function %}\n'
' {{- \'{"type": "function", "function": {\' }}\n'
' {%- for key, val in tool.items() if key != "return" %}\n'
' {%- if val is string %}\n'
' {{- \'"\' + key + \'": "\' + val + \'"\' }}\n'
' {%- else %}\n'
' {{- \'"\' + key + \'": \' + val|tojson }}\n'
' {%- endif %}\n'
' {%- if not loop.last %}\n'
' {{- ", " }}\n'
' {%- endif %}\n'
' {%- endfor %}\n'
' {{- "}}" }}\n'
' {%- if not loop.last %}\n'
' {{- ", " }}\n'
' {%- else %}\n'
' {{- "]" }}\n'
' {%- endif %}\n'
' {%- endfor %}\n'
' {{- "[/AVAILABLE_TOOLS]" }}\n'
' {%- endif %}\n'
' {%- if message[\'content\'] is string %}\n'
' {{- \'[INST]\' + message[\'content\'] + \'[/INST]\' }}\n'
' {%- else %}\n'
' {{- \'[INST]\' }}\n'
' {%- for block in message[\'content\'] %}\n'
' {%- if block[\'type\'] == \'text\' %}\n'
' {{- block[\'text\'] }}\n'
' {%- elif block[\'type\'] == \'image\' or block[\'type\'] == \'image_url\' %}\n'
' {{- \'[IMG]\' }}\n'
' {%- else %}\n'
' {{- raise_exception(\'Only text and image blocks are supported '
'in message content!\') }}\n'
' {%- endif %}\n'
' {%- endfor %}\n'
' {{- \'[/INST]\' }}\n'
' {%- endif %}\n'
' {%- elif message["role"] == "tool_calls" or message.tool_calls is defined %}\n'
' {%- if message.tool_calls is defined %}\n'
' {%- set tool_calls = message.tool_calls %}\n'
' {%- else %}\n'
' {%- set tool_calls = message.content %}\n'
' {%- endif %}\n'
' {{- "[TOOL_CALLS] [" }}\n'
' {%- for tool_call in tool_calls %}\n'
' {%- set out = tool_call.function|tojson %}\n'
' {{- out[:-1] }}\n'
' {%- if not tool_call.id is defined or tool_call.id|length < 9 %}\n'
' {{- raise_exception("Tool call IDs should be alphanumeric strings with '
'length >= 9! (1)" + tool_call.id) }}\n'
' {%- endif %}\n'
' {{- \', "id": "\' + tool_call.id[-9:] + \'"}\' }}\n'
' {%- if not loop.last %}\n'
' {{- ", " }}\n'
' {%- else %}\n'
' {{- "]" + eos_token }}\n'
' {%- endif %}\n'
' {%- endfor %}\n'
' {%- elif message[\'role\'] == \'assistant\' %}\n'
' {%- if message[\'content\'] is string %}\n'
' {{- message[\'content\'] + eos_token }}\n'
' {%- else %}\n'
' {{- message[\'content\'][0][\'text\'] + eos_token }}\n'
' {%- endif %}\n'
' {%- elif message["role"] == "tool_results" or message["role"] == "tool" %}\n'
' {%- if message.content is defined and message.content.content is defined %}\n'
' {%- set content = message.content.content %}\n'
' {%- else %}\n'
' {%- set content = message.content %}\n'
' {%- endif %}\n'
' {{- \'[TOOL_RESULTS] {"content": \' + content|string + ", " }}\n'
' {%- if not message.tool_call_id is defined or message.tool_call_id|length < 9 %}\n'
' {{- raise_exception("Tool call IDs should be alphanumeric strings with '
'length >= 9! (2)" + message.tool_call_id) }}\n'
' {%- endif %}\n'
' {{- \'"call_id": "\' + message.tool_call_id[-9:] + \'"}[/TOOL_RESULTS]\' }}\n'
' {%- else %}\n'
' {{- raise_exception("Only user and assistant roles are supported, with the '
'exception of an initial optional system message!") }}\n'
' {%- endif %}\n'
'{%- endfor %}')
# manually set chat_template, as we're using mistral-3.1-24b-instruct-2503 tokenizer which
# doesn't have the chat_template.json file
engine.processor.chat_template = chat_template
res2 = _infer_model(engine, system=devstral_small_2505_system)
assert res == res2, f'res: {res}, res2: {res2}'
def test_glm4_5():
messages = [{'role': 'user', 'content': '浙江的省会在哪?'}]
engine = TransformersEngine('ZhipuAI/GLM-4.5-Air')
res = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, messages=messages)
assert res == res2, f'res: {res}, res2: {res2}'
def test_gpt_oss():
messages = [{
'role':
'system',
'content':
'<|start|>system<|message|>You are Qwen.\nKnowledge cutoff: 2024-06\n'
'Current date: 2025-08-08\n\nReasoning: medium\n\n'
'# Valid channels: analysis, commentary, final. '
'Channel must be included for every message.<|end|>'
'<|start|>developer<|message|># Instructions\n\nYou are ChatGPT<|end|>'
}, {
'role': 'user',
'content': 'who are you?'
}]
engine = TransformersEngine('openai-mirror/gpt-oss-20b')
res = _infer_model(engine, messages=messages)
assert 'm Qwen' in res.rsplit('<|message|>', 1)[-1]
def test_qwen3_next():
engine = TransformersEngine('Qwen/Qwen3-Next-80B-A3B-Instruct')
res = _infer_model(engine)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine)
assert res == res2, f'res: {res}, res2: {res2}'
def test_ernie_thinking():
engine = TransformersEngine('PaddlePaddle/ERNIE-4.5-21B-A3B-Thinking')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_ring2():
engine = TransformersEngine('inclusionAI/Ring-mini-2.0')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_ling2():
engine = TransformersEngine('inclusionAI/Ling-mini-2.0')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_minimind():
engine = TransformersEngine('gongjy/MiniMind2', model_type='minimind')
swift_response = _infer_model(engine)
engine.template.template_backend = 'jinja'
jinja_response = _infer_model(engine)
assert swift_response == jinja_response
def test_medgemma3():
engine = TransformersEngine('google/medgemma-27b-text-it')
system = 'You are a helpful medical assistant.'
messages = [{'role': 'user', 'content': 'How do you differentiate bacterial from viral pneumonia?'}]
res = _infer_model(engine, system=system, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, system=system, messages=messages)
assert res == res2, f'res: {res}, res2: {res2}'
def test_youtu_llm():
engine = TransformersEngine('Tencent-YouTu-Research/Youtu-LLM-2B')
messages = [{'role': 'user', 'content': '你好'}]
res = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
res2 = _infer_model(engine, messages=messages)
assert res == res2, f'res: {res}, res2: {res2}'
def test_glm4_moe_lite():
engine = TransformersEngine('ZhipuAI/GLM-4.7-Flash')
swift_response = _infer_model(engine)
engine.template.template_backend = 'jinja'
jinja_response = _infer_model(engine)
assert swift_response == jinja_response
def test_olmoe():
engine = TransformersEngine('allenai/OLMoE-1B-7B-0924-Instruct')
# engine = TransformersEngine('allenai/OLMoE-1B-7B-0125-Instruct')
swift_response = _infer_model(engine)
engine.template.template_backend = 'jinja'
jinja_response = _infer_model(engine)
assert swift_response == jinja_response
def test_minicpm5():
engine = TransformersEngine('OpenBMB/MiniCPM5-1B')
swift_response = _infer_model(engine)
engine.template.template_backend = 'jinja'
jinja_response = _infer_model(engine)
assert swift_response == jinja_response
if __name__ == '__main__':
from swift.infer_engine import RequestConfig, TransformersEngine
from swift.utils import get_logger, seed_everything
logger = get_logger()
# test_qwen2_5()
# test_qwen1_5()
# test_qwq()
# test_internlm()
# test_internlm2()
# test_yi_coder()
# test_yi()
# test_deepseek_moe()
# test_codegeex4()
# test_chatglm4()
# test_telechat()
# test_telechat2()
# test_glm_edge()
# test_llama()
# test_openbuddy()
# test_megrez()
# test_skywork_o1()
# test_internlm2_reward()
# test_qwen2_reward()
# test_qwen2_5_math()
# test_skywork_reward()
# test_phi4()
# test_phi4_mini()
# test_internlm3()
# test_deepseek_r1_distill()
# test_deepseek_prover_v2()
# test_qwen2_5_prm()
# test_mistral_small()
# test_baichuan_m1()
# test_moonlight()
# test_ling()
# test_gemma3()
# test_glm4()
# test_qwen3()
# test_qwen3_guard()
# test_mimo()
# test_minicpm()
# test_minimax()
# test_kimi_dev()
# test_hunyuan()
# test_ernie()
# test_glm4_5()
# test_devstral()
# test_gpt_oss()
# test_qwen3_next()
# test_ernie_thinking()
# test_ring2()
# test_ling2()
# test_minimind()
# test_medgemma3()
# test_youtu_llm()
# test_glm4_moe_lite()
# test_olmoe()
test_minicpm5()
@@ -0,0 +1,168 @@
from swift.model import get_processor
from swift.template import TemplateInputs, get_template
def test_deepseek_v2_5():
tokenizer = get_processor('deepseek-ai/DeepSeek-V2.5-1210')
template = get_template(tokenizer)
inputs = TemplateInputs({
'messages': [{
'role': 'system',
'content': '000'
}, {
'role': 'user',
'content': 'aaa'
}, {
'role': 'assistant',
'content': 'bbb'
}, {
'role': 'user',
'content': 'ccc'
}]
})
res = template.encode(inputs)
template.print_inputs(res)
template.template_backend = 'jinja'
res2 = template.encode(inputs)
template.print_inputs(res2)
assert res['input_ids'] == res2['input_ids']
def test_qwen2_5_math_reward():
tokenizer = get_processor('Qwen/Qwen2.5-Math-RM-72B')
template = get_template(tokenizer)
inputs = TemplateInputs({
'messages': [{
'role':
'user',
'content':
'Janets ducks lay 16 eggs per day. She eats three for breakfast every morning and bakes muffins '
"for her friends every day with four. She sells the remainder at the farmers' market daily for $2 per "
"fresh duck egg. How much in dollars does she make every day at the farmers' market?"
}, {
'role':
'assistant',
'content':
"To determine how much Janet makes from selling the duck eggs at the farmers' market, we need to "
'follow these steps:\n\n1. Calculate the total number of eggs laid by the ducks each day.\n2. '
'Determine how many eggs Janet eats and bakes for herself each day.\n3. Find out how many eggs are '
"left to be sold.\n4. Calculate the revenue from selling the remaining eggs at $2 per egg.\n\nLet's "
"start with the first step:\n\n1. Janet's ducks lay 16 eggs per day.\n\nNext, we calculate how many "
'eggs Janet eats and bakes for herself each day:\n\n2. Janet eats 3 eggs for breakfast every morning.'
'\n3. Janet bakes 4 eggs for her friends every day.\n\nSo, the total number of eggs Janet eats and '
'bakes for herself each day is:\n\\[ 3 + 4 = 7 \\text{ eggs} \\]\n\nNow, we find out how many eggs '
'are left to be sold:\n\\[ 16 - 7 = 9 \\text{ eggs} \\]\n\nFinally, we calculate the revenue from '
'selling the remaining eggs at $2 per egg:\n\\[ 9 \\times 2 = 18 \\text{ dollars} \\]\n\nTherefore, '
"Janet makes \\(\\boxed{18}\\) dollars every day at the farmers' market."
}]
})
res = template.encode(inputs)
template.print_inputs(res)
template.template_backend = 'jinja'
res2 = template.encode(inputs)
template.print_inputs(res)
assert res['input_ids'] == res2['input_ids']
assert len(res['input_ids']) == 364
def test_minimax():
tokenizer = get_processor('MiniMax/MiniMax-Text-01')
template = get_template(tokenizer)
inputs = TemplateInputs({
'messages': [{
'role': 'system',
'content': 'You are a helpful assistant created by MiniMax based on MiniMax-Text-01 model.'
}, {
'role': 'user',
'content': 'Hello!'
}]
})
res = template.encode(inputs)
template.print_inputs(res)
assert tokenizer.decode(res['input_ids']) == (
'<beginning_of_sentence>system ai_setting=assistant\nYou are a helpful assistant created by MiniMax based '
'on MiniMax-Text-01 model.<end_of_sentence>\n<beginning_of_sentence>user name=user\nHello!<end_of_sentence>\n'
'<beginning_of_sentence>ai name=assistant\n')
def test_minimax_vl():
tokenizer = get_processor('MiniMax/MiniMax-VL-01')
template = get_template(tokenizer)
inputs = TemplateInputs({
'messages': [{
'role': 'system',
'content': 'You are a helpful assistant created by MiniMax based on MiniMax-VL-01 model.'
}, {
'role': 'user',
'content': '<image>Describe this image.'
}],
'images': ['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png']
})
res = template.encode(inputs)
assert len(res['input_ids']) == 5877
def test_deepseek_v3_1():
tokenizer = get_processor('deepseek-ai/DeepSeek-V3.1')
template = get_template(tokenizer)
inputs = {
'messages': [{
'role': 'system',
'content': '000'
}, {
'role': 'user',
'content': 'aaa'
}, {
'role': 'assistant',
'content': 'bbb'
}, {
'role': 'user',
'content': 'ccc'
}]
}
res = template.encode(inputs)
template.print_inputs(res)
template.template_backend = 'jinja'
res2 = template.encode(inputs)
template.print_inputs(res2)
assert res['input_ids'] == res2['input_ids']
def test_preserve_thinking():
tokenizer = get_processor('Qwen/Qwen3.6-35B-A3B')
template = get_template(tokenizer, preserve_thinking=True)
template.set_mode('train')
inputs = {
'messages': [{
'role': 'system',
'content': '000'
}, {
'role': 'user',
'content': 'aaa'
}, {
'role': 'assistant',
'content': '<think>\nbbb\n</think>\n\nbbb'
}, {
'role': 'user',
'content': 'ccc'
}, {
'role': 'assistant',
'content': '<think>\nddd\n</think>\n\nddd'
}]
}
template.template_backend = 'swift'
res = template.encode(inputs)
template.print_inputs(res)
template.template_backend = 'jinja'
res2 = template.encode(inputs)
template.print_inputs(res2)
assert res['input_ids'] == res2['input_ids']
if __name__ == '__main__':
# test_deepseek_v2_5()
# test_qwen2_5_math_reward()
# test_minimax()
# test_minimax_vl()
# test_deepseek_v3_1()
test_preserve_thinking()
@@ -0,0 +1,74 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['SWIFT_DEBUG'] = '1'
tools = [{
'name': 'get_current_weather',
'description': 'Get the current weather in a given location',
'parameters': {
'type': 'object',
'properties': {
'location': {
'type': 'string',
'description': 'The city and state, e.g. San Francisco, CA'
},
'unit': {
'type': 'string',
'enum': ['celsius', 'fahrenheit']
}
},
'required': ['location']
}
}]
def _test_tool(engine, system=None):
messages = [
{
'role': 'user',
'content': "How's the weather in Beijing today?"
},
{
'role':
'assistant',
'content': ('<tool_call>\n{"name": "get_current_weather", "arguments": '
'{"location": "Beijing, China", "unit": "celsius"}}\n</tool_call>')
},
{
'role': 'tool',
'content': "{'temp': 25, 'description': 'Partly cloudy', 'status': 'success'}"
},
]
request_config = RequestConfig(max_tokens=512, temperature=0)
response = engine.infer([InferRequest(messages=messages, tools=tools)], request_config=request_config)
return response[0].choices[0].message.content
def test_qwen2_5():
engine = TransformersEngine('Qwen/Qwen2.5-7B-Instruct')
response = _test_tool(engine)
assert response == 'Today in Beijing, the temperature is 25 degrees Celsius with partly cloudy skies.'
def test_qwq():
engine = TransformersEngine('Qwen/QwQ-32B')
response = _test_tool(engine)
assert response[-100:] == ('weather in Beijing is **25°C** with **partly cloudy** skies. '
'It looks like a mild day outside—enjoy!')
def test_deepseek_r1_distill():
# TODO
engine = TransformersEngine('deepseek-ai/DeepSeek-R1-Distill-Qwen-7B')
_test_tool(engine, system='')
if __name__ == '__main__':
from swift.infer_engine import InferRequest, RequestConfig, TransformersEngine
from swift.utils import get_logger
logger = get_logger()
# test_qwen2_5()
test_qwq()
# test_deepseek_r1_distill()
@@ -0,0 +1,422 @@
import os
import torch
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1,2,3'
os.environ['SWIFT_DEBUG'] = '1'
def _infer_model(engine, system=None, messages=None, videos=None, max_tokens=128):
seed_everything(42)
request_config = RequestConfig(max_tokens=max_tokens, temperature=0)
if messages is None:
messages = []
if not messages:
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages += [{'role': 'user', 'content': '你好'}]
resp = engine.infer([{'messages': messages}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}, {'role': 'user', 'content': '<video>描述视频'}]
else:
messages = messages.copy()
if videos is None:
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
resp = engine.infer([{'messages': messages, 'videos': videos}], request_config=request_config)
response = resp[0].choices[0].message.content
messages += [{'role': 'assistant', 'content': response}]
logger.info(f'model: {engine.model_info.model_name}, messages: {messages}')
return response
def test_qwen2_vl():
os.environ['FPS_MAX_FRAMES'] = '24'
os.environ['MAX_PIXELS'] = '100352'
os.environ['VIDEO_MAX_PIXELS'] = str(100352 // 4)
engine = TransformersEngine('Qwen/Qwen2-VL-2B-Instruct')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
def test_internvl2_5():
engine = TransformersEngine('OpenGVLab/InternVL2_5-2B')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine, system='你是书生·万象,英文名是InternVL,是由上海人工智能实验室、清华大学及多家合作单位联合开发的多模态大语言模型。')
def test_internvl2_5_mpo():
engine = TransformersEngine('OpenGVLab/InternVL2_5-1B-MPO', model_type='internvl2_5')
response = _infer_model(engine, messages=[{'role': 'user', 'content': '<video>这是什么'}])
assert response == ('这是一段婴儿在阅读的视频。婴儿穿着浅绿色的上衣和粉色的裤子,戴着黑框眼镜,坐在床上,正在翻阅一本打开的书。'
'背景中可以看到婴儿床、衣物和一些家具。视频中可以看到“clipo.com”的水印。婴儿看起来非常专注,似乎在认真地阅读。')
def test_xcomposer2_5():
engine = TransformersEngine('Shanghai_AI_Laboratory/internlm-xcomposer2d5-ol-7b:base', torch.float16)
messages = [{'role': 'user', 'content': '<video>Describe the video'}]
messages_with_system = messages.copy()
messages_with_system.insert(0, {'role': 'system', 'content': ''})
response = _infer_model(engine, messages=messages_with_system)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, system='')
assert response == response2
response = _infer_model(engine, messages=messages)
std_response = (
'The video features a young child sitting on a bed, deeply engaged in reading a book. '
'The child is dressed in a light blue sleeveless top and pink pants, and is wearing glasses. '
'The bed is covered with a textured white blanket, and there are various items scattered on it, '
'including a white cloth and a striped piece of clothing. In the background, '
'a wooden crib and a dresser with a mirror can be seen. The child flips through the pages of the book, '
'occasionally pausing to look at the illustrations. The child appears to be enjoying the book, '
'and the overall atmosphere is one of quiet concentration and enjoyment.')
assert response == std_response[:len(response)]
def test_mplug3():
engine = TransformersEngine('iic/mPLUG-Owl3-7B-240728')
# engine = TransformersEngine('iic/mPLUG-Owl3-7B-241101')
_infer_model(engine, system='')
engine.template.template_backend = 'jinja'
_infer_model(engine, system='')
def test_minicpmv():
engine = TransformersEngine('OpenBMB/MiniCPM-V-2_6')
_infer_model(engine)
engine.template.template_backend = 'jinja'
_infer_model(engine)
def test_minicpmo():
os.environ['VIDEO_MAX_SLICE_NUMS'] = '2'
engine = TransformersEngine('OpenBMB/MiniCPM-o-2_6')
messages = [{'role': 'user', 'content': '<video>Describe the video'}]
response = _infer_model(engine, messages=messages)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages)
assert response == response2 == (
'The video features a young child sitting on a bed, deeply engrossed in reading a large book. The child, '
'dressed in a light blue sleeveless top and pink pants, is surrounded by a cozy and homely environment. '
'The bed is adorned with a patterned blanket, and a white cloth is casually draped over the side. '
'In the background, a crib and a television are visible, adding to the domestic setting. '
'The child is seen flipping through the pages of the book, occasionally pausing to look at the pages, '
'and then continuing to turn them. The video captures the child\'s focused and curious demeanor as they '
'explore the contents of the book, creating a heartwarming '
'scene of a young reader immersed in their world of stories.')[:len(response)]
def test_valley():
engine = TransformersEngine('bytedance-research/Valley-Eagle-7B')
_infer_model(engine)
def _run_qwen2_5_vl_hf(messages, model, template):
from qwen_vl_utils import process_vision_info
processor = template.processor
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
images, videos, video_kwargs = process_vision_info(messages, return_video_kwargs=True)
inputs = processor(text=text, images=images, videos=videos, do_resize=False, return_tensors='pt', **video_kwargs)
inputs = inputs.to(model.device)
generated_ids = model.generate(**inputs, max_new_tokens=128, do_sample=False)
generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
output_text = processor.batch_decode(
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)
return output_text[0]
def test_qwen2_5_vl():
os.environ['FPS'] = '1'
os.environ['VIDEO_MAX_PIXELS'] = str(360 * 420)
engine = TransformersEngine('Qwen/Qwen2.5-VL-7B-Instruct')
query = 'What happened in the video?'
messages = [{'role': 'user', 'content': query}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
messages = [
{
'role': 'user',
'content': [
{
'type': 'video',
'video': videos[0]
},
{
'type': 'text',
'text': query
},
],
},
]
response3 = _run_qwen2_5_vl_hf(messages, engine.model, engine.template)
assert response == response2 == response3
def test_qwen2_5_omni():
USE_AUDIO_IN_VIDEO = True
os.environ['USE_AUDIO_IN_VIDEO'] = str(USE_AUDIO_IN_VIDEO)
engine = TransformersEngine('Qwen/Qwen2.5-Omni-7B', attn_impl='flash_attn')
system = ('You are Qwen, a virtual human developed by the Qwen Team, Alibaba Group, '
'capable of perceiving auditory and visual inputs, as well as generating text and speech.')
messages = [{'role': 'system', 'content': system}, {'role': 'user', 'content': '<video>'}]
videos = ['https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2.5-Omni/draw.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
if USE_AUDIO_IN_VIDEO:
ground_truth = ("Oh, that's a really cool drawing! It looks like a guitar. You've got the body "
'and the neck drawn in a simple yet effective way. The lines are clean and the '
'shape is well-defined. What made you choose to draw a guitar?')
else:
ground_truth = ('嗯,你是在用平板画画呢。你画的这把吉他,看起来很简洁明了。你用的笔触也很流畅,线条很清晰。你对颜色的运用也很不错,整体看起来很协调。你要是还有啥想法或者问题,随时跟我说哈。')
assert response == response2 == ground_truth
def _run_qwen3_omni_hf(model, processor, messages):
from qwen_omni_utils import process_mm_info
text = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
audios, images, videos = process_mm_info(messages, use_audio_in_video=True)
inputs = processor(
text=text,
audio=audios,
images=images,
videos=videos,
return_tensors='pt',
padding=True,
use_audio_in_video=True)
inputs = inputs.to(device=model.device, dtype=model.dtype)
text_ids = model.generate(**inputs, use_audio_in_video=True, do_sample=False, max_new_tokens=128)
text = processor.decode(
text_ids[0][len(inputs['input_ids'][0]):], skip_special_tokens=True, clean_up_tokenization_spaces=False)
return text
def test_qwen3_omni():
USE_AUDIO_IN_VIDEO = True
os.environ['USE_AUDIO_IN_VIDEO'] = str(USE_AUDIO_IN_VIDEO)
engine = TransformersEngine('Qwen/Qwen3-Omni-30B-A3B-Thinking')
query = 'describe the video.'
messages = [{'role': 'user', 'content': query}]
videos = ['https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2.5-Omni/draw.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
messages = [
{
'role': 'user',
'content': [
{
'type': 'video',
'video': videos[0]
},
{
'type': 'text',
'text': query
},
],
},
]
response2 = _run_qwen3_omni_hf(engine.model, engine.processor, messages)
assert response == response2
def test_glm4_1v():
messages = [{'role': 'user', 'content': '<video>What happened in the video?'}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
engine = TransformersEngine('ZhipuAI/GLM-4.1V-9B-Thinking')
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
assert response == response2
def test_glm4_5v():
messages = [{'role': 'user', 'content': '<video>What happened in the video?'}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
engine = TransformersEngine('ZhipuAI/GLM-4.5V')
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
assert response == response2
def test_keye_vl():
engine = TransformersEngine('Kwai-Keye/Keye-VL-8B-Preview')
messages = [{'role': 'user', 'content': '<video>Describe this video.'}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
assert response == response2
def test_keye_vl_1_5():
engine = TransformersEngine('Kwai-Keye/Keye-VL-1_5-8B')
messages = [{'role': 'user', 'content': '<video>Describe this video.'}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
assert response[:200] == ('The video features a young child sitting on a bed, engrossed in '
'reading a book. The child is wearing a light blue sleeveless top and pink '
'pants. The book appears to be a hardcover with illustrations, ')
def test_ovis2_5():
engine = TransformersEngine('AIDC-AI/Ovis2.5-2B')
messages = [{'role': 'user', 'content': '<video>Describe this video in detail.'}]
videos = ['baby.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
print(f'response: {response}')
def run_hf(model, processor, messages):
inputs = processor.apply_chat_template(
messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors='pt').to(
model.device, dtype=torch.bfloat16)
generate_ids = model.generate(**inputs, max_new_tokens=128, do_sample=False)
decoded_output = processor.decode(generate_ids[0, inputs['input_ids'].shape[1]:], skip_special_tokens=True)
return decoded_output
def test_interns1():
engine = TransformersEngine('Shanghai_AI_Laboratory/Intern-S1-mini')
query = 'Describe this video in detail.'
messages = [{'role': 'user', 'content': f'<video>{query}'}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
messages = [{
'role': 'user',
'content': [
{
'type': 'video',
'url': videos[0]
},
{
'type': 'text',
'text': query
},
],
}]
response2 = run_hf(engine.model, engine.processor, messages)
assert response == ('<think>' + response2)[:len(response)]
def test_internvl3_5():
models = [
'OpenGVLab/InternVL3_5-1B', 'OpenGVLab/InternVL3_5-2B', 'OpenGVLab/InternVL3_5-4B', 'OpenGVLab/InternVL3_5-8B',
'OpenGVLab/InternVL3_5-14B', 'OpenGVLab/InternVL3_5-38B', 'OpenGVLab/InternVL3_5-30B-A3B',
'OpenGVLab/InternVL3_5-GPT-OSS-20B-A4B-Preview'
]
for model in models:
engine = TransformersEngine(model)
messages = [{'role': 'user', 'content': '<video>Describe this video in detail.'}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
assert response == response2
def test_minicpmv4_5():
engine = TransformersEngine('OpenBMB/MiniCPM-V-4_5')
messages = [{'role': 'user', 'content': '<video>Describe this video in detail.'}]
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
assert response == response2
def _run_qwen3_vl_hf(messages, model, template):
from qwen_vl_utils import process_vision_info
processor = template.processor
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
images, videos, video_kwargs = process_vision_info(
messages, image_patch_size=16, return_video_kwargs=True, return_video_metadata=True)
if videos is not None:
videos, video_metadatas = zip(*videos)
videos, video_metadatas = list(videos), list(video_metadatas)
else:
video_metadatas = None
inputs = processor(
text=text,
images=images,
videos=videos,
video_metadata=video_metadatas,
do_resize=False,
return_tensors='pt',
**video_kwargs)
inputs = inputs.to(model.device)
generated_ids = model.generate(**inputs, max_new_tokens=128, do_sample=False)
generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
output_text = processor.batch_decode(
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)
return output_text[0]
def test_qwen3_vl():
engine = TransformersEngine('Qwen/Qwen3-VL-4B-Instruct')
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
query = 'describe this video.'
messages = [{'role': 'user', 'content': query}]
response = _infer_model(engine, messages=messages, videos=videos)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine, messages=messages, videos=videos)
messages = [{
'role': 'user',
'content': [
{
'type': 'video',
'video': videos[0],
},
{
'type': 'text',
'text': query
},
],
}]
response3 = _run_qwen3_vl_hf(messages, engine.model, engine.template)
assert response == response2 == response3
def test_qwen3_vl_moe():
engine = TransformersEngine('Qwen/Qwen3-VL-30B-A3B-Instruct')
response = _infer_model(engine)
engine.template.template_backend = 'jinja'
response2 = _infer_model(engine)
assert response == response2
if __name__ == '__main__':
from swift.infer_engine import RequestConfig, TransformersEngine
from swift.utils import get_logger, seed_everything
logger = get_logger()
# test_qwen2_vl()
# test_internvl2_5()
# test_xcomposer2_5()
# test_internvl2_5_mpo()
# test_mplug3()
# test_minicpmv()
# test_minicpmo()
# test_valley()
# test_qwen2_5_vl()
# test_qwen2_5_omni()
# test_qwen3_omni()
# test_glm4_1v() # bug now, wait model fix
# test_keye_vl()
# test_keye_vl_1_5()
# test_glm4_5v()
# test_ovis2_5()
# test_interns1()
# test_internvl3_5()
# test_minicpmv4_5()
test_qwen3_vl()
# test_qwen3_vl_moe()
File diff suppressed because it is too large Load Diff
+208
View File
@@ -0,0 +1,208 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def _infer_audio(model, use_chat_template: bool = True, max_model_len=8192, system=None, limit_mm_per_prompt=None):
limit_mm_per_prompt = limit_mm_per_prompt or {'audio': 2}
engine = VllmEngine(
model,
max_model_len=max_model_len,
gpu_memory_utilization=0.6,
limit_mm_per_prompt=limit_mm_per_prompt,
enforce_eager=True)
if not use_chat_template:
engine.template.use_chat_template = False
audios = ['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/weather.wav']
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages.append({'role': 'user', 'content': 'describe the audio.'})
resp_list = engine.infer([InferRequest(messages=messages, audios=audios)],
RequestConfig(temperature=0, max_tokens=64, repetition_penalty=1.))
return resp_list[0].choices[0].message.content
def _infer_image(model, use_chat_template: bool = True, max_model_len=8192, system=None, limit_mm_per_prompt=None):
limit_mm_per_prompt = limit_mm_per_prompt or {'image': 5, 'video': 0}
engine = VllmEngine(
model,
max_model_len=max_model_len,
gpu_memory_utilization=0.6,
limit_mm_per_prompt=limit_mm_per_prompt,
enforce_eager=True)
if not use_chat_template:
engine.template.use_chat_template = False
images = ['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png']
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages.append({'role': 'user', 'content': 'describe the image.'})
resp_list = engine.infer([InferRequest(messages=messages, images=images)],
RequestConfig(temperature=0, max_tokens=64, repetition_penalty=1.))
return resp_list[0].choices[0].message.content
def _infer_video(model, use_chat_template: bool = True, max_model_len=8192, system=None, limit_mm_per_prompt=None):
limit_mm_per_prompt = limit_mm_per_prompt or {'image': 16, 'video': 2}
engine = VllmEngine(
model,
max_model_len=max_model_len,
limit_mm_per_prompt=limit_mm_per_prompt,
gpu_memory_utilization=0.6,
enforce_eager=True)
if not use_chat_template:
engine.template.use_chat_template = False
videos = ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']
messages = []
if system is not None:
messages += [{'role': 'system', 'content': system}]
messages.append({'role': 'user', 'content': 'describe the video.'})
resp_list = engine.infer([InferRequest(messages=messages, videos=videos)],
RequestConfig(temperature=0, max_tokens=64, repetition_penalty=1.))
return resp_list[0].choices[0].message.content
def test_qwen2_audio():
response = _infer_audio('Qwen/Qwen2-Audio-7B-Instruct')
assert response == "The audio is a man speaking in Mandarin saying '今天天气真好呀'."
def test_qwen2_vl():
response = _infer_image('Qwen/Qwen2-VL-2B-Instruct')
assert response == (
'The image depicts a cute kitten with a fluffy, white and gray striped coat. The kitten has large, '
'expressive blue eyes and is looking directly at the camera. Its ears are perked up, and it has a '
'small red mark on its left ear. The background is blurred, focusing attention on the kitten. The overall')
def test_qwen2_5_vl():
response = _infer_image('Qwen/Qwen2.5-VL-3B-Instruct')
assert response == (
'The image depicts a cute, fluffy kitten with striking blue eyes and a white and gray fur pattern. '
'The kitten has a small, pink nose and is looking directly at the camera with a curious expression. '
"The background is blurred, drawing attention to the kitten's face. "
'The overall appearance is very endearing and charming.')
def test_deepseek_vl_v2():
response = _infer_image('deepseek-ai/deepseek-vl2-tiny', max_model_len=4096)
assert response == ('The image depicts a close-up of a adorable kitten with large, expressive eyes. The kitten has '
'a mix of white and gray fur with distinct black stripes, giving it a tabby-like appearance. '
'Its ears are perked up, and its whiskers are prominently visible. The background is blurred, '
'focusing attention on the kitten')
def test_internvl2():
response = _infer_image('OpenGVLab/InternVL2-2B', max_model_len=4096, system='')
assert response == ('The image features a kitten with striking blue eyes and a mix of white and black fur. '
'The kitten has large, expressive eyes and a small, pink nose. Its ears are perked up, '
'and it appears to be looking directly at the camera. The fur is soft and fluffy, with a mix')
def test_minicpmv_2_5():
response = _infer_image('OpenBMB/MiniCPM-Llama3-V-2_5', max_model_len=4096)
assert response == (
"The image is a digital painting of a kitten that captures the essence of a young feline's innocence "
"and curiosity. The kitten's fur is rendered with a mix of gray, white, and black stripes, "
'giving it a realistic and adorable appearance. Its large, expressive eyes are a striking blue, '
"which draws the viewer's")
def test_minicpmv_2_6():
response = _infer_image('OpenBMB/MiniCPM-V-2_6', max_model_len=4096)
assert response == (
'The image features a close-up of a kitten with striking blue eyes and a mix of '
"white and dark fur, possibly gray or black. The kitten's gaze is directed forward, giving it an "
"expressive and captivating look. The background is blurred, drawing focus to the kitten's face. "
"The overall composition emphasizes the kitten's features")
def test_minicpmo_2_6_video():
response = _infer_video('OpenBMB/MiniCPM-o-2_6')
assert response == ('The video features a young child sitting on a bed, deeply engaged in reading a book. '
'The child, dressed in a light blue sleeveless top and pink pants, is surrounded by a '
'cozy and homely environment. The bed is adorned with a patterned blanket, and a white cloth '
'is casually draped over the side.')
def test_qwen2_5_vl_video():
response = _infer_video('Qwen/Qwen2.5-VL-3B-Instruct')
assert response == ('A baby wearing sunglasses is sitting on a bed and reading a book. '
'The baby is holding the book with both hands and is looking at the pages. '
'The baby is wearing a light blue shirt and pink pants. The baby is sitting '
'on a white blanket. The baby is looking at the book and is smiling. The baby')
def test_qwen2_5_omni():
limit_mm_per_prompt = {'image': 1, 'video': 1, 'audio': 1}
response = _infer_video('Qwen/Qwen2.5-Omni-7B', limit_mm_per_prompt=limit_mm_per_prompt)
# response = _infer_audio('Qwen/Qwen2.5-Omni-7B')
assert response
def test_ovis2():
response = _infer_image('AIDC-AI/Ovis2-1B', max_model_len=4096)
assert response[:200] == ('The image showcases a charming digital painting of a kitten, capturing its '
'adorable features in a unique style. The kitten has a predominantly white face '
'with black stripes and spots, giving it a stri')
def test_keye_vl():
response = _infer_image('Kwai-Keye/Keye-VL-8B-Preview', max_model_len=4096)
assert response[:200] == ('<analysis>This question asks for a description of the image, which is '
'straightforward and involves observing the visual content. Therefore, '
'/no_think is more appropriate.</analysis>The image features ')
def test_kimi_vl():
response = _infer_image('moonshotai/Kimi-VL-A3B-Instruct', max_model_len=4096)
print(f'response: {response}')
def test_glm4v():
response = _infer_image('ZhipuAI/glm-4v-9b', max_model_len=4096)
print(f'response: {response}')
def test_glm4_1v():
response = _infer_image('ZhipuAI/GLM-4.1V-9B-Thinking', max_model_len=4096)
print(f'response: {response}')
def test_paddleocr_vl():
response = _infer_image('PaddlePaddle/PaddleOCR-VL', max_model_len=4096)
print(f'response: {response}')
def test_glm4_5_vl():
response = _infer_image('ZhipuAI/GLM-4.5V', max_model_len=4096)
print(f'response: {response}')
def test_deepseek_ocr():
response = _infer_image('deepseek-ai/DeepSeek-OCR', max_model_len=4096)
print(f'response: {response}')
if __name__ == '__main__':
from swift.infer_engine import InferRequest, RequestConfig, VllmEngine
# test_qwen2_vl()
# test_qwen2_5_vl()
# test_deepseek_vl_v2()
# test_internvl2()
# test_qwen2_audio()
# test_minicpmv_2_5()
# test_minicpmv_2_6()
# test_minicpmo_2_6_video()
# test_qwen2_5_vl_video()
# test_qwen2_5_omni()
# test_ovis2()
# test_keye_vl()
# test_kimi_vl()
# test_glm4v()
# test_glm4_1v()
# test_paddleocr_vl()
test_deepseek_ocr()
+337
View File
@@ -0,0 +1,337 @@
#!/usr/bin/env python
# Copyright (c) ModelScope Contributors. All rights reserved.
import copy
import numpy as np
import os
import pickle
import requests
import shutil
import socket
import subprocess
import sys
import tarfile
import tempfile
import unittest
from collections import OrderedDict
from collections.abc import Mapping
from modelscope.hub.constants import DEFAULT_CREDENTIALS_PATH
from os.path import expanduser
TEST_LEVEL = 2
TEST_LEVEL_STR = 'TEST_LEVEL'
# for user citest and sdkdev
TEST_ACCESS_TOKEN1 = os.environ.get('TEST_ACCESS_TOKEN_CITEST', None)
TEST_ACCESS_TOKEN2 = os.environ.get('TEST_ACCESS_TOKEN_SDKDEV', None)
TEST_MODEL_CHINESE_NAME = '内部测试模型'
TEST_MODEL_ORG = 'citest'
def delete_credential():
path_credential = expanduser(DEFAULT_CREDENTIALS_PATH)
shutil.rmtree(path_credential, ignore_errors=True)
def test_level():
global TEST_LEVEL
if TEST_LEVEL_STR in os.environ:
TEST_LEVEL = int(os.environ[TEST_LEVEL_STR])
return TEST_LEVEL
def require_tf(test_case):
test_case = unittest.skip('test requires TensorFlow')(test_case)
return test_case
def require_torch(test_case):
return test_case
def set_test_level(level: int):
global TEST_LEVEL
TEST_LEVEL = level
class DummyTorchDataset:
def __init__(self, feat, label, num) -> None:
self.feat = feat
self.label = label
self.num = num
def __getitem__(self, index):
import torch
return {'feat': torch.Tensor(self.feat), 'labels': torch.Tensor(self.label)}
def __len__(self):
return self.num
def create_dummy_test_dataset(feat, label, num):
return DummyTorchDataset(feat, label, num)
def download_and_untar(fpath, furl, dst) -> str:
if not os.path.exists(fpath):
r = requests.get(furl)
with open(fpath, 'wb') as f:
f.write(r.content)
file_name = os.path.basename(fpath)
root_dir = os.path.dirname(fpath)
target_dir_name = os.path.splitext(os.path.splitext(file_name)[0])[0]
target_dir_path = os.path.join(root_dir, target_dir_name)
# untar the file
t = tarfile.open(fpath)
t.extractall(path=dst)
return target_dir_path
def get_case_model_info():
status_code, result = subprocess.getstatusoutput(
'grep -rn "damo/" tests/ | grep -v ".pyc" | grep -v "Binary file" | grep -v run.py ')
lines = result.split('\n')
test_cases = OrderedDict()
model_cases = OrderedDict()
for line in lines:
# "tests/msdatasets/test_ms_dataset.py:92: model_id = 'damo/bert-base-sst2'"
line = line.strip()
elements = line.split(':')
test_file = elements[0]
model_pos = line.find('damo')
left_quote = line[model_pos - 1]
rquote_idx = line.rfind(left_quote)
model_name = line[model_pos:rquote_idx]
if test_file not in test_cases:
test_cases[test_file] = set()
model_info = test_cases[test_file]
model_info.add(model_name)
if model_name not in model_cases:
model_cases[model_name] = set()
case_info = model_cases[model_name]
case_info.add(test_file.replace('tests/', '').replace('.py', '').replace('/', '.'))
return model_cases
def compare_arguments_nested(print_content, arg1, arg2, rtol=1.e-3, atol=1.e-8, ignore_unknown_type=True):
type1 = type(arg1)
type2 = type(arg2)
if type1.__name__ != type2.__name__:
if print_content is not None:
print(f'{print_content}, type not equal:{type1.__name__} and {type2.__name__}')
return False
if arg1 is None:
return True
elif isinstance(arg1, (int, str, bool, np.bool_, np.integer, np.str_)):
if arg1 != arg2:
if print_content is not None:
print(f'{print_content}, arg1:{arg1}, arg2:{arg2}')
return False
return True
elif isinstance(arg1, (float, np.floating)):
if not np.isclose(arg1, arg2, rtol=rtol, atol=atol, equal_nan=True):
if print_content is not None:
print(f'{print_content}, arg1:{arg1}, arg2:{arg2}')
return False
return True
elif isinstance(arg1, (tuple, list)):
if len(arg1) != len(arg2):
if print_content is not None:
print(f'{print_content}, length is not equal:{len(arg1)}, {len(arg2)}')
return False
if not all([
compare_arguments_nested(None, sub_arg1, sub_arg2, rtol=rtol, atol=atol)
for sub_arg1, sub_arg2 in zip(arg1, arg2)
]):
if print_content is not None:
print(f'{print_content}')
return False
return True
elif isinstance(arg1, Mapping):
keys1 = arg1.keys()
keys2 = arg2.keys()
if len(keys1) != len(keys2):
if print_content is not None:
print(f'{print_content}, key length is not equal:{len(keys1)}, {len(keys2)}')
return False
if len(set(keys1) - set(keys2)) > 0:
if print_content is not None:
print(f'{print_content}, key diff:{set(keys1) - set(keys2)}')
return False
if not all([compare_arguments_nested(None, arg1[key], arg2[key], rtol=rtol, atol=atol) for key in keys1]):
if print_content is not None:
print(f'{print_content}')
return False
return True
elif isinstance(arg1, np.ndarray):
arg1 = np.where(np.equal(arg1, None), np.NaN, arg1).astype(dtype=float)
arg2 = np.where(np.equal(arg2, None), np.NaN, arg2).astype(dtype=float)
if not all(np.isclose(arg1, arg2, rtol=rtol, atol=atol, equal_nan=True).flatten()):
if print_content is not None:
print(f'{print_content}')
return False
return True
else:
if ignore_unknown_type:
return True
else:
raise ValueError(f'type not supported: {type1}')
_DIST_SCRIPT_TEMPLATE = """
import ast
import argparse
import pickle
import torch
from torch import distributed as dist
from modelscope.utils.torch_utils import get_dist_info
import {}
parser = argparse.ArgumentParser()
parser.add_argument('--save_all_ranks', type=ast.literal_eval, help='save all ranks results')
parser.add_argument('--save_file', type=str, help='save file')
parser.add_argument('--local_rank', type=int, default=0)
args = parser.parse_args()
def main():
results = {}.{}({}) # module.func(params)
if args.save_all_ranks:
save_file = args.save_file + str(dist.get_rank())
with open(save_file, 'wb') as f:
pickle.dump(results, f)
else:
rank, _ = get_dist_info()
if rank == 0:
with open(args.save_file, 'wb') as f:
pickle.dump(results, f)
if __name__ == '__main__':
main()
"""
class DistributedTestCase(unittest.TestCase):
"""Distributed TestCase for test function with distributed mode.
Examples:
>>> import torch
>>> from torch import distributed as dist
>>> from modelscope.utils.torch_utils import init_dist
>>> def _test_func(*args, **kwargs):
>>> init_dist(launcher='pytorch')
>>> rank = dist.get_rank()
>>> if rank == 0:
>>> value = torch.tensor(1.0).cuda()
>>> else:
>>> value = torch.tensor(2.0).cuda()
>>> dist.all_reduce(value)
>>> return value.cpu().numpy()
>>> class DistTest(DistributedTestCase):
>>> def test_function_dist(self):
>>> args = () # args should be python builtin type
>>> kwargs = {} # kwargs should be python builtin type
>>> self.start(
>>> _test_func,
>>> num_gpus=2,
>>> assert_callback=lambda x: self.assertEqual(x, 3.0),
>>> *args,
>>> **kwargs,
>>> )
"""
def _start(self, dist_start_cmd, func, num_gpus, assert_callback=None, save_all_ranks=False, *args, **kwargs):
script_path = func.__code__.co_filename
script_dir, script_name = os.path.split(script_path)
script_name = os.path.splitext(script_name)[0]
func_name = func.__qualname__
func_params = []
for arg in args:
if isinstance(arg, str):
arg = ('\'{}\''.format(arg))
func_params.append(str(arg))
for k, v in kwargs.items():
if isinstance(v, str):
v = ('\'{}\''.format(v))
func_params.append('{}={}'.format(k, v))
func_params = ','.join(func_params).strip(',')
tmp_run_file = tempfile.NamedTemporaryFile(suffix='.py').name
tmp_res_file = tempfile.NamedTemporaryFile(suffix='.pkl').name
with open(tmp_run_file, 'w') as f:
print('save temporary run file to : {}'.format(tmp_run_file))
print('save results to : {}'.format(tmp_res_file))
run_file_content = _DIST_SCRIPT_TEMPLATE.format(script_name, script_name, func_name, func_params)
f.write(run_file_content)
tmp_res_files = []
if save_all_ranks:
for i in range(num_gpus):
tmp_res_files.append(tmp_res_file + str(i))
else:
tmp_res_files = [tmp_res_file]
self.addCleanup(self.clean_tmp, [tmp_run_file] + tmp_res_files)
tmp_env = copy.deepcopy(os.environ)
tmp_env['PYTHONPATH'] = ':'.join((tmp_env.get('PYTHONPATH', ''), script_dir)).lstrip(':')
# avoid distributed test hang
tmp_env['NCCL_P2P_DISABLE'] = '1'
script_params = '--save_all_ranks=%s --save_file=%s' % (save_all_ranks, tmp_res_file)
script_cmd = '%s %s %s' % (dist_start_cmd, tmp_run_file, script_params)
print('script command: %s' % script_cmd)
res = subprocess.call(script_cmd, shell=True, env=tmp_env)
script_res = []
for res_file in tmp_res_files:
with open(res_file, 'rb') as f:
script_res.append(pickle.load(f))
if not save_all_ranks:
script_res = script_res[0]
if assert_callback:
assert_callback(script_res)
self.assertEqual(res, 0, msg='The test function ``{}`` in ``{}`` run failed!'.format(func_name, script_name))
return script_res
def start(self, func, num_gpus, assert_callback=None, save_all_ranks=False, *args, **kwargs):
from .torch_utils import _find_free_port
ip = socket.gethostbyname(socket.gethostname())
if 'dist_start_cmd' in kwargs:
dist_start_cmd = kwargs.pop('dist_start_cmd')
else:
dist_start_cmd = '%s -m torch.distributed.launch --nproc_per_node=%d ' \
'--master_addr=\'%s\' --master_port=%s' % (sys.executable, num_gpus, ip, _find_free_port())
return self._start(
dist_start_cmd=dist_start_cmd,
func=func,
num_gpus=num_gpus,
assert_callback=assert_callback,
save_all_ranks=save_all_ranks,
*args,
**kwargs)
def clean_tmp(self, tmp_file_list):
for file in tmp_file_list:
if os.path.exists(file):
if os.path.isdir(file):
shutil.rmtree(file)
else:
os.remove(file)
+24
View File
@@ -0,0 +1,24 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
def test_channel():
from swift import SftArguments, sft_main
sft_main(
SftArguments(
model='Qwen/Qwen2.5-7B-Instruct',
dataset=['channel.jsonl#1000'],
split_dataset_ratio=0.01,
enable_channel_loss=True,
packing=True,
max_length=128,
attn_impl='flash_attn',
load_from_cache_file=False,
deepspeed='zero2',
eval_steps=5))
if __name__ == '__main__':
test_channel()
+61
View File
@@ -0,0 +1,61 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'per_device_eval_batch_size': 2,
'save_steps': 50,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_llm():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2.5-1.5B-Instruct',
tuner_type='lora',
num_labels=2,
dataset=['DAMO_NLP/jd:cls#2000'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True))
def test_bert():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='answerdotai/ModernBERT-base',
# model='iic/nlp_structbert_backbone_base_std',
tuner_type='full',
num_labels=2,
dataset=['DAMO_NLP/jd:cls#2000'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(model=last_model_checkpoint, load_data_args=True))
def test_mllm():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='OpenGVLab/InternVL2-1B',
tuner_type='lora',
num_labels=2,
dataset=['DAMO_NLP/jd:cls#500'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True))
if __name__ == '__main__':
# test_llm()
# test_bert()
test_mllm()
+88
View File
@@ -0,0 +1,88 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 4,
'save_steps': 5,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_embedding():
from swift import SftArguments, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen3-Embedding-0.6B',
task_type='embedding',
dataset=['sentence-transformers/stsb:positive'],
split_dataset_ratio=0.01,
load_from_cache_file=False,
loss_type='infonce',
attn_impl='flash_attn',
max_length=2048,
**kwargs,
))
last_model_checkpoint = result['last_model_checkpoint']
print(f'last_model_checkpoint: {last_model_checkpoint}')
def test_reranker():
from swift import SftArguments, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen3-Reranker-4B',
tuner_type='lora',
load_from_cache_file=True,
task_type='generative_reranker',
dataset=['MTEB/scidocs-reranking#10000'],
split_dataset_ratio=0.05,
loss_type='pointwise_reranker',
dataloader_drop_last=True,
eval_strategy='steps',
eval_steps=10,
max_length=4096,
attn_impl='flash_attn',
num_train_epochs=1,
save_steps=200,
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
gradient_accumulation_steps=8,
dataset_num_proc=2,
))
last_model_checkpoint = result['last_model_checkpoint']
print(f'last_model_checkpoint: {last_model_checkpoint}')
def test_reranker2():
from swift import SftArguments, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2.5-VL-3B-Instruct',
tuner_type='lora',
load_from_cache_file=True,
task_type='reranker',
dataset=['MTEB/scidocs-reranking'],
split_dataset_ratio=0.05,
loss_type='listwise_reranker',
dataloader_drop_last=True,
eval_strategy='steps',
eval_steps=10,
max_length=4096,
attn_impl='flash_attn',
padding_side='right',
num_train_epochs=1,
save_steps=200,
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
gradient_accumulation_steps=8,
dataset_num_proc=1,
))
last_model_checkpoint = result['last_model_checkpoint']
print(f'last_model_checkpoint: {last_model_checkpoint}')
if __name__ == '__main__':
# test_embedding()
test_reranker()
+27
View File
@@ -0,0 +1,27 @@
def test_export_cached_dataset():
from swift import ExportArguments, export_main
export_main(
ExportArguments(
model='Qwen/Qwen2.5-7B-Instruct',
dataset='swift/Chinese-Qwen3-235B-2507-Distill-data-110k-SFT',
to_cached_dataset=True,
dataset_num_proc=4,
))
print()
def test_sft():
from swift import SftArguments, sft_main
sft_main(
SftArguments(
model='Qwen/Qwen2.5-7B-Instruct',
dataset='liucong/Chinese-DeepSeek-R1-Distill-data-110k-SFT#1000',
dataset_num_proc=2,
packing=True,
attn_impl='flash_attn',
))
if __name__ == '__main__':
# test_export_cached_dataset()
test_sft()
+89
View File
@@ -0,0 +1,89 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'save_steps': 5,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_full_vit():
os.environ['MAX_PIXELS'] = '100352'
os.environ['SIZE_FACTOR'] = '12'
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
from swift import InferArguments, SftArguments, infer_main, sft_main
sft_main(
SftArguments(
model='Qwen/Qwen2-VL-7B-Instruct',
dataset=['modelscope/coco_2014_caption:validation#20', 'AI-ModelScope/alpaca-gpt4-data-en#20'],
split_dataset_ratio=0.01,
tuner_type='full',
freeze_llm=True,
freeze_vit=False,
freeze_aligner=True,
**kwargs))
def test_full_aligner():
os.environ['MAX_PIXELS'] = '100352'
os.environ['SIZE_FACTOR'] = '12'
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
from swift import InferArguments, SftArguments, infer_main, sft_main
sft_main(
SftArguments(
model='Qwen/Qwen2-VL-7B-Instruct',
dataset=['modelscope/coco_2014_caption:validation#20', 'AI-ModelScope/alpaca-gpt4-data-en#20'],
split_dataset_ratio=0.01,
tuner_type='full',
freeze_llm=True,
freeze_vit=True,
freeze_aligner=False,
**kwargs))
def test_lora_vit():
os.environ['MAX_PIXELS'] = '100352'
os.environ['SIZE_FACTOR'] = '12'
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
from swift import InferArguments, SftArguments, infer_main, sft_main
sft_main(
SftArguments(
model='Qwen/Qwen2-VL-7B-Instruct',
dataset=['modelscope/coco_2014_caption:validation#20', 'AI-ModelScope/alpaca-gpt4-data-en#20'],
split_dataset_ratio=0.01,
tuner_type='lora',
freeze_llm=True,
freeze_vit=False,
freeze_aligner=True,
**kwargs))
def test_lora_aligner():
os.environ['MAX_PIXELS'] = '100352'
os.environ['SIZE_FACTOR'] = '12'
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
from swift import InferArguments, SftArguments, infer_main, sft_main
sft_main(
SftArguments(
model='Qwen/Qwen2-VL-7B-Instruct',
dataset=['modelscope/coco_2014_caption:validation#20', 'AI-ModelScope/alpaca-gpt4-data-en#20'],
split_dataset_ratio=0.01,
tuner_type='lora',
freeze_llm=True,
freeze_vit=True,
freeze_aligner=False,
**kwargs))
if __name__ == '__main__':
# test_full_vit()
test_full_aligner()
# test_lora_vit()
# test_lora_aligner()
+80
View File
@@ -0,0 +1,80 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 4,
'save_steps': 5,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_llm():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='gkd',
model='Qwen/Qwen2.5-0.5B',
teacher_model='Qwen/Qwen2.5-1.5B-Instruct',
dataset=['AI-ModelScope/alpaca-gpt4-data-en#2000'],
split_dataset_ratio=0.01,
load_from_cache_file=False,
seq_kd=True,
**kwargs,
))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_mllm():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='gkd',
model='OpenGVLab/InternVL3-2B-Pretrained',
teacher_model='OpenGVLab/InternVL3-8B',
dataset=['AI-ModelScope/LaTeX_OCR#2000', 'AI-ModelScope/alpaca-gpt4-data-en#2000'],
split_dataset_ratio=0.01,
load_from_cache_file=False,
**kwargs,
))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_multi_turn():
"""GKD multi-turn smoke test: verify rollout → encode → loss works with multi_turn_scheduler.
Uses the built-in ``math_tip_trick`` scheduler with max_turns=2 to keep the test
lightweight. The key assertion is that training completes without raising
NotImplementedError (the previous block) and that multi-turn response token ids
are correctly propagated through the GKD loss pipeline.
"""
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='gkd',
model='Qwen/Qwen2.5-0.5B',
teacher_model='Qwen/Qwen2.5-1.5B-Instruct',
dataset=['AI-ModelScope/alpaca-gpt4-data-en#200'],
split_dataset_ratio=0.01,
load_from_cache_file=False,
multi_turn_scheduler='math_tip_trick',
max_turns=2,
max_completion_length=256,
num_generations=2,
per_device_train_batch_size=2,
gradient_accumulation_steps=1,
save_steps=50,
num_train_epochs=1,
))
last_model_checkpoint = result['last_model_checkpoint']
if last_model_checkpoint is not None:
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
if __name__ == '__main__':
# test_llm()
# test_mllm()
test_multi_turn()
+9
View File
@@ -0,0 +1,9 @@
import os
from swift import SftArguments, sft_main
os.environ['MAX_PIXELS'] = str(16 * 28 * 28)
if __name__ == '__main__':
sft_main(
SftArguments(model='Qwen/Qwen2.5-VL-7B-Instruct', dataset='AI-ModelScope/coco#2000', split_dataset_ratio=0.01))
+153
View File
@@ -0,0 +1,153 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
kwargs = {
'per_device_train_batch_size': 2,
'per_device_eval_batch_size': 2,
'save_steps': 50,
'gradient_accumulation_steps': 1,
'num_train_epochs': 1,
}
SYSTEM_PROMPT = ('A conversation between User and Assistant. The user asks a question, and the Assistant solves it. '
'The assistant first thinks about the reasoning process in the mind and then provides the user '
'with the answer. The reasoning process and answer are enclosed within <think> </think> '
'and <answer> </answer> tags, respectively, i.e., <think> reasoning process here </think><answer> '
'answer here </answer>')
def test_llm():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='grpo',
model='Qwen/Qwen2.5-1.5B-Instruct',
tuner_type='full',
dataset=['AI-MO/NuminaMath-TIR#100'],
split_dataset_ratio=0.1,
system=SYSTEM_PROMPT,
reward_funcs=['accuracy', 'format'],
max_completion_length=4096,
num_generations=2,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_llm_zero2():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='grpo',
model='Qwen/Qwen2.5-1.5B-Instruct',
tuner_type='full',
dataset=['AI-MO/NuminaMath-TIR#100'],
system=SYSTEM_PROMPT,
reward_funcs=['accuracy', 'format'],
max_completion_length=4096,
num_generations=2,
deepspeed='zero2',
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_llm_vllm():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='grpo',
model='Qwen/Qwen2.5-1.5B-Instruct',
reward_model='AI-ModelScope/GRM_Llama3.1_8B_rewardmodel-ft',
tuner_type='full',
dataset=['AI-MO/NuminaMath-TIR#100'],
system=SYSTEM_PROMPT,
reward_funcs=['accuracy', 'format'],
use_vllm=True,
max_completion_length=4096,
num_generations=2,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_llm_vllm_zero2():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='grpo',
model='Qwen/Qwen2.5-1.5B-Instruct',
tuner_type='full',
dataset=['AI-MO/NuminaMath-TIR#100'],
system=SYSTEM_PROMPT,
reward_funcs=['accuracy', 'format'],
use_vllm=True,
max_completion_length=4096,
num_generations=2,
deepspeed='zero2',
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_mllm_pt():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='grpo',
model='Qwen/Qwen2-VL-2B-Instruct',
tuner_type='full',
# dataset=['AI-MO/NuminaMath-TIR#100'],
dataset=['modelscope/coco_2014_caption:validation#100'],
system=SYSTEM_PROMPT,
reward_funcs=['format'],
max_completion_length=4096,
num_generations=2,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_grpo_minimal():
import trl
from packaging import version
if version.parse(trl.__version__) < version.parse('0.26'):
print(f'Skipping test_grpo_minimal: trl>=0.26 required, found trl=={trl.__version__}')
return
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='grpo',
model='Qwen/Qwen2-0.5B',
tuner_type='lora',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#20'],
system=SYSTEM_PROMPT,
reward_funcs=['format'],
max_completion_length=128,
num_generations=2,
max_steps=2,
per_device_train_batch_size=2,
gradient_accumulation_steps=1,
save_steps=2,
split_dataset_ratio=0.01,
logging_steps=1,
use_vllm=False,
**{
k: v
for k, v in kwargs.items() if k not in [
'per_device_train_batch_size', 'save_steps', 'gradient_accumulation_steps', 'num_train_epochs',
'per_device_eval_batch_size'
]
}))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True))
if __name__ == '__main__':
# test_llm()
# test_llm_zero3()
# test_llm_vllm()
# test_llm_vllm_zero2()
test_mllm_pt()
# test_grpo_minimal()
+41
View File
@@ -0,0 +1,41 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'save_steps': 5,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_llm():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='kto',
model='Qwen/Qwen2-7B-Instruct',
dataset=['AI-ModelScope/ultrafeedback-binarized-preferences-cleaned-kto#100'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_mllm():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='kto',
model='Qwen/Qwen2-VL-7B-Instruct',
dataset=['AI-ModelScope/ultrafeedback-binarized-preferences-cleaned-kto#100'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
if __name__ == '__main__':
# test_llm()
test_mllm()
+46
View File
@@ -0,0 +1,46 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0,1'
kwargs = {
'per_device_train_batch_size': 2,
'save_steps': 30,
'gradient_accumulation_steps': 2,
'num_train_epochs': 1,
}
def test_sft():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2.5-7B-Instruct',
dataset=['swift/self-cognition#200'],
split_dataset_ratio=0.01,
use_liger_kernel=True,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True))
def test_mllm_dpo():
os.environ['MAX_PIXLES'] = f'{1280 * 28 * 28}'
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='dpo',
model='Qwen/Qwen2.5-VL-3B-Instruct',
tuner_type='full',
dataset=['swift/RLAIF-V-Dataset#1000'],
split_dataset_ratio=0.01,
dataset_num_proc=8,
deepspeed='zero3',
use_liger_kernel=True,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(model=last_model_checkpoint, load_data_args=True))
if __name__ == '__main__':
test_sft()
# test_mllm_dpo()
+46
View File
@@ -0,0 +1,46 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'per_device_eval_batch_size': 2,
'save_steps': 50,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_reg_llm():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2.5-1.5B-Instruct',
tuner_type='lora',
num_labels=1,
dataset=['sentence-transformers/stsb:reg#200'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, metric='acc'))
def test_reg_mllm():
from swift import InferArguments, SftArguments, infer_main, sft_main
# OpenGVLab/InternVL2-1B
result = sft_main(
SftArguments(
model='Qwen/Qwen2-VL-2B-Instruct',
tuner_type='lora',
num_labels=1,
dataset=['sentence-transformers/stsb:reg#200'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, metric='acc'))
if __name__ == '__main__':
# test_reg_llm()
test_reg_mllm()
+48
View File
@@ -0,0 +1,48 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
os.environ['NPROC_PER_NODE'] = '2'
def train():
from swift import RLHFArguments, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='gkd',
model='Qwen/Qwen3.5-4B',
teacher_model='Qwen/Qwen3.5-4B',
tuner_type='lora',
lora_rank=64,
lora_alpha=128,
target_modules=['all-linear'],
use_vllm=True,
vllm_mode='colocate',
vllm_gpu_memory_utilization=0.7,
vllm_max_model_len=10240,
sleep_level=1,
external_plugins=['examples/train/rlhf/opsd/opsd_plugin.py'],
dataset=['open-r1/OpenThoughts-114k-math'],
lmbda=1.0,
beta=0.5,
temperature=1.2,
sft_alpha=0,
torch_dtype='bfloat16',
max_steps=1000,
per_device_train_batch_size=4,
gradient_accumulation_steps=1,
learning_rate=2e-5,
save_steps=100,
save_total_limit=10,
logging_steps=1,
max_length=8192,
max_completion_length=2048,
save_only_model=True,
gradient_checkpointing=True,
deepspeed='zero0',
attn_impl='flash_attn',
))
return result
if __name__ == '__main__':
train()
+66
View File
@@ -0,0 +1,66 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'save_steps': 50,
'gradient_accumulation_steps': 4,
'num_train_epochs': 3,
}
def test_llm():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2-7B-Instruct',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#1000', 'swift/self-cognition#1000'],
split_dataset_ratio=0.01,
packing=True,
max_length=4096,
attn_impl='flash_attn',
logging_steps=1,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_streaming():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2-7B-Instruct',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#10000'],
packing=True,
max_length=4096,
streaming=True,
attn_impl='flash_attn',
max_steps=100,
dataset_num_proc=1,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_mllm_streaming():
from swift import InferArguments, SftArguments, infer_main, sft_main
result = sft_main(
SftArguments(
model='Qwen/Qwen2.5-VL-7B-Instruct',
dataset=['AI-ModelScope/LaTeX_OCR#20000'],
packing=True,
max_length=8192,
streaming=True,
attn_impl='flash_attn',
max_steps=100,
dataset_num_proc=4,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
if __name__ == '__main__':
# test_llm()
# test_streaming()
test_mllm_streaming()
+41
View File
@@ -0,0 +1,41 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'save_steps': 5,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_rm():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='rm',
model='Shanghai_AI_Laboratory/internlm2-1_8b-reward',
dataset=['hjh0119/shareAI-Llama3-DPO-zh-en-emoji#100'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_ppo():
from swift import InferArguments, RLHFArguments, infer_main, rlhf_main
result = rlhf_main(
RLHFArguments(
rlhf_type='ppo',
model='LLM-Research/Llama-3.2-1B-Instruct',
reward_model='AI-ModelScope/GRM-Llama3.2-3B-rewardmodel-ft',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#100', 'AI-ModelScope/alpaca-gpt4-data-en#100'],
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
if __name__ == '__main__':
# test_rm()
test_ppo()
+58
View File
@@ -0,0 +1,58 @@
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
kwargs = {
'per_device_train_batch_size': 2,
'save_steps': 5,
'gradient_accumulation_steps': 4,
'num_train_epochs': 1,
}
def test_llm():
from swift import InferArguments, PretrainArguments, infer_main, pretrain_main
result = pretrain_main(
PretrainArguments(
model='Qwen/Qwen2-7B-Instruct', dataset=['swift/sharegpt:all#100'], split_dataset_ratio=0.01, **kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_mllm():
from swift import InferArguments, PretrainArguments, infer_main, pretrain_main
result = pretrain_main(
PretrainArguments(
model='Qwen/Qwen2-VL-7B-Instruct',
dataset=['modelscope/coco_2014_caption:validation#20', 'AI-ModelScope/alpaca-gpt4-data-en#20'],
split_dataset_ratio=0.01,
**kwargs))
last_model_checkpoint = result['last_model_checkpoint']
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, merge_lora=True))
def test_pretrain_minimal():
from swift import PretrainArguments, pretrain_main
result = pretrain_main(
PretrainArguments(
model='Qwen/Qwen2-0.5B',
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#20'],
max_steps=2,
per_device_train_batch_size=1,
gradient_accumulation_steps=1,
save_steps=2,
split_dataset_ratio=0.01,
tuner_type='lora',
logging_steps=1,
**{
k: v
for k, v in kwargs.items() if k not in
['per_device_train_batch_size', 'save_steps', 'gradient_accumulation_steps', 'num_train_epochs']
}))
assert os.path.isdir(result['last_model_checkpoint'])
if __name__ == '__main__':
# test_llm()
test_mllm()
# test_pretrain_minimal()

Some files were not shown because too many files have changed in this diff Show More