Views
No views yet

cold-start'' data mixture, which includes thinking'', non-thinking'', auto-think'', ``think with image'', and high-quality video data. This mixture teaches the model to decide when and how to reason. Subsequent reinforcement learning (RL) and alignment steps further enhance these reasoning capabilities and correct abnormal model behaviors, such as repetitive outputs. To validate our approach, we conduct extensive evaluations, showing that Keye-VL achieves state-of-the-art results on public video benchmarks and remains highly competitive on general image-based tasks (Figure 1). Furthermore, we develop and release the KC-MMBench, a new benchmark tailored for real-world short-video scenarios, where Keye-VL shows a significant advantage.2025.06.26 🌟 We are very proud to launch Kwai Keye-VL, a cutting-edge multimodal large language model meticulously crafted by the Kwai Keye Team at Kuaishou. As a cornerstone AI product within Kuaishou's advanced technology ecosystem, Keye excels in video understanding, visual perception, and reasoning tasks, setting new benchmarks in performance. Our team is working tirelessly to push the boundaries of what's possible, so stay tuned for more exciting updates!
pip install git+https://github.com/huggingface/transformers accelerate1# It's highly recommanded to use `[decord]` feature for faster video loading.
2pip install "keye-vl-utils[decord]==1.0.0"decord from PyPI. In that case, you can use pip install keye-vl-utils which will fall back to using torchvision for video processing. However, you can still install decord from source to get decord used when loading video.transformers and keye_vl_utils:[!NOTE] Following Qwen3, we also offer a soft switch mechanism that lets users dynamically control the model's behavior. You can add /think, /no_think, or nothing to user prompts to switch the model's thinking modes.
1from transformers import AutoModel, AutoTokenizer, AutoProcessor
2from keye_vl_utils import process_vision_info
3
4# default: Load the model on the available device(s)
5model_path = "Kwai-Keye/Keye-VL-8B-Preview"
6
7model = AutoModel.from_pretrained(
8 model_path,
9 torch_dtype="auto",
10 device_map="auto",
11 trust_remote_code=True,
12)
13
14# We recommend enabling flash_attention_2 for better acceleration and memory saving, especially in multi-image and video scenarios.
15# model = KeyeForConditionalGeneration.from_pretrained(
16# "Kwai-Keye/Keye-VL-8B-Preview",
17# torch_dtype=torch.bfloat16,
18# attn_implementation="flash_attention_2",
19# device_map="auto",
20# )
21
22# default processer
23processor = AutoProcessor.from_pretrained(model_path, trust_remote_code=True)
24
25# The default range for the number of visual tokens per image in the model is 4-16384.
26# You can set min_pixels and max_pixels according to your needs, such as a token range of 256-1280, to balance performance and cost.
27# min_pixels = 256*28*28
28# max_pixels = 1280*28*28
29# processor = AutoProcessor.from_pretrained(model_pat, min_pixels=min_pixels, max_pixels=max_pixels, trust_remote_code=True)
30
31# Non-Thinking Mode
32messages = [
33 {
34 "role": "user",
35 "content": [
36 {
37 "type": "image",
38 "image": "https://s1-11508.kwimgs.com/kos/nlav11508/mllm_all/ziran_jiafeimao_11.jpg",
39 },
40 {"type": "text", "text": "Describe this image./no_think"},
41 ],
42 }
43]
44
45# Auto-Thinking Mode
46messages = [
47 {
48 "role": "user",
49 "content": [
50 {
51 "type": "image",
52 "image": "https://s1-11508.kwimgs.com/kos/nlav11508/mllm_all/ziran_jiafeimao_11.jpg",
53 },
54 {"type": "text", "text": "Describe this image."},
55 ],
56 }
57]
58
59# Thinking mode
60messages = [
61 {
62 "role": "user",
63 "content": [
64 {
65 "type": "image",
66 "image": "https://s1-11508.kwimgs.com/kos/nlav11508/mllm_all/ziran_jiafeimao_11.jpg",
67 },
68 {"type": "text", "text": "Describe this image./think"},
69 ],
70 }
71]
72
73# Preparation for inference
74text = processor.apply_chat_template(
75 messages, tokenize=False, add_generation_prompt=True
76)
77image_inputs, video_inputs = process_vision_info(messages)
78inputs = processor(
79 text=[text],
80 images=image_inputs,
81 videos=video_inputs,
82 padding=True,
83 return_tensors="pt",
84)
85inputs = inputs.to("cuda")
86
87# Inference: Generation of the output
88generated_ids = model.generate(**inputs, max_new_tokens=1024)
89generated_ids_trimmed = [
90 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
91]
92output_text = processor.batch_decode(
93 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
94)
95print(output_text)1# Messages containing a images list as a video and a text query
2messages = [
3 {
4 "role": "user",
5 "content": [
6 {
7 "type": "video",
8 "video": [
9 "file:///path/to/frame1.jpg",
10 "file:///path/to/frame2.jpg",
11 "file:///path/to/frame3.jpg",
12 "file:///path/to/frame4.jpg",
13 ],
14 },
15 {"type": "text", "text": "Describe this video."},
16 ],
17 }
18]
19
20# Messages containing a local video path and a text query
21messages = [
22 {
23 "role": "user",
24 "content": [
25 {
26 "type": "video",
27 "video": "file:///path/to/video1.mp4",
28 "max_pixels": 360 * 420,
29 "fps": 1.0,
30 },
31 {"type": "text", "text": "Describe this video."},
32 ],
33 }
34]
35
36# Messages containing a video url and a text query
37messages = [
38 {
39 "role": "user",
40 "content": [
41 {
42 "type": "video",
43 "video": "http://s2-11508.kwimgs.com/kos/nlav11508/MLLM/videos_caption/98312843263.mp4",
44 },
45 {"type": "text", "text": "Describe this video."},
46 ],
47 }
48]
49
50#In Keye-VL, frame rate information is also input into the model to align with absolute time.
51# Preparation for inference
52text = processor.apply_chat_template(
53 messages, tokenize=False, add_generation_prompt=True
54)
55image_inputs, video_inputs, video_kwargs = process_vision_info(messages, return_video_kwargs=True)
56inputs = processor(
57 text=[text],
58 images=image_inputs,
59 videos=video_inputs,
60 padding=True,
61 return_tensors="pt",
62 **video_kwargs,
63)
64inputs = inputs.to("cuda")
65
66# Inference
67generated_ids = model.generate(**inputs, max_new_tokens=128)
68generated_ids_trimmed = [
69 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
70]
71output_text = processor.batch_decode(
72 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
73)
74print(output_text)FORCE_KEYEVL_VIDEO_READER=torchvision or FORCE_KEYEVL_VIDEO_READER=decord if you prefer not to use the default one.| Backend | HTTP | HTTPS |
|---|---|---|
| torchvision >= 0.19.0 | ✅ | ✅ |
| torchvision < 0.19.0 | ❌ | ❌ |
| decord | ✅ | ❌ |
1# Sample messages for batch inference
2messages1 = [
3 {
4 "role": "user",
5 "content": [
6 {"type": "image", "image": "file:///path/to/image1.jpg"},
7 {"type": "image", "image": "file:///path/to/image2.jpg"},
8 {"type": "text", "text": "What are the common elements in these pictures?"},
9 ],
10 }
11]
12messages2 = [
13 {"role": "system", "content": "You are a helpful assistant."},
14 {"role": "user", "content": "Who are you?"},
15]
16# Combine messages for batch processing
17messages = [messages1, messages2]
18
19# Preparation for batch inference
20texts = [
21 processor.apply_chat_template(msg, tokenize=False, add_generation_prompt=True)
22 for msg in messages
23]
24image_inputs, video_inputs = process_vision_info(messages)
25inputs = processor(
26 text=texts,
27 images=image_inputs,
28 videos=video_inputs,
29 padding=True,
30 return_tensors="pt",
31)
32inputs = inputs.to("cuda")
33
34# Batch Inference
35generated_ids = model.generate(**inputs, max_new_tokens=128)
36generated_ids_trimmed = [
37 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
38]
39output_texts = processor.batch_decode(
40 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
41)
42print(output_texts)1# You can directly insert a local file path, a URL, or a base64-encoded image into the position where you want in the text.
2## Local file path
3messages = [
4 {
5 "role": "user",
6 "content": [
7 {"type": "image", "image": "file:///path/to/your/image.jpg"},
8 {"type": "text", "text": "Describe this image."},
9 ],
10 }
11]
12## Image URL
13messages = [
14 {
15 "role": "user",
16 "content": [
17 {"type": "image", "image": "http://path/to/your/image.jpg"},
18 {"type": "text", "text": "Describe this image."},
19 ],
20 }
21]
22## Base64 encoded image
23messages = [
24 {
25 "role": "user",
26 "content": [
27 {"type": "image", "image": "data:image;base64,/9j/..."},
28 {"type": "text", "text": "Describe this image."},
29 ],
30 }
31]1min_pixels = 256 * 28 * 28
2max_pixels = 1280 * 28 * 28
3processor = AutoProcessor.from_pretrained(
4 "Kwai-Keye/Keye-VL-8B-Preview", min_pixels=min_pixels, max_pixels=max_pixels
5)resized_height and resized_width. These values will be rounded to the nearest multiple of 28.1# min_pixels and max_pixels
2messages = [
3 {
4 "role": "user",
5 "content": [
6 {
7 "type": "image",
8 "image": "file:///path/to/your/image.jpg",
9 "resized_height": 280,
10 "resized_width": 420,
11 },
12 {"type": "text", "text": "Describe this image."},
13 ],
14 }
15]
16# resized_height and resized_width
17messages = [
18 {
19 "role": "user",
20 "content": [
21 {
22 "type": "image",
23 "image": "file:///path/to/your/image.jpg",
24 "min_pixels": 50176,
25 "max_pixels": 50176,
26 },
27 {"type": "text", "text": "Describe this image."},
28 ],
29 }
30]



1@misc{kwaikeyeteam2025kwaikeyevltechnicalreport,
2 title={Kwai Keye-VL Technical Report},
3 author={Kwai Keye Team},
4 year={2025},
5 eprint={2507.01949},
6 archivePrefix={arXiv},
7 primaryClass={cs.CV},
8 url={https://arxiv.org/abs/2507.01949},
9}