Pinaster commited on
Commit
93b243d
·
verified ·
1 Parent(s): 7d6fc52

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ model.safetensors.index.json filter=lfs diff=lfs merge=lfs -text
.model_cache_manifest.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"digest": "fbec78416cd76a046134ea0ef9563c0dffb2db4a6496a2b1eaef89b187b46e82", "files": [{"mtime_ns": 1784270354971892878, "path": ".cache/huggingface/.gitignore", "size": 1}, {"mtime_ns": 1784270408731607996, "path": ".cache/huggingface/CACHEDIR.TAG", "size": 191}, {"mtime_ns": 1784270797090852156, "path": ".cache/huggingface/download/-WzvsaU2AFuFfD2F46mE0X-34RI=.1a7b1127128a0aeaa4e0c7ca3cee85031215fcd8af89868ae98c6c2688513cdb.0938a15c.incomplete", "size": 10796693727}, {"mtime_ns": 1784271204738696547, "path": ".cache/huggingface/download/.gitattributes.metadata", "size": 101}, {"mtime_ns": 1784270789170986478, "path": ".cache/huggingface/download/4ziNTfSqu8bRcnGpewXBgZK0Yvw=.64932d9ac82fd7ec124ecfaa259a0269f7d3d17ed18b812c042ab5390a115536.1b072fcf.incomplete", "size": 10527882281}, {"mtime_ns": 1784270761137436694, "path": ".cache/huggingface/download/7E-IDtlc3aSrNXSEK665cc8JrC8=.958ddcfd5e50e7b7e1b5643730cdb3189a054eaafebd18764bb95a807daab55f.905677fb.incomplete", "size": 10461510089}, {"mtime_ns": 1784270724680397645, "path": ".cache/huggingface/download/DYi6qAmJDdtaV2wd2uK_WMK7JtU=.0a00c731b92ede4dbe88cefff59ddded8fa69db84d7c6f273c20556f0931d54a.88026cd9.incomplete", "size": 9053511070}, {"mtime_ns": 1784270739361217378, "path": ".cache/huggingface/download/GkEi6TLIS96MMDdnWWCAkXFaxZY=.2298a22dde905914e60dfc3bb4220c0832a1b3f3d50f44460c25d03425b38f24.c2a21b58.incomplete", "size": 10730718302}, {"mtime_ns": 1784271222563742194, "path": ".cache/huggingface/download/LICENSE.metadata", "size": 101}, {"mtime_ns": 1784270730570187710, "path": ".cache/huggingface/download/PUYyCHt2eVZgW8Wv7LJPziztwaY=.dd3be96f64bf8193468ab5387fba899e4445e302d80cfb1138e058ab057a1745.f1e6ebb8.incomplete", "size": 6772994979}, {"mtime_ns": 1784270797665126730, "path": ".cache/huggingface/download/UVBDnYyKc1RQKqAGgwSuzIfb2ds=.8f6fd975133dfc2d36919964b4a967eae2d3d5497357e2ad34cb2aaaf3462465.7ba955de.incomplete", "size": 10729940076}, {"mtime_ns": 1784271222511517283, "path": ".cache/huggingface/download/config.json.metadata", "size": 100}, {"mtime_ns": 1784271200303691230, "path": ".cache/huggingface/download/configuration_kimi_k3.py.metadata", "size": 100}, {"mtime_ns": 1784271204447876090, "path": ".cache/huggingface/download/docs/deploy_guidance.md.metadata", "size": 101}, {"mtime_ns": 1784271204619604912, "path": ".cache/huggingface/download/encoding_k3.py.metadata", "size": 100}, {"mtime_ns": 1784271222287315403, "path": ".cache/huggingface/download/generation_config.json.metadata", "size": 100}, {"mtime_ns": 1784271194027385305, "path": ".cache/huggingface/download/kimi_k3_processor.py.metadata", "size": 101}, {"mtime_ns": 1784271204794883272, "path": ".cache/huggingface/download/kimi_k3_vision_processing.py.metadata", "size": 100}, {"mtime_ns": 1784271222652177784, "path": ".cache/huggingface/download/media_utils.py.metadata", "size": 101}, {"mtime_ns": 1784271200026703190, "path": ".cache/huggingface/download/model-00001-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271282534448465, "path": ".cache/huggingface/download/model-00002-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271225243920344, "path": ".cache/huggingface/download/model-00003-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271222572735400, "path": ".cache/huggingface/download/model-00004-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271219947568170, "path": ".cache/huggingface/download/model-00005-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784271226728444790, "path": ".cache/huggingface/download/model-00006-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271225324355871, "path": ".cache/huggingface/download/model-00007-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271219532166396, "path": ".cache/huggingface/download/model-00008-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271248144448541, "path": ".cache/huggingface/download/model-00009-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784271233652226104, "path": ".cache/huggingface/download/model-00010-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271300893131020, "path": ".cache/huggingface/download/model-00011-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271266355836822, "path": ".cache/huggingface/download/model-00012-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271238876810206, "path": ".cache/huggingface/download/model-00013-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784271249705571611, "path": ".cache/huggingface/download/model-00014-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271241812330246, "path": ".cache/huggingface/download/model-00015-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271303660674542, "path": ".cache/huggingface/download/model-00016-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271294953944021, "path": ".cache/huggingface/download/model-00017-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271284135318360, "path": ".cache/huggingface/download/model-00018-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784271308744282590, "path": ".cache/huggingface/download/model-00019-of-000096.safetensors.metadata", "size": 123}, {"mtime_ns": 1784271262916844959, "path": ".cache/huggingface/download/model-00020-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271286011945173, "path": ".cache/huggingface/download/model-00021-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271260115919297, "path": ".cache/huggingface/download/model-00022-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271321333103513, "path": ".cache/huggingface/download/model-00023-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271264636459284, "path": ".cache/huggingface/download/model-00024-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271268992502592, "path": ".cache/huggingface/download/model-00025-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271553642267205, "path": ".cache/huggingface/download/model-00026-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271553482104275, "path": ".cache/huggingface/download/model-00027-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784271617206421954, "path": ".cache/huggingface/download/model-00028-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271615477174388, "path": ".cache/huggingface/download/model-00029-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784271582973823853, "path": ".cache/huggingface/download/model-00030-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271556406919555, "path": ".cache/huggingface/download/model-00031-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271558885809419, "path": ".cache/huggingface/download/model-00032-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271576586337174, "path": ".cache/huggingface/download/model-00033-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271905899878962, "path": ".cache/huggingface/download/model-00034-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271940213543503, "path": ".cache/huggingface/download/model-00035-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784271881380914592, "path": ".cache/huggingface/download/model-00036-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271881716109841, "path": ".cache/huggingface/download/model-00037-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271879234652461, "path": ".cache/huggingface/download/model-00038-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271876707075269, "path": ".cache/huggingface/download/model-00039-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271874845290466, "path": ".cache/huggingface/download/model-00040-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784271891744298179, "path": ".cache/huggingface/download/model-00041-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784272234693644950, "path": ".cache/huggingface/download/model-00042-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272224234615535, "path": ".cache/huggingface/download/model-00043-of-000096.safetensors.metadata", "size": 123}, {"mtime_ns": 1784272209528140865, "path": ".cache/huggingface/download/model-00044-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272212032155384, "path": ".cache/huggingface/download/model-00045-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272220585992108, "path": ".cache/huggingface/download/model-00046-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272216524897877, "path": ".cache/huggingface/download/model-00047-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272202393973181, "path": ".cache/huggingface/download/model-00048-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272220390445977, "path": ".cache/huggingface/download/model-00049-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272601385605841, "path": ".cache/huggingface/download/model-00050-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272553234801646, "path": ".cache/huggingface/download/model-00051-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272551939451207, "path": ".cache/huggingface/download/model-00052-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272606751515159, "path": ".cache/huggingface/download/model-00053-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272547898706960, "path": ".cache/huggingface/download/model-00054-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272582017913325, "path": ".cache/huggingface/download/model-00055-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784272620609179176, "path": ".cache/huggingface/download/model-00056-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272640331960774, "path": ".cache/huggingface/download/model-00057-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272943452856163, "path": ".cache/huggingface/download/model-00058-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272914785152023, "path": ".cache/huggingface/download/model-00059-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272918060716159, "path": ".cache/huggingface/download/model-00060-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272914658148702, "path": ".cache/huggingface/download/model-00061-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272955203832411, "path": ".cache/huggingface/download/model-00062-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784272866599921195, "path": ".cache/huggingface/download/model-00063-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784272950944280119, "path": ".cache/huggingface/download/model-00064-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784272923067414199, "path": ".cache/huggingface/download/model-00065-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273199905676861, "path": ".cache/huggingface/download/model-00066-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273215025578542, "path": ".cache/huggingface/download/model-00067-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784273204225443055, "path": ".cache/huggingface/download/model-00068-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273270241029373, "path": ".cache/huggingface/download/model-00069-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273258692365653, "path": ".cache/huggingface/download/model-00070-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273249284136791, "path": ".cache/huggingface/download/model-00071-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273213158200207, "path": ".cache/huggingface/download/model-00072-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273280316936222, "path": ".cache/huggingface/download/model-00073-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273587620937753, "path": ".cache/huggingface/download/model-00074-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784273563622798777, "path": ".cache/huggingface/download/model-00075-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784273555085334829, "path": ".cache/huggingface/download/model-00076-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784273539989777022, "path": ".cache/huggingface/download/model-00077-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273555608008276, "path": ".cache/huggingface/download/model-00078-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273553434443213, "path": ".cache/huggingface/download/model-00079-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273607770452209, "path": ".cache/huggingface/download/model-00080-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273635799638796, "path": ".cache/huggingface/download/model-00081-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273832956472565, "path": ".cache/huggingface/download/model-00082-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273896413221807, "path": ".cache/huggingface/download/model-00083-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784273834590290908, "path": ".cache/huggingface/download/model-00084-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273851301960083, "path": ".cache/huggingface/download/model-00085-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273897280635162, "path": ".cache/huggingface/download/model-00086-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273876823465785, "path": ".cache/huggingface/download/model-00087-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273865082805120, "path": ".cache/huggingface/download/model-00088-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784273893792790678, "path": ".cache/huggingface/download/model-00089-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784274197851825687, "path": ".cache/huggingface/download/model-00090-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784274200569958195, "path": ".cache/huggingface/download/model-00091-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784274149097194566, "path": ".cache/huggingface/download/model-00092-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784274149099426047, "path": ".cache/huggingface/download/model-00093-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273923307058083, "path": ".cache/huggingface/download/model-00094-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273916347879225, "path": ".cache/huggingface/download/model-00095-of-000096.safetensors.metadata", "size": 124}, {"mtime_ns": 1784273878114692756, "path": ".cache/huggingface/download/model-00096-of-000096.safetensors.metadata", "size": 125}, {"mtime_ns": 1784273876828277303, "path": ".cache/huggingface/download/model.safetensors.index.json.metadata", "size": 125}, {"mtime_ns": 1784273931661677851, "path": ".cache/huggingface/download/modeling_kimi.py.metadata", "size": 101}, {"mtime_ns": 1784273931903232364, "path": ".cache/huggingface/download/modeling_kimi_k3.py.metadata", "size": 101}, {"mtime_ns": 1784270743260246174, "path": ".cache/huggingface/download/o_-rzvI7_xlV7QyzO7ByU2QZGVo=.9a5ee0d401bc676b9c6f9461d1b2790f0fa1e8a870de962d64042b7c32d56dd3.9c8b196f.incomplete", "size": 11066276588}, {"mtime_ns": 1784273924765628513, "path": ".cache/huggingface/download/preprocessor_config.json.metadata", "size": 101}, {"mtime_ns": 1784273871542973839, "path": ".cache/huggingface/download/tiktoken.model.metadata", "size": 125}, {"mtime_ns": 1784273933066719620, "path": ".cache/huggingface/download/tokenization_kimi.py.metadata", "size": 101}, {"mtime_ns": 1784273879964653486, "path": ".cache/huggingface/download/tokenizer_config.json.metadata", "size": 99}, {"mtime_ns": 1784271204481096658, "path": ".gitattributes", "size": 1584}, {"mtime_ns": 1784271204483182031, "path": "LICENSE", "size": 1465}, {"mtime_ns": 1784271257769900164, "path": "config.json", "size": 6992}, {"mtime_ns": 1784271250443247210, "path": "configuration_kimi_k3.py", "size": 11343}, {"mtime_ns": 1784271196443570965, "path": "docs/deploy_guidance.md", "size": 2130}, {"mtime_ns": 1784271200156535353, "path": "encoding_k3.py", "size": 22505}, {"mtime_ns": 1784271250426390361, "path": "generation_config.json", "size": 53}, {"mtime_ns": 1784271250453378985, "path": "kimi_k3_processor.py", "size": 7660}, {"mtime_ns": 1784271196801315515, "path": "kimi_k3_vision_processing.py", "size": 6686}, {"mtime_ns": 1784271194121819349, "path": "media_utils.py", "size": 13844}, {"mtime_ns": 1784270433649807514, "path": "model-00001-of-000096.safetensors", "size": 2341216112}, {"mtime_ns": 1784270497880829294, "path": "model-00002-of-000096.safetensors", "size": 16990911504}, {"mtime_ns": 1784270468081527448, "path": "model-00003-of-000096.safetensors", "size": 16990911504}, {"mtime_ns": 1784270437286382122, "path": "model-00004-of-000096.safetensors", "size": 16567501776}, {"mtime_ns": 1784270472553864406, "path": "model-00005-of-000096.safetensors", "size": 16990911504}, {"mtime_ns": 1784270498776709248, "path": "model-00006-of-000096.safetensors", "size": 16990911504}, {"mtime_ns": 1784270462342153857, "path": "model-00007-of-000096.safetensors", "size": 16990911504}, {"mtime_ns": 1784270500656171243, "path": "model-00008-of-000096.safetensors", "size": 16567501776}, {"mtime_ns": 1784270442643359352, "path": "model-00009-of-000096.safetensors", "size": 16990911504}, {"mtime_ns": 1784270593032371375, "path": "model-00010-of-000096.safetensors", "size": 16990911504}, {"mtime_ns": 1784270590984782976, "path": "model-00011-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270527537144987, "path": "model-00012-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784270538355432166, "path": "model-00013-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270539422516138, "path": "model-00014-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270537099346025, "path": "model-00015-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270591891830708, "path": "model-00016-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784270533807172414, "path": "model-00017-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270677876872339, "path": "model-00018-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270678872964630, "path": "model-00019-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270621951397205, "path": "model-00020-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784270624866135657, "path": "model-00021-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270669094625452, "path": "model-00022-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270685084915769, "path": "model-00023-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784270629316454034, "path": "model-00024-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784270683976888701, "path": "model-00025-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271554558693135, "path": "model-00026-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271550692982351, "path": "model-00027-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271551463312418, "path": "model-00028-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784271614599786250, "path": "model-00029-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271560291308200, "path": "model-00030-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271620256004859, "path": "model-00031-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271612490579951, "path": "model-00032-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784271576468525227, "path": "model-00033-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271941351061924, "path": "model-00034-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271930427165261, "path": "model-00035-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271937014219312, "path": "model-00036-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784271873516426364, "path": "model-00037-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271878911930734, "path": "model-00038-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271898743453038, "path": "model-00039-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784271874379427148, "path": "model-00040-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784271886638337163, "path": "model-00041-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272216761493050, "path": "model-00042-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272226834039833, "path": "model-00043-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272227213492894, "path": "model-00044-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784272273157762983, "path": "model-00045-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272215484657130, "path": "model-00046-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272216500975383, "path": "model-00047-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272228136452442, "path": "model-00048-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784272225925673967, "path": "model-00049-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272548017933368, "path": "model-00050-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272550476319036, "path": "model-00051-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272555484360639, "path": "model-00052-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784272549035960701, "path": "model-00053-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272598837571542, "path": "model-00054-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272617511539249, "path": "model-00055-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272567206706038, "path": "model-00056-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784272640035357974, "path": "model-00057-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272883357820985, "path": "model-00058-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272896839487412, "path": "model-00059-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272867612851574, "path": "model-00060-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784272941581019664, "path": "model-00061-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272899392606947, "path": "model-00062-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272930068852630, "path": "model-00063-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784272911958074603, "path": "model-00064-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784272915084958929, "path": "model-00065-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273191902221929, "path": "model-00066-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273260950825558, "path": "model-00067-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273196223067671, "path": "model-00068-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784273232306335439, "path": "model-00069-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273286743025339, "path": "model-00070-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273231422489999, "path": "model-00071-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273264086298466, "path": "model-00072-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784273308374362704, "path": "model-00073-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273523947205106, "path": "model-00074-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273570900615519, "path": "model-00075-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273537217238930, "path": "model-00076-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784273534937381354, "path": "model-00077-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273532701481149, "path": "model-00078-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273606703873410, "path": "model-00079-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273609994245614, "path": "model-00080-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784273638047349614, "path": "model-00081-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273894111344644, "path": "model-00082-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273886215472359, "path": "model-00083-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273834387006541, "path": "model-00084-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784273914730625502, "path": "model-00085-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273869027023009, "path": "model-00086-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273848186827501, "path": "model-00087-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784273871422640876, "path": "model-00088-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784273890034867390, "path": "model-00089-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784274136465176367, "path": "model-00090-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784274200481986042, "path": "model-00091-of-000096.safetensors", "size": 16990916912}, {"mtime_ns": 1784274141049983767, "path": "model-00092-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784274202021091297, "path": "model-00093-of-000096.safetensors", "size": 16567507176}, {"mtime_ns": 1784273931228028834, "path": "model-00094-of-000096.safetensors", "size": 4697664072}, {"mtime_ns": 1784273916273949911, "path": "model-00095-of-000096.safetensors", "size": 92289328}, {"mtime_ns": 1784273872939831252, "path": "model-00096-of-000096.safetensors", "size": 802448352}, {"mtime_ns": 1784273881105375478, "path": "model.safetensors.index.json", "size": 59764096}, {"mtime_ns": 1784273878298534756, "path": "modeling_kimi.py", "size": 53481}, {"mtime_ns": 1784273874178507744, "path": "modeling_kimi_k3.py", "size": 51906}, {"mtime_ns": 1784273896556080927, "path": "preprocessor_config.json", "size": 1011}, {"mtime_ns": 1784273879445181984, "path": "tiktoken.model", "size": 2795286}, {"mtime_ns": 1784273871757074819, "path": "tokenization_kimi.py", "size": 16145}, {"mtime_ns": 1784273925834043527, "path": "tokenizer_config.json", "size": 3478}]}
LICENSE ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Modified MIT License
2
+
3
+ Copyright (c) 2026 Moonshot AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the “Software”), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ Our only modification part is that, if the Software (or any derivative works
24
+ thereof) is used for any of your commercial products or services that have
25
+ more than 100 million monthly active users, or more than 20 million US dollars
26
+ (or equivalent in other currencies) in monthly revenue, you shall prominently
27
+ display "Kimi K2.5" on the user interface of such product or service.
config.json ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "KimiK3ForConditionalGeneration"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_kimi_k3.KimiK3Config",
7
+ "AutoModel": "modeling_kimi_k3.KimiK3ForConditionalGeneration",
8
+ "AutoModelForCausalLM": "modeling_kimi_k3.KimiK3ForConditionalGeneration"
9
+ },
10
+ "bos_token_id": 163584,
11
+ "dtype": "bfloat16",
12
+ "eos_token_id": 163586,
13
+ "ignore_index": -100,
14
+ "image_placeholder": "<|kimi_image_placeholder|>",
15
+ "media_placeholder_token_id": 163605,
16
+ "model_type": "kimi_k3",
17
+ "pad_token_id": 163839,
18
+ "text_config": {
19
+ "_name_or_path": "",
20
+ "activation_situ_beta": 4.0,
21
+ "activation_situ_linear_beta": 25.0,
22
+ "add_cross_attention": false,
23
+ "architectures": [
24
+ "KimiLinearForCausalLM"
25
+ ],
26
+ "attn_res_block_size": 12,
27
+ "auto_map": {
28
+ "AutoConfig": "configuration_kimi_k3.KimiLinearConfig",
29
+ "AutoModel": "modeling_kimi.KimiLinearModel",
30
+ "AutoModelForCausalLM": "modeling_kimi.KimiLinearForCausalLM"
31
+ },
32
+ "bad_words_ids": null,
33
+ "begin_suppress_tokens": null,
34
+ "bos_token_id": 163584,
35
+ "chunk_size_feed_forward": 0,
36
+ "cross_attention_hidden_size": null,
37
+ "decoder_start_token_id": null,
38
+ "diversity_penalty": 0.0,
39
+ "do_sample": false,
40
+ "dtype": "bfloat16",
41
+ "early_stopping": false,
42
+ "encoder_no_repeat_ngram_size": 0,
43
+ "eos_token_id": 163586,
44
+ "exponential_decay_length_penalty": null,
45
+ "finetuning_task": null,
46
+ "first_k_dense_replace": 1,
47
+ "forced_bos_token_id": null,
48
+ "forced_eos_token_id": null,
49
+ "head_dim": 256,
50
+ "hidden_act": "situ",
51
+ "hidden_size": 7168,
52
+ "id2label": {
53
+ "0": "LABEL_0",
54
+ "1": "LABEL_1"
55
+ },
56
+ "initializer_range": 0.02,
57
+ "intermediate_size": 33792,
58
+ "is_decoder": false,
59
+ "is_encoder_decoder": false,
60
+ "kv_lora_rank": 512,
61
+ "label2id": {
62
+ "LABEL_0": 0,
63
+ "LABEL_1": 1
64
+ },
65
+ "latent_moe_use_norm": true,
66
+ "length_penalty": 1.0,
67
+ "linear_attn_config": {
68
+ "full_attn_layers": [
69
+ 4,
70
+ 8,
71
+ 12,
72
+ 16,
73
+ 20,
74
+ 24,
75
+ 28,
76
+ 32,
77
+ 36,
78
+ 40,
79
+ 44,
80
+ 48,
81
+ 52,
82
+ 56,
83
+ 60,
84
+ 64,
85
+ 68,
86
+ 72,
87
+ 76,
88
+ 80,
89
+ 84,
90
+ 88,
91
+ 92,
92
+ 93,
93
+ 94
94
+ ],
95
+ "gate_lower_bound": -5.0,
96
+ "head_dim": 128,
97
+ "kda_layers": [
98
+ 1,
99
+ 2,
100
+ 3,
101
+ 5,
102
+ 6,
103
+ 7,
104
+ 9,
105
+ 10,
106
+ 11,
107
+ 13,
108
+ 14,
109
+ 15,
110
+ 17,
111
+ 18,
112
+ 19,
113
+ 21,
114
+ 22,
115
+ 23,
116
+ 25,
117
+ 26,
118
+ 27,
119
+ 29,
120
+ 30,
121
+ 31,
122
+ 33,
123
+ 34,
124
+ 35,
125
+ 37,
126
+ 38,
127
+ 39,
128
+ 41,
129
+ 42,
130
+ 43,
131
+ 45,
132
+ 46,
133
+ 47,
134
+ 49,
135
+ 50,
136
+ 51,
137
+ 53,
138
+ 54,
139
+ 55,
140
+ 57,
141
+ 58,
142
+ 59,
143
+ 61,
144
+ 62,
145
+ 63,
146
+ 65,
147
+ 66,
148
+ 67,
149
+ 69,
150
+ 70,
151
+ 71,
152
+ 73,
153
+ 74,
154
+ 75,
155
+ 77,
156
+ 78,
157
+ 79,
158
+ 81,
159
+ 82,
160
+ 83,
161
+ 85,
162
+ 86,
163
+ 87,
164
+ 89,
165
+ 90,
166
+ 91
167
+ ],
168
+ "num_heads": 96,
169
+ "short_conv_kernel_size": 4,
170
+ "use_full_rank_gate": true
171
+ },
172
+ "max_length": 20,
173
+ "max_position_embeddings": 262144,
174
+ "min_length": 0,
175
+ "mla_use_nope": true,
176
+ "mla_use_output_gate": true,
177
+ "model_type": "kimi_linear",
178
+ "moe_intermediate_size": 3072,
179
+ "moe_layer_freq": 1,
180
+ "moe_renormalize": true,
181
+ "moe_router_activation_func": "sigmoid",
182
+ "no_repeat_ngram_size": 0,
183
+ "num_attention_heads": 96,
184
+ "num_beam_groups": 1,
185
+ "num_beams": 1,
186
+ "num_expert_group": 1,
187
+ "num_experts": 896,
188
+ "num_experts_per_token": 16,
189
+ "num_hidden_layers": 4,
190
+ "num_key_value_heads": 96,
191
+ "num_nextn_predict_layers": 0,
192
+ "num_return_sequences": 1,
193
+ "num_shared_experts": 2,
194
+ "output_attentions": false,
195
+ "output_hidden_states": false,
196
+ "output_scores": false,
197
+ "pad_token_id": 163839,
198
+ "prefix": null,
199
+ "problem_type": null,
200
+ "pruned_heads": {},
201
+ "q_lora_rank": 1536,
202
+ "qk_nope_head_dim": 128,
203
+ "qk_rope_head_dim": 64,
204
+ "quantization_config": {
205
+ "config_groups": {
206
+ "group_0": {
207
+ "format": "mxfp4-pack-quantized",
208
+ "input_activations": null,
209
+ "output_activations": null,
210
+ "targets": [
211
+ "Linear"
212
+ ],
213
+ "weights": {
214
+ "actorder": null,
215
+ "block_structure": null,
216
+ "dynamic": false,
217
+ "group_size": 32,
218
+ "num_bits": 4,
219
+ "observer": "minmax",
220
+ "observer_kwargs": {},
221
+ "scale_dtype": "torch.uint8",
222
+ "strategy": "group",
223
+ "symmetric": true,
224
+ "type": "float",
225
+ "zp_dtype": null
226
+ }
227
+ }
228
+ },
229
+ "format": "mxfp4-pack-quantized",
230
+ "global_compression_ratio": null,
231
+ "ignore": [
232
+ "re:.*self_attn.*",
233
+ "re:.*shared_experts.*",
234
+ "re:.*mlp\\.(gate|up|gate_up|down)_proj.*",
235
+ "re:.*lm_head.*",
236
+ "re:.*vision_tower.*",
237
+ "re:.*mm_projector.*"
238
+ ],
239
+ "kv_cache_scheme": null,
240
+ "quant_method": "compressed-tensors",
241
+ "quantization_status": "compressed"
242
+ },
243
+ "remove_invalid_values": false,
244
+ "repetition_penalty": 1.0,
245
+ "return_dict": true,
246
+ "return_dict_in_generate": false,
247
+ "rms_norm_eps": 1e-05,
248
+ "rope_scaling": null,
249
+ "routed_expert_hidden_size": 3584,
250
+ "routed_scaling_factor": 1.0,
251
+ "sep_token_id": null,
252
+ "suppress_tokens": null,
253
+ "task_specific_params": null,
254
+ "temperature": 1.0,
255
+ "tf_legacy_loss": false,
256
+ "tie_encoder_decoder": false,
257
+ "tie_word_embeddings": false,
258
+ "tokenizer_class": null,
259
+ "top_k": 50,
260
+ "top_p": 1.0,
261
+ "topk_group": 1,
262
+ "topk_method": "noaux_tc",
263
+ "torchscript": false,
264
+ "transformers_version": "4.56.2",
265
+ "typical_p": 1.0,
266
+ "use_bfloat16": false,
267
+ "use_cache": true,
268
+ "use_grouped_topk": true,
269
+ "v_head_dim": 128,
270
+ "vocab_size": 163840
271
+ },
272
+ "tie_word_embeddings": false,
273
+ "vision_config": {
274
+ "_attn_implementation": "flash_attention_2",
275
+ "activation_func": "gelu_pytorch_tanh",
276
+ "attn_bias": false,
277
+ "init_pos_emb_height": 64,
278
+ "init_pos_emb_time": 4,
279
+ "init_pos_emb_width": 64,
280
+ "linear_bias": false,
281
+ "merge_kernel_size": [
282
+ 2,
283
+ 2
284
+ ],
285
+ "merge_type": "sd2_tpool",
286
+ "mlp_type": "mlp2",
287
+ "mm_hidden_size": 1024,
288
+ "mm_projector_type": "patchmergerv2",
289
+ "norm_type": "rmsnorm",
290
+ "patch_embed_proj_bias": false,
291
+ "patch_size": 14,
292
+ "pos_emb_interpolation_mode": "bilinear",
293
+ "pos_emb_type": "divided_fixed",
294
+ "projector_hidden_act": "gelu",
295
+ "projector_ln_eps": 1e-05,
296
+ "qkv_hidden_size": 1536,
297
+ "text_hidden_size": 7168,
298
+ "vt_hidden_size": 1024,
299
+ "vt_intermediate_size": 4096,
300
+ "vt_num_attention_heads": 12,
301
+ "vt_num_hidden_layers": 27
302
+ }
303
+ }
config.json.bak-20260909-rope10000 ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "KimiK3ForConditionalGeneration"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_kimi_k3.KimiK3Config",
7
+ "AutoModel": "modeling_kimi_k3.KimiK3ForConditionalGeneration",
8
+ "AutoModelForCausalLM": "modeling_kimi_k3.KimiK3ForConditionalGeneration"
9
+ },
10
+ "bos_token_id": 163584,
11
+ "dtype": "bfloat16",
12
+ "eos_token_id": 163586,
13
+ "ignore_index": -100,
14
+ "image_placeholder": "<|kimi_image_placeholder|>",
15
+ "media_placeholder_token_id": 163605,
16
+ "model_type": "kimi_k3",
17
+ "pad_token_id": 163839,
18
+ "text_config": {
19
+ "_name_or_path": "",
20
+ "activation_situ_beta": 4.0,
21
+ "activation_situ_linear_beta": 25.0,
22
+ "add_cross_attention": false,
23
+ "architectures": [
24
+ "KimiLinearForCausalLM"
25
+ ],
26
+ "attn_res_block_size": 12,
27
+ "auto_map": {
28
+ "AutoConfig": "configuration_kimi_k3.KimiLinearConfig",
29
+ "AutoModel": "modeling_kimi.KimiLinearModel",
30
+ "AutoModelForCausalLM": "modeling_kimi.KimiLinearForCausalLM"
31
+ },
32
+ "bad_words_ids": null,
33
+ "begin_suppress_tokens": null,
34
+ "bos_token_id": 163584,
35
+ "chunk_size_feed_forward": 0,
36
+ "cross_attention_hidden_size": null,
37
+ "decoder_start_token_id": null,
38
+ "diversity_penalty": 0.0,
39
+ "do_sample": false,
40
+ "dtype": "bfloat16",
41
+ "early_stopping": false,
42
+ "encoder_no_repeat_ngram_size": 0,
43
+ "eos_token_id": 163586,
44
+ "exponential_decay_length_penalty": null,
45
+ "finetuning_task": null,
46
+ "first_k_dense_replace": 1,
47
+ "forced_bos_token_id": null,
48
+ "forced_eos_token_id": null,
49
+ "hidden_act": "situ",
50
+ "hidden_size": 7168,
51
+ "id2label": {
52
+ "0": "LABEL_0",
53
+ "1": "LABEL_1"
54
+ },
55
+ "initializer_range": 0.02,
56
+ "intermediate_size": 33792,
57
+ "is_decoder": false,
58
+ "is_encoder_decoder": false,
59
+ "kv_lora_rank": 512,
60
+ "label2id": {
61
+ "LABEL_0": 0,
62
+ "LABEL_1": 1
63
+ },
64
+ "latent_moe_use_norm": true,
65
+ "length_penalty": 1.0,
66
+ "linear_attn_config": {
67
+ "full_attn_layers": [
68
+ 4,
69
+ 8,
70
+ 12,
71
+ 16,
72
+ 20,
73
+ 24,
74
+ 28,
75
+ 32,
76
+ 36,
77
+ 40,
78
+ 44,
79
+ 48,
80
+ 52,
81
+ 56,
82
+ 60,
83
+ 64,
84
+ 68,
85
+ 72,
86
+ 76,
87
+ 80,
88
+ 84,
89
+ 88,
90
+ 92,
91
+ 93
92
+ ],
93
+ "gate_lower_bound": -5.0,
94
+ "head_dim": 128,
95
+ "kda_layers": [
96
+ 1,
97
+ 2,
98
+ 3,
99
+ 5,
100
+ 6,
101
+ 7,
102
+ 9,
103
+ 10,
104
+ 11,
105
+ 13,
106
+ 14,
107
+ 15,
108
+ 17,
109
+ 18,
110
+ 19,
111
+ 21,
112
+ 22,
113
+ 23,
114
+ 25,
115
+ 26,
116
+ 27,
117
+ 29,
118
+ 30,
119
+ 31,
120
+ 33,
121
+ 34,
122
+ 35,
123
+ 37,
124
+ 38,
125
+ 39,
126
+ 41,
127
+ 42,
128
+ 43,
129
+ 45,
130
+ 46,
131
+ 47,
132
+ 49,
133
+ 50,
134
+ 51,
135
+ 53,
136
+ 54,
137
+ 55,
138
+ 57,
139
+ 58,
140
+ 59,
141
+ 61,
142
+ 62,
143
+ 63,
144
+ 65,
145
+ 66,
146
+ 67,
147
+ 69,
148
+ 70,
149
+ 71,
150
+ 73,
151
+ 74,
152
+ 75,
153
+ 77,
154
+ 78,
155
+ 79,
156
+ 81,
157
+ 82,
158
+ 83,
159
+ 85,
160
+ 86,
161
+ 87,
162
+ 89,
163
+ 90,
164
+ 91
165
+ ],
166
+ "num_heads": 96,
167
+ "short_conv_kernel_size": 4,
168
+ "use_full_rank_gate": true
169
+ },
170
+ "max_length": 20,
171
+ "max_position_embeddings": 1048576,
172
+ "min_length": 0,
173
+ "mla_use_nope": true,
174
+ "mla_use_output_gate": true,
175
+ "model_type": "kimi_linear",
176
+ "moe_intermediate_size": 3072,
177
+ "moe_layer_freq": 1,
178
+ "moe_renormalize": true,
179
+ "moe_router_activation_func": "sigmoid",
180
+ "no_repeat_ngram_size": 0,
181
+ "num_attention_heads": 96,
182
+ "num_beam_groups": 1,
183
+ "num_beams": 1,
184
+ "num_expert_group": 1,
185
+ "num_experts": 896,
186
+ "num_experts_per_token": 16,
187
+ "num_hidden_layers": 4,
188
+ "num_key_value_heads": 96,
189
+ "num_nextn_predict_layers": 0,
190
+ "num_return_sequences": 1,
191
+ "num_shared_experts": 2,
192
+ "output_attentions": false,
193
+ "output_hidden_states": false,
194
+ "output_scores": false,
195
+ "pad_token_id": 163839,
196
+ "prefix": null,
197
+ "problem_type": null,
198
+ "pruned_heads": {},
199
+ "q_lora_rank": 1536,
200
+ "qk_nope_head_dim": 128,
201
+ "qk_rope_head_dim": 64,
202
+ "quantization_config": {
203
+ "config_groups": {
204
+ "group_0": {
205
+ "format": "mxfp4-pack-quantized",
206
+ "input_activations": null,
207
+ "output_activations": null,
208
+ "targets": [
209
+ "Linear"
210
+ ],
211
+ "weights": {
212
+ "actorder": null,
213
+ "block_structure": null,
214
+ "dynamic": false,
215
+ "group_size": 32,
216
+ "num_bits": 4,
217
+ "observer": "minmax",
218
+ "observer_kwargs": {},
219
+ "scale_dtype": "torch.uint8",
220
+ "strategy": "group",
221
+ "symmetric": true,
222
+ "type": "float",
223
+ "zp_dtype": null
224
+ }
225
+ }
226
+ },
227
+ "format": "mxfp4-pack-quantized",
228
+ "global_compression_ratio": null,
229
+ "ignore": [
230
+ "re:.*self_attn.*",
231
+ "re:.*shared_experts.*",
232
+ "re:.*mlp\\.(gate|up|gate_up|down)_proj.*",
233
+ "re:.*lm_head.*",
234
+ "re:.*vision_tower.*",
235
+ "re:.*mm_projector.*"
236
+ ],
237
+ "kv_cache_scheme": null,
238
+ "quant_method": "compressed-tensors",
239
+ "quantization_status": "compressed"
240
+ },
241
+ "remove_invalid_values": false,
242
+ "repetition_penalty": 1.0,
243
+ "return_dict": true,
244
+ "return_dict_in_generate": false,
245
+ "rms_norm_eps": 1e-05,
246
+ "routed_expert_hidden_size": 3584,
247
+ "routed_scaling_factor": 1.0,
248
+ "sep_token_id": null,
249
+ "suppress_tokens": null,
250
+ "task_specific_params": null,
251
+ "temperature": 1.0,
252
+ "tf_legacy_loss": false,
253
+ "tie_encoder_decoder": false,
254
+ "tie_word_embeddings": false,
255
+ "tokenizer_class": null,
256
+ "top_k": 50,
257
+ "top_p": 1.0,
258
+ "topk_group": 1,
259
+ "topk_method": "noaux_tc",
260
+ "torchscript": false,
261
+ "transformers_version": "4.56.2",
262
+ "typical_p": 1.0,
263
+ "use_bfloat16": false,
264
+ "use_cache": true,
265
+ "use_grouped_topk": true,
266
+ "v_head_dim": 128,
267
+ "vocab_size": 163840
268
+ },
269
+ "tie_word_embeddings": false,
270
+ "vision_config": {
271
+ "_attn_implementation": "flash_attention_2",
272
+ "activation_func": "gelu_pytorch_tanh",
273
+ "attn_bias": false,
274
+ "init_pos_emb_height": 64,
275
+ "init_pos_emb_time": 4,
276
+ "init_pos_emb_width": 64,
277
+ "linear_bias": false,
278
+ "merge_kernel_size": [
279
+ 2,
280
+ 2
281
+ ],
282
+ "merge_type": "sd2_tpool",
283
+ "mlp_type": "mlp2",
284
+ "mm_hidden_size": 1024,
285
+ "mm_projector_type": "patchmergerv2",
286
+ "norm_type": "rmsnorm",
287
+ "patch_embed_proj_bias": false,
288
+ "patch_size": 14,
289
+ "pos_emb_interpolation_mode": "bilinear",
290
+ "pos_emb_type": "divided_fixed",
291
+ "projector_hidden_act": "gelu",
292
+ "projector_ln_eps": 1e-05,
293
+ "qkv_hidden_size": 1536,
294
+ "text_hidden_size": 7168,
295
+ "vt_hidden_size": 1024,
296
+ "vt_intermediate_size": 4096,
297
+ "vt_num_attention_heads": 12,
298
+ "vt_num_hidden_layers": 27
299
+ }
300
+ }
configuration_kimi_k3.py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ from transformers.configuration_utils import PretrainedConfig
4
+
5
+
6
+ class KimiLinearConfig(PretrainedConfig):
7
+ model_type = "kimi_linear"
8
+ keys_to_ignore_at_inference = ["past_key_values"]
9
+
10
+ def __init__(
11
+ self,
12
+ model_type="kimi_linear",
13
+ vocab_size=163840,
14
+ hidden_size=4096,
15
+ head_dim=None,
16
+ intermediate_size=11008,
17
+ num_hidden_layers=32,
18
+ num_attention_heads=32,
19
+ num_key_value_heads=None,
20
+ hidden_act="silu",
21
+ initializer_range=0.02,
22
+ rms_norm_eps=1e-6,
23
+ use_cache=True,
24
+ pad_token_id=0,
25
+ bos_token_id=1,
26
+ eos_token_id=2,
27
+ rope_theta=10000.0,
28
+ rope_scaling=None,
29
+ tie_word_embeddings=False,
30
+ moe_intermediate_size: Optional[int] = None,
31
+ moe_renormalize: bool = True,
32
+ moe_router_activation_func: str = "sigmoid",
33
+ num_experts: Optional[int] = None,
34
+ num_experts_per_token: Optional[int] = None,
35
+ num_shared_experts: int = 0,
36
+ routed_scaling_factor: float = 1.0,
37
+ first_k_dense_replace: int = 0,
38
+ moe_layer_freq: int = 1,
39
+ use_grouped_topk: bool = True,
40
+ num_expert_group: int = 1,
41
+ topk_group: int = 1,
42
+ q_lora_rank: Optional[int] = None,
43
+ kv_lora_rank: Optional[int] = None,
44
+ qk_nope_head_dim: Optional[int] = None,
45
+ qk_rope_head_dim: Optional[int] = None,
46
+ v_head_dim: Optional[int] = None,
47
+ mla_use_nope: Optional[bool] = False,
48
+ mla_use_output_gate: Optional[bool] = False,
49
+ num_nextn_predict_layers: int = 0,
50
+ linear_attn_config: Optional[dict] = None,
51
+ attn_res_block_size: Optional[int] = None,
52
+ latent_moe_use_norm: bool = False,
53
+ activation_situ_beta: Optional[float] = None,
54
+ activation_situ_linear_beta: Optional[float] = None,
55
+ max_position_embeddings: int = 4096,
56
+ routed_expert_hidden_size: Optional[int] = None,
57
+ topk_method: str = "noaux_tc",
58
+ **kwargs,
59
+ ):
60
+ self.model_type = model_type
61
+ self.vocab_size = vocab_size
62
+ self.hidden_size = hidden_size
63
+ self.head_dim = (
64
+ head_dim if head_dim is not None else hidden_size // num_attention_heads
65
+ )
66
+ self.intermediate_size = intermediate_size
67
+ self.num_hidden_layers = num_hidden_layers
68
+ self.num_attention_heads = num_attention_heads
69
+
70
+ # for backward compatibility
71
+ if num_key_value_heads is None:
72
+ num_key_value_heads = num_attention_heads
73
+
74
+ self.num_key_value_heads = num_key_value_heads
75
+ self.hidden_act = hidden_act
76
+ self.initializer_range = initializer_range
77
+ self.rms_norm_eps = rms_norm_eps
78
+ self.use_cache = use_cache
79
+ self.rope_theta = rope_theta
80
+ self.rope_scaling = rope_scaling
81
+
82
+ self.q_lora_rank = q_lora_rank
83
+ self.kv_lora_rank = kv_lora_rank
84
+ self.qk_nope_head_dim = qk_nope_head_dim
85
+ self.qk_rope_head_dim = qk_rope_head_dim
86
+ self.v_head_dim = v_head_dim
87
+ self.mla_use_nope = mla_use_nope
88
+ self.mla_use_output_gate = mla_use_output_gate
89
+ # moe config
90
+ self.num_experts = num_experts
91
+ self.num_experts_per_token = num_experts_per_token
92
+ self.moe_renormalize = moe_renormalize
93
+ self.num_shared_experts = num_shared_experts
94
+ self.routed_scaling_factor = routed_scaling_factor
95
+ self.moe_router_activation_func = moe_router_activation_func
96
+ assert self.moe_router_activation_func in ("softmax", "sigmoid")
97
+ self.moe_intermediate_size = moe_intermediate_size
98
+ self.first_k_dense_replace = first_k_dense_replace
99
+ self.moe_layer_freq = moe_layer_freq
100
+ self.use_grouped_topk = use_grouped_topk
101
+ self.num_expert_group = num_expert_group
102
+ self.topk_group = topk_group
103
+ self.num_nextn_predict_layers = num_nextn_predict_layers
104
+
105
+ self.attn_res_block_size = attn_res_block_size
106
+ self.latent_moe_use_norm = latent_moe_use_norm
107
+ self.activation_situ_beta = activation_situ_beta
108
+ self.activation_situ_linear_beta = activation_situ_linear_beta
109
+ self.max_position_embeddings = max_position_embeddings
110
+ self.routed_expert_hidden_size = routed_expert_hidden_size
111
+ self.topk_method = topk_method
112
+
113
+ if linear_attn_config is not None:
114
+ assert linear_attn_config["kda_layers"] is not None
115
+ assert linear_attn_config["full_attn_layers"] is not None
116
+ self.linear_attn_config = linear_attn_config
117
+
118
+ super().__init__(
119
+ pad_token_id=pad_token_id,
120
+ bos_token_id=bos_token_id,
121
+ eos_token_id=eos_token_id,
122
+ tie_word_embeddings=tie_word_embeddings,
123
+ **kwargs,
124
+ )
125
+
126
+ @property
127
+ def is_mla(self):
128
+ return (
129
+ self.q_lora_rank is not None
130
+ or self.kv_lora_rank is not None
131
+ or self.qk_nope_head_dim is not None
132
+ or self.qk_rope_head_dim is not None
133
+ or self.v_head_dim is not None
134
+ or self.mla_use_nope is True
135
+ )
136
+
137
+ @property
138
+ def is_moe(self):
139
+ return self.num_experts is not None
140
+
141
+ @property
142
+ def is_linear_attn(self) -> bool:
143
+ return not (
144
+ self.linear_attn_config is None
145
+ or (
146
+ isinstance(self.linear_attn_config, dict)
147
+ and self.linear_attn_config["kda_layers"] is not None
148
+ and len(self.linear_attn_config["kda_layers"]) == 0
149
+ )
150
+ )
151
+
152
+ def is_kda_layer(self, layer_idx: int):
153
+ return (
154
+ self.linear_attn_config is not None
155
+ and (layer_idx + 1) in self.linear_attn_config["kda_layers"]
156
+ )
157
+
158
+
159
+ class KimiK3VisionConfig(PretrainedConfig):
160
+
161
+ def __init__(
162
+ self,
163
+ patch_size: int = 14,
164
+ init_pos_emb_height: int = 64,
165
+ init_pos_emb_width: int = 64,
166
+ init_pos_emb_time: int = 4,
167
+ pos_emb_type: str = 'divided_fixed',
168
+ vt_num_attention_heads: int = 12,
169
+ vt_num_hidden_layers: int = 27,
170
+ vt_hidden_size: int = 1024,
171
+ vt_intermediate_size: int = 4096,
172
+ merge_kernel_size: tuple = (2, 2),
173
+ merge_type: str = 'sd2_tpool',
174
+ _attn_implementation: str = 'flash_attention_2',
175
+ # MM Projector parameters
176
+ mm_projector_type: str = 'patchmergerv2',
177
+ mm_hidden_size: int | None = None,
178
+ projector_hidden_act: str = "gelu",
179
+ projector_ln_eps: float = 1e-5,
180
+ # vision tower parameters
181
+ qkv_hidden_size: int = 1536,
182
+ norm_type: str = 'rmsnorm',
183
+ attn_bias: bool = False,
184
+ patch_embed_proj_bias: bool = False,
185
+ mlp_type: str = 'mlp2',
186
+ linear_bias: bool = False,
187
+ activation_func: str = 'gelu_pytorch_tanh',
188
+ pos_emb_interpolation_mode: str = 'bilinear',
189
+ # Other parameters
190
+ ignore_index: int = -100,
191
+ media_placeholder_token_id: int = 163605,
192
+ pad_token_id: int = 0,
193
+ text_hidden_size=7168,
194
+ **kwargs):
195
+
196
+ self.patch_size = patch_size
197
+ self.init_pos_emb_height = init_pos_emb_height
198
+ self.init_pos_emb_width = init_pos_emb_width
199
+ self.init_pos_emb_time = init_pos_emb_time
200
+ self.pos_emb_type = pos_emb_type
201
+ self.vt_num_attention_heads = vt_num_attention_heads
202
+ self.vt_num_hidden_layers = vt_num_hidden_layers
203
+ self.vt_hidden_size = vt_hidden_size
204
+ self.vt_intermediate_size = vt_intermediate_size
205
+ self.merge_kernel_size = merge_kernel_size
206
+ self.merge_type = merge_type
207
+ self._attn_implementation = _attn_implementation
208
+
209
+ # MM Projector config
210
+ self.mm_projector_type = mm_projector_type
211
+ self.mm_hidden_size = mm_hidden_size if mm_hidden_size is not None else vt_hidden_size
212
+ self.projector_hidden_act = projector_hidden_act
213
+ self.projector_ln_eps = projector_ln_eps
214
+ self.text_hidden_size = text_hidden_size
215
+
216
+ # vision tower parameters
217
+ self.qkv_hidden_size = qkv_hidden_size
218
+ self.norm_type = norm_type
219
+ self.attn_bias = attn_bias
220
+ self.patch_embed_proj_bias = patch_embed_proj_bias
221
+ self.mlp_type = mlp_type
222
+ self.linear_bias = linear_bias
223
+ self.activation_func = activation_func
224
+ self.pos_emb_interpolation_mode = pos_emb_interpolation_mode
225
+
226
+ super().__init__(**kwargs)
227
+
228
+
229
+ class KimiK3Config(PretrainedConfig):
230
+ """Kimi-K3 model configuration.
231
+
232
+ Args:
233
+ text_config (dict | KimiLinearConfig): Configuration for the text model.
234
+
235
+ Vision Tower Parameters (from MoonViT3dConfig):
236
+ patch_size (int): Patch size for vision tower.
237
+ init_pos_emb_height (int): Initial position embedding height.
238
+ init_pos_emb_width (int): Initial position embedding width.
239
+ init_pos_emb_time (int): Initial position embedding time dimension.
240
+ pos_emb_type (str): Type of position embedding.
241
+ vt_num_attention_heads (int): Number of attention heads in vision tower.
242
+ vt_num_hidden_layers (int): Number of hidden layers in vision tower.
243
+ vt_hidden_size (int): Hidden size of vision tower.
244
+ vt_intermediate_size (int): Intermediate size in vision tower FFN.
245
+ merge_kernel_size (tuple): Kernel size for patch merging.
246
+ merge_type (str): Type of merge operation.
247
+ _attn_implementation (str): Attention implementation type.
248
+
249
+ MM Projector Parameters (from MultiModalProjectorConfig):
250
+ mm_projector_type (str): Type of multimodal projector.
251
+ mm_hidden_size (int): Hidden size from vision tower (should match vt_hidden_size).
252
+ projector_hidden_act (str): Activation function for projector.
253
+ projector_ln_eps (float): Layer norm epsilon for projector.
254
+
255
+ Other Parameters:
256
+ ignore_index (int): The ignore index for the loss function.
257
+ media_placeholder_token_id (int): The token ID to use for media placeholders.
258
+ pad_token_id (int): The token ID to use for padding.
259
+ """
260
+
261
+ model_type = "kimi_k3"
262
+
263
+ def __init__(
264
+ self,
265
+ text_config: dict | KimiLinearConfig = None,
266
+ vision_config: dict | KimiK3VisionConfig = None,
267
+ # Other parameters
268
+ ignore_index: int = -100,
269
+ media_placeholder_token_id: int = 163605,
270
+ pad_token_id: int = 0,
271
+ **kwargs,
272
+ ):
273
+ if isinstance(text_config, dict):
274
+ text_config = KimiLinearConfig(**text_config)
275
+ if isinstance(vision_config, dict):
276
+ vision_config = KimiK3VisionConfig(**vision_config)
277
+ self.text_config = text_config
278
+ self.vision_config = vision_config
279
+ # Other config
280
+ self.ignore_index = ignore_index
281
+ self.media_placeholder_token_id = media_placeholder_token_id
282
+ if getattr(self.text_config, "quantization_config", None) is not None:
283
+ self.quantization_config = self.text_config.quantization_config
284
+
285
+ super().__init__(pad_token_id=pad_token_id, **kwargs)
encoding_k3.py ADDED
@@ -0,0 +1,639 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Kimi K3 XTML encoding helpers.
2
+
3
+ This module keeps chat rendering in Python.
4
+ Callers that need token IDs should consume ``EncodeSegment`` objects directly:
5
+ structural markers may be encoded as tiktoken special tokens, while user/tool
6
+ text and attribute values are encoded as ordinary text.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from dataclasses import dataclass
13
+ from typing import Any, Iterable, Optional
14
+
15
+ OPEN_TOKEN = "<|open|>"
16
+ CLOSE_TOKEN = "<|close|>"
17
+ SEP_TOKEN = "<|sep|>"
18
+ END_OF_MSG_TOKEN = "<|end_of_msg|>"
19
+ IMAGE_PLACEHOLDER = "<|kimi_image_placeholder|>"
20
+
21
+ _VALID_THINKING_EFFORTS = {"low", "high", "max"}
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class EncodeSegment:
26
+ text: str
27
+ allow_special: bool = False
28
+
29
+
30
+ class _ImagePromptState:
31
+ def __init__(self, image_prompts: Optional[list[str]] = None):
32
+ self.image_prompts = image_prompts
33
+ self.index = 0
34
+
35
+ def next_prompt(self) -> str:
36
+ if self.image_prompts is None:
37
+ return IMAGE_PLACEHOLDER
38
+ if self.index >= len(self.image_prompts):
39
+ raise ValueError("More image placeholders than image prompts.")
40
+ prompt = self.image_prompts[self.index]
41
+ self.index += 1
42
+ return prompt
43
+
44
+ def assert_consumed(self) -> None:
45
+ if self.image_prompts is None:
46
+ return
47
+ if self.index != len(self.image_prompts):
48
+ raise ValueError(
49
+ f"image prompt count {len(self.image_prompts)} != "
50
+ f"consumed placeholder count {self.index}"
51
+ )
52
+
53
+
54
+ def _segment(text: Any, *, allow_special: bool = False) -> list[EncodeSegment]:
55
+ text = str(text)
56
+ if not text:
57
+ return []
58
+ return [EncodeSegment(text, allow_special=allow_special)]
59
+
60
+
61
+ def _control(text: str) -> list[EncodeSegment]:
62
+ return _segment(text, allow_special=True)
63
+
64
+
65
+ def _text(text: Any) -> list[EncodeSegment]:
66
+ return _segment(text, allow_special=False)
67
+
68
+
69
+ def _append_text(
70
+ segments: list[EncodeSegment],
71
+ text: Any,
72
+ image_state: _ImagePromptState,
73
+ ) -> None:
74
+ text = str(text)
75
+ if text == "":
76
+ return
77
+ if image_state.image_prompts is None or IMAGE_PLACEHOLDER not in text:
78
+ segments.extend(_text(text))
79
+ return
80
+
81
+ parts = text.split(IMAGE_PLACEHOLDER)
82
+ for i, part in enumerate(parts):
83
+ segments.extend(_text(part))
84
+ if i < len(parts) - 1:
85
+ segments.extend(_segment(image_state.next_prompt(),
86
+ allow_special=True))
87
+
88
+
89
+ def _escape_attr_value(value: Any) -> str:
90
+ return str(value).replace("&", "&amp;").replace('"', "&quot;")
91
+
92
+
93
+ def _attr(key: str, value: Any) -> list[EncodeSegment]:
94
+ return (
95
+ _text(f" {key}")
96
+ + _text('="')
97
+ + _text(_escape_attr_value(value))
98
+ + _text('"')
99
+ )
100
+
101
+
102
+ def _open_tag(tag: str, attrs: Iterable[tuple[str, Any]] = ()) -> list[EncodeSegment]:
103
+ segments: list[EncodeSegment] = []
104
+ segments.extend(_control(OPEN_TOKEN))
105
+ segments.extend(_text(tag))
106
+ for key, value in attrs:
107
+ segments.extend(_attr(key, value))
108
+ segments.extend(_control(SEP_TOKEN))
109
+ return segments
110
+
111
+ def _close_tag(tag: str) -> list[EncodeSegment]:
112
+ segments: list[EncodeSegment] = []
113
+ segments.extend(_control(CLOSE_TOKEN))
114
+ segments.extend(_text(tag))
115
+ segments.extend(_control(SEP_TOKEN))
116
+ return segments
117
+
118
+
119
+ def _end_of_msg() -> list[EncodeSegment]:
120
+ return _control(END_OF_MSG_TOKEN)
121
+
122
+
123
+ def _json_compact(value: Any) -> str:
124
+ return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
125
+
126
+
127
+ def _is_mapping(value: Any) -> bool:
128
+ return isinstance(value, dict)
129
+
130
+
131
+ def _xtml_type(value: Any) -> str:
132
+ if isinstance(value, bool):
133
+ return "boolean"
134
+ if value is None:
135
+ return "null"
136
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
137
+ return "number"
138
+ if isinstance(value, str):
139
+ return "string"
140
+ if _is_mapping(value):
141
+ return "object"
142
+ return "array"
143
+
144
+
145
+ def _xtml_value(value: Any) -> str:
146
+ if isinstance(value, str):
147
+ return value
148
+ return json.dumps(value, ensure_ascii=False)
149
+
150
+
151
+ def _get_value(obj: Any, key: str, default: Any = None) -> Any:
152
+ if isinstance(obj, dict):
153
+ return obj.get(key, default)
154
+ return getattr(obj, key, default)
155
+
156
+
157
+ def extract_response_schema(response_format: Any) -> Any:
158
+ if response_format is None:
159
+ return None
160
+
161
+ json_schema = _get_value(response_format, "json_schema")
162
+ if json_schema is None:
163
+ return None
164
+
165
+ if isinstance(json_schema, dict):
166
+ return json_schema.get(
167
+ "schema",
168
+ json_schema.get("json_schema", json_schema),
169
+ )
170
+
171
+ schema = _get_value(json_schema, "schema")
172
+ if schema is not None:
173
+ return schema
174
+
175
+ schema = _get_value(json_schema, "json_schema")
176
+ if schema is not None:
177
+ return schema
178
+
179
+ return json_schema
180
+
181
+
182
+ def deep_sort_dict(obj: Any) -> Any:
183
+ if isinstance(obj, dict):
184
+ return {k: deep_sort_dict(v) for k, v in sorted(obj.items())}
185
+ if isinstance(obj, list):
186
+ return [deep_sort_dict(item) for item in obj]
187
+ return obj
188
+
189
+
190
+ def normalize_tool_arguments(arguments: Any) -> tuple[dict[str, Any], Optional[str]]:
191
+ if arguments is None:
192
+ return {}, None
193
+ if isinstance(arguments, dict):
194
+ return arguments, None
195
+ if isinstance(arguments, str):
196
+ if not arguments.strip():
197
+ return {}, None
198
+ try:
199
+ parsed = json.loads(arguments)
200
+ except json.JSONDecodeError:
201
+ return {}, arguments
202
+ if not isinstance(parsed, dict):
203
+ raise ValueError("Kimi K3 tool call arguments must be a JSON object.")
204
+ return parsed, None
205
+ raise TypeError(
206
+ "Kimi K3 tool call arguments must be a dict or a JSON object string."
207
+ )
208
+
209
+
210
+ def normalize_message(message: Any) -> Any:
211
+ if not isinstance(message, dict):
212
+ return message
213
+
214
+ normalized = dict(message)
215
+
216
+ tools = normalized.get("tools")
217
+ if tools is not None:
218
+ normalized["tools"] = deep_sort_dict(tools)
219
+
220
+ tool_calls = normalized.get("tool_calls")
221
+ if not tool_calls:
222
+ return normalized
223
+
224
+ normalized_calls = []
225
+ for tool_call in tool_calls:
226
+ if not isinstance(tool_call, dict):
227
+ normalized_calls.append(tool_call)
228
+ continue
229
+
230
+ tc = dict(tool_call)
231
+ function = tc.get("function")
232
+ if isinstance(function, dict):
233
+ fn = dict(function)
234
+ arguments, json_block = normalize_tool_arguments(fn.get("arguments"))
235
+ fn["arguments"] = arguments
236
+ if json_block is None:
237
+ fn.pop("_xtml_json_block", None)
238
+ else:
239
+ fn["_xtml_json_block"] = json_block
240
+ tc["function"] = fn
241
+ else:
242
+ arguments, json_block = normalize_tool_arguments(tc.get("arguments"))
243
+ tc["arguments"] = arguments
244
+ if json_block is None:
245
+ tc.pop("_xtml_json_block", None)
246
+ else:
247
+ tc["_xtml_json_block"] = json_block
248
+ normalized_calls.append(tc)
249
+
250
+ normalized["tool_calls"] = normalized_calls
251
+ return normalized
252
+
253
+
254
+ def normalize_conversation(conversation: Any) -> Any:
255
+ if not isinstance(conversation, list):
256
+ return conversation
257
+
258
+ def normalize_messages(messages: list[Any]) -> list[Any]:
259
+ return [normalize_message(message) for message in messages]
260
+
261
+ if conversation and isinstance(conversation[0], list):
262
+ return [normalize_messages(messages) for messages in conversation]
263
+ return normalize_messages(conversation)
264
+
265
+
266
+ def _tool_call_id_index(tool_calls: Any) -> dict:
267
+ """Map assistant ``tool_calls[].id`` to ``(1-based position, function name)``.
268
+
269
+ The position mirrors the chat template's enumeration over ``tool_calls``
270
+ (every entry advances the position, even an id-less one). Duplicate ids keep
271
+ their first occurrence.
272
+ """
273
+ index: dict = {}
274
+ if not isinstance(tool_calls, list):
275
+ return index
276
+ for position, tool_call in enumerate(tool_calls, start=1):
277
+ if not isinstance(tool_call, dict):
278
+ continue
279
+ call_id = tool_call.get("id")
280
+ if call_id is None:
281
+ continue
282
+ key = str(call_id)
283
+ if key in index:
284
+ continue
285
+ function = tool_call.get("function")
286
+ name = (
287
+ function.get("name") if isinstance(function, dict) else tool_call.get("name")
288
+ )
289
+ index[key] = (position, name)
290
+ return index
291
+
292
+
293
+ def normalize_xtml_tool_result_messages(messages: list[Any]) -> list[Any]:
294
+ """Re-sort K3 XTML tool results into assistant ``tool_calls`` order.
295
+
296
+ Serving frameworks generally deliver tool results already in call order. A
297
+ direct Transformers caller, however, may pass OpenAI-style tool messages in any
298
+ order, so each run of consecutive tool messages is matched against the most
299
+ recent preceding assistant ``tool_calls`` by opaque ``tool_call_id`` ==
300
+ ``tool_calls[].id`` (K3 drops the ``func:index`` format requirement) and
301
+ sorted by the matched 1-based position. The matched call is authoritative,
302
+ so each matched message's ``tool`` is set to that call's function name --
303
+ this keeps an explicit (and possibly stale) ``tool``/``name`` from drifting
304
+ out of sync with the reordered position. ``index`` is still derived from the
305
+ rendered position by the chat template. A run that cannot be fully matched is
306
+ left untouched. Re-running is idempotent.
307
+
308
+ This function is side-effect free: matched tool messages are shallow-copied
309
+ before their ``tool``/``name`` is rewritten, and every other message is
310
+ appended to the output as-is. The input list and its message objects are
311
+ never mutated.
312
+ """
313
+ if not isinstance(messages, list):
314
+ return messages
315
+
316
+ output: list[Any] = []
317
+ current_index: dict = {}
318
+ i = 0
319
+ n = len(messages)
320
+
321
+ while i < n:
322
+ message = messages[i]
323
+
324
+ if isinstance(message, dict) and message.get("role") == "assistant":
325
+ tool_calls = message.get("tool_calls")
326
+ current_index = _tool_call_id_index(tool_calls) if tool_calls else {}
327
+ output.append(message)
328
+ i += 1
329
+ continue
330
+
331
+ if not isinstance(message, dict) or message.get("role") != "tool":
332
+ output.append(message)
333
+ i += 1
334
+ continue
335
+
336
+ run: list[tuple] = [] # (position, original_offset, message, name)
337
+ unresolved = False
338
+ offset = 0
339
+ while (
340
+ i < n and isinstance(messages[i], dict) and messages[i].get("role") == "tool"
341
+ ):
342
+ tool_message = messages[i]
343
+ call_id = tool_message.get("tool_call_id", tool_message.get("id"))
344
+ matched = current_index.get(str(call_id)) if call_id is not None else None
345
+ if matched is None:
346
+ unresolved = True
347
+ run.append((None, offset, tool_message, None))
348
+ else:
349
+ position, name = matched
350
+ run.append((position, offset, tool_message, name))
351
+ offset += 1
352
+ i += 1
353
+
354
+ if unresolved:
355
+ output.extend(item[2] for item in run)
356
+ else:
357
+ run.sort(key=lambda item: (item[0], item[1]))
358
+ for _, _, tool_message, name in run:
359
+ if name is None:
360
+ output.append(tool_message)
361
+ continue
362
+ # The id-matched call is authoritative: align tool (and any
363
+ # explicit name) so the rendered XTML tool attribute cannot
364
+ # disagree with the reordered position. Copy first so the
365
+ # caller's message object is never mutated.
366
+ resolved = dict(tool_message)
367
+ resolved["tool"] = name
368
+ if "name" in resolved:
369
+ resolved["name"] = name
370
+ output.append(resolved)
371
+
372
+ return output
373
+
374
+
375
+ def is_batched_conversation(conversation: Any) -> bool:
376
+ return (
377
+ isinstance(conversation, list)
378
+ and bool(conversation)
379
+ and isinstance(conversation[0], list)
380
+ )
381
+
382
+
383
+ def _render_content_segments(
384
+ content: Any,
385
+ image_state: _ImagePromptState,
386
+ ) -> list[EncodeSegment]:
387
+ segments: list[EncodeSegment] = []
388
+ if isinstance(content, str):
389
+ _append_text(segments, content, image_state)
390
+ elif content is not None:
391
+ for part in content:
392
+ if part["type"] in ["image", "image_url"]:
393
+ segments.extend(
394
+ _segment(image_state.next_prompt(), allow_special=True))
395
+ else:
396
+ _append_text(segments, part["text"], image_state)
397
+ return segments
398
+
399
+
400
+ def _internal_system_message(message_type: str, body: str) -> list[EncodeSegment]:
401
+ segments: list[EncodeSegment] = []
402
+ segments.extend(_open_tag("message", [("role", "system"), ("type", message_type)]))
403
+ segments.extend(_text(body.strip()))
404
+ segments.extend(_close_tag("message"))
405
+ segments.extend(_end_of_msg())
406
+ return segments
407
+
408
+
409
+ def _render_assistant_segments(
410
+ message: dict[str, Any],
411
+ image_state: _ImagePromptState,
412
+ ) -> list[EncodeSegment]:
413
+ segments: list[EncodeSegment] = []
414
+ reasoning_content = message.get("reasoning_content") or message.get("reasoning")
415
+ if reasoning_content is not None and str(reasoning_content).strip():
416
+ segments.extend(_open_tag("think"))
417
+ _append_text(segments, reasoning_content, image_state)
418
+ segments.extend(_close_tag("think"))
419
+
420
+ segments.extend(_open_tag("response"))
421
+ segments.extend(_render_content_segments(message.get("content"), image_state))
422
+ segments.extend(_close_tag("response"))
423
+
424
+ tool_calls = message.get("tool_calls")
425
+ if tool_calls:
426
+ segments.extend(_open_tag("tools"))
427
+ for index, tool_call in enumerate(tool_calls, start=1):
428
+ fn = tool_call.get("function", tool_call)
429
+ segments.extend(
430
+ _open_tag("call", [("tool", fn["name"]), ("index", index)])
431
+ )
432
+ args = fn.get("arguments", {})
433
+ json_block = fn.get("_xtml_json_block")
434
+ if json_block is not None:
435
+ segments.extend(_open_tag("json", [("type", "object")]))
436
+ _append_text(segments, json_block, image_state)
437
+ segments.extend(_close_tag("json"))
438
+ elif _is_mapping(args):
439
+ for key, value in args.items():
440
+ segments.extend(
441
+ _open_tag(
442
+ "argument",
443
+ [("key", key), ("type", _xtml_type(value))],
444
+ )
445
+ )
446
+ _append_text(segments, _xtml_value(value), image_state)
447
+ segments.extend(_close_tag("argument"))
448
+ segments.extend(_close_tag("call"))
449
+ segments.extend(_close_tag("tools"))
450
+
451
+ return segments
452
+
453
+
454
+ def _render_tool_declare(tools: Any, *, dynamic: bool = False) -> list[EncodeSegment]:
455
+ if dynamic:
456
+ body = (
457
+ "## New Tools Available\n"
458
+ "The system dynamically extends the toolset via lazy-loading.\n"
459
+ "You have access to all existing and extended tools.\n"
460
+ "Here are the specs for the extended tools.\n\n"
461
+ "```json\n"
462
+ f"{_json_compact(tools)}\n"
463
+ "```"
464
+ )
465
+ else:
466
+ body = (
467
+ "# Tools\n"
468
+ "Here are the available tools, described in JSONSchema.\n\n"
469
+ "```json\n"
470
+ f"{_json_compact(tools)}\n"
471
+ "```"
472
+ )
473
+ segments: list[EncodeSegment] = []
474
+ segments.extend(_open_tag("message", [("role", "system"), ("type", "tool-declare")]))
475
+ segments.extend(_text(body))
476
+ segments.extend(_close_tag("message"))
477
+ segments.extend(_end_of_msg())
478
+ return segments
479
+
480
+
481
+ def build_chat_segments(
482
+ messages: list[Any],
483
+ tools: Optional[list[dict]] = None,
484
+ *,
485
+ add_generation_prompt: bool = True,
486
+ thinking: bool = True,
487
+ image_prompts: Optional[list[str]] = None,
488
+ **kwargs: Any,
489
+ ) -> list[EncodeSegment]:
490
+ # Re-sort tool results by tool_call_id at the lowest layer so every caller
491
+ # (processor or direct tokenizer) gets correctly ordered XTML. The helper is
492
+ # side-effect free, so the caller's message objects are left untouched.
493
+ messages = normalize_xtml_tool_result_messages(messages)
494
+ messages = normalize_conversation(messages)
495
+ tools = deep_sort_dict(tools)
496
+
497
+ kwargs = dict(kwargs)
498
+ response_format = kwargs.get("response_format")
499
+ if "response_schema" not in kwargs:
500
+ response_schema = extract_response_schema(response_format)
501
+ if response_schema is not None:
502
+ kwargs["response_schema"] = response_schema
503
+ if kwargs.get("response_schema") is not None:
504
+ kwargs["response_schema"] = deep_sort_dict(kwargs["response_schema"])
505
+
506
+ image_state = _ImagePromptState(image_prompts)
507
+ segments: list[EncodeSegment] = []
508
+
509
+ tool_calls = None
510
+ tool_index = 0
511
+
512
+ if tools:
513
+ segments.extend(_render_tool_declare(tools))
514
+
515
+ thinking_effort = kwargs.get("thinking_effort")
516
+ if thinking and thinking_effort is not None:
517
+ assert thinking_effort in _VALID_THINKING_EFFORTS, (
518
+ f"Unsupported thinking_effort={thinking_effort!r}; "
519
+ f"supported values are {sorted(_VALID_THINKING_EFFORTS)}."
520
+ )
521
+ if thinking and thinking_effort in _VALID_THINKING_EFFORTS:
522
+ segments.extend(
523
+ _internal_system_message(
524
+ "thinking-effort",
525
+ "`thinking_effort` guides on how much to think in your "
526
+ "thinking channel (not including the response channel), "
527
+ "supported values include `low`, `medium`, `high`, and `max`.\n"
528
+ f"Now the system is invoked with `thinking_effort={thinking_effort}`.",
529
+ )
530
+ )
531
+
532
+ for message_index, message in enumerate(messages):
533
+ if not isinstance(message, dict):
534
+ continue
535
+
536
+ role = message["role"]
537
+ if role == "user":
538
+ attrs = [("role", "user")]
539
+ if message.get("name"):
540
+ attrs.append(("name", message["name"]))
541
+ segments.extend(_open_tag("message", attrs))
542
+ segments.extend(_render_content_segments(message.get("content"), image_state))
543
+ segments.extend(_close_tag("message"))
544
+ segments.extend(_end_of_msg())
545
+ elif role == "system" and message.get("tools"):
546
+ segments.extend(_render_tool_declare(message["tools"], dynamic=True))
547
+ elif role == "system":
548
+ attrs = [("role", "system")]
549
+ if message.get("name"):
550
+ attrs.append(("name", message["name"]))
551
+ segments.extend(_open_tag("message", attrs))
552
+ segments.extend(_render_content_segments(message.get("content"), image_state))
553
+ segments.extend(_close_tag("message"))
554
+ segments.extend(_end_of_msg())
555
+ elif role == "tool":
556
+ tool_index += 1
557
+ tool_name = message.get("tool", message.get("name"))
558
+ if (
559
+ tool_name is None
560
+ and tool_calls is not None
561
+ and tool_index <= len(tool_calls)
562
+ ):
563
+ tc = tool_calls[tool_index - 1]
564
+ fn = tc.get("function", tc)
565
+ tool_name = fn["name"]
566
+ if tool_name is None:
567
+ raise ValueError(
568
+ "Kimi K3 tool messages need a resolvable tool name: "
569
+ "carry `tool`/`name`, or match a preceding assistant "
570
+ "tool_call by order."
571
+ )
572
+ segments.extend(
573
+ _open_tag(
574
+ "message",
575
+ [("role", "tool"), ("tool", tool_name), ("index", tool_index)],
576
+ )
577
+ )
578
+ segments.extend(_render_content_segments(message.get("content"), image_state))
579
+ segments.extend(_close_tag("message"))
580
+ segments.extend(_end_of_msg())
581
+ elif role == "assistant":
582
+ tool_calls = message.get("tool_calls")
583
+ tool_index = 0
584
+ attrs = [("role", "assistant")]
585
+ if message.get("name"):
586
+ attrs.append(("name", message["name"]))
587
+ segments.extend(_open_tag("message", attrs))
588
+ segments.extend(_render_assistant_segments(message, image_state))
589
+ segments.extend(_close_tag("message"))
590
+ segments.extend(_end_of_msg())
591
+
592
+ tool_choice = kwargs.get("tool_choice")
593
+ if tool_choice == "required":
594
+ segments.extend(
595
+ _internal_system_message(
596
+ "tool-choice",
597
+ "The system is invoked with `tool_choice=required`.\n"
598
+ "You MUST call tools in the next message.",
599
+ )
600
+ )
601
+ elif tool_choice == "none":
602
+ segments.extend(
603
+ _internal_system_message(
604
+ "tool-choice",
605
+ "The system is invoked with `tool_choice=none`.\n"
606
+ "You MUST NOT call any tools in the next message.",
607
+ )
608
+ )
609
+
610
+ rf = kwargs.get("response_format")
611
+ rf_type = _get_value(rf, "type", rf) if isinstance(rf, dict) else rf
612
+ if rf_type == "json_object":
613
+ segments.extend(
614
+ _internal_system_message(
615
+ "response-format",
616
+ "The system is invoked with `response_format=json_object`.\n"
617
+ "Your response must be raw JSON data without markdown code "
618
+ "blocks (```json) or any additional formatting.",
619
+ )
620
+ )
621
+ elif rf_type == "json_schema":
622
+ schema = _json_compact(kwargs.get("response_schema"))
623
+ segments.extend(
624
+ _internal_system_message(
625
+ "response-format",
626
+ "The system is invoked with `response_format=json_schema`.\n"
627
+ "Your response must be raw JSON data without markdown code "
628
+ "blocks (```json) or any additional formatting.\n"
629
+ "The JSON data must match the following schema:\n"
630
+ f"```json\n{schema}\n```",
631
+ )
632
+ )
633
+
634
+ if add_generation_prompt:
635
+ segments.extend(_open_tag("message", [("role", "assistant")]))
636
+ segments.extend(_open_tag("think" if thinking else "response"))
637
+
638
+ image_state.assert_consumed()
639
+ return segments
generation_config.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "max_length": 1048576,
3
+ "eos_token_id": 163586
4
+ }
kimi_k3_processor.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Kimi-K3 processor: wraps vision processor + tokenizer into a single interface.
2
+
3
+ Chat rendering (including XTML tool-result ordering) is handled by the
4
+ tokenizer's Python encoder; this processor adds multimodal media preprocessing.
5
+ """
6
+
7
+ from transformers.feature_extraction_utils import BatchFeature
8
+ from transformers.processing_utils import ProcessorMixin
9
+ from transformers.utils import logging
10
+
11
+ from .media_utils import ensure_media_type
12
+
13
+ logger = logging.get_logger(__name__)
14
+
15
+ # ── KimiK3Processor ───────────────────────────────────────────────────
16
+
17
+
18
+ class KimiK3Processor(ProcessorMixin):
19
+ r"""
20
+ Constructs a KimiK3 processor which wraps a KimiK3 image processor
21
+ and a tokenizer into a single processor.
22
+
23
+ [`KimiK3Processor`] offers all the functionalities of
24
+ [`KimiK3VisionProcessor`] and [`TikTokenTokenizer`].
25
+
26
+ Args:
27
+ image_processor ([`KimiK3VisionProcessor`], *optional*):
28
+ The image processor is a required input.
29
+ tokenizer ([`TikTokenTokenizer`], *optional*):
30
+ The tokenizer is a required input.
31
+ chat_template (`str`, *optional*): Kept for ProcessorMixin
32
+ compatibility. Kimi K3 chat encoding is implemented in Python by
33
+ the tokenizer.
34
+ """
35
+
36
+ attributes = ["image_processor", "tokenizer"]
37
+ valid_kwargs = ["chat_template"]
38
+ image_processor_class = "AutoImageProcessor"
39
+ tokenizer_class = "AutoTokenizer"
40
+
41
+ def __init__(
42
+ self,
43
+ image_processor=None,
44
+ tokenizer=None,
45
+ chat_template=None,
46
+ **kwargs,
47
+ ):
48
+ super().__init__(image_processor,
49
+ tokenizer,
50
+ chat_template=chat_template)
51
+ self.media_processor = image_processor
52
+ self.image_placeholder = "<|kimi_image_placeholder|>"
53
+
54
+ # ── Media preprocessing ────────────────────────────────────────────
55
+
56
+ def update_raw_text(self, text: str, image_prompts: list[str]) -> str:
57
+ # Replace image placeholders
58
+ image_count = text.count(self.image_placeholder)
59
+ if image_count > 0:
60
+ assert image_count == len(image_prompts), (
61
+ f"image placeholder count {image_count} != "
62
+ f"image_prompts count {len(image_prompts)}")
63
+ text_parts = text.split(self.image_placeholder)
64
+ assert len(text_parts) == len(image_prompts) + 1
65
+ text = "".join([
66
+ text_parts[i] + image_prompts[i]
67
+ for i in range(len(image_prompts))
68
+ ])
69
+ text += text_parts[-1]
70
+
71
+ return text
72
+
73
+ def preprocess_medias(self,
74
+ medias: list[dict]) -> tuple[list[dict], list[str]]:
75
+ """Process media items and generate corresponding prompts.
76
+
77
+ Returns:
78
+ A tuple of (updated_medias, image_prompts).
79
+ """
80
+ updated_medias = []
81
+ image_prompts = []
82
+ for media in medias:
83
+ if media['type'] == 'image':
84
+ updated_medias.append(media)
85
+ img = ensure_media_type(
86
+ media,
87
+ transparent_bg_config=self.media_processor.
88
+ _transparent_bg_config,
89
+ transparent_bg_fill_stage=self.media_processor.
90
+ _transparent_bg_fill_stage,
91
+ )['image']
92
+ w, h = img.size
93
+ image_prompts.append(
94
+ self.media_processor.make_image_prompt(w, h))
95
+ else:
96
+ raise ValueError(f"unsupported media type: {media['type']}")
97
+ return updated_medias, image_prompts
98
+
99
+ # ── Main entry points ──────────────────────────────────────────────
100
+
101
+ def __call__(self,
102
+ messages: list[dict] = None,
103
+ medias: list[dict] = None,
104
+ text: str = None,
105
+ return_tensors: str = "pt",
106
+ **kwargs) -> BatchFeature:
107
+ """
108
+ Process multimodal inputs for Kimi-K3 model.
109
+
110
+ Args:
111
+ messages: List of message dicts with 'role' and 'content' fields.
112
+ If provided, medias and text will be extracted automatically.
113
+ medias: Pre-extracted list of media dicts.
114
+ text: Pre-formatted text string.
115
+ return_tensors: Format of returned tensors. Default: 'pt'.
116
+ **kwargs: Additional arguments passed to apply_chat_template.
117
+
118
+ Returns:
119
+ BatchFeature with fields: input_ids, attention_mask,
120
+ pixel_values, grid_thws.
121
+ """
122
+ if messages is None and (medias is None or text is None):
123
+ raise ValueError(
124
+ "Provide either 'messages' or both 'medias' and 'text'")
125
+
126
+ if medias is not None and text is not None:
127
+ updated_medias, image_prompts = (self.preprocess_medias(medias))
128
+ preprocessed = self.media_processor.preprocess(
129
+ updated_medias, return_tensors=return_tensors)
130
+ text = self.update_raw_text(text, image_prompts)
131
+ text_inputs = self.tokenizer(text, return_tensors=return_tensors)
132
+ return BatchFeature(data={**text_inputs, **preprocessed.data})
133
+
134
+ if medias is None:
135
+ medias = self._extract_medias_from_messages(messages)
136
+ updated_medias, image_prompts = (self.preprocess_medias(medias))
137
+ preprocessed = self.media_processor.preprocess(
138
+ updated_medias, return_tensors=return_tensors)
139
+
140
+ if text is None:
141
+ text_inputs = self.tokenizer.apply_chat_template(
142
+ messages,
143
+ tokenize=True,
144
+ return_tensors=return_tensors,
145
+ return_dict=True,
146
+ image_prompts=image_prompts,
147
+ **kwargs)
148
+ return BatchFeature(data={**text_inputs, **preprocessed.data})
149
+
150
+ text = self.update_raw_text(text, image_prompts)
151
+ text_inputs = self.tokenizer(text, return_tensors=return_tensors)
152
+ return BatchFeature(data={**text_inputs, **preprocessed.data})
153
+
154
+ @staticmethod
155
+ def _extract_medias_from_messages(messages: list[dict]) -> list[dict]:
156
+ """Extract media items from messages in a single pass."""
157
+ medias = []
158
+ for msg in messages:
159
+ if msg['role'] != 'user' or not msg.get('content'):
160
+ continue
161
+
162
+ for content_part in msg['content']:
163
+ if not isinstance(content_part, dict):
164
+ continue
165
+
166
+ content_type = content_part.get('type')
167
+ if content_type in ['image_url', 'image']:
168
+ image_data = content_part.get(content_type)
169
+ assert image_data is not None, f"image data is missing for content part: {content_part}"
170
+ medias.append({
171
+ 'type': 'image',
172
+ 'image': image_data,
173
+ })
174
+ return medias
175
+
176
+ def apply_chat_template(self, messages, **kwargs):
177
+ return self.tokenizer.apply_chat_template(messages, **kwargs)
178
+
179
+ def batch_decode(self, *args, **kwargs):
180
+ return self.tokenizer.batch_decode(*args, **kwargs)
181
+
182
+ def decode(self, *args, **kwargs):
183
+ return self.tokenizer.decode(*args, **kwargs)
184
+
185
+ @property
186
+ def model_input_names(self):
187
+ return ['input_ids', 'attention_mask', 'pixel_values', 'grid_thws']
kimi_k3_vision_processing.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Image processor class for Kimi-K3.
2
+ """
3
+
4
+ import json
5
+ from typing import Any, Dict, Optional, Union
6
+
7
+ import numpy as np
8
+ import torch
9
+ from PIL import Image
10
+ from transformers.image_processing_utils import (BaseImageProcessor,
11
+ BatchFeature)
12
+ from transformers.utils import TensorType
13
+
14
+ from .media_utils import (MediaInput, TransparentBgConfig, _to_tensor,
15
+ ensure_media_type, image_to_np, navit_patchify,
16
+ navit_resize_image, normalize)
17
+
18
+
19
+ class KimiK3VisionProcessor(BaseImageProcessor):
20
+ model_type = "kimi_k3"
21
+
22
+ def __init__(
23
+ self,
24
+ media_proc_cfg: dict,
25
+ **kwargs,
26
+ ):
27
+ super().__init__(**kwargs)
28
+ self.media_proc_cfg = media_proc_cfg
29
+
30
+ @property
31
+ def _transparent_bg_config(self) -> Optional[TransparentBgConfig]:
32
+ cfg = self.media_proc_cfg.get("transparent_bg_config")
33
+ if cfg is None:
34
+ return None
35
+ if isinstance(cfg, TransparentBgConfig):
36
+ return cfg
37
+ return TransparentBgConfig(**cfg)
38
+
39
+ @property
40
+ def _transparent_bg_fill_stage(self) -> str:
41
+ return self.media_proc_cfg.get("transparent_bg_fill_stage",
42
+ "before_resize")
43
+
44
+ def media_tokens_calculator(self, media: MediaInput):
45
+ media = ensure_media_type(
46
+ media,
47
+ transparent_bg_config=self._transparent_bg_config,
48
+ transparent_bg_fill_stage=self._transparent_bg_fill_stage,
49
+ )
50
+ ret = self.get_resize_config(media)
51
+ return ret['num_tokens']
52
+
53
+ @classmethod
54
+ def make_image_prompt(cls, width: int, height: int) -> str:
55
+ """Build the K3 image placeholder with resolution info."""
56
+ return (f"<|media_begin|>image {width}x{height}"
57
+ f"<|media_content|><|media_pad|><|media_end|>")
58
+
59
+ def get_resize_config(self, media_input: MediaInput) -> dict:
60
+ if media_input['type'] == 'image':
61
+ w, h = media_input['image'].size
62
+ ret = navit_resize_image(
63
+ w, h, self.media_proc_cfg['patch_size'],
64
+ self.media_proc_cfg['merge_kernel_size'],
65
+ self.media_proc_cfg['in_patch_limit'],
66
+ self.media_proc_cfg['patch_limit_on_one_side'],
67
+ self.media_proc_cfg['fixed_output_tokens'])
68
+ return ret
69
+ else:
70
+ raise ValueError("Unsupported type: {}".format(
71
+ media_input['type']))
72
+
73
+ def resize_image(self, image: Image.Image, new_width: int, new_height: int,
74
+ pad_width: int, pad_height: int) -> np.ndarray:
75
+ image_np = image_to_np(
76
+ image,
77
+ (new_width, new_height),
78
+ "resize",
79
+ transparent_bg_config=self._transparent_bg_config,
80
+ transparent_bg_fill_stage=self._transparent_bg_fill_stage,
81
+ )
82
+ image_np = np.pad(
83
+ image_np,
84
+ ((0, pad_height), (0, pad_width), (0, 0)),
85
+ mode="constant",
86
+ constant_values=0,
87
+ )
88
+ return image_np
89
+
90
+ def preprocess(
91
+ self,
92
+ medias: list[MediaInput],
93
+ return_tensors: Optional[Union[str, TensorType]] = None,
94
+ ) -> BatchFeature:
95
+ """
96
+ Preprocess a atom vision input (images) into model-ready tensors.
97
+
98
+ Args:
99
+ medias: List of MediaInput.
100
+ return_tensors: Desired output format ('pt', 'np', 'tf', or None).
101
+
102
+ Returns:
103
+ BatchFeature containing 'pixel_values' and 'grid_thws' tensors.
104
+ """
105
+ if not isinstance(medias, list):
106
+ medias = [medias]
107
+ if medias:
108
+ pixel_values = []
109
+ for item in medias:
110
+ item = ensure_media_type(
111
+ item,
112
+ transparent_bg_config=self._transparent_bg_config,
113
+ transparent_bg_fill_stage=self._transparent_bg_fill_stage,
114
+ )
115
+ resize_config = self.get_resize_config(item)
116
+ new_width, new_height, pad_width, pad_height = resize_config[
117
+ 'new_width'], resize_config['new_height'], resize_config[
118
+ 'pad_width'], resize_config['pad_height']
119
+ if item['type'] == 'image':
120
+ image = item['image']
121
+ image_np = self.resize_image(image, new_width, new_height,
122
+ pad_width, pad_height)
123
+ pixel_values.append(np.expand_dims(image_np, axis=0))
124
+ else:
125
+ raise ValueError("Unsupported type: {}".format(
126
+ item['type']))
127
+ normalized_pixel_values = []
128
+ image_std_inv = 1.0 / np.array(self.media_proc_cfg['image_std'])
129
+ image_mean = np.array(self.media_proc_cfg['image_mean'])
130
+ for pixels in pixel_values:
131
+ pixels = normalize(pixels, image_mean, image_std_inv)
132
+ pixels_and_thw = navit_patchify(
133
+ pixels,
134
+ self.media_proc_cfg['patch_size'],
135
+ )
136
+ normalized_pixel_values.append(pixels_and_thw)
137
+
138
+ pixel_values = torch.cat([
139
+ _to_tensor(pixel_value['pixel_values'])
140
+ for pixel_value in normalized_pixel_values
141
+ ])
142
+ grid_thws = torch.cat([
143
+ _to_tensor(pixel_value['grid_thw'],
144
+ dtype=torch.int64).unsqueeze(0)
145
+ for pixel_value in normalized_pixel_values
146
+ ])
147
+
148
+ data = {
149
+ 'pixel_values': pixel_values,
150
+ 'grid_thws': grid_thws,
151
+ }
152
+
153
+ else:
154
+ data = {}
155
+
156
+ return BatchFeature(data=data, tensor_type=return_tensors)
157
+
158
+ def __repr__(self):
159
+ return f"KimiK3VisionProcessor(media_proc_cfg={self.media_proc_cfg})"
160
+
161
+ def to_dict(self) -> Dict[str, Any]:
162
+ output = super().to_dict()
163
+ output["media_proc_cfg"] = self.media_proc_cfg
164
+ if "media_processor" in output:
165
+ del output["media_processor"]
166
+ return output
167
+
168
+ @classmethod
169
+ def from_dict(cls, config_dict: Dict[str, Any], **kwargs):
170
+ config = config_dict.copy()
171
+ media_proc_cfg = config.pop("media_proc_cfg", {})
172
+ return cls(media_proc_cfg=media_proc_cfg, **config, **kwargs)
173
+
174
+ def to_json_string(self):
175
+ dictionary = self.to_dict()
176
+ for key, value in dictionary.items():
177
+ if hasattr(value, 'tolist'):
178
+ dictionary[key] = value.tolist()
179
+ return json.dumps(dictionary, indent=2, sort_keys=True) + "\n"
media_utils.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import functools
3
+ import io
4
+ import math
5
+ from dataclasses import dataclass
6
+ from typing import Literal, TypedDict
7
+
8
+ import numpy as np
9
+ from PIL import Image
10
+
11
+
12
+ class ImageInput(TypedDict):
13
+ type: Literal['image']
14
+ image: Image.Image
15
+
16
+
17
+ MediaInput = ImageInput
18
+
19
+
20
+ @dataclass
21
+ class TransparentBgConfig:
22
+ """The config of the transparent background."""
23
+
24
+ pattern: Literal["white", "black", "gray", "chessboard"] = "black"
25
+ """The pattern of the transparent background."""
26
+
27
+ chessboard_square_size: int = 16
28
+ """The size of the squares in the chessboard background."""
29
+
30
+ chessboard_square_on_top_left: bool = True
31
+ """Whether to start the chessboard with a white square on the top left."""
32
+
33
+ chessboard_white_value: int = 255
34
+ """The value of the white pixels in the background."""
35
+
36
+ chessboard_gray_value: int = 200
37
+ """The value of the gray pixels in the background."""
38
+
39
+
40
+ @functools.lru_cache(maxsize=256)
41
+ def _create_chessboard_background(
42
+ height: int,
43
+ width: int,
44
+ square_size: int,
45
+ square_on_top_left: bool,
46
+ white_value: int,
47
+ gray_value: int,
48
+ ) -> np.ndarray:
49
+ """Create a chessboard background."""
50
+ bg = np.ones((height, width, 3), dtype=np.uint8) * white_value
51
+ for y in range(0, height, square_size):
52
+ for x in range(0, width, square_size):
53
+ if (y // square_size + x // square_size) % 2 == (
54
+ 1 if square_on_top_left else 0):
55
+ bg[y:y + square_size, x:x + square_size] = gray_value
56
+ return bg
57
+
58
+
59
+ def fill_transparent_bg_with(
60
+ image: Image.Image,
61
+ transparent_bg_config: TransparentBgConfig | None = None,
62
+ ) -> Image.Image:
63
+ """Composite a (possibly) transparent image onto a configured background.
64
+
65
+ When ``transparent_bg_config`` is ``None``, the image is simply converted
66
+ to RGB (preserving the historical behavior). Otherwise the alpha channel
67
+ is alpha-composited over a background generated according to the config.
68
+ """
69
+ if transparent_bg_config is None:
70
+ return image.convert("RGB")
71
+
72
+ if image.mode == "RGB":
73
+ return image
74
+
75
+ has_alpha = "A" in image.getbands() or "transparency" in image.info
76
+ if not has_alpha:
77
+ return image.convert("RGB")
78
+
79
+ img = np.array(image.convert("RGBA"))
80
+ height, width = img.shape[:2]
81
+ bg_pattern = transparent_bg_config.pattern
82
+ if bg_pattern == "white":
83
+ bg = np.full((height, width, 3), 255, dtype=np.uint8)
84
+ elif bg_pattern == "black":
85
+ bg = np.zeros((height, width, 3), dtype=np.uint8)
86
+ elif bg_pattern == "gray":
87
+ bg = np.full((height, width, 3), 128, dtype=np.uint8)
88
+ elif bg_pattern == "chessboard":
89
+ bg = _create_chessboard_background(
90
+ height,
91
+ width,
92
+ transparent_bg_config.chessboard_square_size,
93
+ transparent_bg_config.chessboard_square_on_top_left,
94
+ transparent_bg_config.chessboard_white_value,
95
+ transparent_bg_config.chessboard_gray_value,
96
+ )
97
+ else:
98
+ raise ValueError(f"Invalid background pattern: {bg_pattern}")
99
+
100
+ alpha = img[:, :, 3]
101
+ img_rgb = img[:, :, :3]
102
+ alpha_normalized = alpha.astype(np.float32) / 255.0
103
+ alpha_3d = np.stack([alpha_normalized] * 3, axis=2)
104
+ result = alpha_3d * img_rgb + (1 - alpha_3d) * bg
105
+ result = result.astype(np.uint8)
106
+ return Image.fromarray(result)
107
+
108
+
109
+ def navit_resize_image(
110
+ width: int,
111
+ height: int,
112
+ patch_size: int,
113
+ merge_kernel_size: int,
114
+ in_patch_limit: int,
115
+ patch_limit_on_one_side: int,
116
+ fixed_output_tokens: int | None,
117
+ ):
118
+ # Apply the patch limits.
119
+ s1 = math.sqrt(
120
+ in_patch_limit /
121
+ (max(1.0, width // patch_size) * max(1.0, height // patch_size)))
122
+ s2 = patch_limit_on_one_side * patch_size / width
123
+ s3 = patch_limit_on_one_side * patch_size / height
124
+ scale = min(1.0, s1, s2, s3)
125
+ new_w, new_h = max(1, int(width * scale)), max(1, int(height * scale))
126
+ new_w = min(new_w, patch_limit_on_one_side * patch_size)
127
+ new_h = min(new_h, patch_limit_on_one_side * patch_size)
128
+
129
+ # Calculate the padding to make the height and width divisible by the merge kernel size and patch size.
130
+ factor = merge_kernel_size * patch_size
131
+
132
+ pad_height = (factor - new_h % factor) % factor
133
+ pad_width = (factor - new_w % factor) % factor
134
+
135
+ if fixed_output_tokens is not None:
136
+ num_tokens = fixed_output_tokens
137
+ else:
138
+ # Calculate new dimensions after padding and patching
139
+ token_height = (new_h + pad_height) // factor
140
+ token_width = (new_w + pad_width) // factor
141
+
142
+ assert token_height * merge_kernel_size <= patch_limit_on_one_side, (
143
+ f"token_height {token_height} * merge_kernel_size {merge_kernel_size} > patch_limit_on_one_side {patch_limit_on_one_side}"
144
+ )
145
+ assert token_width * merge_kernel_size <= patch_limit_on_one_side, (
146
+ f"token_width {token_width} * merge_kernel_size {merge_kernel_size} > patch_limit_on_one_side {patch_limit_on_one_side}"
147
+ )
148
+
149
+ num_tokens = token_height * token_width
150
+ return {
151
+ "num_tokens": num_tokens,
152
+ "new_width": new_w,
153
+ "new_height": new_h,
154
+ "pad_width": pad_width,
155
+ "pad_height": pad_height,
156
+ "sampled_nframes": 1,
157
+ }
158
+
159
+
160
+ def _to_pil(
161
+ data: str | bytes | Image.Image,
162
+ transparent_bg_config: TransparentBgConfig | None = None,
163
+ to_rgb: bool = True,
164
+ ) -> Image.Image:
165
+ """Load an image and (optionally) composite its transparent background.
166
+
167
+ Args:
168
+ data: A PIL Image, a base64 ``data:`` URL, a file path, or raw bytes.
169
+ transparent_bg_config: The config used to fill the transparent
170
+ background. ``None`` keeps the historical behavior of converting
171
+ to RGB without compositing.
172
+ to_rgb: If ``False`` the image is returned as-is (the
173
+ ``transparent_bg_config`` is ignored). The caller is then
174
+ expected to call :func:`fill_transparent_bg_with` later — e.g.
175
+ after a resize.
176
+ """
177
+ if isinstance(data, Image.Image):
178
+ image = data
179
+ elif isinstance(data, str):
180
+ if data.startswith("data:"):
181
+ raw_base64 = data.split(",")[1]
182
+ image = Image.open(io.BytesIO(base64.b64decode(raw_base64)))
183
+ else:
184
+ image = Image.open(data)
185
+ elif isinstance(data, bytes):
186
+ image = Image.open(io.BytesIO(data))
187
+ else:
188
+ raise ValueError(f"Unsupported data type: {type(data)}")
189
+
190
+ if not to_rgb:
191
+ return image
192
+
193
+ return fill_transparent_bg_with(image, transparent_bg_config)
194
+
195
+
196
+ def ensure_media_type(
197
+ media: MediaInput,
198
+ transparent_bg_config: TransparentBgConfig | None = None,
199
+ transparent_bg_fill_stage: Literal["before_resize",
200
+ "after_resize"] = "before_resize",
201
+ ) -> MediaInput:
202
+ if media['type'] == 'image':
203
+ media['image'] = _to_pil(
204
+ media['image'],
205
+ transparent_bg_config=transparent_bg_config,
206
+ to_rgb=transparent_bg_fill_stage == "before_resize",
207
+ )
208
+ return media
209
+ else:
210
+ raise ValueError(f"Unsupported media type: {media['type']}")
211
+
212
+
213
+ def image_to_np(
214
+ image: Image.Image,
215
+ resize_to: tuple[int, int] | None = None,
216
+ mode: str = "resize",
217
+ raise_error_for_ill_resize: bool = True,
218
+ transparent_bg_config: TransparentBgConfig | None = None,
219
+ transparent_bg_fill_stage: Literal["before_resize",
220
+ "after_resize"] = "before_resize",
221
+ ) -> np.ndarray:
222
+ """Convert an image to a numpy array.
223
+
224
+ Args:
225
+ content: The image to convert.
226
+ resize_to: The size to resize the image to.
227
+ mode: The mode to resize the image to.
228
+ raise_error_for_ill_resize: Whether to raise an error for ill-sized resize.
229
+ transparent_bg_config: The config of the transparent background. Only
230
+ used when ``transparent_bg_fill_stage == "after_resize"`` (the
231
+ caller is responsible for filling before resize otherwise).
232
+ transparent_bg_fill_stage: When to composite the transparent
233
+ background — before or after the resize step.
234
+
235
+ Returns:
236
+ A numpy array.
237
+ """
238
+ assert isinstance(image, Image.Image), "image must be a PIL Image"
239
+ if resize_to is not None:
240
+ if mode == "resize":
241
+ image = image.resize(resize_to, resample=Image.Resampling.BICUBIC)
242
+ if transparent_bg_fill_stage == "after_resize":
243
+ image = fill_transparent_bg_with(image, transparent_bg_config)
244
+
245
+ elif mode == "rescale_and_pad_to_center":
246
+ scale = min(resize_to[0] / image.width,
247
+ resize_to[1] / image.height, 1.0)
248
+ new_width = round(image.width * scale)
249
+ new_height = round(image.height * scale)
250
+ if new_width == 0 or new_height == 0:
251
+ if raise_error_for_ill_resize:
252
+ raise ValueError(
253
+ f"Invalid resize to: {resize_to}, from image size: {image.size}"
254
+ )
255
+ else:
256
+ return np.zeros((resize_to[1], resize_to[0], 3),
257
+ dtype=np.uint8)
258
+
259
+ image = image.resize((new_width, new_height),
260
+ resample=Image.Resampling.BICUBIC)
261
+ if transparent_bg_fill_stage == "after_resize":
262
+ image = fill_transparent_bg_with(image, transparent_bg_config)
263
+ padding_left = (resize_to[0] - new_width) // 2
264
+ padding_right = resize_to[0] - new_width - padding_left
265
+ padding_top = (resize_to[1] - new_height) // 2
266
+ padding_bottom = resize_to[1] - new_height - padding_top
267
+ image = np.asarray(image)
268
+ image = np.pad(
269
+ image,
270
+ ((padding_top, padding_bottom), (padding_left, padding_right),
271
+ (0, 0)),
272
+ mode="constant",
273
+ constant_values=0,
274
+ )
275
+ assert image.shape == (resize_to[1], resize_to[0], 3)
276
+
277
+ elif mode == "rescale_and_pad_to_rightbottom":
278
+ scale = min(resize_to[0] / image.width,
279
+ resize_to[1] / image.height, 1.0)
280
+ new_width = round(image.width * scale)
281
+ new_height = round(image.height * scale)
282
+ if new_width == 0 or new_height == 0:
283
+ if raise_error_for_ill_resize:
284
+ raise ValueError(
285
+ f"Invalid resize to: {resize_to}, from image size: {image.size}"
286
+ )
287
+ else:
288
+ return np.zeros((resize_to[1], resize_to[0], 3),
289
+ dtype=np.uint8)
290
+
291
+ image = image.resize((new_width, new_height),
292
+ resample=Image.Resampling.BICUBIC)
293
+ if transparent_bg_fill_stage == "after_resize":
294
+ image = fill_transparent_bg_with(image, transparent_bg_config)
295
+ padding_right = resize_to[0] - new_width
296
+ padding_bottom = resize_to[1] - new_height
297
+ image = np.asarray(image)
298
+ image = np.pad(
299
+ image,
300
+ ((0, padding_bottom), (0, padding_right), (0, 0)),
301
+ mode="constant",
302
+ constant_values=0,
303
+ )
304
+ assert image.shape == (resize_to[1], resize_to[0], 3)
305
+
306
+ else:
307
+ raise ValueError(f"Invalid mode: {mode}")
308
+
309
+ if isinstance(image, Image.Image):
310
+ return np.asarray(image)
311
+ else:
312
+ return image
313
+
314
+
315
+ def navit_patchify(pixel_values: np.ndarray,
316
+ patch_size: int) -> dict[str, np.ndarray]:
317
+ """Reshape the pixel values to a navit shape.
318
+
319
+ Args:
320
+ pixel_values: np.ndarray, shape (t, h, w, c)
321
+ patch_size: int
322
+
323
+ Returns:
324
+ dict[str, np.ndarray]
325
+ - patches: np.ndarray, shape (t * h//patch_size * w//patch_size, c, patch_size, patch_size)
326
+ - grid_thw: np.ndarray, (t, h//patch_size, w//patch_size)
327
+ """
328
+ T, H, W, C = pixel_values.shape
329
+ assert C == 3, "pixel_values must have 3 channels"
330
+
331
+ patches = pixel_values.reshape(T, H // patch_size, patch_size,
332
+ W // patch_size, patch_size, C)
333
+ # (T, H//patch_size, W//patch_size, C, patch_size, patch_size)
334
+ patches = patches.transpose(0, 1, 3, 5, 2, 4)
335
+ patches = patches.reshape(-1, C, patch_size, patch_size)
336
+ grid_thw = np.array([T, H // patch_size, W // patch_size])
337
+ return {"pixel_values": patches, "grid_thw": grid_thw}
338
+
339
+
340
+ def normalize(x: np.ndarray,
341
+ mean,
342
+ std_inv,
343
+ pixels_dtype: np.dtype = np.float32) -> np.ndarray:
344
+ """Normalize the image.
345
+
346
+ Args:
347
+ x: The image to normalize. The shape is (..., 3). The dtype is uint8. The range is [0, 255].
348
+ mean: The mean of the image.
349
+ std_inv: The inverse of the std of the image.
350
+ pixels_dtype: The dtype of the image.
351
+ Returns:
352
+ The normalized image. The shape is (..., 3). The dtype is determined by the pixels_dtype.
353
+ """
354
+ x = (x / 255.0).astype(pixels_dtype)
355
+ x -= mean
356
+ x *= std_inv
357
+ return x
358
+
359
+
360
+ def _to_tensor(data, **kwargs):
361
+ import torch
362
+
363
+ if isinstance(data, np.ndarray):
364
+ return torch.from_numpy(data).to(**kwargs)
365
+ elif isinstance(data, torch.Tensor):
366
+ return data.to(**kwargs)
367
+ elif isinstance(data, list):
368
+ return [_to_tensor(item, **kwargs) for item in data]
369
+ elif isinstance(data, tuple):
370
+ return tuple(_to_tensor(item, **kwargs) for item in data)
371
+ elif isinstance(data, dict):
372
+ return {k: _to_tensor(v, **kwargs) for k, v in data.items()}
373
+ elif data is None:
374
+ return None
375
+ else:
376
+ raise ValueError(f"Unsupported data type: {type(data)}")
model-00001-of-000096.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:be478de491408af1be42f4b9d79deb1c18d7011c4fba00a1c226e2d3035da5e5
3
+ size 2341216112
model-00002-of-000096.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:807ec91246906c355bb0b851dbb44a5d0366f45f02a43739d7e52e2b1d2f5ef9
3
+ size 16990911504
model-00003-of-000096.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7c97d5a3039f295c6a68364abf93a7b9480183a8bbd221942cae8a64f06f51de
3
+ size 16990911504
model-00004-of-000096.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:39b1267686d7412d875637b16f208c2d77e25cf35de0e282c87440a053200ae1
3
+ size 16567501776
model-00094-of-000096.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b9d74423d6aa7f303077ed53b835ab6e36b55642b6505e112f4bab8f466a464c
3
+ size 4697664072
model-00095-of-000096.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3ea817239c7a529fceebfa56de072b96e2b0759c03d2978d4697f3eed5bf0374
3
+ size 92289328
model-00096-of-000096.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:875232213a78ad4b03bb5acecf800f118209a48f4990bc42e8e50f81e1467344
3
+ size 802448352
model.safetensors.index.json ADDED
The diff for this file is too large to render. See raw diff
 
modeling_kimi.py ADDED
@@ -0,0 +1,1376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from collections.abc import Callable
3
+ from typing import Any
4
+
5
+ import torch
6
+ import torch.nn.functional as F
7
+ import transformers
8
+ from einops import rearrange, repeat
9
+ from packaging import version
10
+ from torch import nn
11
+ from transformers.activations import ACT2FN
12
+ from transformers.cache_utils import Cache
13
+ from transformers.generation import GenerationMixin
14
+ from transformers.masking_utils import create_causal_mask
15
+ from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
16
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
17
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
18
+ from transformers.processing_utils import Unpack
19
+ from transformers.pytorch_utils import ALL_LAYERNORM_LAYERS
20
+ from transformers.utils import TransformersKwargs, auto_docstring, can_return_tuple, logging
21
+ from transformers.utils.generic import OutputRecorder, check_model_inputs
22
+
23
+ try:
24
+ from fla.modules import FusedRMSNormGated, ShortConvolution
25
+ from fla.ops.kda import chunk_kda, fused_recurrent_kda
26
+ # from fla.ops.kda.gate import fused_kda_gate # deprecated, gate is now computed inside chunk_kda/fused_recurrent_kda
27
+ from fla.ops.utils.index import prepare_cu_seqlens_from_mask, prepare_lens_from_mask
28
+ from fla.utils import tensor_cache
29
+ except ImportError:
30
+ raise ImportError("Plese run `pip install -U fla-core`")
31
+
32
+ from .configuration_kimi_k3 import KimiLinearConfig
33
+
34
+ assert version.parse(transformers.__version__) >= version.parse("4.56.0"), \
35
+ "Please upgrade transformers to >= 4.56.0"
36
+
37
+ logger = logging.get_logger(__name__)
38
+
39
+
40
+ # Register Moonshot-specific activation functions
41
+ class SituAndMul(nn.Module):
42
+ """
43
+ SituAndMul activation: beta * tanh(gate / beta) * sigmoid(gate) * up
44
+ When linear_beta is set, up is also transformed by linear_beta * tanh(up / linear_beta).
45
+ """
46
+
47
+ def __init__(self, beta: float = 1.0, linear_beta: float | None = None):
48
+ super().__init__()
49
+ self.beta = beta
50
+ self.linear_beta = linear_beta
51
+
52
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
53
+ d = x.shape[-1] // 2
54
+ gate = x[..., :d].to(torch.float32)
55
+ up = x[..., d:].to(torch.float32)
56
+ situ_a = self.beta * torch.tanh(gate / self.beta) * torch.sigmoid(gate)
57
+ if self.linear_beta is not None:
58
+ up = self.linear_beta * torch.tanh(up / self.linear_beta)
59
+ return (situ_a * up).to(x.dtype)
60
+
61
+
62
+ ACT2FN["situ"] = SituAndMul
63
+
64
+
65
+ def _get_situ_activation_params(config: KimiLinearConfig):
66
+ beta = getattr(config, "activation_situ_beta", None)
67
+ linear_beta = getattr(config, "activation_situ_linear_beta", None)
68
+ return beta or 1.0, linear_beta
69
+
70
+
71
+ def index_first_axis(x, indices):
72
+ other_shape = x.shape[1:]
73
+ second_dim = other_shape.numel()
74
+ return torch.gather(
75
+ rearrange(x, "b ... -> b (...)"), 0, repeat(indices, "z -> z d", d=second_dim),
76
+ ).reshape(-1, *other_shape)
77
+
78
+
79
+ def index_put_first_axis(x, indices, first_axis_dim):
80
+ y = torch.zeros(first_axis_dim, *x.shape[1:], device=x.device, dtype=x.dtype)
81
+ # TODO [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
82
+ y[indices] = x
83
+ # y.scatter_(0, repeat(indices, 'z -> z d', d=x.shape[1]), x)
84
+ return y
85
+
86
+
87
+ @tensor_cache
88
+ def get_unpad_data(
89
+ attention_mask: torch.Tensor,
90
+ ) -> tuple[torch.Tensor, torch.Tensor, int]:
91
+ lens = prepare_lens_from_mask(attention_mask)
92
+ indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
93
+ max_seqlen_in_batch = lens.max().item()
94
+ cu_seqlens = prepare_cu_seqlens_from_mask(attention_mask)
95
+ return indices, cu_seqlens, max_seqlen_in_batch
96
+
97
+
98
+ def unpad_input(
99
+ q: torch.Tensor,
100
+ states: tuple[torch.Tensor],
101
+ attention_mask: torch.Tensor,
102
+ q_len: int,
103
+ keepdim: bool = False,
104
+ ):
105
+ indices_k, cu_seqlens_k, max_seqlen_in_batch_k = get_unpad_data(attention_mask)
106
+ batch_size, seq_len, *_ = states[0].shape
107
+
108
+ state = tuple(
109
+ index_first_axis(rearrange(s, "b s ... -> (b s) ..."), indices_k)
110
+ for s in states
111
+ )
112
+
113
+ if q_len == seq_len:
114
+ q = index_first_axis(rearrange(q, "b s ... -> (b s) ..."), indices_k)
115
+ cu_seqlens_q = cu_seqlens_k
116
+ max_seqlen_in_batch_q = max_seqlen_in_batch_k
117
+ indices_q = indices_k
118
+ elif q_len == 1:
119
+ max_seqlen_in_batch_q = 1
120
+ cu_seqlens_q = torch.arange(batch_size + 1, dtype=torch.int32, device=q.device)
121
+ indices_q = cu_seqlens_q[:-1]
122
+ q = q.squeeze(1)
123
+ else:
124
+ raise NotImplementedError("We only support either q_len == k_len (prefilling) or q_len == 1 (decoding)")
125
+
126
+ if keepdim:
127
+ q = q.unsqueeze(0)
128
+ state = tuple(s.unsqueeze(0) for s in state)
129
+
130
+ return (
131
+ q,
132
+ state,
133
+ indices_q,
134
+ (cu_seqlens_q, cu_seqlens_k),
135
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k),
136
+ )
137
+
138
+
139
+ def pad_input(
140
+ hidden_states: torch.Tensor,
141
+ indices: torch.LongTensor,
142
+ batch_size: int,
143
+ seq_len: int,
144
+ ) -> torch.Tensor:
145
+ output = index_put_first_axis(hidden_states, indices, batch_size * seq_len)
146
+ return rearrange(output, "(b s) ... -> b s ...", b=batch_size)
147
+
148
+
149
+ class KimiDynamicCache:
150
+ """
151
+ Dynamic cache for Kimi model.
152
+ Inspired by Qwen3-Next
153
+ """
154
+ is_compileable = False
155
+
156
+ def __init__(self, config: KimiLinearConfig):
157
+ super().__init__()
158
+ self.config = config
159
+
160
+ if config.linear_attn_config is not None:
161
+ self.layer_types = []
162
+ for i in range(config.num_hidden_layers):
163
+ if config.is_kda_layer(i):
164
+ self.layer_types.append("linear_attention")
165
+ else:
166
+ self.layer_types.append("full_attention")
167
+ else:
168
+ self.layer_types = ["full_attention"] * config.num_hidden_layers
169
+
170
+ self.transformer_layers = [
171
+ i for i in range(config.num_hidden_layers) if self.layer_types[i] == "full_attention"
172
+ ]
173
+
174
+ linear_layers = [i for i in range(
175
+ config.num_hidden_layers) if self.layer_types[i] == "linear_attention"]
176
+ self.last_linear_layer = linear_layers[-1] if linear_layers else -1
177
+
178
+ self.conv_states = [None for _ in range(config.num_hidden_layers)]
179
+ self.recurrent_states = [None for _ in range(config.num_hidden_layers)]
180
+ self.key_cache = [None for _ in range(config.num_hidden_layers)]
181
+ self.value_cache = [None for _ in range(config.num_hidden_layers)]
182
+
183
+ def __len__(self):
184
+ return len(self.layer_types)
185
+
186
+ def update(
187
+ self,
188
+ key_states: torch.Tensor,
189
+ value_states: torch.Tensor,
190
+ layer_idx: int,
191
+ cache_kwargs: dict[str, Any] | None = None,
192
+ ) -> tuple[torch.Tensor, torch.Tensor]:
193
+ if self.key_cache[layer_idx] is None:
194
+ self.key_cache[layer_idx] = key_states
195
+ self.value_cache[layer_idx] = value_states
196
+ else:
197
+ self.key_cache[layer_idx] = torch.cat(
198
+ [self.key_cache[layer_idx], key_states], dim=2)
199
+ self.value_cache[layer_idx] = torch.cat(
200
+ [self.value_cache[layer_idx], value_states], dim=2)
201
+
202
+ return self.key_cache[layer_idx], self.value_cache[layer_idx]
203
+
204
+ def reorder_cache(self, beam_idx: torch.LongTensor):
205
+ """Reorders the cache for beam search, given the selected beam indices."""
206
+ for layer_idx in range(len(self.key_cache)):
207
+ if self.key_cache[layer_idx] is not None:
208
+ device = self.key_cache[layer_idx].device
209
+ beam_idx = beam_idx.to(device)
210
+ self.key_cache[layer_idx] = self.key_cache[layer_idx].index_select(
211
+ 0, beam_idx)
212
+ self.value_cache[layer_idx] = self.value_cache[layer_idx].index_select(
213
+ 0, beam_idx)
214
+
215
+ if self.conv_states[layer_idx] is not None:
216
+ device = self.conv_states[layer_idx][0].device
217
+ beam_idx = beam_idx.to(device)
218
+ q_conv, k_conv, v_conv = self.conv_states[layer_idx]
219
+ self.conv_states[layer_idx] = (
220
+ q_conv.index_select(0, beam_idx),
221
+ k_conv.index_select(0, beam_idx),
222
+ v_conv.index_select(0, beam_idx),
223
+ )
224
+ self.recurrent_states[layer_idx] = self.recurrent_states[layer_idx].index_select(
225
+ 0, beam_idx)
226
+
227
+ def get_seq_length(self, layer_idx: int | None = 0) -> int:
228
+ """Returns the sequence length of the cached states. A layer index can be optionally passed."""
229
+ # take any layer that contains cache and not empty tensor
230
+ layer_idx = self.transformer_layers[0] if layer_idx not in self.transformer_layers else layer_idx
231
+ if len(self.key_cache) <= layer_idx or self.key_cache[layer_idx] is None:
232
+ return 0
233
+ return self.key_cache[layer_idx].shape[-2]
234
+
235
+ def get_mask_sizes(self, cache_position: torch.Tensor, layer_idx: int) -> tuple[int, int]:
236
+ """
237
+ Return a tuple (kv_length, kv_offset) corresponding to the length and offset that will be returned for
238
+ the given layer at `layer_idx`.
239
+ The masks are then prepared according to the given lengths (kv_length, kv_offset) and patterns for each layer.
240
+ """
241
+ kv_offset = 0
242
+ query_length = cache_position.shape[0]
243
+ past_seen_tokens = self.get_seq_length(layer_idx)
244
+ kv_length = query_length + past_seen_tokens
245
+ return kv_length, kv_offset
246
+
247
+ @property
248
+ def has_previous_state(self):
249
+ """We have a previous state if the last linear (conv) layer was already updated."""
250
+ if self.last_linear_layer == -1:
251
+ return False
252
+ return self.conv_states[self.last_linear_layer] is not None
253
+
254
+
255
+ class KimiRMSNorm(nn.Module):
256
+ def __init__(self, hidden_size, eps=1e-6):
257
+ """
258
+ KimiRMSNorm is equivalent to T5LayerNorm
259
+ """
260
+ super().__init__()
261
+ self.weight = nn.Parameter(torch.ones(hidden_size))
262
+ self.variance_epsilon = eps
263
+
264
+ def forward(self, hidden_states):
265
+ input_dtype = hidden_states.dtype
266
+ hidden_states = hidden_states.to(torch.float32)
267
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
268
+ hidden_states = hidden_states * \
269
+ torch.rsqrt(variance + self.variance_epsilon)
270
+ return self.weight * hidden_states.to(input_dtype)
271
+
272
+
273
+ ALL_LAYERNORM_LAYERS.append(KimiRMSNorm)
274
+
275
+
276
+ class KimiBlockSparseMLP(nn.Module):
277
+ def __init__(self, config: KimiLinearConfig, hidden_size=None, intermediate_size=None):
278
+ super().__init__()
279
+ self.config = config
280
+ self.ffn_dim = config.intermediate_size if intermediate_size is None else intermediate_size
281
+ self.hidden_dim = config.hidden_size if hidden_size is None else hidden_size
282
+
283
+ self.w1 = nn.Linear(self.hidden_dim, self.ffn_dim, bias=False) # gate
284
+ self.w2 = nn.Linear(self.ffn_dim, self.hidden_dim, bias=False) # down
285
+ self.w3 = nn.Linear(self.hidden_dim, self.ffn_dim, bias=False) # up
286
+
287
+ if config.hidden_act == "situ":
288
+ beta, linear_beta = _get_situ_activation_params(config)
289
+ self.act_fn = SituAndMul(
290
+ beta=beta,
291
+ linear_beta=linear_beta,
292
+ )
293
+ else:
294
+ self.act_fn = ACT2FN[config.hidden_act]
295
+
296
+ def forward(self, hidden_states):
297
+ if self.config.hidden_act == "situ":
298
+ gate_up = torch.cat([self.w1(hidden_states), self.w3(hidden_states)], dim=-1)
299
+ current_hidden_states = self.act_fn(gate_up)
300
+ else:
301
+ current_hidden_states = self.act_fn(
302
+ self.w1(hidden_states)) * self.w3(hidden_states)
303
+ current_hidden_states = self.w2(current_hidden_states)
304
+ return current_hidden_states
305
+
306
+
307
+ class KimiMLP(nn.Module):
308
+ def __init__(self, config: KimiLinearConfig, hidden_size=None, intermediate_size=None):
309
+ super().__init__()
310
+ self.config = config
311
+ self.hidden_size = config.hidden_size if hidden_size is None else hidden_size
312
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
313
+ self.gate_proj = nn.Linear(
314
+ self.hidden_size, self.intermediate_size, bias=False)
315
+ self.up_proj = nn.Linear(
316
+ self.hidden_size, self.intermediate_size, bias=False)
317
+ self.down_proj = nn.Linear(
318
+ self.intermediate_size, self.hidden_size, bias=False)
319
+ if config.hidden_act == "situ":
320
+ beta, linear_beta = _get_situ_activation_params(config)
321
+ self.act_fn = SituAndMul(
322
+ beta=beta,
323
+ linear_beta=linear_beta,
324
+ )
325
+ else:
326
+ self.act_fn = ACT2FN[config.hidden_act]
327
+
328
+ def forward(self, x):
329
+ if self.config.hidden_act == "situ":
330
+ gate_up = torch.cat([self.gate_proj(x), self.up_proj(x)], dim=-1)
331
+ down_proj = self.down_proj(self.act_fn(gate_up))
332
+ else:
333
+ down_proj = self.down_proj(self.act_fn(
334
+ self.gate_proj(x)) * self.up_proj(x))
335
+ return down_proj
336
+
337
+
338
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
339
+ """
340
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
341
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
342
+ """
343
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
344
+ if n_rep == 1:
345
+ return hidden_states
346
+ hidden_states = hidden_states[:, :, None, :, :].expand(
347
+ batch, num_key_value_heads, n_rep, slen, head_dim)
348
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
349
+
350
+
351
+ def eager_attention_forward(
352
+ module: nn.Module,
353
+ query: torch.Tensor,
354
+ key: torch.Tensor,
355
+ value: torch.Tensor,
356
+ attention_mask: torch.Tensor | None,
357
+ scaling: float,
358
+ dropout: float = 0.0,
359
+ **kwargs: Unpack[TransformersKwargs],
360
+ ):
361
+ key_states = repeat_kv(key, module.num_key_value_groups)
362
+ value_states = repeat_kv(value, module.num_key_value_groups)
363
+
364
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
365
+ if attention_mask is not None:
366
+ causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
367
+ attn_weights = attn_weights + causal_mask
368
+
369
+ attn_weights = nn.functional.softmax(
370
+ attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
371
+ attn_weights = nn.functional.dropout(
372
+ attn_weights, p=dropout, training=module.training)
373
+ attn_output = torch.matmul(attn_weights, value_states)
374
+ attn_output = attn_output.transpose(1, 2).contiguous()
375
+
376
+ return attn_output, attn_weights
377
+
378
+
379
+ class KimiMLAAttention(nn.Module):
380
+ """
381
+ Multi-Latent Attention adapted from deepseek-v3
382
+ """
383
+
384
+ def __init__(self, config: KimiLinearConfig, layer_idx: int):
385
+ nn.Module.__init__(self)
386
+ self.config = config
387
+ self.layer_idx = layer_idx
388
+ self.hidden_size = config.hidden_size
389
+ self.num_heads = config.num_attention_heads
390
+ self.num_key_value_heads = config.num_key_value_heads
391
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
392
+
393
+ self.attention_dropout = getattr(config, "attention_dropout", 0.0)
394
+
395
+ try:
396
+ self.q_lora_rank = config.q_lora_rank
397
+ self.qk_rope_head_dim = config.qk_rope_head_dim
398
+ self.kv_lora_rank = config.kv_lora_rank
399
+ self.v_head_dim = config.v_head_dim
400
+ self.qk_nope_head_dim = config.qk_nope_head_dim
401
+ self.q_head_dim = self.qk_nope_head_dim + self.qk_rope_head_dim
402
+ self.use_nope = config.mla_use_nope
403
+ self.scaling = self.q_head_dim ** (-0.5)
404
+ except Exception as e:
405
+ raise ValueError(
406
+ f"Kimi MLA config is not found or not properly formatted: {e}")
407
+
408
+ if self.q_lora_rank is not None:
409
+ self.q_a_proj = nn.Linear(
410
+ self.hidden_size, self.q_lora_rank, bias=False,
411
+ )
412
+ self.q_a_layernorm = KimiRMSNorm(self.q_lora_rank)
413
+ self.q_b_proj = nn.Linear(
414
+ self.q_lora_rank,
415
+ self.num_heads * self.q_head_dim,
416
+ bias=False,
417
+ )
418
+ else:
419
+ self.q_proj = nn.Linear(
420
+ self.hidden_size, self.num_heads * self.q_head_dim, bias=False,
421
+ )
422
+ self.kv_a_proj_with_mqa = nn.Linear(
423
+ self.hidden_size,
424
+ self.kv_lora_rank + self.qk_rope_head_dim,
425
+ bias=False,
426
+ )
427
+ self.kv_a_layernorm = KimiRMSNorm(self.kv_lora_rank)
428
+ self.kv_b_proj = nn.Linear(
429
+ self.kv_lora_rank,
430
+ self.num_heads
431
+ * (self.q_head_dim - self.qk_rope_head_dim + self.v_head_dim),
432
+ bias=False,
433
+ )
434
+ self.o_proj = nn.Linear(
435
+ self.num_heads * self.v_head_dim,
436
+ self.hidden_size,
437
+ bias=False,
438
+ )
439
+ self.is_causal = True
440
+ assert self.use_nope
441
+
442
+ self.use_output_gate = getattr(config, "mla_use_output_gate", False)
443
+ if self.use_output_gate:
444
+ projection_size = self.num_heads * self.v_head_dim
445
+ self.g_proj = nn.Linear(self.hidden_size, projection_size, bias=False)
446
+
447
+ self.rotary_emb = None
448
+
449
+ def forward(
450
+ self,
451
+ hidden_states: torch.Tensor,
452
+ attention_mask: torch.Tensor | None = None,
453
+ position_ids: torch.LongTensor | None = None,
454
+ past_key_values: Cache | None = None,
455
+ **kwargs,
456
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
457
+ batch_size, seq_length = hidden_states.shape[:-1]
458
+ query_shape = (batch_size, seq_length, -1, self.q_head_dim)
459
+ key_shape = (batch_size, seq_length, -1,
460
+ self.qk_nope_head_dim + self.v_head_dim)
461
+
462
+ if self.q_lora_rank is not None:
463
+ q_states = self.q_b_proj(self.q_a_layernorm(self.q_a_proj(hidden_states)))
464
+ else:
465
+ q_states = self.q_proj(hidden_states)
466
+ q_states = q_states.view(query_shape).transpose(1, 2)
467
+ q_pass, q_rot = torch.split(
468
+ q_states, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
469
+
470
+ compressed_kv = self.kv_a_proj_with_mqa(hidden_states)
471
+ k_pass, k_rot = torch.split(
472
+ compressed_kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
473
+
474
+ k_pass = self.kv_b_proj(self.kv_a_layernorm(
475
+ k_pass)).view(key_shape).transpose(1, 2)
476
+ k_pass, value_states = torch.split(
477
+ k_pass, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
478
+
479
+ k_rot = k_rot.view(batch_size, 1, seq_length, self.qk_rope_head_dim)
480
+
481
+ k_rot = k_rot.expand(*k_pass.shape[:-1], -1)
482
+
483
+ query_states = torch.cat((q_pass, q_rot), dim=-1)
484
+ key_states = torch.cat((k_pass, k_rot), dim=-1)
485
+
486
+ if past_key_values is not None:
487
+ key_states, value_states = past_key_values.update(
488
+ key_states, value_states, self.layer_idx)
489
+
490
+ if self.config._attn_implementation == "flash_attention_2" and self.q_head_dim != self.v_head_dim:
491
+ value_states = F.pad(
492
+ value_states, [0, self.q_head_dim - self.v_head_dim])
493
+
494
+ attention_interface: Callable = eager_attention_forward
495
+ if self.config._attn_implementation != "eager":
496
+ attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
497
+
498
+ attn_output, _ = attention_interface(
499
+ self,
500
+ query_states,
501
+ key_states,
502
+ value_states,
503
+ attention_mask,
504
+ dropout=0.0 if not self.training else self.attention_dropout,
505
+ scaling=self.scaling,
506
+ **kwargs,
507
+ )
508
+
509
+ if self.config._attn_implementation == "flash_attention_2" and self.q_head_dim != self.v_head_dim:
510
+ attn_output = attn_output[:, :, :, : self.v_head_dim]
511
+
512
+ attn_output = attn_output.reshape(
513
+ batch_size, seq_length, -1).contiguous()
514
+ if self.use_output_gate:
515
+ g = self.g_proj(hidden_states).sigmoid()
516
+ attn_output = attn_output * g
517
+ attn_output = self.o_proj(attn_output)
518
+ return attn_output
519
+
520
+
521
+ class KimiDeltaAttention(nn.Module):
522
+ def __init__(self, config: KimiLinearConfig, layer_idx: int):
523
+ super().__init__()
524
+ self.config = config
525
+ self.mode = "chunk"
526
+
527
+ self.hidden_size = config.hidden_size
528
+ self.conv_size = config.linear_attn_config["short_conv_kernel_size"]
529
+ self.head_dim = config.linear_attn_config["head_dim"]
530
+ self.num_heads = config.linear_attn_config["num_heads"]
531
+ self.head_k_dim = self.head_dim
532
+ self.num_k_heads = self.num_heads
533
+
534
+ self.layer_idx = layer_idx
535
+
536
+ assert self.mode in [
537
+ 'chunk', 'fused_recurrent'], f"Not suppoerted mode `{self.mode}`."
538
+
539
+ projection_k_size = self.head_k_dim * self.num_k_heads
540
+ projection_size = self.head_dim * self.num_heads
541
+
542
+ self.q_proj = nn.Linear(
543
+ self.hidden_size, projection_k_size, bias=False)
544
+ self.k_proj = nn.Linear(
545
+ self.hidden_size, projection_k_size, bias=False)
546
+ self.v_proj = nn.Linear(self.hidden_size, projection_size, bias=False)
547
+
548
+ self.q_conv1d = ShortConvolution(
549
+ hidden_size=projection_k_size,
550
+ kernel_size=self.conv_size,
551
+ activation='silu',
552
+ )
553
+ self.k_conv1d = ShortConvolution(
554
+ hidden_size=projection_k_size,
555
+ kernel_size=self.conv_size,
556
+ activation='silu',
557
+ )
558
+ self.v_conv1d = ShortConvolution(
559
+ hidden_size=projection_size,
560
+ kernel_size=self.conv_size,
561
+ activation='silu',
562
+ )
563
+
564
+ self.A_log = torch.nn.Parameter(torch.log(torch.empty(
565
+ self.num_heads, dtype=torch.float32).uniform_(1, 16)))
566
+
567
+ self.f_a_proj = nn.Linear(self.hidden_size, self.head_dim, bias=False)
568
+ self.f_b_proj = nn.Linear(self.head_dim, projection_size, bias=False)
569
+
570
+ self.dt_bias = nn.Parameter(
571
+ torch.empty(projection_size, dtype=torch.float32))
572
+
573
+ self.b_proj = nn.Linear(self.hidden_size, self.num_heads, bias=False)
574
+
575
+ self.use_full_rank_gate = config.linear_attn_config.get("use_full_rank_gate", False)
576
+ self.gate_lower_bound = config.linear_attn_config.get("gate_lower_bound", None)
577
+ if self.use_full_rank_gate:
578
+ self.g_proj = nn.Linear(self.hidden_size, projection_size, bias=False)
579
+ else:
580
+ self.g_a_proj = nn.Linear(self.hidden_size, self.head_dim, bias=False)
581
+ self.g_b_proj = nn.Linear(self.head_dim, projection_size, bias=False)
582
+
583
+ self.o_norm = FusedRMSNormGated(
584
+ self.head_dim, eps=config.rms_norm_eps, activation='sigmoid')
585
+ self.o_proj = nn.Linear(projection_size, self.hidden_size, bias=False)
586
+
587
+ def forward(
588
+ self,
589
+ hidden_states: torch.Tensor,
590
+ attention_mask: torch.Tensor | None = None,
591
+ cache_params: KimiDynamicCache | None = None,
592
+ **kwargs: Unpack[dict],
593
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
594
+ if attention_mask is not None:
595
+ if attention_mask.dim() != 2:
596
+ attention_mask = kwargs.get("padding_mask")
597
+
598
+ if attention_mask is not None and attention_mask.dim() != 2:
599
+ raise ValueError(
600
+ "attention_mask must be a 0-1 matrix of shape [batch_size, seq_len] "
601
+ "(0 = padding). 3D masks are not supported here.",
602
+ )
603
+ use_cache = cache_params is not None
604
+ batch_size, q_len, _ = hidden_states.shape
605
+ mode = 'fused_recurrent' if use_cache and q_len == 1 else self.mode
606
+ if self.training:
607
+ assert mode == 'chunk', "Only chunk mode is supported in training."
608
+
609
+ cu_seqlens = kwargs.get('cu_seqlens')
610
+ indices = None
611
+ if attention_mask is not None:
612
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
613
+ hidden_states = index_first_axis(
614
+ rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
615
+
616
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
617
+ recurrent_state = None
618
+ if cache_params is not None:
619
+ if cache_params.conv_states[self.layer_idx] is not None:
620
+ conv_state_q, conv_state_k, conv_state_v = cache_params.conv_states[
621
+ self.layer_idx]
622
+ recurrent_state = cache_params.recurrent_states[self.layer_idx]
623
+
624
+ q_proj_states = self.q_proj(hidden_states)
625
+ k_proj_states = self.k_proj(hidden_states)
626
+ v_proj_states = self.v_proj(hidden_states)
627
+ q, conv_state_q = self.q_conv1d(
628
+ x=q_proj_states,
629
+ cache=conv_state_q,
630
+ output_final_state=use_cache,
631
+ cu_seqlens=cu_seqlens,
632
+ )
633
+ k, conv_state_k = self.k_conv1d(
634
+ x=k_proj_states,
635
+ cache=conv_state_k,
636
+ output_final_state=use_cache,
637
+ cu_seqlens=cu_seqlens,
638
+ )
639
+ v, conv_state_v = self.v_conv1d(
640
+ x=v_proj_states,
641
+ cache=conv_state_v,
642
+ output_final_state=use_cache,
643
+ cu_seqlens=cu_seqlens,
644
+ )
645
+ g = self.f_b_proj(self.f_a_proj(hidden_states))
646
+ g = rearrange(g, '... (h d) -> ... h d', d=self.head_dim)
647
+ beta = self.b_proj(hidden_states).float()
648
+
649
+ q, k = map(lambda x: rearrange(
650
+ x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
651
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
652
+
653
+ if mode == 'chunk':
654
+ o, recurrent_state = chunk_kda(
655
+ q=q,
656
+ k=k,
657
+ v=v,
658
+ g=g,
659
+ beta=beta,
660
+ A_log=self.A_log,
661
+ dt_bias=self.dt_bias,
662
+ initial_state=recurrent_state,
663
+ output_final_state=True,
664
+ use_qk_l2norm_in_kernel=True,
665
+ use_gate_in_kernel=True,
666
+ use_beta_sigmoid_in_kernel=True,
667
+ safe_gate=self.gate_lower_bound is not None,
668
+ lower_bound=self.gate_lower_bound,
669
+ transpose_state_layout=True,
670
+ cu_seqlens=cu_seqlens,
671
+ )
672
+ else:
673
+ o, recurrent_state = fused_recurrent_kda(
674
+ q=q,
675
+ k=k,
676
+ v=v,
677
+ g=g,
678
+ beta=beta,
679
+ A_log=self.A_log,
680
+ dt_bias=self.dt_bias,
681
+ initial_state=recurrent_state,
682
+ output_final_state=True,
683
+ use_qk_l2norm_in_kernel=True,
684
+ use_gate_in_kernel=True,
685
+ use_beta_sigmoid_in_kernel=True,
686
+ lower_bound=self.gate_lower_bound,
687
+ transpose_state_layout=True,
688
+ cu_seqlens=cu_seqlens,
689
+ )
690
+ if cache_params is not None:
691
+ cache_params.recurrent_states[self.layer_idx] = recurrent_state
692
+ cache_params.conv_states[self.layer_idx] = (
693
+ conv_state_q, conv_state_k, conv_state_v)
694
+
695
+ if self.use_full_rank_gate:
696
+ g = self.g_proj(hidden_states)
697
+ else:
698
+ g = self.g_b_proj(self.g_a_proj(hidden_states))
699
+ g = rearrange(g, '... (h d) -> ... h d', d=self.head_dim)
700
+ o = self.o_norm(o, g)
701
+
702
+ o = rearrange(o, 'b t h d -> b t (h d)')
703
+ o = self.o_proj(o)
704
+ if attention_mask is not None:
705
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
706
+
707
+ return o
708
+
709
+
710
+ class KimiMoEGate(nn.Module):
711
+ """
712
+ MoEGate adapted from Deepseek-V3.
713
+ Parameter correspondences:
714
+ num_experts -> n_routed_experts
715
+ num_experts_per_token -> num_experts_per_tok
716
+ num_expert_group -> n_group
717
+ moe_router_activation_func -> scoring_func
718
+ """
719
+
720
+ def __init__(self, config: KimiLinearConfig):
721
+ super().__init__()
722
+ self.config = config
723
+ self.top_k = config.num_experts_per_token
724
+ self.num_experts = config.num_experts
725
+ self.routed_scaling_factor = config.routed_scaling_factor
726
+ self.moe_router_activation_func = config.moe_router_activation_func
727
+ self.num_expert_group = getattr(config, "num_expert_group", 1)
728
+ self.topk_group = getattr(config, "topk_group", 1)
729
+
730
+ # topk selection algorithm
731
+ self.moe_renormalize = config.moe_renormalize
732
+ self.gating_dim = config.hidden_size
733
+ self.weight = nn.Parameter(
734
+ torch.empty((self.num_experts, self.gating_dim)),
735
+ )
736
+
737
+ self.e_score_correction_bias = nn.Parameter(
738
+ torch.empty(self.num_experts),
739
+ )
740
+ self.reset_parameters()
741
+
742
+ def reset_parameters(self) -> None:
743
+ import torch.nn.init as init
744
+
745
+ init.kaiming_uniform_(self.weight, a=math.sqrt(5))
746
+
747
+ def forward(self, hidden_states):
748
+ bsz, seq_len, h = hidden_states.shape
749
+ # compute gating score
750
+ hidden_states = hidden_states.view(-1, h)
751
+ logits = F.linear(
752
+ hidden_states.type(torch.float32), self.weight.type(
753
+ torch.float32), None,
754
+ )
755
+ if self.moe_router_activation_func == "sigmoid":
756
+ scores = logits.sigmoid()
757
+ elif self.moe_router_activation_func == "softmax":
758
+ scores = logits.softmax(dim=1)
759
+ else:
760
+ raise NotImplementedError(
761
+ f"insupportable scoring function for MoE gating: {self.moe_router_activation_func}",
762
+ )
763
+
764
+ # select top-k experts
765
+ assert not self.training
766
+ scores = scores.view(bsz * seq_len, -1)
767
+ scores_for_choice = scores + self.e_score_correction_bias.unsqueeze(0)
768
+ if self.num_expert_group > 1 and self.num_expert_group > self.topk_group:
769
+ group_scores = (
770
+ scores_for_choice.view(
771
+ bsz * seq_len, self.num_expert_group, -1).topk(2, dim=-1)[0].sum(dim=-1)
772
+ ) # [n, num_expert_group]
773
+ group_idx = torch.topk(
774
+ group_scores, k=self.topk_group, dim=-1, sorted=False,
775
+ )[
776
+ 1
777
+ ] # [n, top_k_group]
778
+ group_mask = torch.zeros_like(group_scores) # [n, num_expert_group]
779
+ group_mask.scatter_(1, group_idx, 1) # [n, num_expert_group]
780
+ score_mask = (
781
+ group_mask.unsqueeze(-1)
782
+ .expand(
783
+ bsz * seq_len, self.num_expert_group, self.num_experts // self.num_expert_group,
784
+ )
785
+ .reshape(bsz * seq_len, -1)
786
+ ) # [n, e]
787
+ tmp_scores = scores_for_choice.masked_fill(
788
+ ~score_mask.bool(), float("-inf")) # [n, e]
789
+ else:
790
+ tmp_scores = scores_for_choice
791
+ _, topk_idx = torch.topk(
792
+ tmp_scores, k=self.top_k, dim=-1, sorted=False,
793
+ )
794
+ topk_weight = scores.gather(1, topk_idx)
795
+
796
+ # norm gate to sum 1
797
+ if self.top_k > 1 and self.moe_renormalize:
798
+ denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20
799
+ topk_weight = topk_weight / denominator
800
+ # must multiply the scaling factor
801
+ topk_weight = topk_weight * self.routed_scaling_factor
802
+
803
+ return topk_idx, topk_weight
804
+
805
+
806
+ class KimiSparseMoeBlock(nn.Module):
807
+ """
808
+ Adapted from Deepseek-V3's MOE implementation
809
+ The namings are consistent with Kimi's version.
810
+ """
811
+
812
+ def __init__(self, config: KimiLinearConfig):
813
+ super().__init__()
814
+ self.config = config
815
+ self.hidden_dim = config.hidden_size
816
+ self.num_experts = config.num_experts
817
+ self.top_k = config.num_experts_per_token
818
+ self.moe_renormalize = config.moe_renormalize
819
+
820
+ self.use_latent_moe = getattr(config, "routed_expert_hidden_size", None) is not None
821
+ self.moe_hidden_size = (
822
+ config.routed_expert_hidden_size
823
+ if self.use_latent_moe else config.hidden_size
824
+ )
825
+ self.latent_moe_use_norm = getattr(config, "latent_moe_use_norm", False)
826
+
827
+ self.ep_size = 1
828
+ self.experts_per_rank = config.num_experts
829
+ self.ep_rank = 0
830
+ self.experts = nn.ModuleList(
831
+ [
832
+ KimiBlockSparseMLP(
833
+ config,
834
+ hidden_size=self.moe_hidden_size,
835
+ intermediate_size=config.moe_intermediate_size,
836
+ )
837
+ for _ in range(config.num_experts)
838
+ ],
839
+ )
840
+ self.gate = KimiMoEGate(config)
841
+ if config.num_shared_experts is not None:
842
+ intermediate_size = config.moe_intermediate_size * config.num_shared_experts
843
+ self.shared_experts = KimiMLP(
844
+ config=config, intermediate_size=intermediate_size,
845
+ )
846
+
847
+ if self.use_latent_moe:
848
+ self.routed_expert_down_proj = nn.Linear(
849
+ config.hidden_size, self.moe_hidden_size, bias=False,
850
+ )
851
+ self.routed_expert_up_proj = nn.Linear(
852
+ self.moe_hidden_size, config.hidden_size, bias=False,
853
+ )
854
+ if self.latent_moe_use_norm:
855
+ self.routed_expert_norm = KimiRMSNorm(
856
+ self.moe_hidden_size, eps=config.rms_norm_eps,
857
+ )
858
+
859
+ def forward(self, hidden_states):
860
+ identity = hidden_states
861
+ orig_shape = hidden_states.shape
862
+ topk_idx, topk_weight = self.gate(hidden_states)
863
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
864
+
865
+ if self.use_latent_moe:
866
+ hidden_states = self.routed_expert_down_proj(hidden_states)
867
+
868
+ if not self.training:
869
+ y = self.moe_infer(hidden_states, topk_idx, topk_weight)
870
+ else:
871
+ raise NotImplementedError("Training mode is not supported in KimiSparseMoeBlock")
872
+
873
+ if self.use_latent_moe:
874
+ if self.latent_moe_use_norm:
875
+ y = self.routed_expert_norm(y)
876
+ y = self.routed_expert_up_proj(y)
877
+
878
+ y = y.view(*orig_shape)
879
+
880
+ if self.config.num_shared_experts is not None:
881
+ y = y + self.shared_experts(identity)
882
+ return y
883
+
884
+ @torch.no_grad()
885
+ def moe_infer(self, x, topk_ids, topk_weight):
886
+ cnts = topk_ids.new_zeros((topk_ids.shape[0], len(self.experts)))
887
+ cnts.scatter_(1, topk_ids, 1)
888
+ tokens_per_expert = cnts.sum(dim=0)
889
+ idxs = topk_ids.view(-1).argsort()
890
+ sorted_tokens = x[idxs // topk_ids.shape[1]]
891
+
892
+ tokens_per_expert = tokens_per_expert.cpu().numpy()
893
+
894
+ outputs = []
895
+ start_idx = 0
896
+ for i, num_tokens in enumerate(tokens_per_expert):
897
+ end_idx = start_idx + num_tokens
898
+ if num_tokens == 0:
899
+ continue
900
+ expert = self.experts[i + self.ep_rank * self.experts_per_rank]
901
+ tokens_for_this_expert = sorted_tokens[start_idx:end_idx]
902
+ expert_out = expert(tokens_for_this_expert)
903
+ outputs.append(expert_out)
904
+ start_idx = end_idx
905
+
906
+ outs = torch.cat(outputs, dim=0) if len(
907
+ outputs) else sorted_tokens.new_empty(0)
908
+
909
+ new_x = torch.empty_like(outs)
910
+ new_x[idxs] = outs
911
+ final_out = (
912
+ new_x.view(*topk_ids.shape, -1)
913
+ .type(topk_weight.dtype)
914
+ .mul_(topk_weight.unsqueeze(dim=-1))
915
+ .sum(dim=1)
916
+ .type(new_x.dtype)
917
+ )
918
+ return final_out
919
+
920
+
921
+ class KimiDecoderLayer(nn.Module):
922
+ def __init__(self, config: KimiLinearConfig, layer_idx: int):
923
+ super().__init__()
924
+ self.hidden_size = config.hidden_size
925
+ self.config = config
926
+ self.layer_idx = layer_idx
927
+ if config.is_kda_layer(layer_idx):
928
+ self.is_linear_attn = True
929
+ self.self_attn = KimiDeltaAttention(
930
+ config=config, layer_idx=layer_idx)
931
+ elif config.is_mla:
932
+ self.is_linear_attn = False
933
+ self.self_attn = KimiMLAAttention(
934
+ config=config, layer_idx=layer_idx)
935
+ else:
936
+ raise NotImplementedError
937
+ if (
938
+ config.num_experts is not None
939
+ and layer_idx >= config.first_k_dense_replace
940
+ and layer_idx % getattr(config, "moe_layer_freq", 1) == 0
941
+ ):
942
+ self.block_sparse_moe = KimiSparseMoeBlock(config)
943
+ else:
944
+ self.mlp = KimiMLP(config)
945
+ self.input_layernorm = KimiRMSNorm(
946
+ config.hidden_size, eps=config.rms_norm_eps)
947
+ self.post_attention_layernorm = KimiRMSNorm(
948
+ config.hidden_size, eps=config.rms_norm_eps)
949
+
950
+ # Attention residual
951
+ self.use_attn_residuals = getattr(config, "attn_res_block_size", None) is not None
952
+ if self.use_attn_residuals:
953
+ self.attn_res_block_size = config.attn_res_block_size
954
+ self.self_attention_res_norm = KimiRMSNorm(
955
+ config.hidden_size, eps=config.rms_norm_eps)
956
+ self.mlp_res_norm = KimiRMSNorm(
957
+ config.hidden_size, eps=config.rms_norm_eps)
958
+ self.self_attention_res_proj = nn.Linear(
959
+ config.hidden_size, 1, bias=False)
960
+ self.mlp_res_proj = nn.Linear(
961
+ config.hidden_size, 1, bias=False)
962
+
963
+ def forward(
964
+ self,
965
+ hidden_states: torch.Tensor,
966
+ attention_mask: torch.Tensor | None = None,
967
+ position_ids: torch.LongTensor | None = None,
968
+ past_key_values: tuple[torch.Tensor] | None = None,
969
+ output_attentions: bool | None = False,
970
+ use_cache: bool | None = False,
971
+ block_residual: torch.Tensor | None = None,
972
+ **kwargs: Unpack[FlashAttentionKwargs],
973
+ ):
974
+ if self.use_attn_residuals:
975
+ return self._forward_attn_residual(
976
+ hidden_states, attention_mask, position_ids,
977
+ past_key_values, output_attentions, use_cache,
978
+ block_residual, **kwargs)
979
+
980
+ residual = hidden_states
981
+
982
+ hidden_states = self.input_layernorm(hidden_states)
983
+
984
+ # Self Attention
985
+ if self.is_linear_attn is False:
986
+ hidden_states = self.self_attn(
987
+ hidden_states=hidden_states,
988
+ attention_mask=attention_mask,
989
+ position_ids=position_ids,
990
+ past_key_values=past_key_values,
991
+ output_attentions=output_attentions,
992
+ use_cache=use_cache,
993
+ **kwargs,
994
+ )
995
+ else:
996
+ hidden_states = self.self_attn(
997
+ hidden_states=hidden_states,
998
+ attention_mask=attention_mask,
999
+ cache_params=past_key_values,
1000
+ output_attentions=output_attentions,
1001
+ use_cache=use_cache,
1002
+ **kwargs,
1003
+ )
1004
+ hidden_states = residual + hidden_states
1005
+
1006
+ # Fully Connected
1007
+ residual = hidden_states
1008
+ hidden_states = self.post_attention_layernorm(hidden_states)
1009
+ if hasattr(self, "block_sparse_moe"):
1010
+ hidden_states = self.block_sparse_moe(hidden_states)
1011
+ else:
1012
+ hidden_states = self.mlp(hidden_states)
1013
+ hidden_states = residual + hidden_states
1014
+
1015
+ return hidden_states
1016
+
1017
+ def _forward_attn_residual(
1018
+ self,
1019
+ hidden_states: torch.Tensor,
1020
+ attention_mask: torch.Tensor | None = None,
1021
+ position_ids: torch.LongTensor | None = None,
1022
+ past_key_values: tuple[torch.Tensor] | None = None,
1023
+ output_attentions: bool | None = False,
1024
+ use_cache: bool | None = False,
1025
+ block_residual: torch.Tensor | None = None,
1026
+ **kwargs: Unpack[FlashAttentionKwargs],
1027
+ ):
1028
+ batch_size, seq_len, hidden_size = hidden_states.shape
1029
+ prefix_sum = hidden_states
1030
+
1031
+ if block_residual is not None and block_residual.shape[1] > 0:
1032
+ hidden_states = _apply_attn_res(
1033
+ prefix_sum.view(-1, hidden_size),
1034
+ block_residual,
1035
+ self.self_attention_res_proj,
1036
+ self.self_attention_res_norm,
1037
+ ).view(batch_size, seq_len, hidden_size)
1038
+
1039
+ if self.layer_idx % self.attn_res_block_size == 0:
1040
+ block_residual = torch.cat(
1041
+ [block_residual, prefix_sum.view(-1, hidden_size).unsqueeze(1)], dim=1)
1042
+ prefix_sum = None
1043
+
1044
+ hidden_states = self.input_layernorm(hidden_states)
1045
+
1046
+ # Self Attention
1047
+ if self.is_linear_attn is False:
1048
+ hidden_states = self.self_attn(
1049
+ hidden_states=hidden_states,
1050
+ attention_mask=attention_mask,
1051
+ position_ids=position_ids,
1052
+ past_key_values=past_key_values,
1053
+ output_attentions=output_attentions,
1054
+ use_cache=use_cache,
1055
+ **kwargs,
1056
+ )
1057
+ else:
1058
+ hidden_states = self.self_attn(
1059
+ hidden_states=hidden_states,
1060
+ attention_mask=attention_mask,
1061
+ cache_params=past_key_values,
1062
+ output_attentions=output_attentions,
1063
+ use_cache=use_cache,
1064
+ **kwargs,
1065
+ )
1066
+
1067
+ if prefix_sum is not None:
1068
+ prefix_sum = prefix_sum + hidden_states
1069
+ else:
1070
+ prefix_sum = hidden_states
1071
+
1072
+ hidden_states = _apply_attn_res(
1073
+ prefix_sum.view(-1, hidden_size),
1074
+ block_residual,
1075
+ self.mlp_res_proj,
1076
+ self.mlp_res_norm,
1077
+ ).view(batch_size, seq_len, hidden_size)
1078
+
1079
+ hidden_states = self.post_attention_layernorm(hidden_states)
1080
+ if hasattr(self, "block_sparse_moe"):
1081
+ hidden_states = self.block_sparse_moe(hidden_states)
1082
+ else:
1083
+ hidden_states = self.mlp(hidden_states)
1084
+
1085
+ if prefix_sum is None:
1086
+ prefix_sum = hidden_states
1087
+ else:
1088
+ prefix_sum = prefix_sum + hidden_states
1089
+
1090
+ return prefix_sum, block_residual
1091
+
1092
+
1093
+ class KimiPreTrainedModel(PreTrainedModel):
1094
+ config_class = KimiLinearConfig
1095
+ base_model_prefix = "model"
1096
+ supports_gradient_checkpointing = True
1097
+ _no_split_modules = ["KimiDecoderLayer"]
1098
+ _skip_keys_device_placement = "past_key_values"
1099
+ _supports_flash_attn_2 = True
1100
+ _can_record_outputs = {
1101
+ "router_logits": OutputRecorder(KimiBlockSparseMLP, index=1),
1102
+ "hidden_states": KimiDecoderLayer,
1103
+ "attentions": KimiMLAAttention,
1104
+ }
1105
+ _is_stateful = True
1106
+
1107
+ def _init_weights(self, module):
1108
+ std = self.config.initializer_range
1109
+ if isinstance(module, nn.Linear):
1110
+ module.weight.data.normal_(mean=0.0, std=std)
1111
+ if module.bias is not None:
1112
+ module.bias.data.zero_()
1113
+ elif isinstance(module, nn.Embedding):
1114
+ module.weight.data.normal_(mean=0.0, std=std)
1115
+ if module.padding_idx is not None:
1116
+ module.weight.data[module.padding_idx].zero_()
1117
+
1118
+
1119
+ def _apply_attn_res(prefix_sum, block_residual, proj, norm):
1120
+ """
1121
+ prefix_sum: (num_tokens, hidden_size)
1122
+ block_residual: (num_tokens, num_blocks, hidden_size)
1123
+ """
1124
+ v = torch.cat((block_residual, prefix_sum.unsqueeze(1)), dim=1)
1125
+ v_float = v.float()
1126
+ variance = v_float.pow(2).mean(-1, keepdim=True)
1127
+ k = v_float * torch.rsqrt(variance + norm.variance_epsilon)
1128
+ score_weight = norm.weight.float() * proj.weight.squeeze(0).float()
1129
+ scores = (k * score_weight).sum(-1)
1130
+ probs = scores.softmax(-1).unsqueeze(1)
1131
+ hidden_states = torch.matmul(probs, v_float).squeeze(1)
1132
+ return hidden_states.to(v.dtype)
1133
+
1134
+ class KimiLinearModel(KimiPreTrainedModel):
1135
+ def __init__(self, config: KimiLinearConfig):
1136
+ super().__init__(config)
1137
+ self.padding_idx = config.pad_token_id
1138
+ self.vocab_size = config.vocab_size
1139
+
1140
+ self.embed_tokens = nn.Embedding(
1141
+ config.vocab_size, config.hidden_size, self.padding_idx)
1142
+ self.layers = nn.ModuleList([KimiDecoderLayer(
1143
+ config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
1144
+ self.norm = KimiRMSNorm(
1145
+ config.hidden_size, eps=config.rms_norm_eps)
1146
+
1147
+ self.use_attn_residuals = getattr(config, "attn_res_block_size", None) is not None
1148
+ if self.use_attn_residuals:
1149
+ self.output_attn_res_norm = KimiRMSNorm(
1150
+ config.hidden_size, eps=config.rms_norm_eps)
1151
+ self.output_attn_res_proj = nn.Linear(
1152
+ config.hidden_size, 1, bias=False)
1153
+
1154
+ if getattr(config, "_attn_implementation", None) is not None:
1155
+ if config._attn_implementation != "flash_attention_2":
1156
+ logger.warning_once(
1157
+ f"Ignoring the provided attention implementation {config._attn_implementation}")
1158
+ logger.warning_once("Using flash_attention_2 backend instead.")
1159
+ config._attn_implementation = "flash_attention_2"
1160
+ else:
1161
+ config._attn_implementation = "flash_attention_2"
1162
+
1163
+ self._use_flash_attention_2 = config._attn_implementation == "flash_attention_2"
1164
+ self.gradient_checkpointing = False
1165
+ # Initialize weights and apply final processing
1166
+ self.post_init()
1167
+
1168
+ def _update_linear_attn_mask(self, attention_mask, cache_position):
1169
+ """
1170
+ NOTE: Left-padding is used for linear attention mask.
1171
+ No need for zeroing states when
1172
+ 1. Cached forward
1173
+ 2. Attending to all inputs
1174
+ """
1175
+ linear_attn_mask = attention_mask
1176
+ if cache_position[0] > 0 or (attention_mask is not None and torch.all(attention_mask == 1)):
1177
+ linear_attn_mask = None
1178
+ return linear_attn_mask
1179
+
1180
+ @check_model_inputs
1181
+ # @auto_docstring
1182
+ def forward(
1183
+ self,
1184
+ input_ids: torch.LongTensor = None,
1185
+ attention_mask: torch.Tensor | None = None,
1186
+ position_ids: torch.LongTensor | None = None,
1187
+ past_key_values: Cache | None = None,
1188
+ inputs_embeds: torch.FloatTensor | None = None,
1189
+ cache_position: torch.LongTensor | None = None,
1190
+ use_cache: bool | None = None,
1191
+ **kwargs: Unpack[TransformersKwargs],
1192
+ ) -> tuple | BaseModelOutputWithPast:
1193
+
1194
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
1195
+
1196
+ if (input_ids is None) and (inputs_embeds is None):
1197
+ raise ValueError(
1198
+ "You must specify exactly one of input_ids or inputs_embeds")
1199
+
1200
+ # Get inputs_embeds
1201
+ if inputs_embeds is None:
1202
+ inputs_embeds = self.embed_tokens(input_ids)
1203
+
1204
+ if use_cache and past_key_values is None:
1205
+ past_key_values = KimiDynamicCache(config=self.config)
1206
+
1207
+ if cache_position is None:
1208
+ past_seen_tokens = past_key_values.get_seq_length(
1209
+ ) if past_key_values is not None else 0
1210
+ cache_position: torch.Tensor = torch.arange(
1211
+ past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device,
1212
+ )
1213
+
1214
+ if position_ids is None:
1215
+ position_ids = cache_position.unsqueeze(0)
1216
+
1217
+ causal_mask = create_causal_mask(
1218
+ config=self.config,
1219
+ input_embeds=inputs_embeds,
1220
+ attention_mask=attention_mask,
1221
+ cache_position=cache_position,
1222
+ past_key_values=past_key_values,
1223
+ position_ids=position_ids,
1224
+ )
1225
+ linear_attn_mask = self._update_linear_attn_mask(
1226
+ attention_mask, cache_position)
1227
+
1228
+ hidden_states = inputs_embeds
1229
+ if past_key_values is not None:
1230
+ assert isinstance(past_key_values, KimiDynamicCache)
1231
+
1232
+ block_residual = None
1233
+ if self.use_attn_residuals:
1234
+ block_residual = hidden_states.new_zeros(
1235
+ hidden_states.shape[0] * hidden_states.shape[1], 0,
1236
+ hidden_states.shape[2])
1237
+
1238
+ for decoder_layer in self.layers:
1239
+ layer_mask = linear_attn_mask if decoder_layer.is_linear_attn else causal_mask
1240
+
1241
+ if self.use_attn_residuals:
1242
+ hidden_states, block_residual = decoder_layer(
1243
+ hidden_states,
1244
+ attention_mask=layer_mask,
1245
+ past_key_values=past_key_values,
1246
+ cache_position=cache_position,
1247
+ block_residual=block_residual,
1248
+ **kwargs,
1249
+ )
1250
+ else:
1251
+ hidden_states = decoder_layer(
1252
+ hidden_states,
1253
+ attention_mask=layer_mask,
1254
+ past_key_values=past_key_values,
1255
+ cache_position=cache_position,
1256
+ **kwargs,
1257
+ )
1258
+
1259
+ if self.use_attn_residuals:
1260
+ hidden_states = self._apply_output_attn_res(
1261
+ hidden_states, block_residual)
1262
+
1263
+ hidden_states = self.norm(hidden_states)
1264
+
1265
+ return BaseModelOutputWithPast(
1266
+ last_hidden_state=hidden_states,
1267
+ past_key_values=past_key_values,
1268
+ )
1269
+
1270
+ def _apply_output_attn_res(self, hidden_states, block_residual):
1271
+ batch_size, seq_len, hidden_size = hidden_states.shape
1272
+ return _apply_attn_res(
1273
+ hidden_states.view(-1, hidden_size),
1274
+ block_residual,
1275
+ self.output_attn_res_proj,
1276
+ self.output_attn_res_norm,
1277
+ ).view(batch_size, seq_len, hidden_size)
1278
+
1279
+
1280
+ class KimiLinearForCausalLM(KimiPreTrainedModel, GenerationMixin):
1281
+ @classmethod
1282
+ def _supports_default_dynamic_cache(cls) -> bool:
1283
+ return False
1284
+
1285
+ _tied_weights_keys = ["lm_head.weight"]
1286
+
1287
+ def __init__(self, config):
1288
+ super().__init__(config)
1289
+ self.model = KimiLinearModel(config)
1290
+ self.vocab_size = config.vocab_size
1291
+ self.lm_head = nn.Linear(
1292
+ config.hidden_size, config.vocab_size, bias=False)
1293
+
1294
+ # Initialize weights and apply final processing
1295
+ self.post_init()
1296
+
1297
+ @can_return_tuple
1298
+ # @auto_docstring
1299
+ def forward(
1300
+ self,
1301
+ input_ids: torch.LongTensor = None,
1302
+ attention_mask: torch.Tensor | None = None,
1303
+ position_ids: torch.LongTensor | None = None,
1304
+ past_key_values: list[torch.FloatTensor] | None = None,
1305
+ inputs_embeds: torch.FloatTensor | None = None,
1306
+ labels: torch.LongTensor | None = None,
1307
+ use_cache: bool | None = None,
1308
+ output_attentions: bool | None = None,
1309
+ output_hidden_states: bool | None = None,
1310
+ generation_mode: bool | None = None,
1311
+ return_dict: bool | None = None,
1312
+ cache_position: torch.LongTensor | None = None,
1313
+ **kwargs: Unpack[TransformersKwargs],
1314
+ ) -> tuple | CausalLMOutputWithPast:
1315
+ r"""
1316
+ Args:
1317
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1318
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
1319
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
1320
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
1321
+
1322
+ Returns:
1323
+
1324
+ Example:
1325
+
1326
+ ```python
1327
+ >>> from transformers import AutoTokenizer, KimiLinearForCausalLM
1328
+
1329
+ >>> model = KimiLinearForCausalLM.from_pretrained(PATH_TO_CONVERTED_WEIGHTS)
1330
+ >>> tokenizer = AutoTokenizer.from_pretrained(PATH_TO_CONVERTED_TOKENIZER)
1331
+
1332
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
1333
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
1334
+
1335
+ >>> # Generate
1336
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
1337
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
1338
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
1339
+ ```"""
1340
+
1341
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
1342
+ output_hidden_states = (
1343
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
1344
+ )
1345
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1346
+
1347
+ outputs = self.model(
1348
+ input_ids=input_ids,
1349
+ attention_mask=attention_mask,
1350
+ position_ids=position_ids,
1351
+ past_key_values=past_key_values,
1352
+ inputs_embeds=inputs_embeds,
1353
+ use_cache=use_cache,
1354
+ output_attentions=output_attentions,
1355
+ output_hidden_states=output_hidden_states,
1356
+ return_dict=return_dict,
1357
+ cache_position=cache_position,
1358
+ )
1359
+
1360
+ logits = outputs[0]
1361
+ if generation_mode:
1362
+ logits = logits[:, -1:]
1363
+ logits = self.lm_head(logits)
1364
+
1365
+ loss = None
1366
+ if labels is not None:
1367
+ loss = self.loss_function(
1368
+ logits, labels, self.vocab_size, **kwargs)
1369
+
1370
+ return CausalLMOutputWithPast(
1371
+ loss=loss,
1372
+ logits=logits,
1373
+ past_key_values=outputs.past_key_values,
1374
+ hidden_states=outputs.hidden_states,
1375
+ attentions=outputs.attentions,
1376
+ )
modeling_kimi_k3.py ADDED
@@ -0,0 +1,1286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from collections.abc import Sequence
3
+ from copy import deepcopy
4
+ from typing import Optional
5
+
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from transformers import activations
11
+
12
+ try:
13
+ from transformers.activations import PytorchGELUTanh
14
+ except ImportError:
15
+ from transformers.activations import GELUTanh
16
+ activations.PytorchGELUTanh = GELUTanh
17
+ PytorchGELUTanh = GELUTanh
18
+ from transformers.activations import PytorchGELUTanh
19
+ from transformers.configuration_utils import PretrainedConfig
20
+ from transformers.modeling_utils import PreTrainedModel
21
+ from transformers.models.llava.modeling_llava import \
22
+ LlavaCausalLMOutputWithPast
23
+ from transformers.utils import is_flash_attn_2_available
24
+
25
+ from .configuration_kimi_k3 import KimiK3Config
26
+ from .modeling_kimi import KimiLinearForCausalLM
27
+
28
+ # Flash attention imports
29
+ if is_flash_attn_2_available():
30
+ from flash_attn import flash_attn_varlen_func
31
+ else:
32
+ flash_attn_varlen_func = None
33
+
34
+
35
+ def multihead_attention(
36
+ q: torch.Tensor,
37
+ k: torch.Tensor,
38
+ v: torch.Tensor,
39
+ q_cu_seqlens: torch.Tensor | None = None,
40
+ k_cu_seqlens: torch.Tensor | None = None,
41
+ max_seqlen_q: int | None = None,
42
+ max_seqlen_k: int | None = None,
43
+ deterministic: bool = False,
44
+ ):
45
+ """Multi-head attention using flash attention 2.
46
+
47
+ Args:
48
+ q, k, v: tensor of shape (batch_size, seqlen, num_heads, head_dim),
49
+ or (tot_seqlens, num_heads, head_dim) if packing.
50
+ q_cu_seqlens (torch.Tensor): cumulative sequence lengths of q.
51
+ The first element should be 0 and the last element should be q.shape[0].
52
+ k_cu_seqlens (torch.Tensor): cumulative sequence lengths of k.
53
+ The first element should be 0 and the last element should be k.shape[0].
54
+
55
+ Returns:
56
+ output: shape (batch_size, seqlen, dim) or (tot_seqlens, dim) if packing,
57
+ where dim = num_heads * head_dim
58
+ """
59
+ attn_out = flash_attn_varlen_func(
60
+ q,
61
+ k,
62
+ v,
63
+ q_cu_seqlens,
64
+ k_cu_seqlens,
65
+ max_seqlen_q,
66
+ max_seqlen_k,
67
+ causal=False,
68
+ deterministic=deterministic,
69
+ )
70
+ if isinstance(attn_out, tuple):
71
+ attn_out = attn_out[0]
72
+
73
+ attn_out = attn_out.flatten(start_dim=-2)
74
+
75
+ return attn_out
76
+
77
+
78
+ def eager_attention(
79
+ q: torch.Tensor,
80
+ k: torch.Tensor,
81
+ v: torch.Tensor,
82
+ q_cu_seqlens: Optional[torch.Tensor] = None,
83
+ k_cu_seqlens: Optional[torch.Tensor] = None,
84
+ **kwargs,
85
+ ) -> torch.Tensor:
86
+ seq_length = q.shape[0]
87
+ attention_mask = torch.zeros([1, seq_length, seq_length],
88
+ device=q.device,
89
+ dtype=torch.bool)
90
+ for i in range(1, len(q_cu_seqlens)):
91
+ attention_mask[
92
+ ...,
93
+ q_cu_seqlens[i - 1]:q_cu_seqlens[i],
94
+ q_cu_seqlens[i - 1]:q_cu_seqlens[i],
95
+ ] = True
96
+ q = q.transpose(0, 1)
97
+ k = k.transpose(0, 1)
98
+ v = v.transpose(0, 1)
99
+
100
+ attn_weight = q @ k.transpose(-2, -1) / math.sqrt(q.shape[-1])
101
+ attn_weight += attention_mask
102
+ attn_weight = torch.softmax(attn_weight, dim=-1,
103
+ dtype=torch.float32).to(q.dtype)
104
+
105
+ attn_output = attn_weight @ v
106
+ attn_output = attn_output.transpose(0, 1)
107
+ attn_output = attn_output.reshape(seq_length, -1)
108
+ return attn_output
109
+
110
+
111
+ VL_VISION_ATTENTION_FUNCTIONS = {
112
+ "flash_attention_2": multihead_attention,
113
+ "eager": eager_attention,
114
+ }
115
+
116
+
117
+ def _apply_rope_input_validation(x, freqs_cis):
118
+ assert x.ndim == freqs_cis.ndim + 1, (x.shape, freqs_cis.shape)
119
+ assert x.shape[:-2] == freqs_cis.shape[:-1], (x.shape, freqs_cis.shape)
120
+ assert x.shape[-1] == 2 * freqs_cis.shape[-1], (x.shape, freqs_cis.shape)
121
+ assert freqs_cis.dtype == torch.complex64, freqs_cis.dtype
122
+
123
+
124
+ def get_rope_shape_decorate(func):
125
+ _get_rope_shape_first_call_flag = set()
126
+
127
+ def wrapper(org, interpolation_mode, shape):
128
+ key = (org.requires_grad, torch.is_grad_enabled(), interpolation_mode)
129
+ if key not in _get_rope_shape_first_call_flag:
130
+ _get_rope_shape_first_call_flag.add(key)
131
+ _ = func(org, interpolation_mode, shape=(64, 64))
132
+ return func(org, interpolation_mode, shape)
133
+
134
+ return wrapper
135
+
136
+
137
+ @get_rope_shape_decorate
138
+ @torch.compile(dynamic=True)
139
+ def get_rope_shape(org, interpolation_mode, shape):
140
+ return (F.interpolate(
141
+ org.permute((2, 0, 1)).unsqueeze(0),
142
+ size=shape,
143
+ mode=interpolation_mode,
144
+ ).squeeze(0).permute((1, 2, 0)).flatten(end_dim=1))
145
+
146
+
147
+ def apply_rope(xq: torch.Tensor, xk: torch.Tensor,
148
+ freqs_cis: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
149
+ """
150
+ Args: (The leading dimensions of all inputs should be the same)
151
+ xq: query, tensor of shape (..., num_heads, head_dim)
152
+ xk: key, tensor of shape (..., num_heads, head_dim)
153
+ freqs_cis: tensor of shape (..., head_dim/2), dtype=torch.complex64. It contains the precomputed cis(freqs) for each position in the 2D grid.
154
+ Returns:
155
+ xq_out, xk_out: tensors of shape (..., num_heads, head_dim)
156
+ """
157
+ _apply_rope_input_validation(xq, freqs_cis)
158
+ _apply_rope_input_validation(xk, freqs_cis)
159
+
160
+ freqs_cis = freqs_cis.unsqueeze(-2) # ..., 1, head_dim/2
161
+ # ..., num_heads, head_dim/2
162
+ xq_ = torch.view_as_complex(xq.float().view(*xq.shape[:-1], -1, 2))
163
+ xk_ = torch.view_as_complex(xk.float().view(*xq.shape[:-1], -1, 2))
164
+ xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(
165
+ -2) # ..., num_heads, head_dim
166
+ xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(
167
+ -2) # ..., num_heads, head_dim
168
+ return xq_out.type_as(xq), xk_out.type_as(xk)
169
+
170
+
171
+ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
172
+ """
173
+ From:
174
+ https://github.com/OpenGVLab/InternVideo/blob/421f6d2361fc8f61a3394244571f2601a4e99e29/InternVideo2/multi_modality/models/backbones/internvideo2/pos_embed.py#L86
175
+ embed_dim: output dimension for each position
176
+ pos: a list of positions to be encoded: size (M,)
177
+ out: (M, D)
178
+ """
179
+ assert embed_dim % 2 == 0
180
+ omega = np.arange(embed_dim // 2, dtype=np.float32)
181
+ omega /= embed_dim / 2.0
182
+ omega = 1.0 / 10000**omega # (D/2,)
183
+
184
+ pos = pos.reshape(-1) # (M,)
185
+ out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product
186
+
187
+ emb_sin = np.sin(out) # (M, D/2)
188
+ emb_cos = np.cos(out) # (M, D/2)
189
+
190
+ emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D)
191
+ return emb
192
+
193
+
194
+ def get_1d_sincos_pos_embed(embed_dim, t_size, cls_token=False):
195
+ """
196
+ t_size: int of the temporal size
197
+ return:
198
+ pos_embed: [t_size, embed_dim] or [1+t_size, embed_dim] (w/ or w/o cls_token)
199
+ """
200
+ grid_t = np.arange(t_size, dtype=np.float32)
201
+ pos_embed = get_1d_sincos_pos_embed_from_grid(embed_dim, grid_t)
202
+ if cls_token:
203
+ pos_embed = np.concatenate([np.zeros([1, embed_dim]), pos_embed],
204
+ axis=0)
205
+ return pos_embed
206
+
207
+
208
+ class Learnable2DInterpPosEmbDivided_fixed(nn.Module):
209
+
210
+ def __init__(self,
211
+ height: int,
212
+ width: int,
213
+ num_frames: int,
214
+ dim: int,
215
+ interpolation_mode: str = 'bicubic') -> None:
216
+ super().__init__()
217
+ self.height = height
218
+ self.width = width
219
+ self.num_frames = num_frames
220
+ self.dim = dim
221
+ self.interpolation_mode = interpolation_mode
222
+ self.weight = nn.Parameter(torch.empty(height, width, dim))
223
+ self.register_buffer('time_weight',
224
+ torch.from_numpy(
225
+ get_1d_sincos_pos_embed(
226
+ self.dim,
227
+ self.num_frames)).float().unsqueeze(1),
228
+ persistent=False)
229
+
230
+ self.reset_parameters()
231
+
232
+ def reset_parameters(self):
233
+ nn.init.normal_(self.weight)
234
+
235
+ def forward(self, x: torch.Tensor,
236
+ grid_thws: torch.Tensor) -> torch.Tensor:
237
+ pos_embs = []
238
+ for t, h, w in grid_thws.tolist():
239
+ assert t <= self.num_frames, f't:{t} > self.num_frames:{self.num_frames}'
240
+ if (h, w) == self.weight.shape[:-1]:
241
+ pos_emb_2d = self.weight.flatten(end_dim=1)
242
+ else:
243
+ pos_emb_2d = get_rope_shape(
244
+ self.weight,
245
+ interpolation_mode=self.interpolation_mode,
246
+ shape=(h, w),
247
+ )
248
+
249
+ if t == 1:
250
+ pos_emb_3d = pos_emb_2d
251
+ else:
252
+ pos_emb_3d = pos_emb_2d.unsqueeze(0).repeat(
253
+ t, 1, 1) + self.time_weight[0:t]
254
+
255
+ pos_embs.append(pos_emb_3d.reshape(-1, pos_emb_3d.shape[-1]))
256
+
257
+ out = x + torch.cat(pos_embs)
258
+ return out
259
+
260
+
261
+ class MoonVision3dPatchEmbed(nn.Module):
262
+
263
+ def __init__(self,
264
+ out_dim: int,
265
+ in_dim: int = 3,
266
+ patch_size: int | tuple[int, int] = (14, 14),
267
+ pos_emb_height: int = 14,
268
+ pos_emb_width: int = 14,
269
+ pos_emb_time: int = 4,
270
+ pos_emb_type: str = 'divided_fixed',
271
+ patch_embed_proj_bias: bool = True):
272
+ super().__init__()
273
+ assert isinstance(
274
+ patch_size,
275
+ int | Sequence), f'Invalid patch_size type: {type(patch_size)}'
276
+ if isinstance(patch_size, int):
277
+ patch_size = (patch_size, patch_size)
278
+ assert (len(patch_size) == 2
279
+ ), f'Expected patch_size to be a tuple of 2, got {patch_size}'
280
+ self.patch_size = patch_size
281
+
282
+ self.proj = nn.Conv2d(in_dim,
283
+ out_dim,
284
+ kernel_size=patch_size,
285
+ stride=patch_size,
286
+ bias=patch_embed_proj_bias)
287
+
288
+ if pos_emb_type == 'divided_fixed':
289
+ self.pos_emb = Learnable2DInterpPosEmbDivided_fixed(
290
+ height=pos_emb_height,
291
+ width=pos_emb_width,
292
+ num_frames=pos_emb_time,
293
+ dim=out_dim)
294
+ else:
295
+ raise NotImplementedError(
296
+ f'Not support pos_emb_type: {pos_emb_type}')
297
+
298
+ def forward(self, x: torch.Tensor,
299
+ grid_thws: torch.Tensor) -> torch.Tensor:
300
+ """
301
+ Args:
302
+ x (L, Channels): input tensor
303
+ grid_hws (N, 3): temporal, height and width
304
+
305
+ Returns:
306
+ (L, Cout) tensor
307
+ """
308
+ x = self.proj(x).view(x.size(0), -1)
309
+ # apply positional embedding
310
+ x = self.pos_emb(x, grid_thws)
311
+ return x
312
+
313
+
314
+ class Rope2DPosEmbRepeated(nn.Module):
315
+ """2D rotary position embedding with multi-resolution support.
316
+
317
+ This class is intended to be used in the following way:
318
+ 1. Before training, create an instance of Rope2DPosEmb. This instance will hold the precomputed cis.
319
+ 2. Before each forward pass, call `get_freqs_cis_by_*` to get the `freqs_cis` tensor for this iteration.
320
+ 3. During the forward pass, pass the `freqs_cis` tensor to each attention layer, and call `apply` just before each attention operation.
321
+ The rope is shared across all attention layers and all heads.
322
+
323
+ Refs:
324
+ - RoFormer: https://arxiv.org/abs/2104.09864
325
+ - VisionLLaMA: https://arxiv.org/abs/2403.00522
326
+ - https://github.com/Meituan-AutoML/VisionLLaMA/blob/main/dit/models.py
327
+
328
+ Args:
329
+ dim (int): usually the multi-head attention dimension, should be divisible by 4 (TODO: relax this constraint if needed)
330
+ max_height (int): the maximum height of the 2D grid
331
+ max_width (int): the maximum width of the 2D grid
332
+ theta_base (float): the base of the theta
333
+ device (str): the device to store the precomputed cis
334
+ """
335
+
336
+ def __init__(self,
337
+ dim: int,
338
+ max_height: int,
339
+ max_width: int,
340
+ theta_base=10000):
341
+ super().__init__()
342
+ self.dim = dim
343
+ assert self.dim % 4 == 0, 'dim must be divisible by 4'
344
+ self.max_height = max_height
345
+ self.max_width = max_width
346
+ self.theta_base = theta_base
347
+
348
+ def extra_repr(self):
349
+ return f'dim={self.dim}, max_height={self.max_height}, max_width={self.max_width}, theta_base={self.theta_base}'
350
+
351
+ def _precompute_freqs_cis(self, device: torch.device) -> torch.Tensor:
352
+ """Calculate the cis(freqs) for each position in the 2D grid.
353
+
354
+ Return: complex tensor of shape (max_height, max_width, dim//2) and value:
355
+ height axis: ret[h, w, 2*i] = cis(h * theta_base**(-4*i/dim))
356
+ weight axis: ret[h, w, 2*i+1] = cis(w * theta_base**(-4*i/dim)) with (i in [0, dim//4))
357
+ note: `cis` is a mathematical notation defined by cis x = cos x + i sin x,
358
+ """
359
+ N = self.max_height * self.max_width
360
+ flat_pos = torch.arange(0, N).float().to(device)
361
+ x_pos = flat_pos % self.max_width
362
+ y_pos = flat_pos // self.max_width
363
+ dim_range = (torch.arange(0, self.dim,
364
+ 4)[:(self.dim // 4)].float().to(device)
365
+ ) # C/4
366
+ freqs = 1.0 / (self.theta_base**(dim_range / self.dim))
367
+ x_freqs = torch.outer(x_pos, freqs).float() # N, C/4
368
+ y_freqs = torch.outer(y_pos, freqs).float() # N, C/4
369
+ x_cis = torch.polar(torch.ones_like(x_freqs), x_freqs) # N, C/4
370
+ y_cis = torch.polar(torch.ones_like(y_freqs), y_freqs) # N, C/4
371
+ # N, C/4, 2
372
+ freqs_cis = torch.cat(
373
+ [x_cis.unsqueeze(dim=-1),
374
+ y_cis.unsqueeze(dim=-1)], dim=-1)
375
+ # max_height, max_width, C/2
376
+ freqs_cis = freqs_cis.reshape(self.max_height, self.max_width, -1)
377
+ return freqs_cis
378
+
379
+ def get_freqs_cis(self, grid_thws: torch.Tensor,
380
+ device: torch.device) -> torch.Tensor:
381
+ """
382
+ Args:
383
+ grid_thws (torch.Tensor): grid time, height and width
384
+
385
+ Returns:
386
+ freqs_cis: tensor of shape (sum(t * height * width), dim//2)
387
+ """
388
+ if not hasattr(self, 'freqs_cis'):
389
+ self.register_buffer('freqs_cis',
390
+ self._precompute_freqs_cis(device),
391
+ persistent=False)
392
+
393
+ shapes = grid_thws.tolist()
394
+ assert all(1 <= h <= self.max_height and 1 <= w <= self.max_width
395
+ for t, h, w in shapes), (
396
+ shapes,
397
+ self.max_height,
398
+ self.max_width,
399
+ )
400
+ freqs_cis = torch.cat(
401
+ [
402
+ self.freqs_cis[:h, :w].reshape(-1, self.dim // 2).repeat(t, 1)
403
+ for t, h, w in shapes
404
+ ],
405
+ dim=0,
406
+ )
407
+ return freqs_cis
408
+
409
+
410
+ class MLP2(nn.Module):
411
+ """
412
+ Args:
413
+ dims: [in_dim, hidden_dim, out_dim]
414
+ bias: whether to use bias in linear layer.
415
+ """
416
+
417
+ def __init__(self, dims: list[int], activation, bias=True):
418
+ super().__init__()
419
+ assert len(dims) == 3
420
+ self.fc0 = nn.Linear(dims[0], dims[1], bias=bias)
421
+ self.fc1 = nn.Linear(dims[1], dims[2], bias=bias)
422
+ self.activation = activation
423
+ for m in [self.fc0, self.fc1]:
424
+ nn.init.trunc_normal_(m.weight, std=math.sqrt(2 / m.in_features))
425
+ if m.bias is not None:
426
+ nn.init.zeros_(m.bias)
427
+
428
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
429
+ x = self.fc0(x)
430
+ x = self.activation(x)
431
+ return self.fc1(x)
432
+
433
+
434
+ class MoonViTEncoderLayer(nn.Module):
435
+
436
+ def __init__(
437
+ self,
438
+ num_heads: int,
439
+ hidden_dim: int,
440
+ mlp_dim: int,
441
+ qkv_hidden_size: int | None = None,
442
+ norm_type: str = 'layernorm',
443
+ mlp_type: str = 'mlp2',
444
+ *,
445
+ attn_implementation: str = 'flash_attention_2',
446
+ activation=F.gelu,
447
+ attn_bias: bool = False,
448
+ linear_bias: bool = True,
449
+ use_deterministic_attn: bool = False,
450
+ ):
451
+ super().__init__()
452
+ self.num_heads = num_heads
453
+ self.hidden_dim = hidden_dim
454
+ self.qkv_hidden_size = hidden_dim if qkv_hidden_size is None else qkv_hidden_size
455
+ self.hidden_size_per_attention_head = self.qkv_hidden_size // self.num_heads
456
+ self.attn_implementation = attn_implementation
457
+ self.use_deterministic_attn = use_deterministic_attn
458
+
459
+ if norm_type == "layernorm":
460
+ self.norm0 = nn.LayerNorm(hidden_dim)
461
+ self.norm1 = nn.LayerNorm(hidden_dim)
462
+ elif norm_type == "rmsnorm":
463
+ self.norm0 = nn.RMSNorm(hidden_dim)
464
+ self.norm1 = nn.RMSNorm(hidden_dim)
465
+ else:
466
+ raise NotImplementedError(f"Not support norm_type: {norm_type}")
467
+
468
+ if mlp_type == "mlp2":
469
+ self.mlp = MLP2([hidden_dim, mlp_dim, hidden_dim],
470
+ activation,
471
+ bias=linear_bias)
472
+ else:
473
+ raise NotImplementedError(f"Not support mlp_type: {mlp_type}")
474
+
475
+ self.wqkv = nn.Linear(hidden_dim,
476
+ self.qkv_hidden_size * 3,
477
+ bias=attn_bias)
478
+ self.wo = nn.Linear(self.qkv_hidden_size, hidden_dim, bias=attn_bias)
479
+
480
+ def attention_qkvpacked(
481
+ self,
482
+ x: torch.Tensor,
483
+ cu_seqlens: torch.Tensor,
484
+ max_seqlen: torch.Tensor,
485
+ rope_freqs_cis: torch.Tensor | None = None,
486
+ ):
487
+ """
488
+ Args:
489
+ x (torch.Tensor): (batch_size, seqlen, hidden_dim)
490
+ cu_seqlens (torch.Tensor):
491
+ """
492
+ xqkv = self.wqkv(x)
493
+
494
+ qkv_shape = xqkv.size()[:-1] + (
495
+ 3,
496
+ self.num_heads,
497
+ self.hidden_size_per_attention_head,
498
+ )
499
+ # xqkv: (batch_size, seqlen, 3, nheads, headdim)
500
+ xqkv = xqkv.view(*qkv_shape)
501
+ xq, xk, xv = torch.unbind(xqkv, dim=-3)
502
+
503
+ xq, xk = apply_rope(xq, xk, rope_freqs_cis)
504
+
505
+ attn_func = VL_VISION_ATTENTION_FUNCTIONS[self.attn_implementation]
506
+ attn_out = attn_func(xq,
507
+ xk,
508
+ xv,
509
+ q_cu_seqlens=cu_seqlens,
510
+ k_cu_seqlens=cu_seqlens,
511
+ max_seqlen_k=max_seqlen,
512
+ max_seqlen_q=max_seqlen,
513
+ deterministic=self.use_deterministic_attn)
514
+
515
+ attn_out = self.wo(attn_out)
516
+ return attn_out
517
+
518
+ def forward(
519
+ self,
520
+ hidden_states: torch.Tensor,
521
+ cu_seqlens: torch.Tensor,
522
+ max_seqlen: int,
523
+ rope_freqs_cis: torch.Tensor | None = None,
524
+ ):
525
+ residual = hidden_states
526
+ hidden_states = self.norm0(hidden_states)
527
+
528
+ hidden_states = self.attention_qkvpacked(hidden_states, cu_seqlens,
529
+ max_seqlen, rope_freqs_cis)
530
+ hidden_states = residual + hidden_states
531
+
532
+ residual = hidden_states
533
+ hidden_states = self.norm1(hidden_states)
534
+ hidden_states = self.mlp(hidden_states)
535
+ hidden_states = residual + hidden_states
536
+
537
+ return hidden_states
538
+
539
+
540
+ class MoonViT3dEncoder(nn.Module):
541
+
542
+ def __init__(self,
543
+ hidden_dim: int,
544
+ num_layers: int,
545
+ block_cfg: dict,
546
+ use_deterministic_attn: bool = False) -> None:
547
+ super().__init__()
548
+ self.use_deterministic_attn = use_deterministic_attn
549
+
550
+ qkv_hidden_size = block_cfg['hidden_dim'] if block_cfg.get(
551
+ 'qkv_hidden_size') is None else block_cfg['qkv_hidden_size']
552
+ self.rope_2d = Rope2DPosEmbRepeated(
553
+ qkv_hidden_size // block_cfg['num_heads'], 512, 512)
554
+ self.blocks = nn.ModuleList([
555
+ MoonViTEncoderLayer(
556
+ **block_cfg,
557
+ use_deterministic_attn=self.use_deterministic_attn)
558
+ for _ in range(num_layers)
559
+ ])
560
+ norm_type = block_cfg.get('norm_type', 'layernorm')
561
+ if norm_type == "layernorm":
562
+ self.final_layernorm = nn.LayerNorm(hidden_dim)
563
+ elif norm_type == "rmsnorm":
564
+ self.final_layernorm = nn.RMSNorm(hidden_dim)
565
+ else:
566
+ raise NotImplementedError(f"Not support norm_type: {norm_type}")
567
+
568
+ def forward(
569
+ self,
570
+ hidden_states: torch.Tensor,
571
+ grid_thws: torch.Tensor,
572
+ ) -> torch.Tensor:
573
+ rope_freqs_cis = self.rope_2d.get_freqs_cis(
574
+ grid_thws=grid_thws, device=hidden_states.device)
575
+
576
+ lengths = torch.cat((
577
+ torch.zeros(1, dtype=grid_thws.dtype, device=grid_thws.device),
578
+ grid_thws[:, 0] * grid_thws[:, 1] * grid_thws[:, 2],
579
+ ))
580
+
581
+ max_seqlen = lengths.max()
582
+ cu_seqlens = lengths.to(hidden_states.device).cumsum(dim=0,
583
+ dtype=torch.int32)
584
+ for block in self.blocks:
585
+ hidden_states = block(hidden_states,
586
+ cu_seqlens,
587
+ max_seqlen,
588
+ rope_freqs_cis=rope_freqs_cis)
589
+
590
+ hidden_states = self.final_layernorm(hidden_states)
591
+ return hidden_states
592
+
593
+
594
+ def tpool_patch_merger(
595
+ x: torch.Tensor,
596
+ grid_thws: torch.Tensor,
597
+ merge_kernel_size: tuple[int, int] = (2, 2),
598
+ ) -> list[torch.Tensor]:
599
+ d_model = x.size(-1)
600
+
601
+ outputs = []
602
+ pre_sum = 0
603
+ for t, h, w in grid_thws.tolist():
604
+ # Get the current sequence
605
+ seq = x[pre_sum:pre_sum + t * h * w]
606
+ # Reshape along self.merge_kernel_size and concat to the last dimension
607
+ kernel_height, kernel_width = merge_kernel_size
608
+ new_height, new_width = h // kernel_height, w // kernel_width
609
+ reshaped_seq = seq.view(t, new_height, kernel_height, new_width,
610
+ kernel_width, d_model)
611
+ reshaped_seq = reshaped_seq.permute(0, 1,
612
+ 3, 2, 4, 5).contiguous().mean(
613
+ dim=0) # temporal pooling
614
+ padded_seq = reshaped_seq.view(new_height * new_width,
615
+ kernel_height * kernel_width, -1)
616
+ outputs.append(padded_seq)
617
+ pre_sum += t * h * w
618
+
619
+ return outputs
620
+
621
+
622
+ class MoonViT3dPretrainedModel(PreTrainedModel):
623
+ config_class = None
624
+ model_type = 'moonvit3d'
625
+ _no_split_modules = ['PackingTransformer']
626
+ _supports_flash_attn_2 = True
627
+ _supports_sdpa = True
628
+
629
+ def __init__(self, config, *inputs, **kwargs):
630
+ super().__init__(config, *inputs, **kwargs)
631
+ config = deepcopy(config)
632
+ self.merge_kernel_size = config.merge_kernel_size
633
+ self.patch_size = config.patch_size
634
+ self.merge_type = config.merge_type
635
+
636
+ self.patch_embed = MoonVision3dPatchEmbed(
637
+ out_dim=config.hidden_size,
638
+ patch_size=config.patch_size,
639
+ pos_emb_height=config.init_pos_emb_height,
640
+ pos_emb_width=config.init_pos_emb_width,
641
+ pos_emb_time=config.init_pos_emb_time,
642
+ pos_emb_type=config.pos_emb_type,
643
+ patch_embed_proj_bias=getattr(config, 'patch_embed_proj_bias',
644
+ True),
645
+ )
646
+
647
+ self.encoder = MoonViT3dEncoder(
648
+ hidden_dim=config.hidden_size,
649
+ num_layers=config.num_hidden_layers,
650
+ block_cfg={
651
+ 'num_heads': config.num_attention_heads,
652
+ 'hidden_dim': config.hidden_size,
653
+ 'qkv_hidden_size': getattr(config, 'qkv_hidden_size', None),
654
+ 'mlp_dim': config.intermediate_size,
655
+ 'norm_type': getattr(config, 'norm_type', 'layernorm'),
656
+ 'mlp_type': getattr(config, 'mlp_type', 'mlp2'),
657
+ 'activation': PytorchGELUTanh(),
658
+ 'attn_bias': getattr(config, 'attn_bias', True),
659
+ 'linear_bias': getattr(config, 'linear_bias', True),
660
+ 'attn_implementation': config._attn_implementation,
661
+ },
662
+ use_deterministic_attn=getattr(self, 'use_deterministic_attn',
663
+ False))
664
+
665
+ def forward(self, pixel_values: torch.Tensor,
666
+ grid_thws: torch.Tensor) -> torch.Tensor:
667
+ """
668
+ Args:
669
+ pixel_values (torch.Tensor): The input pixel values.
670
+ grid_thws (torch.Tensor): Temporal, height and width.
671
+
672
+ Returns:
673
+ torch.Tensor: The output tokens.
674
+ """
675
+ # grid_thws = grid_thws.to('cpu')
676
+ assert grid_thws.ndim == 2, f'grid_thws should be 2D, got {grid_thws.ndim}'
677
+ assert grid_thws.size(1) == 3, f'No support for thw: {grid_thws}'
678
+ hidden_states = self.patch_embed(pixel_values, grid_thws)
679
+ hidden_states = self.encoder(hidden_states, grid_thws)
680
+ if self.merge_type == 'sd2_tpool': # spatial downsampling 2x with temporal pooling all
681
+ hidden_states = tpool_patch_merger(
682
+ hidden_states,
683
+ grid_thws,
684
+ merge_kernel_size=self.merge_kernel_size)
685
+ else:
686
+ raise NotImplementedError(f'Not support {self.merge_type}')
687
+
688
+ return hidden_states
689
+
690
+
691
+ # ============================================================================
692
+ # MM Projector Helper Classes (from mm_projector/modeling_mm_projectors.py)
693
+ # ============================================================================
694
+
695
+
696
+ class IdentityMap(nn.Module):
697
+
698
+ def __init__(self):
699
+ super().__init__()
700
+
701
+ def forward(self, x, *args, **kwargs):
702
+ return x
703
+
704
+
705
+ class MLP(nn.Module):
706
+
707
+ def __init__(self, config):
708
+ super().__init__()
709
+ # TODO, use faster LayerNorm
710
+ self.pre_norm = nn.LayerNorm(config.mm_hidden_size)
711
+ self.proj = nn.Sequential(
712
+ nn.Linear(config.mm_hidden_size, config.hidden_size), nn.GELU(),
713
+ nn.Linear(config.hidden_size, config.hidden_size))
714
+
715
+ def forward(self, x, *args, **kwargs):
716
+ assert isinstance(x,
717
+ list | tuple), f'x is not a list or tuple: {type(x)}'
718
+ lengths = [item.shape[0] for item in x]
719
+ x = torch.cat(x, dim=0)
720
+ x = self.pre_norm(x)
721
+ x = self.proj(x)
722
+ x = torch.split(x, lengths, dim=0)
723
+
724
+ return x
725
+
726
+
727
+ class PatchMergerMLP(nn.Module):
728
+
729
+ def __init__(self, config):
730
+ super().__init__()
731
+ eps = config.projector_ln_eps
732
+ self.hidden_size = config.mm_hidden_size * (
733
+ config.merge_kernel_size[0] * config.merge_kernel_size[1])
734
+ self.pre_norm = nn.LayerNorm(config.mm_hidden_size, eps=eps)
735
+ self.proj = nn.Sequential(
736
+ nn.Linear(self.hidden_size, self.hidden_size),
737
+ nn.GELU(),
738
+ nn.Linear(self.hidden_size, config.hidden_size),
739
+ )
740
+
741
+ def forward(self, x, *args, **kwargs):
742
+ if isinstance(x, list) or isinstance(x, tuple):
743
+ x = [
744
+ self.proj(self.pre_norm(item).view(item.shape[0], -1))
745
+ for item in x
746
+ ]
747
+ else:
748
+ # B, N, N_k, C = x.shape
749
+ B = x.shape[0]
750
+ x = self.proj(self.pre_norm(x).view(B, -1, self.hidden_size))
751
+ return x
752
+
753
+
754
+ class PatchMergerMLPV2(nn.Module):
755
+
756
+ def __init__(self, config):
757
+ super().__init__()
758
+ eps = config.projector_ln_eps
759
+ self.hidden_size = config.mm_hidden_size * (
760
+ config.merge_kernel_size[0] * config.merge_kernel_size[1])
761
+ self.proj = nn.Sequential(
762
+ nn.Linear(self.hidden_size, self.hidden_size, bias=False),
763
+ nn.GELU(),
764
+ nn.Linear(self.hidden_size, config.hidden_size, bias=False),
765
+ )
766
+ self.post_norm = nn.RMSNorm(config.hidden_size, eps=eps)
767
+ for m in self.proj.modules():
768
+ if isinstance(m, nn.Linear):
769
+ nn.init.trunc_normal_(m.weight,
770
+ std=math.sqrt(2 / m.in_features))
771
+ if m.bias is not None:
772
+ nn.init.zeros_(m.bias)
773
+
774
+ def forward(self, x, *args, **kwargs):
775
+ if isinstance(x, list) or isinstance(x, tuple):
776
+ lengths = [item.shape[0] for item in x]
777
+ x = torch.concat([item.view(item.shape[0], -1) for item in x],
778
+ dim=0)
779
+ x = self.post_norm(self.proj(x))
780
+ x = torch.split(x, lengths, dim=0)
781
+ else:
782
+ # B, N, N_k, C = x.shape
783
+ B = x.shape[0]
784
+ x = self.proj(x.view(B, -1, self.hidden_size))
785
+ x = self.post_norm(x)
786
+ return x
787
+
788
+
789
+ class KimiK3PreTrainedModel(PreTrainedModel):
790
+ config_class = KimiK3Config
791
+ base_model_prefix = "model"
792
+ _no_split_modules = [
793
+ "MoonViT3dPretrainedModel",
794
+ "MoonViTEncoderLayer",
795
+ "KimiLinearDecoderLayer",
796
+ "PatchMergerMLP",
797
+ "PatchMergerMLPV2",
798
+ ]
799
+ _skip_keys_device_placement = "past_key_values"
800
+ _supports_flash_attn_2 = True
801
+ _supports_sdpa = False
802
+
803
+ def _init_weights(self, module):
804
+ # important: this ported version of Llava isn't meant for training from scratch - only
805
+ # inference and fine-tuning - so the proper init weights code has been removed - the original codebase
806
+ # https://github.com/haotian-liu/LLaVA/tree/main/llava should serve for that purpose
807
+ std = (self.config.initializer_range if hasattr(
808
+ self.config, "initializer_range") else
809
+ self.config.text_config.initializer_range)
810
+
811
+ if hasattr(module, "class_embedding"):
812
+ module.class_embedding.data.normal_(mean=0.0, std=std)
813
+
814
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
815
+ module.weight.data.normal_(mean=0.0, std=std)
816
+ if module.bias is not None:
817
+ module.bias.data.zero_()
818
+ elif isinstance(module, nn.Embedding):
819
+ module.weight.data.normal_(mean=0.0, std=std)
820
+ if module.padding_idx is not None:
821
+ module.weight.data[module.padding_idx].zero_()
822
+
823
+
824
+ class VisionTowerConfig(PretrainedConfig):
825
+ model_type = 'moonvit3d'
826
+
827
+ def __init__(self, config: KimiK3Config, **kwargs):
828
+ super().__init__(**kwargs)
829
+ self.patch_size = config.patch_size
830
+ self.init_pos_emb_height = config.init_pos_emb_height
831
+ self.init_pos_emb_width = config.init_pos_emb_width
832
+ self.init_pos_emb_time = config.init_pos_emb_time
833
+ self.pos_emb_type = config.pos_emb_type
834
+ self.num_attention_heads = config.vt_num_attention_heads
835
+ self.num_hidden_layers = config.vt_num_hidden_layers
836
+ self.hidden_size = config.vt_hidden_size
837
+ self.intermediate_size = config.vt_intermediate_size
838
+ self.merge_kernel_size = config.merge_kernel_size
839
+ self.merge_type = config.merge_type
840
+ self._attn_implementation = config._attn_implementation
841
+ self.qkv_hidden_size = getattr(config, 'qkv_hidden_size', None)
842
+ self.norm_type = getattr(config, 'norm_type', 'layernorm')
843
+ self.attn_bias = getattr(config, 'attn_bias', True)
844
+ self.patch_embed_proj_bias = getattr(config, 'patch_embed_proj_bias',
845
+ True)
846
+ self.mlp_type = getattr(config, 'mlp_type', 'mlp2')
847
+ self.linear_bias = getattr(config, 'linear_bias', True)
848
+
849
+
850
+ class ProjectorConfig:
851
+
852
+ def __init__(self, config: KimiK3Config):
853
+ self.mm_projector_type = config.mm_projector_type
854
+ self.mm_hidden_size = config.mm_hidden_size
855
+ self.hidden_size = config.text_hidden_size
856
+ self.merge_kernel_size = config.merge_kernel_size
857
+ self.projector_hidden_act = config.projector_hidden_act
858
+ self.projector_ln_eps = config.projector_ln_eps
859
+
860
+
861
+ # ref https://github.com/huggingface/transformers/blob/78b2929c0554b79e0489b451ce4ece14d265ead2/src/transformers/models/llava/modeling_llava.py#L240
862
+ class KimiK3ForConditionalGeneration(KimiK3PreTrainedModel):
863
+
864
+ @classmethod
865
+ def _supports_default_dynamic_cache(cls) -> bool:
866
+ return False
867
+
868
+ def __init__(self, config: KimiK3Config):
869
+ super().__init__(config)
870
+
871
+ vt_config = VisionTowerConfig(config.vision_config)
872
+ self.vision_tower = MoonViT3dPretrainedModel(vt_config)
873
+
874
+ proj_config = ProjectorConfig(config.vision_config)
875
+ if proj_config.mm_projector_type == 'identity':
876
+ self.mm_projector = IdentityMap()
877
+ elif proj_config.mm_projector_type == 'mlp':
878
+ self.mm_projector = MLP(proj_config)
879
+ elif proj_config.mm_projector_type == 'patchmerger':
880
+ self.mm_projector = PatchMergerMLP(proj_config)
881
+ elif proj_config.mm_projector_type == 'patchmergerv2':
882
+ self.mm_projector = PatchMergerMLPV2(proj_config)
883
+ else:
884
+ raise ValueError(
885
+ f"Unsupported mm_projector_type: {proj_config.mm_projector_type}"
886
+ )
887
+
888
+ self.language_model = KimiLinearForCausalLM(config.text_config)
889
+ self.post_init()
890
+
891
+ if hasattr(self.language_model, 'dtype'):
892
+ target_dtype = self.language_model.dtype
893
+ self.vision_tower = self.vision_tower.to(dtype=target_dtype)
894
+ self.mm_projector = self.mm_projector.to(dtype=target_dtype)
895
+
896
+ def get_input_embeddings(self):
897
+ return self.language_model.get_input_embeddings()
898
+
899
+ def set_input_embeddings(self, value):
900
+ self.language_model.set_input_embeddings(value)
901
+
902
+ def get_output_embeddings(self):
903
+ return self.language_model.get_output_embeddings()
904
+
905
+ def set_output_embeddings(self, new_embeddings):
906
+ self.language_model.set_output_embeddings(new_embeddings)
907
+
908
+ def set_decoder(self, decoder):
909
+ self.language_model.set_decoder(decoder)
910
+
911
+ def get_decoder(self):
912
+ return self.language_model.get_decoder()
913
+
914
+ def tie_weights(self):
915
+ return self.language_model.tie_weights()
916
+
917
+ def resize_token_embeddings(self,
918
+ new_num_tokens: int | None = None,
919
+ pad_to_multiple_of=None) -> nn.Embedding:
920
+ model_embeds = self.language_model.resize_token_embeddings(
921
+ new_num_tokens, pad_to_multiple_of)
922
+ # update vocab size
923
+ self.config.text_config.vocab_size = model_embeds.num_embeddings
924
+ self.vocab_size = model_embeds.num_embeddings
925
+ return model_embeds
926
+
927
+ def _merge_input_ids_with_image_features(
928
+ self,
929
+ image_features: list[torch.Tensor],
930
+ inputs_embeds: torch.Tensor,
931
+ input_ids: torch.Tensor,
932
+ attention_mask: torch.Tensor,
933
+ labels: torch.Tensor | None = None,
934
+ ):
935
+ """
936
+ Args:
937
+ image_features (:obj:`torch.Tensor` of shape :obj:`(num_image_tokens, embed_dim)`):
938
+ The image features to merge with the input embeddings.
939
+ inputs_embeds (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length, embed_dim)`):
940
+ The input embeddings.
941
+ input_ids (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length)`):
942
+ The input ids.
943
+ attention_mask (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length)`):
944
+ The attention mask.
945
+ labels (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length)`, *optional*):
946
+ The labels.
947
+ """
948
+ _, embed_dim = image_features[0].shape
949
+ feature_lengths = [x.shape[0] for x in image_features]
950
+ image_features = torch.cat(image_features, dim=0)
951
+
952
+ image_token_index: int = self.config.media_placeholder_token_id
953
+ pad_token_id: int = self.config.pad_token_id
954
+ ignore_index: int = self.config.ignore_index
955
+
956
+ batch_size, sequence_length = input_ids.shape
957
+ left_padding = not torch.sum(
958
+ input_ids[:, -1] == torch.tensor(pad_token_id))
959
+
960
+ # 1. Create a mask to know where special image tokens are
961
+ _token_occupation_table = torch.ones_like(input_ids.flatten())
962
+ _token_occupation_table[input_ids.flatten() ==
963
+ image_token_index] = torch.tensor(
964
+ feature_lengths,
965
+ dtype=torch.long,
966
+ device=input_ids.device)
967
+ _token_occupation_table = _token_occupation_table.reshape(
968
+ input_ids.shape)
969
+
970
+ max_embed_dim = _token_occupation_table.sum(-1).max().item()
971
+ assert (
972
+ max_embed_dim >= sequence_length
973
+ ), f"The maximum embedding dimension ({max_embed_dim}) is less than the sequence length ({sequence_length})"
974
+ batch_indices, non_image_indices = torch.where(
975
+ input_ids != image_token_index)
976
+
977
+ # 2. Compute the positions where text should be written
978
+ # Calculate new positions for text tokens in merged image-text sequence.
979
+ new_token_positions = torch.cumsum(_token_occupation_table, -1) - 1
980
+ nb_image_pad = max_embed_dim - 1 - new_token_positions[:, -1]
981
+ if left_padding:
982
+ new_token_positions += nb_image_pad[:,
983
+ None] # offset for left padding
984
+ text_to_overwrite = new_token_positions[batch_indices,
985
+ non_image_indices]
986
+
987
+ # 3. Create the full embedding, already padded to the maximum position
988
+ final_embedding = torch.zeros(
989
+ batch_size,
990
+ max_embed_dim,
991
+ embed_dim,
992
+ dtype=inputs_embeds.dtype,
993
+ device=inputs_embeds.device,
994
+ )
995
+ final_attention_mask = torch.zeros(batch_size,
996
+ max_embed_dim,
997
+ dtype=attention_mask.dtype,
998
+ device=inputs_embeds.device)
999
+ if labels is not None:
1000
+ final_labels = torch.full(
1001
+ (batch_size, max_embed_dim),
1002
+ ignore_index,
1003
+ dtype=input_ids.dtype,
1004
+ device=input_ids.device,
1005
+ )
1006
+ # In case the Vision model or the Language model has been offloaded to CPU, we need to manually
1007
+ # set the corresponding tensors into their correct target device.
1008
+ target_device = inputs_embeds.device
1009
+ batch_indices, non_image_indices, text_to_overwrite = (
1010
+ batch_indices.to(target_device),
1011
+ non_image_indices.to(target_device),
1012
+ text_to_overwrite.to(target_device),
1013
+ )
1014
+ attention_mask = attention_mask.to(target_device)
1015
+
1016
+ # 4. Fill the embeddings based on the mask.
1017
+ final_embedding[batch_indices,
1018
+ text_to_overwrite] = inputs_embeds[batch_indices,
1019
+ non_image_indices]
1020
+ final_attention_mask[batch_indices,
1021
+ text_to_overwrite] = attention_mask[
1022
+ batch_indices, non_image_indices]
1023
+ if labels is not None:
1024
+ final_labels[batch_indices,
1025
+ text_to_overwrite] = labels[batch_indices,
1026
+ non_image_indices]
1027
+
1028
+ # 5. Fill the embeddings corresponding to the images. Anything that is not `text_positions` needs filling (#29835)
1029
+ image_to_overwrite = torch.full((batch_size, max_embed_dim),
1030
+ True,
1031
+ dtype=torch.bool,
1032
+ device=inputs_embeds.device)
1033
+ image_to_overwrite[batch_indices, text_to_overwrite] = False
1034
+ image_to_overwrite &= image_to_overwrite.cumsum(
1035
+ -1) - 1 >= nb_image_pad[:, None].to(target_device)
1036
+
1037
+ if image_to_overwrite.sum() != image_features.shape[:-1].numel():
1038
+ raise ValueError(
1039
+ f"The input provided to the model are wrong. The number of image tokens is {image_to_overwrite.sum()} while"
1040
+ f" the number of image features given to the model is {image_features.shape[:-1].numel()}. "
1041
+ "This prevents correct indexing and breaks batch generation.")
1042
+
1043
+ final_embedding[image_to_overwrite] = (
1044
+ image_features.contiguous().reshape(-1,
1045
+ embed_dim).to(target_device))
1046
+ final_attention_mask |= image_to_overwrite
1047
+ position_ids = (final_attention_mask.cumsum(-1) - 1).masked_fill_(
1048
+ (final_attention_mask == 0), 1)
1049
+
1050
+ # 6. Mask out the embedding at padding positions, as we later use the past_key_value value to determine the non-attended tokens.
1051
+ batch_indices, pad_indices = torch.where(input_ids == pad_token_id)
1052
+ indices_to_mask = new_token_positions[batch_indices, pad_indices]
1053
+
1054
+ final_embedding[batch_indices, indices_to_mask] = 0
1055
+
1056
+ if labels is None:
1057
+ final_labels = None
1058
+
1059
+ return final_embedding, final_attention_mask, final_labels, position_ids
1060
+
1061
+ def _extract_image_features(self, pixel_values: torch.Tensor,
1062
+ grid_thws: torch.Tensor) -> list[torch.Tensor]:
1063
+ """
1064
+ Args:
1065
+ pixel_values (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, num_channels, height, width)`):
1066
+ The pixel values of the images processed by image processor.
1067
+ grid_thws (:obj:`torch.Tensor` of shape :obj:`(batch_size, 3)`):
1068
+ The grid, height, width of the images.
1069
+
1070
+ Returns:
1071
+ selected_image_feature (:obj:`torch.FloatTensor` of shape :obj:`(num_image_tokens, embed_dim)`):
1072
+ The selected image features to use as input to the projector head.
1073
+
1074
+ """
1075
+
1076
+ target_dtype = self.vision_tower.patch_embed.proj.weight.dtype
1077
+ pixel_values = pixel_values.to(target_dtype)
1078
+
1079
+ image_features = self.vision_tower(pixel_values, grid_thws)
1080
+ return image_features
1081
+
1082
+ def forward(
1083
+ self,
1084
+ input_ids: torch.LongTensor | None = None,
1085
+ pixel_values: torch.FloatTensor | list[torch.FloatTensor]
1086
+ | None = None,
1087
+ grid_thws: torch.Tensor | None = None,
1088
+ attention_mask: torch.Tensor | None = None,
1089
+ position_ids: torch.LongTensor | None = None,
1090
+ past_key_values: list[torch.FloatTensor] | None = None,
1091
+ inputs_embeds: torch.FloatTensor | None = None,
1092
+ labels: torch.LongTensor | None = None,
1093
+ use_cache: bool | None = None,
1094
+ output_attentions: bool | None = None,
1095
+ output_hidden_states: bool | None = None,
1096
+ return_dict: bool | None = None,
1097
+ ) -> tuple | LlavaCausalLMOutputWithPast:
1098
+ r"""
1099
+ Args:
1100
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1101
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
1102
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
1103
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
1104
+
1105
+ ```"""
1106
+ assert self.vision_tower is not None, "vision_tower is not loaded"
1107
+ output_attentions = (output_attentions if output_attentions is not None
1108
+ else self.config.output_attentions)
1109
+ output_hidden_states = (output_hidden_states
1110
+ if output_hidden_states is not None else
1111
+ self.config.output_hidden_states)
1112
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1113
+
1114
+ if inputs_embeds is None:
1115
+ # 1. Extra the input embeddings
1116
+ inputs_embeds = self.get_input_embeddings()(input_ids)
1117
+
1118
+ # 2. Merge text and images
1119
+ if pixel_values is not None and len(
1120
+ pixel_values) > 0 and input_ids.shape[1] != 1:
1121
+ image_features = self._extract_image_features(
1122
+ pixel_values, grid_thws)
1123
+ if self.mm_projector:
1124
+ image_features = self.mm_projector(image_features)
1125
+
1126
+ inputs_embeds = inputs_embeds.to(
1127
+ image_features[0].dtype) # num_tokens, embed_dim
1128
+ inputs_embeds, attention_mask, labels, position_ids = (
1129
+ self._merge_input_ids_with_image_features(
1130
+ image_features,
1131
+ inputs_embeds,
1132
+ input_ids,
1133
+ attention_mask,
1134
+ labels,
1135
+ ))
1136
+
1137
+ # In case input_ids.shape[1] == 1 & pixel_values==None & past_key_values != None, we are in the case of
1138
+ # generation with cache
1139
+ elif (past_key_values is not None and pixel_values is not None
1140
+ and input_ids.shape[1] == 1):
1141
+ # Retrieve the first layer to inspect the logits and mask out the hidden states
1142
+ # that are set to 0
1143
+ first_layer_past_key_value = past_key_values[0][0][:, :, :, 0]
1144
+
1145
+ # Sum all dimensions of head_dim (-2) to avoid random errors such as: https://github.com/huggingface/transformers/pull/28032#issuecomment-1863691941
1146
+ batch_index, non_attended_tokens = torch.where(
1147
+ first_layer_past_key_value.float().sum(-2) == 0)
1148
+
1149
+ # Get the target length
1150
+ target_length = input_ids.shape[1]
1151
+ past_length = first_layer_past_key_value.shape[-1]
1152
+
1153
+ extended_attention_mask = torch.ones(
1154
+ (attention_mask.shape[0], past_length),
1155
+ dtype=attention_mask.dtype,
1156
+ device=attention_mask.device,
1157
+ )
1158
+
1159
+ # Filter out only the tokens that can be un-attended, this can happen
1160
+ # if one uses Llava + Fused modules where the cache on the
1161
+ # first iteration is already big enough, or if one passes custom cache
1162
+ valid_indices = non_attended_tokens < extended_attention_mask.size(
1163
+ -1)
1164
+ new_batch_index = batch_index[valid_indices]
1165
+ new_non_attended_tokens = non_attended_tokens[valid_indices]
1166
+
1167
+ # Zero-out the places where we don't need to attend
1168
+ extended_attention_mask[new_batch_index,
1169
+ new_non_attended_tokens] = 0
1170
+
1171
+ attention_mask = torch.cat(
1172
+ (extended_attention_mask, attention_mask[:,
1173
+ -target_length:]),
1174
+ dim=1)
1175
+ position_ids = torch.sum(attention_mask,
1176
+ dim=1).unsqueeze(-1) - 1
1177
+
1178
+ outputs = self.language_model(
1179
+ attention_mask=attention_mask,
1180
+ position_ids=position_ids,
1181
+ past_key_values=past_key_values,
1182
+ inputs_embeds=inputs_embeds,
1183
+ use_cache=use_cache,
1184
+ output_attentions=output_attentions,
1185
+ output_hidden_states=output_hidden_states,
1186
+ return_dict=return_dict,
1187
+ )
1188
+
1189
+ logits = outputs[0]
1190
+
1191
+ loss = None
1192
+ if labels is not None:
1193
+ # Shift so that tokens < n predict n
1194
+ if attention_mask is not None:
1195
+ shift_attention_mask = attention_mask[..., 1:]
1196
+ shift_logits = logits[..., :-1, :][shift_attention_mask.to(
1197
+ logits.device) != 0].contiguous()
1198
+ shift_labels = labels[..., 1:][shift_attention_mask.to(
1199
+ labels.device) != 0].contiguous()
1200
+ else:
1201
+ shift_logits = logits[..., :-1, :].contiguous()
1202
+ shift_labels = labels[..., 1:].contiguous()
1203
+ # Flatten the tokens
1204
+ loss_fct = nn.CrossEntropyLoss()
1205
+ loss = loss_fct(
1206
+ shift_logits.view(-1, shift_logits.size(-1)),
1207
+ shift_labels.view(-1).to(shift_logits.device),
1208
+ )
1209
+
1210
+ if not return_dict:
1211
+ output = (logits, ) + outputs[1:]
1212
+ return (loss, ) + output if loss is not None else output
1213
+
1214
+ return LlavaCausalLMOutputWithPast(
1215
+ loss=loss,
1216
+ logits=logits,
1217
+ past_key_values=outputs.past_key_values,
1218
+ hidden_states=outputs.hidden_states,
1219
+ attentions=outputs.attentions,
1220
+ )
1221
+
1222
+ def prepare_inputs_for_generation(
1223
+ self,
1224
+ input_ids,
1225
+ past_key_values=None,
1226
+ inputs_embeds=None,
1227
+ pixel_values=None,
1228
+ grid_thws=None,
1229
+ attention_mask=None,
1230
+ **kwargs,
1231
+ ):
1232
+ if past_key_values is not None:
1233
+ if hasattr(past_key_values, "get_seq_length"):
1234
+ cache_length = past_key_values.get_seq_length()
1235
+ past_length = getattr(past_key_values, 'seen_tokens',
1236
+ cache_length)
1237
+ else:
1238
+ cache_length = past_length = past_key_values[0][0].shape[2]
1239
+
1240
+ # Keep only the unprocessed tokens:
1241
+ # 1 - If the length of the attention_mask exceeds the length of input_ids, then we are in a setting where
1242
+ # some of the inputs are exclusively passed as part of the cache (e.g. when passing input_embeds as
1243
+ # input)
1244
+ if attention_mask is not None and attention_mask.shape[
1245
+ 1] > input_ids.shape[1]:
1246
+ input_ids = input_ids[:, -(attention_mask.shape[1] -
1247
+ past_length):]
1248
+ # 2 - If the past_length is smaller than input_ids', then input_ids holds all input tokens. We can discard
1249
+ # input_ids based on the past_length.
1250
+ elif past_length < input_ids.shape[1]:
1251
+ input_ids = input_ids[:, past_length:]
1252
+ # 3 - Otherwise (past_length >= input_ids.shape[1]), let's assume input_ids only has unprocessed tokens.
1253
+ elif self.config.media_placeholder_token_id in input_ids:
1254
+ input_ids = input_ids[:, input_ids.shape[1] - 1:]
1255
+ # If the cache has seen more tokens than it can hold, then the cache has a size limit. Let's discard the
1256
+ # older attention values, as their corresponding values are not part of the input.
1257
+ if cache_length < past_length and attention_mask is not None:
1258
+ attention_mask = attention_mask[:, -(cache_length +
1259
+ input_ids.shape[1]):]
1260
+
1261
+ position_ids = kwargs.get("position_ids", None)
1262
+ if attention_mask is not None and position_ids is None:
1263
+ # create position_ids on the fly for batch generation
1264
+ position_ids = attention_mask.long().cumsum(-1) - 1
1265
+ position_ids.masked_fill_(attention_mask == 0, 1)
1266
+ if past_key_values:
1267
+ position_ids = position_ids[:, -input_ids.shape[1]:]
1268
+
1269
+ # if `inputs_embeds` are passed, we only want to use them in the 1st generation step
1270
+ if inputs_embeds is not None and past_key_values is None:
1271
+ model_inputs = {"inputs_embeds": inputs_embeds}
1272
+ else:
1273
+ model_inputs = {"input_ids": input_ids}
1274
+
1275
+ model_inputs.update({
1276
+ "position_ids": position_ids,
1277
+ "past_key_values": past_key_values,
1278
+ "use_cache": kwargs.get("use_cache"),
1279
+ "attention_mask": attention_mask,
1280
+ "pixel_values": pixel_values,
1281
+ "grid_thws": grid_thws,
1282
+ })
1283
+ return model_inputs
1284
+
1285
+ def _reorder_cache(self, *args, **kwargs):
1286
+ return self.language_model._reorder_cache(*args, **kwargs)
preprocessor_config.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "media_proc_cfg": {
3
+ "in_patch_limit": 65536,
4
+ "patch_size": 14,
5
+ "image_mean": [
6
+ 0.5,
7
+ 0.5,
8
+ 0.5
9
+ ],
10
+ "image_std": [
11
+ 0.5,
12
+ 0.5,
13
+ 0.5
14
+ ],
15
+ "merge_kernel_size": 2,
16
+ "fixed_output_tokens": null,
17
+ "patch_limit_on_one_side": 512,
18
+ "in_patch_limit_each_frame": 16384,
19
+ "in_patch_limit_video": 655360,
20
+ "sample_fps": 8.0,
21
+ "max_num_frames_each_video": null,
22
+ "temporal_merge_kernel_size": 4,
23
+ "timestamp_mode": "hh:mm:ss.fff",
24
+ "transparent_bg_config": {
25
+ "pattern": "chessboard",
26
+ "chessboard_square_size": 8,
27
+ "chessboard_square_on_top_left": true,
28
+ "chessboard_white_value": 255,
29
+ "chessboard_gray_value": 180
30
+ },
31
+ "transparent_bg_fill_stage": "after_resize",
32
+ "config_type": "media_proc.processors.moonvit.MoonViTMediaProcessorConfig"
33
+ },
34
+ "auto_map": {
35
+ "AutoProcessor": "kimi_k3_processor.KimiK3Processor",
36
+ "AutoImageProcessor": "kimi_k3_vision_processing.KimiK3VisionProcessor"
37
+ }
38
+ }
tiktoken.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b6c497a7469b33ced9c38afb1ad6e47f03f5e5dc05f15930799210ec050c5103
3
+ size 2795286
tokenization_kimi.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from logging import getLogger
3
+ from pathlib import Path
4
+ from shutil import copyfile
5
+ from typing import Dict, Iterator, List, Optional, Tuple, Union, cast
6
+
7
+ import tiktoken
8
+ from tiktoken.load import load_tiktoken_bpe
9
+ from tokenizers import AddedToken
10
+ from transformers.convert_slow_tokenizer import bytes_to_unicode
11
+ from transformers.tokenization_utils import PreTrainedTokenizer
12
+
13
+ try:
14
+ from .encoding_k3 import build_chat_segments, is_batched_conversation
15
+ except ImportError: # pragma: no cover - supports direct file execution/import.
16
+ from encoding_k3 import build_chat_segments, is_batched_conversation
17
+
18
+ logger = getLogger(__name__)
19
+ VOCAB_FILES_NAMES = {"vocab_file": "tiktoken.model"}
20
+
21
+
22
+ class TikTokenTokenizer(PreTrainedTokenizer):
23
+ """
24
+ Tokenizing and encoding/decoding text using the Tiktoken tokenizer. See megatron/tokenizer/tiktoken_tokenizer.py.
25
+
26
+ This tokenizer inherits from [`PreTrainedTokenizer`] which contains most of the main methods. Users should refer to
27
+ this superclass for more information regarding those methods.
28
+
29
+ Args:
30
+ vocab_file (`str`):
31
+ The path to the Tiktoken model file.
32
+ bos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|begin_of_text|>",`):
33
+ The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.
34
+ eos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|end_of_text|>"`):
35
+ The end of sequence token.
36
+ unk_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|reserved_special_token_249|>"`):
37
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
38
+ token instead. The second to last item in special_tokens.
39
+ pad_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|reserved_special_token_250|>"`):
40
+ The token used for padding, for example when batching sequences of different lengths.
41
+ additional_special_tokens (list of `str`, *optional*):
42
+ A tuple or a list of additional tokens, which will be marked as `special`, meaning that they will be
43
+ skipped when decoding if `skip_special_tokens` is set to `True`.
44
+ """
45
+
46
+ vocab_files_names = VOCAB_FILES_NAMES
47
+
48
+ model_input_names = ["input_ids", "attention_mask"]
49
+
50
+ special_tokens: Dict[str, int]
51
+
52
+ num_reserved_special_tokens = 256
53
+
54
+ pat_str = "|".join([
55
+ r"""[\p{Han}]+""",
56
+ r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]*[\p{Ll}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
57
+ r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]+[\p{Ll}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
58
+ r"""\p{N}{1,3}""",
59
+ r""" ?[^\s\p{L}\p{N}]+[\r\n]*""",
60
+ r"""\s*[\r\n]+""",
61
+ r"""\s+(?!\S)""",
62
+ r"""\s+""",
63
+ ])
64
+
65
+ def __init__(
66
+ self,
67
+ vocab_file,
68
+ bos_token: Union[str, AddedToken] = "[BOS]",
69
+ eos_token: Union[str, AddedToken] = "[EOS]",
70
+ unk_token: Union[str, AddedToken, None] = None,
71
+ pad_token: Union[str, AddedToken, None] = None,
72
+ additional_special_tokens: List[str] = None,
73
+ added_tokens_decoder: Optional[dict] = None,
74
+ **kwargs,
75
+ ):
76
+ assert os.path.isfile(vocab_file), vocab_file
77
+
78
+ if additional_special_tokens is None:
79
+ additional_special_tokens = [
80
+ "<|im_end|>",
81
+ "<|im_user|>",
82
+ "<|im_assistant|>",
83
+ "<|start_header_id|>",
84
+ "<|end_header_id|>",
85
+ "[EOT]",
86
+ "<|im_system|>",
87
+ "<|im_middle|>",
88
+ ]
89
+
90
+ if added_tokens_decoder:
91
+ special_tokens_mapping = {
92
+ i: added_tokens_decoder[i].content
93
+ for i in added_tokens_decoder
94
+ }
95
+ else:
96
+ special_tokens_mapping = {}
97
+
98
+ self.vocab_file = vocab_file
99
+ mergeable_ranks = load_tiktoken_bpe(vocab_file)
100
+ num_base_tokens = len(mergeable_ranks)
101
+ self.special_tokens = {
102
+ special_tokens_mapping.get(i, f"<|reserved_token_{i}|>"): i
103
+ for i in range(num_base_tokens, num_base_tokens +
104
+ self.num_reserved_special_tokens)
105
+ }
106
+
107
+ self.model = tiktoken.Encoding(
108
+ name=Path(vocab_file).name,
109
+ pat_str=self.pat_str,
110
+ mergeable_ranks=mergeable_ranks,
111
+ special_tokens=self.special_tokens,
112
+ )
113
+ logger.info(f"Reloaded tiktoken model from {vocab_file}")
114
+
115
+ self.n_words: int = self.model.n_vocab
116
+ # BOS / EOS token IDs
117
+ self.bos_id: int = self.special_tokens[str(bos_token)]
118
+ self.eos_id: int = self.special_tokens[str(eos_token)]
119
+ logger.info(
120
+ f"#words: {self.n_words} - BOS ID: {self.bos_id} - EOS ID: {self.eos_id}"
121
+ )
122
+
123
+ self.pad_id: int = self.special_tokens[str(pad_token)]
124
+ self.unk_id: int = self.special_tokens[str(unk_token)]
125
+
126
+ self.byte_encoder = bytes_to_unicode()
127
+ self.byte_decoder = {v: k for k, v in self.byte_encoder.items()}
128
+
129
+ self.decoder = {}
130
+ for i in range(self.n_words):
131
+ # Taken from https://gist.github.com/xenova/a452a6474428de0182b17605a98631ee
132
+ decoding = ''.join([
133
+ self.byte_encoder[ord(char)] for char in
134
+ self.model.decode_single_token_bytes(i).decode('latin-1')
135
+ ])
136
+ self.decoder[i] = decoding
137
+
138
+ self.encoder = {}
139
+ for i in range(self.n_words):
140
+ if i in self.decoder:
141
+ self.encoder[self.decoder[i]] = i
142
+
143
+ super().__init__(
144
+ bos_token=bos_token,
145
+ eos_token=eos_token,
146
+ unk_token=unk_token,
147
+ pad_token=pad_token,
148
+ additional_special_tokens=additional_special_tokens,
149
+ added_tokens_decoder=added_tokens_decoder,
150
+ **kwargs,
151
+ )
152
+ self.all_special_ids_set = set(self.all_special_ids)
153
+
154
+ def _encode_text_piece(self, text: str,
155
+ allow_special_tokens: bool = True) -> List[int]:
156
+ # The tiktoken tokenizer can handle <=400k chars without
157
+ # pyo3_runtime.PanicException.
158
+ TIKTOKEN_MAX_ENCODE_CHARS = 400_000
159
+
160
+ # https://github.com/openai/tiktoken/issues/195
161
+ # Here we iterate over subsequences and split if we exceed the limit
162
+ # of max consecutive non-whitespace or whitespace characters.
163
+ MAX_NO_WHITESPACES_CHARS = 25_000
164
+
165
+ t: List[int] = []
166
+ for i in range(0, len(text), TIKTOKEN_MAX_ENCODE_CHARS):
167
+ for substr in self._split_whitespaces_or_nonwhitespaces(
168
+ text[i:i + TIKTOKEN_MAX_ENCODE_CHARS],
169
+ MAX_NO_WHITESPACES_CHARS,
170
+ ):
171
+ if allow_special_tokens:
172
+ t.extend(
173
+ # structural markers: encode <|...|> as their special token IDs
174
+ self.model.encode(
175
+ substr,
176
+ allowed_special="all",
177
+ ))
178
+ else:
179
+ t.extend(
180
+ # user/tool text: encode any <|...|> as ordinary BPE tokens (never as control tokens)
181
+ self.model.encode(
182
+ substr,
183
+ disallowed_special=(),
184
+ ))
185
+
186
+ return t
187
+
188
+ def encode(self,
189
+ text: str,
190
+ allow_special_tokens: bool = True,
191
+ **kwargs) -> List[int]:
192
+ """
193
+ Encodes a string into a list of token IDs.
194
+
195
+ Args:
196
+ text (str): The input string to be encoded.
197
+
198
+ Returns:
199
+ list[int]: A list of token IDs.
200
+ """
201
+ # If there are other args, we should call super().encode because there are a lot of code
202
+ # to handle those args. supper().encode finally will call _tokenize and _convert_token_to_id.
203
+ # NOTE: our encode method is not compatible with the super().encode method,
204
+ # e.g. split_special_tokens' default is True in our encode method.
205
+ if len(kwargs) > 0:
206
+ logger.warning(f"Calling super().encode with {kwargs}")
207
+ return super().encode(text, **kwargs)
208
+
209
+ assert type(text) is str
210
+ return self._encode_text_piece(text,
211
+ allow_special_tokens=allow_special_tokens)
212
+
213
+ def decode(self, token_ids: Union[int, List[int]], **kwargs) -> str:
214
+ """
215
+ Decodes a list of token IDs into a string.
216
+
217
+ Args:
218
+ token_ids (List[int]): The list of token IDs to be decoded.
219
+
220
+ Returns:
221
+ str: The decoded string.
222
+ """
223
+ # If there are other args, we should call super().decode because there are a lot of code
224
+ # to handle those args. supper().encode finally will call convert_tokens_to_string and _convert_id_to_token.
225
+ if len(kwargs) > 0:
226
+ return super().decode(token_ids, **kwargs)
227
+
228
+ if type(token_ids) is int:
229
+ token_ids = [token_ids]
230
+
231
+ return self.model.decode(cast(List[int], token_ids))
232
+
233
+ @staticmethod
234
+ def _split_whitespaces_or_nonwhitespaces(
235
+ s: str, max_consecutive_slice_len: int) -> Iterator[str]:
236
+ """
237
+ Splits the string `s` so that each substring contains no more than `max_consecutive_slice_len`
238
+ consecutive whitespaces or consecutive non-whitespaces.
239
+ """
240
+ current_slice_len = 0
241
+ current_slice_is_space = s[0].isspace() if len(s) > 0 else False
242
+ slice_start = 0
243
+
244
+ for i in range(len(s)):
245
+ is_now_space = s[i].isspace()
246
+
247
+ if current_slice_is_space ^ is_now_space:
248
+ current_slice_len = 1
249
+ current_slice_is_space = is_now_space
250
+ else:
251
+ current_slice_len += 1
252
+ if current_slice_len > max_consecutive_slice_len:
253
+ yield s[slice_start:i]
254
+ slice_start = i
255
+ current_slice_len = 1
256
+ yield s[slice_start:]
257
+
258
+ def _encode_chat_segments(self, segments) -> List[int]:
259
+ token_ids: List[int] = []
260
+ for segment in segments:
261
+ token_ids.extend(
262
+ self._encode_text_piece(
263
+ segment.text,
264
+ allow_special_tokens=segment.allow_special,
265
+ ))
266
+ return token_ids
267
+
268
+ @staticmethod
269
+ def _truncate(ids: List[int],
270
+ truncation: bool = False,
271
+ max_length: Optional[int] = None) -> List[int]:
272
+ if truncation and max_length is not None:
273
+ return ids[:max_length]
274
+ return ids
275
+
276
+ def _format_chat_token_output(self,
277
+ encoded_inputs: List[List[int]],
278
+ *,
279
+ is_batched: bool,
280
+ padding=False,
281
+ truncation: bool = False,
282
+ max_length: Optional[int] = None,
283
+ return_tensors=None,
284
+ return_dict: bool = False):
285
+ encoded_inputs = [
286
+ self._truncate(ids, truncation=truncation, max_length=max_length)
287
+ for ids in encoded_inputs
288
+ ]
289
+
290
+ needs_batch_encoding = (
291
+ is_batched or padding or return_tensors is not None or return_dict)
292
+ if not needs_batch_encoding:
293
+ return encoded_inputs[0]
294
+
295
+ features = [{
296
+ "input_ids": ids,
297
+ "attention_mask": [1] * len(ids)
298
+ } for ids in encoded_inputs]
299
+ batch = self.pad(features,
300
+ padding=padding,
301
+ max_length=max_length if padding else None,
302
+ return_attention_mask=True,
303
+ return_tensors=return_tensors)
304
+
305
+ if return_dict:
306
+ return batch
307
+ if is_batched:
308
+ return batch["input_ids"]
309
+ return batch["input_ids"][0] if return_tensors is None else batch[
310
+ "input_ids"]
311
+
312
+ """ ----- Below are the abstract methods required by PreTrainedTokenizer ----- """
313
+
314
+ @property
315
+ def vocab_size(self) -> int:
316
+ return self.n_words
317
+
318
+ def get_vocab(self) -> Dict[str, int]:
319
+ return self.encoder
320
+
321
+ def _tokenize(self, text: str, **kwargs) -> List[str]:
322
+ return [self.decoder[t] for t in self.encode(text)]
323
+
324
+ def _convert_token_to_id(self, token: str) -> int:
325
+ return self.encoder.get(token, self.unk_id)
326
+
327
+ def _convert_id_to_token(self, index: int) -> str:
328
+ return self.decoder.get(index)
329
+
330
+ @staticmethod
331
+ def clean_up_tokenization(out_string: str) -> str:
332
+ return out_string
333
+
334
+ def convert_tokens_to_string(self, tokens: List[str]) -> str:
335
+ text = ''.join(tokens)
336
+ text = bytearray([self.byte_decoder[c]
337
+ for c in text]).decode('utf-8', 'replace')
338
+ return text
339
+
340
+ def save_vocabulary(self,
341
+ save_directory: str,
342
+ filename_prefix: Optional[str] = None) -> Tuple[str]:
343
+ if not os.path.isdir(save_directory):
344
+ raise ValueError(
345
+ f"vocabulary path ({save_directory}) should be a directory")
346
+ out_vocab_file = os.path.join(
347
+ save_directory,
348
+ (filename_prefix + "-" if filename_prefix else "") +
349
+ VOCAB_FILES_NAMES["vocab_file"])
350
+
351
+ if os.path.abspath(self.vocab_file) != os.path.abspath(
352
+ out_vocab_file) and os.path.isfile(self.vocab_file):
353
+ copyfile(self.vocab_file, out_vocab_file)
354
+
355
+ return (out_vocab_file, )
356
+
357
+ def apply_chat_template(self,
358
+ conversation,
359
+ tools: Optional[list[dict]] = None,
360
+ tokenize: bool = False,
361
+ add_generation_prompt: bool = True,
362
+ thinking: bool = True,
363
+ padding=False,
364
+ truncation: bool = False,
365
+ max_length: Optional[int] = None,
366
+ return_tensors=None,
367
+ return_dict: bool = False,
368
+ **kwargs):
369
+ # Tokenizer-level rendering reorders tool result messages to match
370
+ # assistant tool_calls, normalizes per-call arguments and response
371
+ # schema, then encodes the resulting XTML structure segment-by-segment.
372
+ is_batched = is_batched_conversation(conversation)
373
+ conversations = conversation if is_batched else [conversation]
374
+ image_prompts = kwargs.pop("image_prompts", None)
375
+ if is_batched and image_prompts is not None:
376
+ raise ValueError("image_prompts is only supported for one chat.")
377
+
378
+ # by default set thinking effort to max
379
+ kwargs.setdefault("thinking_effort", "max")
380
+
381
+ segment_batches = [
382
+ build_chat_segments(
383
+ messages,
384
+ tools=tools,
385
+ add_generation_prompt=add_generation_prompt,
386
+ thinking=thinking,
387
+ image_prompts=image_prompts,
388
+ **kwargs,
389
+ ) for messages in conversations
390
+ ]
391
+
392
+ if not tokenize:
393
+ rendered = ["".join(segment.text for segment in segments)
394
+ for segments in segment_batches]
395
+ return rendered if is_batched else rendered[0]
396
+
397
+ encoded_inputs = [
398
+ self._encode_chat_segments(segments) for segments in segment_batches
399
+ ]
400
+ return self._format_chat_token_output(
401
+ encoded_inputs,
402
+ is_batched=is_batched,
403
+ padding=padding,
404
+ truncation=truncation,
405
+ max_length=max_length,
406
+ return_tensors=return_tensors,
407
+ return_dict=return_dict,
408
+ )
tokenizer_config.json ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "163584": {
4
+ "content": "[BOS]",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "163585": {
12
+ "content": "[EOS]",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "163586": {
20
+ "content": "<|end_of_msg|>",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "163587": {
28
+ "content": "<|open|>",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": false
34
+ },
35
+ "163588": {
36
+ "content": "<|close|>",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": false
42
+ },
43
+ "163589": {
44
+ "content": "<|sep|>",
45
+ "lstrip": false,
46
+ "normalized": false,
47
+ "rstrip": false,
48
+ "single_word": false,
49
+ "special": false
50
+ },
51
+ "163590": {
52
+ "content": "[start_header_id]",
53
+ "lstrip": false,
54
+ "normalized": false,
55
+ "rstrip": false,
56
+ "single_word": false,
57
+ "special": true
58
+ },
59
+ "163591": {
60
+ "content": "[end_header_id]",
61
+ "lstrip": false,
62
+ "normalized": false,
63
+ "rstrip": false,
64
+ "single_word": false,
65
+ "special": true
66
+ },
67
+ "163593": {
68
+ "content": "[EOT]",
69
+ "lstrip": false,
70
+ "normalized": false,
71
+ "rstrip": false,
72
+ "single_word": false,
73
+ "special": true
74
+ },
75
+ "163602": {
76
+ "content": "<|media_begin|>",
77
+ "lstrip": false,
78
+ "normalized": false,
79
+ "rstrip": false,
80
+ "single_word": false,
81
+ "special": true
82
+ },
83
+ "163603": {
84
+ "content": "<|media_content|>",
85
+ "lstrip": false,
86
+ "normalized": false,
87
+ "rstrip": false,
88
+ "single_word": false,
89
+ "special": true
90
+ },
91
+ "163604": {
92
+ "content": "<|media_end|>",
93
+ "lstrip": false,
94
+ "normalized": false,
95
+ "rstrip": false,
96
+ "single_word": false,
97
+ "special": true
98
+ },
99
+ "163605": {
100
+ "content": "<|media_pad|>",
101
+ "lstrip": false,
102
+ "normalized": false,
103
+ "rstrip": false,
104
+ "single_word": false,
105
+ "special": true
106
+ },
107
+ "163649": {
108
+ "content": "<osagent_mode>",
109
+ "lstrip": false,
110
+ "normalized": false,
111
+ "rstrip": false,
112
+ "single_word": false,
113
+ "special": true
114
+ },
115
+ "163838": {
116
+ "content": "[UNK]",
117
+ "lstrip": false,
118
+ "normalized": false,
119
+ "rstrip": false,
120
+ "single_word": false,
121
+ "special": true
122
+ },
123
+ "163839": {
124
+ "content": "[PAD]",
125
+ "lstrip": false,
126
+ "normalized": false,
127
+ "rstrip": false,
128
+ "single_word": false,
129
+ "special": true
130
+ }
131
+ },
132
+ "additional_special_tokens": [
133
+ "<|end_of_msg|>",
134
+ "[start_header_id]",
135
+ "[end_header_id]",
136
+ "[EOT]",
137
+ "<|media_begin|>",
138
+ "<|media_content|>",
139
+ "<|media_end|>",
140
+ "<|media_pad|>",
141
+ "<osagent_mode>"
142
+ ],
143
+ "bos_token": "[BOS]",
144
+ "clean_up_tokenization_spaces": false,
145
+ "eos_token": "[EOS]",
146
+ "extra_special_tokens": {},
147
+ "model_max_length": 1000000000000000019884624838656,
148
+ "pad_token": "[PAD]",
149
+ "tokenizer_class": "TikTokenTokenizer",
150
+ "unk_token": "[UNK]",
151
+ "auto_map": {
152
+ "AutoTokenizer": [
153
+ "tokenization_kimi.TikTokenTokenizer",
154
+ null
155
+ ]
156
+ }
157
+ }