ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

kimi source tree.txt · Lines 4501–6000

Source identity, revision, and recovered contents.

donor model source and installation dossierSource text recovered

A continuation of kimi source tree.txt. The complete source identity, every recorded occurrence, and linked equations are on its first page.

Source transcript · continued

Download complete source text ↓6,675 lines · 275.4 KB
r""" Constructs a KimiK25 processor which wraps a KimiK25 image processor and a tokenizer into a single processor. [`KimiK25Processor`] offers all the functionalities of [`KimiK25ImageProcessor`] and [`TikTokenTokenizer`]. See the [`~KimiK25Processor.__call__`] and [`~KimiK25Processor.decode`] for more information. Args: image_processor ([`KimiK25ImageProcessor`], *optional*): The image processor is a required input. tokenizer ([`TikTokenTokenizer`], *optional*): The tokenizer is a required input. chat_template (`str`, *optional*): A Jinja template which will be used to convert lists of messages in a chat into a tokenizable string. """ attributes = ["image_processor", "tokenizer"] valid_kwargs = ["chat_template"] image_processor_class = "AutoImageProcessor" tokenizer_class = "AutoTokenizer" def __init__( self, image_processor=None, tokenizer=None, chat_template=None, **kwargs, ): super().__init__(image_processor, tokenizer, chat_template=chat_template) self.media_processor = image_processor # A special temporal placeholder to be replaced by actual video placeholders self.video_placeholder = "<|kimi_k25_video_placeholder|>" def update_raw_text(self, text: str, video_prompts: list[str]) -> str: # replace video prompt in text with video chunk prompts video_count = text.count(self.video_placeholder) if video_count == 0: return text assert video_count == len(video_prompts) text_parts = text.split(self.video_placeholder) assert len(text_parts) == len(video_prompts) + 1 text = "".join([ text_parts[i] + video_prompts[i] for i in range(len(video_prompts)) ]) text += text_parts[-1] return text def preprocess_medias(self, medias: list[dict]) -> list[dict]: updated_medias = [] video_prompts = [] for media in medias: if media['type'] == 'image': updated_medias.append(media) elif media['type'] == 'video': video_chunks = self.media_processor.split_video_chunks( media['video']) updated_medias.extend(video_chunks) video_prompts.append("".join( [vc['prompt'] for vc in video_chunks])) else: raise ValueError(f"unsupported media type: {media['type']}") return updated_medias, video_prompts def __call__(self, messages: list[dict] = None, medias: list[dict] = None, text: str = None, return_tensors: str = "pt", **kwargs) -> BatchFeature: """ Process multimodal inputs for Kimi-K2.5 model. This processor accepts ordered messages and extracts both media and text in a single pass. text will be automatically updated if video input detected in messages Args: messages: List of message dicts with 'role' and 'content' fields. If provided, medias and text will be extracted automatically. medias: Pre-extracted list of media dicts. If None, extracted from messages. text: Pre-formatted text string. If None, generated via apply_chat_template. return_tensors: Format of returned tensors ('pt', 'np', 'tf'). Default: 'pt'. **kwargs: Additional arguments passed to tokenizer.apply_chat_template. Returns: BatchFeature with fields: input_ids, attention_mask, pixel_values, grid_thws. """ if messages is None and (medias is None or text is None): raise ValueError( "Provide either 'messages' or both 'medias' and 'text'") if medias is not None and text is not None: updated_medias, video_prompts = self.preprocess_medias(medias) preprocessed = self.media_processor.preprocess( updated_medias, return_tensors=return_tensors) text = self.update_raw_text(text, video_prompts) text_inputs = self.tokenizer(text, return_tensors=return_tensors) return BatchFeature(data={**text_inputs, **preprocessed.data}) if medias is None: medias = self._extract_medias_from_messages(messages) updated_medias, video_prompts = self.preprocess_medias(medias) preprocessed = self.media_processor.preprocess( updated_medias, return_tensors=return_tensors) # Generate text if not provided if text is None: text = self.tokenizer.apply_chat_template(messages, **kwargs) text = self.update_raw_text(text, video_prompts) text_inputs = self.tokenizer(text, return_tensors=return_tensors) return BatchFeature(data={**text_inputs, **preprocessed.data}) @staticmethod def _extract_medias_from_messages(messages: list[dict]) -> list[dict]: """ Extract media items from messages in a single pass. This is an optimized version that processes messages only once. Kept as internal method since external callers should use __call__. """ medias = [] for msg in messages: if msg['role'] != 'user' or not msg.get('content'): continue for content_part in msg['content']: if not isinstance(content_part, dict): continue content_type = content_part.get('type') if content_type in ['video_url', 'video']: medias.append({ 'type': 'video', 'video': content_part['video_url']['url'], 'first_frame_timestamp': 0.0 }) elif content_type in ['image_url', 'image']: medias.append({ 'type': 'image', 'image': content_part['image_url'], }) return medias def apply_chat_template(self, messages, **kwargs): return self.tokenizer.apply_chat_template(messages, **kwargs) def batch_decode(self, *args, **kwargs): return self.tokenizer.batch_decode(*args, **kwargs) def decode(self, *args, **kwargs): return self.tokenizer.decode(*args, **kwargs) @property def model_input_names(self): return ['input_ids', 'attention_mask', 'pixel_values', 'grid_thws'] >>>>> FILE: kimi_k25_vision_processing.py (10019 bytes) <<<<< """Image processor class for Kimi-K2.5. """ import json from typing import Any, Dict, Optional, Union import numpy as np import torch from PIL import Image from transformers.image_processing_utils import (BaseImageProcessor, BatchFeature) from transformers.utils import TensorType from .media_utils import (MediaInput, VideoChunkInput, _to_tensor, ensure_media_type, get_video_meta, image_to_np, navit_patchify, navit_resize_image, navit_resize_video, normalize, real_sample_fps_and_max_num_frames, timestamp_as_str) try: from mecord import VideoReader except ImportError: VideoReader = None def resampling(video_bytes: bytes, sample_indices: list[int], key_indices=None, frame_time_info=None, num_threads=4) -> str: video = VideoReader(video_bytes, num_threads=num_threads, frame_time_info=frame_time_info, key_indices=key_indices) # extract target frames frames = video[sample_indices] frames = [Image.fromarray(frame) for frame in frames] return frames class KimiK25VisionProcessor(BaseImageProcessor): model_type = "kimi_k25" def __init__( self, media_proc_cfg: dict, **kwargs, ): super().__init__(**kwargs) self.media_proc_cfg = media_proc_cfg self.num_frames_per_chunk = media_proc_cfg[ 'temporal_merge_kernel_size'] def media_tokens_calculator(self, media: MediaInput): media = ensure_media_type(media) ret = self.get_resize_config(media) return ret['num_tokens'] @classmethod def make_chunk_prompt(cls, timestamp_text: str) -> str: return f"{timestamp_text}<|media_begin|>video<|media_content|><|media_pad|><|media_end|>" def split_video_chunks(self, video_url: str | bytes) -> list[list[Image.Image]]: # video_url should be base64 str or bytes video_spec = get_video_meta(video_url) sample_fps = min(self.media_proc_cfg['sample_fps'], video_spec.fps) sampled_nframes = max( round(video_spec.num_frames * sample_fps / video_spec.fps), 1) frame_inds = np.linspace(0, video_spec.num_frames - 1, sampled_nframes).round().astype(int) frame_inds = frame_inds.tolist() sampled_frame_ids = [] temporal_merge_kernel_size = self.media_proc_cfg[ "temporal_merge_kernel_size"] num_chunks = 0 chunk_timestamp = [] for i in range(0, len(frame_inds), temporal_merge_kernel_size): sampled_frame_ids.extend(frame_inds[i:i + temporal_merge_kernel_size]) start_time = frame_inds[i] / float(video_spec.fps) timestamp_text = timestamp_as_str( start_time, self.media_proc_cfg["timestamp_mode"]) chunk_timestamp.append(timestamp_text) num_chunks += 1 sampled_frames = resampling(video_url, sampled_frame_ids) chunks = [] for chunk_id in range(num_chunks): chunk = sampled_frames[chunk_id * temporal_merge_kernel_size:(chunk_id + 1) * temporal_merge_kernel_size] chunks.append( VideoChunkInput(type="video_chunk", video_chunk=chunk, prompt=self.make_chunk_prompt( chunk_timestamp[chunk_id]))) return chunks def get_resize_config(self, media_input: MediaInput) -> dict: if media_input['type'] == 'image': w, h = media_input['image'].size ret = navit_resize_image( w, h, self.media_proc_cfg['patch_size'], self.media_proc_cfg['merge_kernel_size'], self.media_proc_cfg['in_patch_limit'], self.media_proc_cfg['patch_limit_on_one_side'], self.media_proc_cfg['fixed_output_tokens']) return ret elif media_input['type'] == 'video_chunk': frame = media_input['video_chunk'][0] width, height = frame.size num_frames = len(media_input["video_chunk"]) fps = 1.0 sample_fps, max_num_frames_each_video = real_sample_fps_and_max_num_frames( media_input["type"], self.media_proc_cfg['sample_fps'], self.media_proc_cfg['max_num_frames_each_video'], ) in_patch_limit_each_frame = self.media_proc_cfg[ 'in_patch_limit_each_frame'] if in_patch_limit_each_frame is None: in_patch_limit_each_frame = self.media_proc_cfg[ 'in_patch_limit'] ret = navit_resize_video( width, height, num_frames, fps, sample_fps, self.media_proc_cfg['patch_size'], self.media_proc_cfg['merge_kernel_size'], in_patch_limit_each_frame, self.media_proc_cfg['patch_limit_on_one_side'], self.media_proc_cfg['in_patch_limit_video'], max_num_frames_each_video, self.media_proc_cfg['fixed_output_tokens'], ) return ret else: raise ValueError("Unsupported type: {}".format( media_input['type'])) def resize_image(self, image: Image.Image, new_width: int, new_height: int, pad_width: int, pad_height: int) -> np.ndarray: image_np = image_to_np(image, (new_width, new_height), "resize") image_np = np.pad( image_np, ((0, pad_height), (0, pad_width), (0, 0)), mode="constant", constant_values=0, ) return image_np def preprocess( self, medias: list[MediaInput], return_tensors: Optional[Union[str, TensorType]] = None, ) -> BatchFeature: """ Preprocess a atom vision input (images/video_chunk) into model-ready tensors. Args: medias: List of MediaInput. return_tensors: Desired output format ('pt', 'np', 'tf', or None). Returns: BatchFeature containing 'pixel_values' and 'grid_thws' tensors. """ if not isinstance(medias, list): medias = [medias] if medias: pixel_values = [] for item in medias: item = ensure_media_type(item) resize_config = self.get_resize_config(item) new_width, new_height, pad_width, pad_height = resize_config[ 'new_width'], resize_config['new_height'], resize_config[ 'pad_width'], resize_config['pad_height'] if item['type'] == 'image': image = item['image'] image_np = self.resize_image(image, new_width, new_height, pad_width, pad_height) pixel_values.append(np.expand_dims(image_np, axis=0)) elif item['type'] == 'video_chunk': pixels = [] for frame in item['video_chunk']: frame_np = self.resize_image(frame, new_width, new_height, pad_width, pad_height) pixels.append(frame_np) pixel_values.append(np.stack(pixels, axis=0)) else: raise ValueError("Unsupported type: {}".format( item['type'])) normalized_pixel_values = [] image_std_inv = 1.0 / np.array(self.media_proc_cfg['image_std']) image_mean = np.array(self.media_proc_cfg['image_mean']) for pixels in pixel_values: pixels = normalize(pixels, image_mean, image_std_inv) pixels_and_thw = navit_patchify( pixels, self.media_proc_cfg['patch_size'], ) normalized_pixel_values.append(pixels_and_thw) pixel_values = torch.cat([ _to_tensor(pixel_value['pixel_values']) for pixel_value in normalized_pixel_values ]) grid_thws = torch.cat([ _to_tensor(pixel_value['grid_thw'], dtype=torch.int64).unsqueeze(0) for pixel_value in normalized_pixel_values ]) data = { 'pixel_values': pixel_values, 'grid_thws': grid_thws, } else: data = {} return BatchFeature(data=data, tensor_type=return_tensors) def __repr__(self): return f"KimiK25VisionProcessor(media_proc_cfg={self.media_proc_cfg})" def to_dict(self) -> Dict[str, Any]: output = super().to_dict() output["media_proc_cfg"] = self.media_proc_cfg if "media_processor" in output: del output["media_processor"] return output @classmethod def from_dict(cls, config_dict: Dict[str, Any], **kwargs): config = config_dict.copy() media_proc_cfg = config.pop("media_proc_cfg", {}) return cls(media_proc_cfg=media_proc_cfg, **config, **kwargs) def to_json_string(self): dictionary = self.to_dict() for key, value in dictionary.items(): if hasattr(value, 'tolist'): dictionary[key] = value.tolist() return json.dumps(dictionary, indent=2, sort_keys=True) + "\n" >>>>> FILE: media_utils.py (13179 bytes) <<<<< import base64 import io import math import os from datetime import datetime, timezone from typing import List, Literal, Optional, TypedDict import numpy as np from PIL import Image from pydantic import BaseModel, Field try: from mecord import VideoReader except ImportError: VideoReader = None class VideoSpec(BaseModel): media_type: str = Literal['video'] height: int = Field(..., gt=0, description="video frame height") width: int = Field(..., gt=0, description="video frame width") num_frames: int = Field(..., gt=0, description="num frames") fps: float = Field(..., gt=0, description="average fps") # optional, help to accelerate video reading key_indices: list[int] = Field(None, description="key indices") frame_time_info: dict = Field(None, description="frame time info") class ImageInput(TypedDict): type: Literal['image'] image: Image.Image class VideoChunkInput(TypedDict): type: Literal['video_chunk'] video_chunk: List[Image.Image] prompt: Optional[str] = None MediaInput = ImageInput | VideoChunkInput def get_video_meta(video_src: bytes | str | os.PathLike, accurate: bool = True) -> dict: """Get the dimensions of a video.""" if isinstance(video_src, os.PathLike): video_src = str(video_src) # if b64 string, decode to bytes if isinstance(video_src, str) and video_src.startswith('data:video/mp4;base64,'): video_src = base64.b64decode(video_src.split(',')[1]) video = VideoReader(video_src, auto_init=accurate, num_threads=1) assert video.num_frames > 0, "Invalid video format." assert video.original_width > 0 and video.original_height > 0, ( "Invalid video format.") assert video.avg_fps > 0, "Invalid video format." return VideoSpec(media_type='video', height=video.original_height, width=video.original_width, num_frames=video.num_frames, fps=video.avg_fps, key_indices=video.key_indices, frame_time_info=video.frame_time_info) def timestamp_as_str(timestamp: float, timestamp_mode: str = "hh:mm:ss.fff") -> str: """Convert a timestamp to a string in the format of HH:MM:SS.mmm.""" if timestamp_mode == "hh:mm:ss.fff": return (datetime.fromtimestamp(timestamp, tz=timezone.utc).strftime("%H:%M:%S") + f".{int((timestamp % 1) * 1000):03d}") elif timestamp_mode == "mm:ss.fff": return (datetime.fromtimestamp(timestamp, tz=timezone.utc).strftime("%M:%S") + f".{int((timestamp % 1) * 1000):03d}") elif timestamp_mode == "mm:ss": return datetime.fromtimestamp(timestamp, tz=timezone.utc).strftime("%M:%S") else: raise ValueError(f"Invalid timestamp mode: {timestamp_mode}") def navit_resize_image( width: int, height: int, patch_size: int, merge_kernel_size: int, in_patch_limit: int, patch_limit_on_one_side: int, fixed_output_tokens: int | None, ): # Apply the patch limits. s1 = math.sqrt( in_patch_limit / (max(1.0, width // patch_size) * max(1.0, height // patch_size))) s2 = patch_limit_on_one_side * patch_size / width s3 = patch_limit_on_one_side * patch_size / height scale = min(1.0, s1, s2, s3) new_w, new_h = max(1, int(width * scale)), max(1, int(height * scale)) new_w = min(new_w, patch_limit_on_one_side * patch_size) new_h = min(new_h, patch_limit_on_one_side * patch_size) # Calculate the padding to make the height and width divisible by the merge kernel size and patch size. factor = merge_kernel_size * patch_size pad_height = (factor - new_h % factor) % factor pad_width = (factor - new_w % factor) % factor if fixed_output_tokens is not None: num_tokens = fixed_output_tokens else: # Calculate new dimensions after padding and patching token_height = (new_h + pad_height) // factor token_width = (new_w + pad_width) // factor assert token_height * merge_kernel_size <= patch_limit_on_one_side, ( f"token_height {token_height} * merge_kernel_size {merge_kernel_size} > patch_limit_on_one_side {patch_limit_on_one_side}" ) assert token_width * merge_kernel_size <= patch_limit_on_one_side, ( f"token_width {token_width} * merge_kernel_size {merge_kernel_size} > patch_limit_on_one_side {patch_limit_on_one_side}" ) num_tokens = token_height * token_width return { "num_tokens": num_tokens, "new_width": new_w, "new_height": new_h, "pad_width": pad_width, "pad_height": pad_height, "sampled_nframes": 1, } def navit_resize_video( width: int, height: int, nframes: int, avg_fps: float, sample_fps: float, patch_size: int, merge_kernel_size: int, in_patch_limit_each_frame: int, patch_limit_on_one_side: int, in_patch_limit_total: int | None, max_num_frames_each_video: int | None, fixed_output_tokens_each_frame: int | None, ): sample_fps = min(sample_fps, avg_fps) # Calculate the number of frames to sample based on target FPS sampled_nframes = max(round(nframes * sample_fps / avg_fps), 1) if max_num_frames_each_video is not None: sampled_nframes = min(sampled_nframes, max_num_frames_each_video) if in_patch_limit_total is not None: in_patch_limit_each_frame = min( round(in_patch_limit_total / sampled_nframes), in_patch_limit_each_frame) ret = navit_resize_image( width, height, patch_size, merge_kernel_size, in_patch_limit_each_frame, patch_limit_on_one_side, fixed_output_tokens_each_frame, ) ret["sampled_nframes"] = sampled_nframes return ret def real_sample_fps_and_max_num_frames( type_name: Literal["video", "video_chunk"], sample_fps: float, max_num_frames_each_video: int | None, ) -> tuple[int, int | None]: if type_name == "video": return sample_fps, max_num_frames_each_video elif type_name == "video_chunk": max_num_frames_each_video = None sample_fps = math.inf return sample_fps, max_num_frames_each_video else: return math.inf, None def _to_pil(data: str | bytes): if isinstance(data, Image.Image): return data.convert("RGB") elif isinstance(data, str): if data.startswith("data:"): raw_base64 = data.split(",")[1] return Image.open(io.BytesIO( base64.b64decode(raw_base64))).convert("RGB") else: return Image.open(data).convert("RGB") elif isinstance(data, bytes): return Image.open(io.BytesIO(data)).convert("RGB") else: raise ValueError(f"Unsupported data type: {type(data)}") def ensure_media_type(media: MediaInput) -> MediaInput: if media['type'] == 'image': media['image'] = _to_pil(media['image']) return media elif media['type'] == 'video_chunk': media['video_chunk'] = [ _to_pil(frame) for frame in media['video_chunk'] ] return media else: raise ValueError(f"Unsupported media type: {media['type']}") def image_to_np( image: Image.Image, resize_to: tuple[int, int] | None = None, mode: str = "resize", raise_error_for_ill_resize: bool = True, ) -> np.ndarray: """Convert an image to a numpy array. Args: content: The image to convert. resize_to: The size to resize the image to. mode: The mode to resize the image to. raise_error_for_ill_resize: Whether to raise an error for ill-sized resize. Returns: A numpy array. """ assert isinstance(image, Image.Image), "image must be a PIL Image" if resize_to is not None: if mode == "resize": image = image.resize(resize_to, resample=Image.Resampling.BICUBIC) elif mode == "rescale_and_pad_to_center": scale = min(resize_to[0] / image.width, resize_to[1] / image.height, 1.0) new_width = round(image.width * scale) new_height = round(image.height * scale) if new_width == 0 or new_height == 0: if raise_error_for_ill_resize: raise ValueError( f"Invalid resize to: {resize_to}, from image size: {image.size}" ) else: return np.zeros((resize_to[1], resize_to[0], 3), dtype=np.uint8) image = image.resize((new_width, new_height), resample=Image.Resampling.BICUBIC) padding_left = (resize_to[0] - new_width) // 2 padding_right = resize_to[0] - new_width - padding_left padding_top = (resize_to[1] - new_height) // 2 padding_bottom = resize_to[1] - new_height - padding_top image = np.asarray(image) image = np.pad( image, ((padding_top, padding_bottom), (padding_left, padding_right), (0, 0)), mode="constant", constant_values=0, ) assert image.shape == (resize_to[1], resize_to[0], 3) elif mode == "rescale_and_pad_to_rightbottom": scale = min(resize_to[0] / image.width, resize_to[1] / image.height, 1.0) new_width = round(image.width * scale) new_height = round(image.height * scale) if new_width == 0 or new_height == 0: if raise_error_for_ill_resize: raise ValueError( f"Invalid resize to: {resize_to}, from image size: {image.size}" ) else: return np.zeros((resize_to[1], resize_to[0], 3), dtype=np.uint8) image = image.resize((new_width, new_height), resample=Image.Resampling.BICUBIC) padding_right = resize_to[0] - new_width padding_bottom = resize_to[1] - new_height image = np.asarray(image) image = np.pad( image, ((0, padding_bottom), (0, padding_right), (0, 0)), mode="constant", constant_values=0, ) assert image.shape == (resize_to[1], resize_to[0], 3) else: raise ValueError(f"Invalid mode: {mode}") if isinstance(image, Image.Image): return np.asarray(image) else: return image def navit_patchify(pixel_values: np.ndarray, patch_size: int) -> dict[str, np.ndarray]: """Reshape the pixel values to a navit shape. Args: pixel_values: np.ndarray, shape (t, h, w, c) patch_size: int Returns: dict[str, np.ndarray] - patches: np.ndarray, shape (t * h//patch_size * w//patch_size, c, patch_size, patch_size) - grid_thw: np.ndarray, (t, h//patch_size, w//patch_size) """ T, H, W, C = pixel_values.shape assert C == 3, "pixel_values must have 3 channels" patches = pixel_values.reshape(T, H // patch_size, patch_size, W // patch_size, patch_size, C) # (T, H//patch_size, W//patch_size, C, patch_size, patch_size) patches = patches.transpose(0, 1, 3, 5, 2, 4) patches = patches.reshape(-1, C, patch_size, patch_size) grid_thw = np.array([T, H // patch_size, W // patch_size]) return {"pixel_values": patches, "grid_thw": grid_thw} def normalize(x: np.ndarray, mean, std_inv, pixels_dtype: np.dtype = np.float32) -> np.ndarray: """Normalize the image. Args: x: The image to normalize. The shape is (..., 3). The dtype is uint8. The range is [0, 255]. mean: The mean of the image. std_inv: The inverse of the std of the image. pixels_dtype: The dtype of the image. Returns: The normalized image. The shape is (..., 3). The dtype is determined by the pixels_dtype. """ x = (x / 255.0).astype(pixels_dtype) x -= mean x *= std_inv return x def _to_tensor(data, **kwargs): import torch if isinstance(data, np.ndarray): return torch.from_numpy(data).to(**kwargs) elif isinstance(data, torch.Tensor): return data.to(**kwargs) elif isinstance(data, list): return [_to_tensor(item, **kwargs) for item in data] elif isinstance(data, tuple): return tuple(_to_tensor(item, **kwargs) for item in data) elif isinstance(data, dict): return {k: _to_tensor(v, **kwargs) for k, v in data.items()} elif data is None: return None else: raise ValueError(f"Unsupported data type: {type(data)}") >>>>> FILE: tool_declaration_ts.py (16517 bytes) <<<<< """ Encode structured tool declaration to typescript style string. """ import dataclasses import json import logging from collections.abc import Sequence from typing import Any logger = logging.getLogger(__name__) _TS_INDENT = " " _TS_FIELD_DELIMITER = ",\n" class _SchemaRegistry: """Registry for schema definitions to handle $ref resolution""" def __init__(self): self.definitions = {} self.has_self_ref = False def register_definitions(self, defs: dict[str, Any]): """Register schema definitions from $defs section""" if not defs: return for def_name, def_schema in defs.items(): self.definitions[def_name] = def_schema def resolve_ref(self, ref: str) -> dict[str, Any]: """Resolve a reference to its schema definition""" if ref == "#": self.has_self_ref = True return {"$self_ref": True} elif ref.startswith("#/$defs/"): def_name = ref.split("/")[-1] if def_name not in self.definitions: raise ValueError(f"Reference not found: {ref}") return self.definitions[def_name] else: raise ValueError(f"Unsupported reference format: {ref}") def _format_description(description: str, indent: str = "") -> str: return "\n".join([ f"{indent}// {line}" if line else "" for line in description.split("\n") ]) class _BaseType: description: str constraints: dict[str, Any] def __init__( self, extra_props: dict[str, Any], *, allowed_constraint_keys: Sequence[str] = (), ): self.description = extra_props.get("description", "") self.constraints = { k: v for k, v in extra_props.items() if k in allowed_constraint_keys } def to_typescript_style(self, indent: str = "") -> str: raise NotImplementedError def format_docstring(self, indent: str) -> str: lines = [] if self.description: lines.append(_format_description(self.description, indent)) if self.constraints: constraints_str = ", ".join(f"{k}: {v}" for k, v in sorted( self.constraints.items(), key=lambda kv: kv[0])) lines.append(f"{indent}// {constraints_str}") return "".join(x + "\n" for x in lines) class _ParameterTypeScalar(_BaseType): type: str def __init__(self, type: str, extra_props: dict[str, Any] | None = None): self.type = type allowed_constraint_keys: list[str] = [] if self.type == "string": allowed_constraint_keys = ["maxLength", "minLength", "pattern"] elif self.type in ("number", "integer"): allowed_constraint_keys = ["maximum", "minimum"] super().__init__(extra_props or {}, allowed_constraint_keys=allowed_constraint_keys) def to_typescript_style(self, indent: str = "") -> str: # Map integer to number in TypeScript if self.type == "integer": return "number" return self.type class _ParameterTypeObject(_BaseType): properties: list["_Parameter"] additional_properties: Any | None = None def __init__(self, json_schema_object: dict[str, Any], registry: _SchemaRegistry | None = None): super().__init__(json_schema_object) self.properties = [] self.additional_properties = None if not json_schema_object: return if "$defs" in json_schema_object and registry: registry.register_definitions(json_schema_object["$defs"]) self.additional_properties = json_schema_object.get( "additionalProperties") if isinstance(self.additional_properties, dict): self.additional_properties = _parse_parameter_type( self.additional_properties, registry) if "properties" not in json_schema_object: return required_parameters = json_schema_object.get("required", []) optional_parameters = set( json_schema_object["properties"].keys()) - set(required_parameters) self.properties = [ _Parameter( name=name, type=_parse_parameter_type(prop, registry), optional=name in optional_parameters, default=prop.get("default") if isinstance(prop, dict) else None, ) for name, prop in json_schema_object["properties"].items() ] def to_typescript_style(self, indent: str = "") -> str: # sort by optional, make the required parameters first parameters = [p for p in self.properties if not p.optional] opt_params = [p for p in self.properties if p.optional] parameters = sorted(parameters, key=lambda p: p.name) parameters.extend(sorted(opt_params, key=lambda p: p.name)) param_strs = [] for p in parameters: one = p.to_typescript_style(indent=indent + _TS_INDENT) param_strs.append(one) if self.additional_properties is not None: ap_type_str = "any" if self.additional_properties is True: ap_type_str = "any" elif self.additional_properties is False: ap_type_str = "never" elif isinstance(self.additional_properties, _ParameterType): ap_type_str = self.additional_properties.to_typescript_style( indent=indent + _TS_INDENT) else: raise ValueError( f"Unknown additionalProperties: {self.additional_properties}" ) param_strs.append( f"{indent + _TS_INDENT}[k: string]: {ap_type_str}") if not param_strs: return "{}" params_str = _TS_FIELD_DELIMITER.join(param_strs) if params_str: # add new line before and after params_str = f"\n{params_str}\n" # always wrap with object return f"{{{params_str}{indent}}}" class _ParameterTypeArray(_BaseType): item: "_ParameterType" def __init__(self, json_schema_object: dict[str, Any], registry: _SchemaRegistry | None = None): super().__init__(json_schema_object, allowed_constraint_keys=("minItems", "maxItems")) if json_schema_object.get("items"): self.item = _parse_parameter_type(json_schema_object["items"], registry) else: self.item = _ParameterTypeScalar(type="any") def to_typescript_style(self, indent: str = "") -> str: item_docstring = self.item.format_docstring(indent + _TS_INDENT) if item_docstring: return ("Array<\n" + item_docstring + indent + _TS_INDENT + self.item.to_typescript_style(indent=indent + _TS_INDENT) + "\n" + indent + ">") else: return f"Array<{self.item.to_typescript_style(indent=indent)}>" class _ParameterTypeEnum(_BaseType): # support scalar types only enum: list[str | int | float | bool | None] def __init__(self, json_schema_object: dict[str, Any]): super().__init__(json_schema_object) self.enum = json_schema_object["enum"] # Validate enum values against declared type if present if "type" in json_schema_object: typ = json_schema_object["type"] if isinstance(typ, list): if len(typ) == 1: typ = typ[0] elif len(typ) == 2: if "null" not in typ: raise ValueError(f"Enum type {typ} is not supported") else: typ = typ[0] if typ[0] != "null" else typ[1] else: raise ValueError(f"Enum type {typ} is not supported") for val in self.enum: if val is None: continue if typ == "string" and not isinstance(val, str): raise ValueError(f"Enum value {val} is not a string") elif typ == "number" and not isinstance(val, (int, float)): raise ValueError(f"Enum value {val} is not a number") elif typ == "integer" and not isinstance(val, int): raise ValueError(f"Enum value {val} is not an integer") elif typ == "boolean" and not isinstance(val, bool): raise ValueError(f"Enum value {val} is not a boolean") def to_typescript_style(self, indent: str = "") -> str: return " | ".join( [f'"{e}"' if isinstance(e, str) else str(e) for e in self.enum]) class _ParameterTypeAnyOf(_BaseType): types: list["_ParameterType"] def __init__( self, json_schema_object: dict[str, Any], registry: _SchemaRegistry | None = None, ): super().__init__(json_schema_object) self.types = [ _parse_parameter_type(t, registry) for t in json_schema_object["anyOf"] ] def to_typescript_style(self, indent: str = "") -> str: return " | ".join( [t.to_typescript_style(indent=indent) for t in self.types]) class _ParameterTypeUnion(_BaseType): types: list[str] def __init__(self, json_schema_object: dict[str, Any]): super().__init__(json_schema_object) mapping = { "string": "string", "number": "number", "integer": "number", "boolean": "boolean", "null": "null", "object": "{}", "array": "Array<any>", } self.types = [mapping[t] for t in json_schema_object["type"]] def to_typescript_style(self, indent: str = "") -> str: return " | ".join(self.types) class _ParameterTypeRef(_BaseType): ref_name: str is_self_ref: bool = False def __init__(self, json_schema_object: dict[str, Any], registry: _SchemaRegistry): super().__init__(json_schema_object) ref = json_schema_object["$ref"] resolved_schema = registry.resolve_ref(ref) if resolved_schema.get("$self_ref", False): self.ref_name = "parameters" self.is_self_ref = True else: self.ref_name = ref.split("/")[-1] def to_typescript_style(self, indent: str = "") -> str: return self.ref_name _ParameterType = (_ParameterTypeScalar | _ParameterTypeObject | _ParameterTypeArray | _ParameterTypeEnum | _ParameterTypeAnyOf | _ParameterTypeUnion | _ParameterTypeRef) @dataclasses.dataclass class _Parameter: """ A parameter in a function, or a field in a object. It consists of the type as well as the name. """ type: _ParameterType name: str = "_" optional: bool = True default: Any | None = None @classmethod def parse_extended(cls, attributes: dict[str, Any]) -> "_Parameter": if not attributes: raise ValueError("attributes is empty") return cls( name=attributes.get("name", "_"), type=_parse_parameter_type(attributes), optional=attributes.get("optional", False), default=attributes.get("default"), ) def to_typescript_style(self, indent: str = "") -> str: comments = self.type.format_docstring(indent) if self.default is not None: default_repr = (json.dumps(self.default, ensure_ascii=False) if not isinstance(self.default, (int, float, bool)) else repr(self.default)) comments += f"{indent}// Default: {default_repr}\n" return ( comments + f"{indent}{self.name}{'?' if self.optional else ''}: {self.type.to_typescript_style(indent=indent)}" ) def _parse_parameter_type( json_schema_object: dict[str, Any] | bool, registry: _SchemaRegistry | None = None) -> _ParameterType: if isinstance(json_schema_object, bool): if json_schema_object: return _ParameterTypeScalar(type="any") else: logger.warning( f"Warning: Boolean value {json_schema_object} is not supported, use null instead." ) return _ParameterTypeScalar(type="null") if "$ref" in json_schema_object and registry: return _ParameterTypeRef(json_schema_object, registry) if "anyOf" in json_schema_object: return _ParameterTypeAnyOf(json_schema_object, registry) elif "enum" in json_schema_object: return _ParameterTypeEnum(json_schema_object) elif "type" in json_schema_object: typ = json_schema_object["type"] if isinstance(typ, list): return _ParameterTypeUnion(json_schema_object) elif typ == "object": return _ParameterTypeObject(json_schema_object, registry) elif typ == "array": return _ParameterTypeArray(json_schema_object, registry) else: return _ParameterTypeScalar(typ, json_schema_object) elif json_schema_object == {}: return _ParameterTypeScalar(type="any") else: raise ValueError(f"Invalid JSON Schema object: {json_schema_object}") def _openai_function_to_typescript_style(function: dict[str, Any], ) -> str: """Convert OpenAI function definition (dict) to TypeScript style string.""" registry = _SchemaRegistry() parameters = function.get("parameters") or {} parsed = _ParameterTypeObject(parameters, registry) interfaces = [] root_interface_name = None if registry.has_self_ref: root_interface_name = "parameters" params_str = _TS_FIELD_DELIMITER.join([ p.to_typescript_style(indent=_TS_INDENT) for p in parsed.properties ]) params_str = f"\n{params_str}\n" if params_str else "" interface_def = f"interface {root_interface_name} {{{params_str}}}" interfaces.append(interface_def) definitions_copy = dict(registry.definitions) for def_name, def_schema in definitions_copy.items(): obj_type = _parse_parameter_type(def_schema, registry) params_str = obj_type.to_typescript_style() description_part = "" if obj_description := def_schema.get("description", ""): description_part = _format_description(obj_description) + "\n" interface_def = f"{description_part}interface {def_name} {params_str}" interfaces.append(interface_def) interface_str = "\n".join(interfaces) function_name = function.get("name", "function") if root_interface_name: type_def = f"type {function_name} = (_: {root_interface_name}) => any;" else: params_str = parsed.to_typescript_style() type_def = f"type {function_name} = (_: {params_str}) => any;" description = function.get("description") return "\n".join( filter( bool, [ interface_str, ((description and _format_description(description)) or ""), type_def, ], )) def encode_tools_to_typescript_style(tools: list[dict[str, Any]], ) -> str: """ Convert tools (list of dict) to TypeScript style string. Supports OpenAI format: {"type": "function", "function": {...}} Args: tools: List of tool definitions in dict format Returns: TypeScript style string representation of the tools """ if not tools: return "" functions = [] for tool in tools: tool_type = tool.get("type") if tool_type == "function": func_def = tool.get("function", {}) if func_def: functions.append( _openai_function_to_typescript_style(func_def)) else: # Skip unsupported tool types (like "_plugin") continue if not functions: return "" functions_str = "\n".join(functions) result = "# Tools\n\n" if functions_str: result += "## functions\nnamespace functions {\n" result += functions_str + "\n" result += "}\n" return result ================================================================================ PART 6 - DOCS / LICENSE (verbatim) ================================================================================ >>>>> FILE: README.md <<<<< --- tags: - compressed-tensors license: other license_name: modified-mit library_name: transformers pipeline_tag: image-text-to-text paper: arxiv.org/abs/2602.02276 --- <div align="center"> <picture> <img src="figures/kimi-logo.png" width="30%" alt="Kimi K2.5"> </picture> </div> <hr> <div align="center" style="line-height:1"> <a href="https://www.kimi.com" target="_blank"><img alt="Chat" src="https://img.shields.io/badge/🤖%20Chat-Kimi%20K2.5-ff6b6b?color=1783ff&logoColor=white"/></a> <a href="https://github.com/moonshotai/Kimi-K2.5"><img alt="github" src="https://img.shields.io/badge/Github-Kimi%20K2.5-181717?logo=github&color=1783ff&logoColor=white"/></a> <a href="https://www.moonshot.ai" target="_blank"><img alt="Homepage" src="https://img.shields.io/badge/Homepage-Moonshot%20AI-white?logo=Kimi&logoColor=white"/></a> </div> <div align="center" style="line-height: 1;"> <a href="https://huggingface.co/moonshotai" target="_blank"><img alt="Hugging Face" src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Moonshot%20AI-ffc107?color=ffc107&logoColor=white"/></a> <a href="https://twitter.com/kimi_moonshot" target="_blank"><img alt="Twitter Follow" src="https://img.shields.io/badge/Twitter-Kimi.ai-white?logo=x&logoColor=white"/></a> <a href="https://discord.gg/TYU2fdJykW" target="_blank"><img alt="Discord" src="https://img.shields.io/badge/Discord-Kimi.ai-white?logo=discord&logoColor=white"/></a> </div> <div align="center" style="line-height: 1;"> <a href="https://huggingface.co/moonshotai/Kimi-K2.5/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-Modified_MIT-f5de53?&color=f5de53"/></a> </div> <p align="center"> <b>📰&nbsp;&nbsp;<a href="https://www.kimi.com/blog/kimi-k2-5.html">Tech Blog</a></b> &nbsp;&nbsp;&nbsp; | &nbsp;&nbsp;&nbsp; <b>📄&nbsp;&nbsp;<a href="https://arxiv.org/abs/2602.02276">Paper</a></b> </p> </p> ## 0. Changelog - 2026.1.29: - The default system prompt might cause confusion to users and unexpected behaviours, so we remove it. - The token `<|media_start|>` is incorrect; it has been replaced with `<|media_begin|>` in the chat template. ## 1. Model Introduction Kimi K2.5 is an open-source, native multimodal agentic model built through continual pretraining on approximately 15 trillion mixed visual and text tokens atop Kimi-K2-Base. It seamlessly integrates vision and language understanding with advanced agentic capabilities, instant and thinking modes, as well as conversational and agentic paradigms. ### Key Features - **Native Multimodality**: Pre-trained on vision–language tokens, K2.5 excels in visual knowledge, cross-modal reasoning, and agentic tool use grounded in visual inputs. - **Coding with Vision**: K2.5 generates code from visual specifications (UI designs, video workflows) and autonomously orchestrates tools for visual data processing. - **Agent Swarm**: K2.5 transitions from single-agent scaling to a self-directed, coordinated swarm-like execution scheme. It decomposes complex tasks into parallel sub-tasks executed by dynamically instantiated, domain-specific agents. ## 2. Model Summary <div align="center"> | | | |:---:|:---:| | **Architecture** | Mixture-of-Experts (MoE) | | **Total Parameters** | 1T | | **Activated Parameters** | 32B | | **Number of Layers** (Dense layer included) | 61 | | **Number of Dense Layers** | 1 | | **Attention Hidden Dimension** | 7168 | | **MoE Hidden Dimension** (per Expert) | 2048 | | **Number of Attention Heads** | 64 | | **Number of Experts** | 384 | | **Selected Experts per Token** | 8 | | **Number of Shared Experts** | 1 | | **Vocabulary Size** | 160K | | **Context Length** | 256K | | **Attention Mechanism** | MLA | | **Activation Function** | SwiGLU | | **Vision Encoder** | MoonViT | | **Parameters of Vision Encoder** | 400M | </div> ## 3. Evaluation Results <div align="center"> <table> <thead> <tr> <th align="center">Benchmark</th> <th align="center"><sup>Kimi K2.5<br><sup>(Thinking)</sup></sup></th> <th align="center"><sup>GPT-5.2 <br><sup>(xhigh)</sup></sup></th> <th align="center"><sup>Claude 4.5 Opus <br><sup>(Extended Thinking)</sup></sup></th> <th align="center"><sup>Gemini 3 Pro <br><sup>(High Thinking Level)</sup></sup></th> <th align="center"><sup>DeepSeek V3.2 <br><sup>(Thinking)</sup></sup></th> <th align="center"><sup>Qwen3-VL-<br>235B-A22B-<br>Thinking</sup></th> </tr> </thead> <tbody> <tr> <td align="center" colspan=8><strong>Reasoning &amp; Knowledge</strong></td> </tr> <tr> <td align="center" style="vertical-align: middle">HLE-Full</td> <td align="center" style="vertical-align: middle">30.1</td> <td align="center" style="vertical-align: middle">34.5</td> <td align="center" style="vertical-align: middle">30.8</td> <td align="center" style="vertical-align: middle">37.5</td> <td align="center" style="vertical-align: middle">25.1<sup>†</sup></td> <td align="center" style="vertical-align: middle">-</td> </tr> <tr> <td align="center" style="vertical-align: middle">HLE-Full<br>(w/ tools)</td> <td align="center" style="vertical-align: middle">50.2</td> <td align="center" style="vertical-align: middle">45.5</td> <td align="center" style="vertical-align: middle">43.2</td> <td align="center" style="vertical-align: middle">45.8</td> <td align="center" style="vertical-align: middle">40.8<sup>†</sup></td> <td align="center" style="vertical-align: middle">-</td> </tr> <tr> <td align="center" style="vertical-align: middle">AIME 2025</td> <td align="center" style="vertical-align: middle">96.1</td> <td align="center" style="vertical-align: middle">100</td> <td align="center" style="vertical-align: middle">92.8</td> <td align="center" style="vertical-align: middle">95.0</td> <td align="center" style="vertical-align: middle">93.1</td> <td align="center" style="vertical-align: middle">-</td> </tr> <tr> <td align="center" style="vertical-align: middle">HMMT 2025 (Feb)</td> <td align="center" style="vertical-align: middle">95.4</td> <td align="center" style="vertical-align: middle">99.4</td> <td align="center" style="vertical-align: middle">92.9*</td> <td align="center" style="vertical-align: middle">97.3*</td> <td align="center" style="vertical-align: middle">92.5</td> <td align="center" style="vertical-align: middle">-</td> </tr> <tr> <td align="center" style="vertical-align: middle">IMO-AnswerBench</td> <td align="center" style="vertical-align: middle">81.8</td> <td align="center" style="vertical-align: middle">86.3</td> <td align="center" style="vertical-align: middle">78.5*</td> <td align="center" style="vertical-align: middle">83.1*</td> <td align="center" style="vertical-align: middle">78.3</td> <td align="center" style="vertical-align: middle">-</td> </tr> <tr> <td align="center" style="vertical-align: middle">GPQA-Diamond</td> <td align="center" style="vertical-align: middle">87.6</td> <td align="center" style="vertical-align: middle">92.4</td> <td align="center" style="vertical-align: middle">87.0</td> <td align="center" style="vertical-align: middle">91.9</td> <td align="center" style="vertical-align: middle">82.4</td> <td align="center" style="vertical-align: middle">-</td> </tr> <tr> <td align="center" style="vertical-align: middle">MMLU-Pro</td> <td align="center" style="vertical-align: middle">87.1</td> <td align="center" style="vertical-align: middle">86.7*</td> <td align="center" style="vertical-align: middle">89.3*</td> <td align="center" style="vertical-align: middle">90.1</td> <td align="center" style="vertical-align: middle">85.0</td> <td align="center" style="vertical-align: middle">-</td> </tr> <tr> <td align="center" colspan=8><strong>Image &amp; Video</strong></td> </tr> <tr> <td align="center" style="vertical-align: middle">MMMU-Pro</td> <td align="center" style="vertical-align: middle">78.5</td> <td align="center" style="vertical-align: middle">79.5*</td> <td align="center" style="vertical-align: middle">74.0</td> <td align="center" style="vertical-align: middle">81.0</td> <td align="center" style="vertical-align: middle">-</td> <td align="center" style="vertical-align: middle">69.3</td> </tr> <tr> <td align="center" style="vertical-align: middle">CharXiv (RQ)</td> <td align="center" style="vertical-align: middle">77.5</td> <td align="center" style="vertical-align: middle">82.1</td> <td align="center" style="vertical-align: middle">67.2*</td> <td align="center" style="vertical-align: middle">81.4</td> <td align="center" style="vertical-align: middle">-</td> <td align="center" style="vertical-align: middle">66.1</td> </tr> <tr> <td align="center" style="vertical-align: middle">MathVision</td> <td align="center" style="vertical-align: middle">84.2</td> <td align="center" style="vertical-align: middle">83.0</td> <td align="center" style="vertical-align: middle">77.1*</td> <td align="center" style="vertical-align: middle">86.1*</td> <td align="center" style="vertical-align: middle">-</td> <td align="center" style="vertical-align: middle">74.6</td> </tr> <tr> <td align="center" style="vertical-align: middle">MathVista (mini)</td> <td align="center" style="vertical-align: middle">90.1</td> <td align="center" style="vertical-align: middle">82.8*</td> <td align="center" style="vertical-align: middle">80.2*</td> <td align="center" style="vertical-align: middle">89.8*</td> <td align="center" style="vertical-align: middle">-</td> <td align="center" style="vertical-align: middle">85.8</td> </tr> <tr> <td align="center" style="vertical-align: middle">ZeroBench</td> <td align="center" style="vertical-align: middle">9</td> <td align="center" style="vertical-align: middle">9*</td> <td align="center" style="vertical-align: middle">3*</td> <td align="center" style="vertical-align: middle">8*</td> <td align="center" style="vertical-align: middle">-</td> <td align="center" style="vertical-align: middle">4*</td> </tr> <tr> <td align="center" style="vertical-align: middle">ZeroBench<br>(w/ tools)</td> <td align="center" style="vertical-align: middle">11</td> <td align="center" style="vertical-align: middle">7*</td> <td align="center" style="vertical-align: middle">9*</td> <td align="center" style="vertical-align: middle">12*</td> <td align="center" style="vertical-align: middle">-</td> <td align="center" style="vertical-align: middle">3*</td> </tr> <tr> <td align="center" style="vertical-align: middle">OCRBench</td> <td align="center" style="vertical-align: middle">92.3</td> <td align="center" style="vertical-align: middle">80.7*</td> <td align="center" style="vertical-align: middle">86.5*</td> <td align="center" style="vertical-align: middle">90.3*</td> <td align="center" style="vertical-align: middle">-</td> <td align="center" style="vertical-align: middle">87.5</td> </tr> <tr> <td align="center" style="vertical-align: middle">OmniDocBench 1.5</td> <td align="center" style="vertical-align: middle">88.8</td> <td align="center" style="vertical-align: middle">85.7</td> <td align="center" style="vertical-align: middle">87.7*</td>

Search equations, builds, manuscript text, and recovered sources.