Image-Text-to-Text
Transformers
Safetensors
modilify_mk1
text-generation
diffusion
multimodal
mixture-of-experts
trust-remote-code
conversational
custom_code
Instructions to use modilify/Modilify-Mk1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use modilify/Modilify-Mk1 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="modilify/Modilify-Mk1", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("modilify/Modilify-Mk1", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use modilify/Modilify-Mk1 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "modilify/Modilify-Mk1" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "modilify/Modilify-Mk1", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/modilify/Modilify-Mk1
- SGLang
How to use modilify/Modilify-Mk1 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "modilify/Modilify-Mk1" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "modilify/Modilify-Mk1", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "modilify/Modilify-Mk1" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "modilify/Modilify-Mk1", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use modilify/Modilify-Mk1 with Docker Model Runner:
docker model run hf.co/modilify/Modilify-Mk1
Publish Modilify Mk1
Browse files- .gitattributes +2 -34
- LICENSE +125 -0
- NOTICE.md +263 -0
- README.md +265 -0
- assets/01-LOGO.jpg +3 -0
- chat_template.jinja +387 -0
- commit_policy.py +323 -0
- config.json +155 -0
- configuration_modilify_mk1.py +169 -0
- generation_config.json +16 -0
- generation_modilify_mk1.py +835 -0
- latent_deliberation.py +490 -0
- model-00001-of-00011.safetensors +3 -0
- model-00002-of-00011.safetensors +3 -0
- model-00003-of-00011.safetensors +3 -0
- model-00004-of-00011.safetensors +3 -0
- model-00005-of-00011.safetensors +3 -0
- model-00006-of-00011.safetensors +3 -0
- model-00007-of-00011.safetensors +3 -0
- model-00008-of-00011.safetensors +3 -0
- model-00009-of-00011.safetensors +3 -0
- model-00010-of-00011.safetensors +3 -0
- model-00011-of-00011.safetensors +3 -0
- model.safetensors.index.json +0 -0
- modeling_modilify_mk1.py +688 -0
- processor_config.json +75 -0
- tokenizer.json +3 -0
- tokenizer_config.json +96 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,3 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
assets/01-LOGO.jpg filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LICENSE
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Modilify Open Model License 1.0
|
| 2 |
+
|
| 3 |
+
Copyright 2026 Modilify
|
| 4 |
+
|
| 5 |
+
1. Definitions
|
| 6 |
+
|
| 7 |
+
"Model" means the weights, configuration, inference code, tokenizer, processor,
|
| 8 |
+
and documentation distributed with this license. "Derivative Model" means a
|
| 9 |
+
modified, fine-tuned, distilled, merged, quantized, or otherwise adapted version
|
| 10 |
+
of the Model. "You" means the individual or legal entity exercising permissions
|
| 11 |
+
under this license. "High-Risk Use" means a use that can materially affect a
|
| 12 |
+
person's safety, liberty, access to essential services, employment, housing,
|
| 13 |
+
credit, education, legal rights, or medical care, or that controls critical
|
| 14 |
+
infrastructure, weapons, or large-scale biometric surveillance.
|
| 15 |
+
|
| 16 |
+
2. Copyright Grant
|
| 17 |
+
|
| 18 |
+
Subject to this license, Modilify grants You a worldwide, perpetual,
|
| 19 |
+
non-exclusive, royalty-free, irrevocable copyright license to use, reproduce,
|
| 20 |
+
prepare derivative works of, publicly display, publicly perform, sublicense,
|
| 21 |
+
host as a service, and distribute the Model and Derivative Models, including for
|
| 22 |
+
commercial purposes.
|
| 23 |
+
|
| 24 |
+
3. Patent Grant
|
| 25 |
+
|
| 26 |
+
Each contributor grants You a worldwide, perpetual, non-exclusive, royalty-free,
|
| 27 |
+
irrevocable patent license, except as stated in this section, to make, have made,
|
| 28 |
+
use, offer to sell, sell, import, and otherwise transfer the Model where the
|
| 29 |
+
license applies only to those patent claims licensable by that contributor that
|
| 30 |
+
are necessarily infringed by that contributor's contribution alone or in
|
| 31 |
+
combination with the Model. If You institute patent litigation alleging that the
|
| 32 |
+
Model or a contribution constitutes patent infringement, patent licenses granted
|
| 33 |
+
to You under this license terminate as of the filing date.
|
| 34 |
+
|
| 35 |
+
4. Conditions on Redistribution
|
| 36 |
+
|
| 37 |
+
If You distribute the Model or a Derivative Model, You must:
|
| 38 |
+
|
| 39 |
+
a. provide recipients a copy of this license;
|
| 40 |
+
b. retain copyright, patent, attribution, and NOTICE statements;
|
| 41 |
+
c. state clearly that You modified the Model and identify material modifications;
|
| 42 |
+
d. preserve applicable third-party license and attribution notices; and
|
| 43 |
+
e. publish with the distributed model a reasonably accessible impact statement
|
| 44 |
+
describing intended uses, material limitations, evaluation scope, known
|
| 45 |
+
safety risks, and risk mitigations for the Derivative Model.
|
| 46 |
+
|
| 47 |
+
The impact statement may be maintained in a public model card or equivalent
|
| 48 |
+
document. You are not required to submit it separately to Modilify.
|
| 49 |
+
|
| 50 |
+
5. Responsible Use and High-Risk Uses
|
| 51 |
+
|
| 52 |
+
You must not use the Model or a Derivative Model:
|
| 53 |
+
|
| 54 |
+
a. to develop, operate, or materially facilitate weapons, autonomous targeting,
|
| 55 |
+
or systems intended to cause physical harm;
|
| 56 |
+
b. for unlawful mass surveillance, biometric identification without lawful
|
| 57 |
+
authority and appropriate safeguards, or social scoring that determines
|
| 58 |
+
access to rights or essential services;
|
| 59 |
+
c. to exploit children or vulnerable persons, facilitate human trafficking, or
|
| 60 |
+
generate non-consensual intimate content;
|
| 61 |
+
d. to impersonate a person or deceptively represent machine output as an
|
| 62 |
+
authentic human communication where the deception is reasonably likely to
|
| 63 |
+
cause material harm; or
|
| 64 |
+
e. to make a final decision in a High-Risk Use without meaningful qualified
|
| 65 |
+
human review, proportionate testing, monitoring, appeal or correction paths,
|
| 66 |
+
and compliance with applicable law.
|
| 67 |
+
|
| 68 |
+
Before deploying the Model in a High-Risk Use, You must perform safety and impact
|
| 69 |
+
due diligence proportionate to foreseeable harm. At minimum, document the use
|
| 70 |
+
context, evaluate relevant failure modes and affected groups, apply reasonable
|
| 71 |
+
technical and organizational safeguards, monitor material incidents, and update
|
| 72 |
+
or suspend the deployment when its residual risk is not reasonable. Research,
|
| 73 |
+
testing, auditing, and defensive safety work are permitted when conducted with
|
| 74 |
+
appropriate safeguards.
|
| 75 |
+
|
| 76 |
+
6. Trademarks
|
| 77 |
+
|
| 78 |
+
This license does not grant permission to use the trade names, trademarks,
|
| 79 |
+
service marks, or product names of Modilify or any contributor, except as needed
|
| 80 |
+
for reasonable and customary attribution or to describe the origin of the Model.
|
| 81 |
+
|
| 82 |
+
7. Third-Party Components
|
| 83 |
+
|
| 84 |
+
The Model includes or derives from third-party components identified in
|
| 85 |
+
NOTICE.md. Those components remain subject to their applicable
|
| 86 |
+
licenses and terms. In particular, rights and obligations associated with the
|
| 87 |
+
Google DiffusionGemma base are not removed, narrowed, or replaced by this
|
| 88 |
+
license. You are responsible for complying with all applicable upstream terms.
|
| 89 |
+
|
| 90 |
+
8. Termination and Reinstatement
|
| 91 |
+
|
| 92 |
+
Your rights terminate automatically if You materially violate this license and
|
| 93 |
+
do not cure the violation within 30 days after becoming aware of it. Rights are
|
| 94 |
+
reinstated upon timely cure unless a rights holder provides written notice of a
|
| 95 |
+
substantially similar repeated violation. Sections intended by their nature to
|
| 96 |
+
survive termination remain effective.
|
| 97 |
+
|
| 98 |
+
9. Disclaimer of Warranty
|
| 99 |
+
|
| 100 |
+
THE MODEL IS PROVIDED "AS IS," WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
| 101 |
+
EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF TITLE, NON-INFRINGEMENT,
|
| 102 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, OR SAFETY. YOU ARE
|
| 103 |
+
SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING OR REDISTRIBUTING
|
| 104 |
+
THE MODEL AND ASSUME ALL RISKS ASSOCIATED WITH YOUR EXERCISE OF PERMISSIONS.
|
| 105 |
+
|
| 106 |
+
10. Limitation of Liability
|
| 107 |
+
|
| 108 |
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR SHALL
|
| 109 |
+
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
| 110 |
+
CONSEQUENTIAL DAMAGES ARISING FROM THIS LICENSE OR THE USE OR INABILITY TO USE
|
| 111 |
+
THE MODEL, HOWEVER CAUSED AND UNDER ANY THEORY OF LIABILITY, EVEN IF ADVISED OF
|
| 112 |
+
THE POSSIBILITY OF SUCH DAMAGES.
|
| 113 |
+
|
| 114 |
+
11. Governing Law and Venue
|
| 115 |
+
|
| 116 |
+
This license is governed by the laws of the State of California, excluding its
|
| 117 |
+
conflict-of-law rules. Any dispute arising from this license must be brought in
|
| 118 |
+
the state or federal courts located in Santa Clara County, California, and each
|
| 119 |
+
party consents to their personal jurisdiction and venue.
|
| 120 |
+
|
| 121 |
+
12. Entire License; Severability
|
| 122 |
+
|
| 123 |
+
This document states the complete Modilify license for the Model, subject to
|
| 124 |
+
applicable third-party terms. If a provision is unenforceable, it will be limited
|
| 125 |
+
to the minimum extent necessary and the remaining provisions remain effective.
|
NOTICE.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modilify Mk1 — Notices
|
| 2 |
+
|
| 3 |
+
Copyright 2026 Modilify
|
| 4 |
+
|
| 5 |
+
This distribution is derived from `google/diffusiongemma-26B-A4B-it`, published
|
| 6 |
+
by Google DeepMind under Apache License 2.0. It retains the upstream multimodal
|
| 7 |
+
encoder, vision tower, vision projection, tokenizer, processor assets, and base
|
| 8 |
+
language-model parameters. Modilify merged the step-1000 low-rank updates and
|
| 9 |
+
added recurrent latent deliberation plus an excess-entropy
|
| 10 |
+
confidence-and-entropy commit policy.
|
| 11 |
+
|
| 12 |
+
The Modilify Open Model License 1.0 applies to Modilify's distribution and
|
| 13 |
+
original contributions. It does not erase, narrow, or replace rights and notices
|
| 14 |
+
applicable to upstream components. Users remain responsible for complying with
|
| 15 |
+
all applicable upstream terms.
|
| 16 |
+
|
| 17 |
+
- Upstream model: https://huggingface.co/google/diffusiongemma-26B-A4B-it
|
| 18 |
+
- Transformers project: https://github.com/huggingface/transformers
|
| 19 |
+
|
| 20 |
+
The remote model implementation subclasses public DiffusionGemma interfaces in
|
| 21 |
+
Hugging Face Transformers, which is also distributed under Apache License 2.0.
|
| 22 |
+
|
| 23 |
+
## Derivative Model Impact Statement Template
|
| 24 |
+
|
| 25 |
+
When distributing a derivative of Modilify Mk1, include a public impact
|
| 26 |
+
statement covering the following items. No separate submission to Modilify is
|
| 27 |
+
required.
|
| 28 |
+
|
| 29 |
+
### Identity and modifications
|
| 30 |
+
|
| 31 |
+
- Model name, version, publisher, and contact.
|
| 32 |
+
- Base version.
|
| 33 |
+
- Material modifications, data sources, merges, quantization, or adaptation.
|
| 34 |
+
|
| 35 |
+
### Intended and excluded uses
|
| 36 |
+
|
| 37 |
+
- Intended users and use cases.
|
| 38 |
+
- Explicitly excluded uses.
|
| 39 |
+
- Deployment context and degree of human oversight.
|
| 40 |
+
|
| 41 |
+
### Evaluation scope
|
| 42 |
+
|
| 43 |
+
- Evaluated capabilities and datasets.
|
| 44 |
+
- Languages, modalities, populations, or contexts not evaluated.
|
| 45 |
+
- Hardware and software used.
|
| 46 |
+
|
| 47 |
+
### Known limitations and foreseeable risks
|
| 48 |
+
|
| 49 |
+
- Reliability limitations.
|
| 50 |
+
- Safety, bias, privacy, security, and misuse risks.
|
| 51 |
+
- High-risk decisions the model must not make autonomously.
|
| 52 |
+
|
| 53 |
+
### Mitigations and monitoring
|
| 54 |
+
|
| 55 |
+
- Technical and organizational safeguards.
|
| 56 |
+
- Human review, appeal, and correction mechanisms.
|
| 57 |
+
- Monitoring, incident response, and update policy.
|
| 58 |
+
|
| 59 |
+
## Apache License 2.0
|
| 60 |
+
|
| 61 |
+
The complete license text applicable to the upstream components follows.
|
| 62 |
+
|
| 63 |
+
Apache License
|
| 64 |
+
Version 2.0, January 2004
|
| 65 |
+
http://www.apache.org/licenses/
|
| 66 |
+
|
| 67 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 68 |
+
|
| 69 |
+
1. Definitions.
|
| 70 |
+
|
| 71 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 72 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 73 |
+
|
| 74 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 75 |
+
the copyright owner that is granting the License.
|
| 76 |
+
|
| 77 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 78 |
+
other entities that control, are controlled by, or are under common
|
| 79 |
+
control with that entity. For the purposes of this definition,
|
| 80 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 81 |
+
direction or management of such entity, whether by contract or
|
| 82 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 83 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 84 |
+
|
| 85 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 86 |
+
exercising permissions granted by this License.
|
| 87 |
+
|
| 88 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 89 |
+
including but not limited to software source code, documentation
|
| 90 |
+
source, and configuration files.
|
| 91 |
+
|
| 92 |
+
"Object" form shall mean any form resulting from mechanical
|
| 93 |
+
transformation or translation of a Source form, including but
|
| 94 |
+
not limited to compiled object code, generated documentation,
|
| 95 |
+
and conversions to other media types.
|
| 96 |
+
|
| 97 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 98 |
+
Object form, made available under the License, as indicated by a
|
| 99 |
+
copyright notice that is included in or attached to the work
|
| 100 |
+
(an example is provided in the Appendix below).
|
| 101 |
+
|
| 102 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 103 |
+
form, that is based on (or derived from) the Work and for which the
|
| 104 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 105 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 106 |
+
of this License, Derivative Works shall not include works that remain
|
| 107 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 108 |
+
the Work and Derivative Works thereof.
|
| 109 |
+
|
| 110 |
+
"Contribution" shall mean any work of authorship, including
|
| 111 |
+
the original version of the Work and any modifications or additions
|
| 112 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 113 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 114 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 115 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 116 |
+
means any form of electronic, verbal, or written communication sent
|
| 117 |
+
to the Licensor or its representatives, including but not limited to
|
| 118 |
+
communication on electronic mailing lists, source code control systems,
|
| 119 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 120 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 121 |
+
excluding communication that is conspicuously marked or otherwise
|
| 122 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 123 |
+
|
| 124 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 125 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 126 |
+
subsequently incorporated within the Work.
|
| 127 |
+
|
| 128 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 129 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 130 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 131 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 132 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 133 |
+
Work and such Derivative Works in Source or Object form.
|
| 134 |
+
|
| 135 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 136 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 137 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 138 |
+
(except as stated in this section) patent license to make, have made,
|
| 139 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 140 |
+
where such license applies only to those patent claims licensable
|
| 141 |
+
by such Contributor that are necessarily infringed by their
|
| 142 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 143 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 144 |
+
institute patent litigation against any entity (including a
|
| 145 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 146 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 147 |
+
or contributory patent infringement, then any patent licenses
|
| 148 |
+
granted to You under this License for that Work shall terminate
|
| 149 |
+
as of the date such litigation is filed.
|
| 150 |
+
|
| 151 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 152 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 153 |
+
modifications, and in Source or Object form, provided that You
|
| 154 |
+
meet the following conditions:
|
| 155 |
+
|
| 156 |
+
(a) You must give any other recipients of the Work or
|
| 157 |
+
Derivative Works a copy of this License; and
|
| 158 |
+
|
| 159 |
+
(b) You must cause any modified files to carry prominent notices
|
| 160 |
+
stating that You changed the files; and
|
| 161 |
+
|
| 162 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 163 |
+
that You distribute, all copyright, patent, trademark, and
|
| 164 |
+
attribution notices from the Source form of the Work,
|
| 165 |
+
excluding those notices that do not pertain to any part of
|
| 166 |
+
the Derivative Works; and
|
| 167 |
+
|
| 168 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 169 |
+
distribution, then any Derivative Works that You distribute must
|
| 170 |
+
include a readable copy of the attribution notices contained
|
| 171 |
+
within such NOTICE file, excluding those notices that do not
|
| 172 |
+
pertain to any part of the Derivative Works, in at least one
|
| 173 |
+
of the following places: within a NOTICE text file distributed
|
| 174 |
+
as part of the Derivative Works; within the Source form or
|
| 175 |
+
documentation, if provided along with the Derivative Works; or,
|
| 176 |
+
within a display generated by the Derivative Works, if and
|
| 177 |
+
wherever such third-party notices normally appear. The contents
|
| 178 |
+
of the NOTICE file are for informational purposes only and
|
| 179 |
+
do not modify the License. You may add Your own attribution
|
| 180 |
+
notices within Derivative Works that You distribute, alongside
|
| 181 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 182 |
+
that such additional attribution notices cannot be construed
|
| 183 |
+
as modifying the License.
|
| 184 |
+
|
| 185 |
+
You may add Your own copyright statement to Your modifications and
|
| 186 |
+
may provide additional or different license terms and conditions
|
| 187 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 188 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 189 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 190 |
+
the conditions stated in this License.
|
| 191 |
+
|
| 192 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 193 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 194 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 195 |
+
this License, without any additional terms or conditions.
|
| 196 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 197 |
+
the terms of any separate license agreement you may have executed
|
| 198 |
+
with Licensor regarding such Contributions.
|
| 199 |
+
|
| 200 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 201 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 202 |
+
except as required for reasonable and customary use in describing the
|
| 203 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 204 |
+
|
| 205 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 206 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 207 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 208 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 209 |
+
implied, including, without limitation, any warranties or conditions
|
| 210 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 211 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 212 |
+
appropriateness of using or redistributing the Work and assume any
|
| 213 |
+
risks associated with Your exercise of permissions under this License.
|
| 214 |
+
|
| 215 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 216 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 217 |
+
unless required by applicable law (such as deliberate and grossly
|
| 218 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 219 |
+
liable to You for damages, including any direct, indirect, special,
|
| 220 |
+
incidental, or consequential damages of any character arising as a
|
| 221 |
+
result of this License or out of the use or inability to use the
|
| 222 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 223 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 224 |
+
other commercial damages or losses), even if such Contributor
|
| 225 |
+
has been advised of the possibility of such damages.
|
| 226 |
+
|
| 227 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 228 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 229 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 230 |
+
or other liability obligations and/or rights consistent with this
|
| 231 |
+
License. However, in accepting such obligations, You may act only
|
| 232 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 233 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 234 |
+
defend, and hold each Contributor harmless for any liability
|
| 235 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 236 |
+
of your accepting any such warranty or additional liability.
|
| 237 |
+
|
| 238 |
+
END OF TERMS AND CONDITIONS
|
| 239 |
+
|
| 240 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 241 |
+
|
| 242 |
+
To apply the Apache License to your work, attach the following
|
| 243 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 244 |
+
replaced with your own identifying information. (Don't include
|
| 245 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 246 |
+
comment syntax for the file format. We also recommend that a
|
| 247 |
+
file or class name and description of purpose be included on the
|
| 248 |
+
same "printed page" as the copyright notice for easier
|
| 249 |
+
identification within third-party archives.
|
| 250 |
+
|
| 251 |
+
Copyright [yyyy] [name of copyright owner]
|
| 252 |
+
|
| 253 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 254 |
+
you may not use this file except in compliance with the License.
|
| 255 |
+
You may obtain a copy of the License at
|
| 256 |
+
|
| 257 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 258 |
+
|
| 259 |
+
Unless required by applicable law or agreed to in writing, software
|
| 260 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 261 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 262 |
+
See the License for the specific language governing permissions and
|
| 263 |
+
limitations under the License.
|
README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
license_name: modilify-open-model-license-1.0
|
| 4 |
+
license_link: LICENSE
|
| 5 |
+
library_name: transformers
|
| 6 |
+
pipeline_tag: image-text-to-text
|
| 7 |
+
tags:
|
| 8 |
+
- diffusion
|
| 9 |
+
- multimodal
|
| 10 |
+
- image-text-to-text
|
| 11 |
+
- mixture-of-experts
|
| 12 |
+
- trust-remote-code
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+

|
| 16 |
+
|
| 17 |
+
# Modilify Mk1
|
| 18 |
+
|
| 19 |
+
A 26B-A5B multimodal block-diffusion model that thinks in latent space, commits only when it is ready, and was trained on a single Apple silicon machine in less than a day.
|
| 20 |
+
|
| 21 |
+
Modilify Mk1 is not another long-context decoder with a bigger reasoning budget. It is a Transformer-in-Transformer: the heavy DiffusionGemma trunk still sees text, images, and video, while a recurrent latent deliberation stack compresses the entire chain of thought into a compact hidden trajectory. Visible tokens are no longer the only place intelligence can live. The exclusive excess-entropy commit formula decides, every denoise, how many tokens the model is allowed to lock in. Easy problems finish fast. Hard problems keep deliberating.
|
| 22 |
+
|
| 23 |
+
This official release is the first public Mk1 checkpoint. It is materially more stable than [Modilify Mk1 Preview](https://huggingface.co/modilify/Modilify-Mk1-preview), restores the full vision tower, and ships default inference settings that run about **6× faster** than autoregressive models.
|
| 24 |
+
|
| 25 |
+
## Breakthroughs
|
| 26 |
+
|
| 27 |
+
| | |
|
| 28 |
+
| --- | --- |
|
| 29 |
+
| **One Mac. One day.** | Trained on a **single Apple silicon** machine in **less than 24 hours**. |
|
| 30 |
+
| **Seven million tokens.** | The adaptation used about **7 million training tokens**, not a web-scale second pretrain. |
|
| 31 |
+
| **Intelligence density** | Far more capability per activated parameter, and far more capability per training token, than a conventional post-train at this size. |
|
| 32 |
+
| **Transformer-in-Transformer** | A latent Transformer sits inside every heavy denoise and writes a recurrent memory that survives canvas commits. |
|
| 33 |
+
| **Latent CoT compression** | Chain-of-thought is compressed into token latents and 64 memory slots instead of being dumped into visible tokens. |
|
| 34 |
+
| **Exclusive commit formula** | Excess-entropy fusion, `p²` when entropy is honest, a hard prefix-risk budget, and a stagnation jump. Not a confidence threshold. |
|
| 35 |
+
| **6× default throughput** | Default settings target speed. Preview's quality-oriented knobs are still available when you want them. |
|
| 36 |
+
| **Adjustable inference speed** | Move `commit_failure_budget`, `denoise_temperature`, and the ponder / jump limits. Same weights, different operating point. |
|
| 37 |
+
| **More stable than Preview** | Cleaner commit geometry, stronger latent addressing, and no leftover adapter surface. |
|
| 38 |
+
| **Better agents** | Native thinking-channel control, tool-ready Gemma turns, and a latent scratchpad that does not pollute the user-visible transcript. |
|
| 39 |
+
|
| 40 |
+
## Why this is different
|
| 41 |
+
|
| 42 |
+
Most reasoning models buy intelligence with more visible tokens. That is expensive, leaky, and hard to stop. Mk1 buys it with **latent deliberation**:
|
| 43 |
+
|
| 44 |
+
1. Each heavy denoise still runs the 26B-A4B MoE trunk over a 256-token rolling canvas.
|
| 45 |
+
2. A 4-layer latent Transformer reads the noisy canvas, confidence, entropy, and age, then updates per-token latents plus a 64-slot persistent memory.
|
| 46 |
+
3. That compact state is mapped back through the frozen self-conditioning bridge and conditions the next heavy pass.
|
| 47 |
+
4. The exclusive commit formula then locks a variable-length prefix. The memory slots do not shift. The thought continues even after the visible tokens have moved on.
|
| 48 |
+
|
| 49 |
+
The result is elastic inference. You can spend more heavy-denoise work on a hard agent turn, or commit more tokens per pass and finish sooner when the problem is easy. Throughput is a configuration choice, not a second model.
|
| 50 |
+
|
| 51 |
+
## Efficiency
|
| 52 |
+
|
| 53 |
+
Mk1 is an argument about **intelligence per parameter** and **intelligence per training token**.
|
| 54 |
+
|
| 55 |
+
The released model activates 4.159B text parameters on a heavy denoise, plus the 570M vision encoder when images or video are present. The latent stack is small. The adaptation that produced this checkpoint ran on **one Apple silicon machine**, finished in **under 24 hours**, and saw about **7 million tokens**. That is not a claim that data does not matter. It is a claim that a better architecture can extract more from each token and each watt.
|
| 56 |
+
|
| 57 |
+
Default inference is the fast operating point. Compared with the slower Preview evaluation settings (`denoise_temperature=0.4`, `commit_failure_budget=0.05`, `jump_on_no_progress_after=32`), the Mk1 defaults are built for about **6× higher throughput**. Tighten the budget if you want Preview-like caution. Loosen it if you want the model to finish.
|
| 58 |
+
|
| 59 |
+
## Model Summary
|
| 60 |
+
|
| 61 |
+
| | |
|
| 62 |
+
| --- | ---: |
|
| 63 |
+
| Architecture | Mixture-of-Experts block diffusion + latent Transformer-in-Transformer |
|
| 64 |
+
| Total Parameters | 26.139B |
|
| 65 |
+
| Activated Parameters | 4.729B, including the vision encoder |
|
| 66 |
+
| Text Heavy-Denoise Activated Parameters | 4.159B |
|
| 67 |
+
| FLOPs per Heavy Denoise | ~2.12 TFLOPs at batch 1, 256-token canvas, empty KV prefix |
|
| 68 |
+
| Layers | 30 |
|
| 69 |
+
| Number of Experts | 128 |
|
| 70 |
+
| Selected Experts per Token | 8 |
|
| 71 |
+
| Number of Shared Experts | 1 |
|
| 72 |
+
| Vocabulary Size | 262,144 |
|
| 73 |
+
| Context Length | 262,144 tokens |
|
| 74 |
+
| Activation Function | GELU, tanh approximation |
|
| 75 |
+
| Vision Encoder | Gemma 4 Vision |
|
| 76 |
+
| Vision Encoder Parameters | 569.550M |
|
| 77 |
+
| Modality | Text, Image, Video |
|
| 78 |
+
| Sliding Window | 1024 tokens |
|
| 79 |
+
| Canvas Length | 256 |
|
| 80 |
+
| Latent Memory | 64 slots × 1,536-d, 4 layers |
|
| 81 |
+
| Training tokens | ~7 million |
|
| 82 |
+
|
| 83 |
+
The heavy-denoise FLOPs estimate counts multiply-adds as two FLOPs and covers decoder, expert, latent deliberation, and attention work only. It excludes the encoder pass, sampling/softmax, and elementwise ops. Batch size scales it roughly linearly: a 256-token prefix raises the estimate to ~2.16 TFLOPs, and a 4,096-token prefix to ~2.38 TFLOPs because some layers use full attention.
|
| 84 |
+
|
| 85 |
+
## Benchmark Results
|
| 86 |
+
|
| 87 |
+
| Benchmark | Modilify Mk1 | DiffusionGemma 26B A4B | Gemma 4 26B A4B |
|
| 88 |
+
| --- | ---: | --- | --- |
|
| 89 |
+
| MMLU Pro | 86.8 | 77.6 | 82.6 |
|
| 90 |
+
|
| 91 |
+
Only part of each dataset was evaluated, with one-shot prompting. Treat these values as unstable and non-comparable until the full benchmark release.
|
| 92 |
+
|
| 93 |
+
## Getting Started
|
| 94 |
+
|
| 95 |
+
Transformers 5.14.1 is the minimum supported version.
|
| 96 |
+
|
| 97 |
+
```shell
|
| 98 |
+
pip install -U transformers torch accelerate
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
### Text generation
|
| 102 |
+
|
| 103 |
+
```python
|
| 104 |
+
import torch
|
| 105 |
+
from transformers import AutoModelForMultimodalLM, AutoProcessor
|
| 106 |
+
|
| 107 |
+
model_id = "modilify/Modilify-Mk1"
|
| 108 |
+
processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
|
| 109 |
+
model = AutoModelForMultimodalLM.from_pretrained(
|
| 110 |
+
model_id,
|
| 111 |
+
trust_remote_code=True,
|
| 112 |
+
dtype=torch.bfloat16,
|
| 113 |
+
device_map="auto",
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
messages = [{"role": "user", "content": "Explain why the sky is blue."}]
|
| 117 |
+
inputs = processor.apply_chat_template(
|
| 118 |
+
messages,
|
| 119 |
+
tokenize=True,
|
| 120 |
+
add_generation_prompt=True,
|
| 121 |
+
enable_thinking=False,
|
| 122 |
+
return_dict=True,
|
| 123 |
+
return_tensors="pt",
|
| 124 |
+
).to(model.device)
|
| 125 |
+
|
| 126 |
+
output = model.generate(**inputs, max_new_tokens=256)
|
| 127 |
+
new_tokens = output.sequences[:, inputs["input_ids"].shape[1]:]
|
| 128 |
+
print(processor.batch_decode(new_tokens, skip_special_tokens=False)[0])
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
+
### Image input
|
| 132 |
+
|
| 133 |
+
```python
|
| 134 |
+
from PIL import Image
|
| 135 |
+
|
| 136 |
+
image = Image.open("example.jpg").convert("RGB")
|
| 137 |
+
messages = [{
|
| 138 |
+
"role": "user",
|
| 139 |
+
"content": [
|
| 140 |
+
{"type": "image", "image": image},
|
| 141 |
+
{"type": "text", "text": "Describe the image and identify uncertainty."},
|
| 142 |
+
],
|
| 143 |
+
}]
|
| 144 |
+
inputs = processor.apply_chat_template(
|
| 145 |
+
messages,
|
| 146 |
+
tokenize=True,
|
| 147 |
+
add_generation_prompt=True,
|
| 148 |
+
enable_thinking=True,
|
| 149 |
+
return_dict=True,
|
| 150 |
+
return_tensors="pt",
|
| 151 |
+
).to(model.device)
|
| 152 |
+
output = model.generate(**inputs, max_new_tokens=256)
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
### Video-frame input
|
| 156 |
+
|
| 157 |
+
The processor represents video as a sampled sequence of frames. The following example uses PyAV to decode a short local clip and samples at most 32 RGB frames.
|
| 158 |
+
|
| 159 |
+
```python
|
| 160 |
+
import av
|
| 161 |
+
from PIL import Image
|
| 162 |
+
|
| 163 |
+
container = av.open("short_clip.mp4")
|
| 164 |
+
decoded = [Image.fromarray(frame.to_rgb().to_ndarray()) for frame in container.decode(video=0)]
|
| 165 |
+
stride = max(1, len(decoded) // 32)
|
| 166 |
+
frames = decoded[::stride][:32]
|
| 167 |
+
|
| 168 |
+
messages = [{
|
| 169 |
+
"role": "user",
|
| 170 |
+
"content": [
|
| 171 |
+
{"type": "video", "video": frames},
|
| 172 |
+
{"type": "text", "text": "Summarize the main visual events in order."},
|
| 173 |
+
],
|
| 174 |
+
}]
|
| 175 |
+
inputs = processor.apply_chat_template(
|
| 176 |
+
messages,
|
| 177 |
+
tokenize=True,
|
| 178 |
+
add_generation_prompt=True,
|
| 179 |
+
enable_thinking=True,
|
| 180 |
+
return_dict=True,
|
| 181 |
+
return_tensors="pt",
|
| 182 |
+
).to(model.device)
|
| 183 |
+
output = model.generate(**inputs, max_new_tokens=256)
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
## Thinking mode
|
| 187 |
+
|
| 188 |
+
The official Gemma chat template controls the prompt, not the model's first generated tokens.
|
| 189 |
+
|
| 190 |
+
- `enable_thinking=True` inserts a system turn that contains `<|think|>` and still ends the prompt at `<|turn>model`.
|
| 191 |
+
- `enable_thinking=False` does **not** inject an empty thought channel. The prompt ends at `<|turn>model`.
|
| 192 |
+
|
| 193 |
+
The model may still open `<|channel>thought` on its own. That is generation, not a template artifact. Applications should not assume hidden reasoning is complete, correct, or appropriate to expose to end users.
|
| 194 |
+
|
| 195 |
+
## Configurable inference parameters
|
| 196 |
+
|
| 197 |
+
All model-owned values below are serialized in `config.json` and may be changed before loading or through a copied configuration object.
|
| 198 |
+
|
| 199 |
+
| Parameter | Default | Meaning |
|
| 200 |
+
| --- | ---: | --- |
|
| 201 |
+
| `canvas_length` | 256 | Rolling diffusion canvas length |
|
| 202 |
+
| `denoise_temperature` | 0.8 | Sampling temperature |
|
| 203 |
+
| `commit_failure_budget` | 0.2 | Normal cumulative prefix risk limit |
|
| 204 |
+
| `jump_failure_budget` | 2.0 | Forced-jump cumulative risk limit |
|
| 205 |
+
| `jump_on_no_progress_after` | 12 | Stagnation threshold |
|
| 206 |
+
| `max_ponder_steps` | 64 | Watchdog multiplier per requested token |
|
| 207 |
+
| `min_trajectory_progress` | 0.005 | Minimum fused-risk improvement |
|
| 208 |
+
| `repetition_penalty` | 1.0 | Transformers-style repetition penalty |
|
| 209 |
+
| `latent_dim` | 1,536 | Latent state width |
|
| 210 |
+
| `latent_memory_slots` | 64 | Persistent memory slot count |
|
| 211 |
+
| `latent_num_layers` | 4 | Latent Transformer depth |
|
| 212 |
+
| `latent_num_heads` | 16 | Latent attention heads |
|
| 213 |
+
| `latent_local_attention_window` | 128 | Local token-attention window |
|
| 214 |
+
| `latent_dropout` | 0.0 | Inference dropout probability |
|
| 215 |
+
| `turn_end_token_id` | 106 | Gemma turn terminator |
|
| 216 |
+
|
| 217 |
+
Example override:
|
| 218 |
+
|
| 219 |
+
```python
|
| 220 |
+
from transformers import AutoConfig
|
| 221 |
+
|
| 222 |
+
config = AutoConfig.from_pretrained(model_id, trust_remote_code=True)
|
| 223 |
+
config.max_ponder_steps = 32
|
| 224 |
+
config.commit_failure_budget = 0.15
|
| 225 |
+
model = AutoModelForMultimodalLM.from_pretrained(
|
| 226 |
+
model_id,
|
| 227 |
+
config=config,
|
| 228 |
+
trust_remote_code=True,
|
| 229 |
+
dtype=torch.bfloat16,
|
| 230 |
+
device_map="auto",
|
| 231 |
+
)
|
| 232 |
+
```
|
| 233 |
+
|
| 234 |
+
Generation supports left-padded batches with independent stopping and `generated_lengths` for every row. Batch prompts of similar lengths together for the best throughput; KV-cache and canvas memory grow with batch size. Streaming and caller-supplied KV caches remain limited to batch size 1.
|
| 235 |
+
|
| 236 |
+
## Details
|
| 237 |
+
|
| 238 |
+
Trained on a single Apple silicon machine, in less than 24 hours, on about 7 million tokens.
|
| 239 |
+
|
| 240 |
+
Developed on Mac by Modilify.
|
| 241 |
+
|
| 242 |
+
## Evaluation status, limitations, and risks
|
| 243 |
+
|
| 244 |
+
The benchmark values above are partial one-shot estimates, not a complete evaluation. Export checks established checkpoint structure, exact adapter application, valid safetensors indexing, absence of residual adapters, and byte-level preservation of the vision tower and projection; they do not establish accuracy, robustness, calibration, fairness, safety, or fitness for use.
|
| 245 |
+
|
| 246 |
+
The model can hallucinate facts, citations, visual details, or temporal relationships; reproduce bias, unsafe content, personal information, or copyrighted material; and consume substantial time and memory during long iterative generation. Confidence-based commits are compute-control decisions, not guarantees of correctness. Visual performance can degrade with poor resolution, motion, occlusion, unusual aspect ratios, or domain shift.
|
| 247 |
+
|
| 248 |
+
Evaluate the exact deployment on representative, adversarial, and out-of-distribution inputs. Use layered safeguards, monitoring, incident response, and qualified human review, and never delegate autonomous high-risk medical, legal, financial, employment, housing, education, critical-infrastructure, or safety decisions to the model.
|
| 249 |
+
|
| 250 |
+
## License
|
| 251 |
+
|
| 252 |
+
Released under the [Modilify Open Model License 1.0](LICENSE), subject to its responsible-use and derivative-impact terms. Upstream rights, attribution, Apache-2.0 text, and the impact-statement template are retained in [NOTICE.md](NOTICE.md).
|
| 253 |
+
|
| 254 |
+
## Citation
|
| 255 |
+
|
| 256 |
+
```bibtex
|
| 257 |
+
@software{modilify_mk1_2026,
|
| 258 |
+
title = {Modilify Mk1},
|
| 259 |
+
author = {Modilify},
|
| 260 |
+
year = {2026},
|
| 261 |
+
note = {A multimodal latent-deliberation derivative of DiffusionGemma, trained on one Apple silicon machine}
|
| 262 |
+
}
|
| 263 |
+
```
|
| 264 |
+
|
| 265 |
+
Also cite the upstream DiffusionGemma release as requested by Google DeepMind.
|
assets/01-LOGO.jpg
ADDED
|
Git LFS Details
|
chat_template.jinja
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{#
|
| 2 |
+
Template: Google Gemma 4 Canonical Chat Template
|
| 3 |
+
Author: Google Gemma Engineering Team
|
| 4 |
+
Published: 2026-07-09
|
| 5 |
+
Context: Fixed tool-calling loops, turn closures, and thinking content-ordering.
|
| 6 |
+
#}
|
| 7 |
+
{%- macro format_parameters(properties, required, filter_keys=false) -%}
|
| 8 |
+
{%- set standard_keys = ['description', 'type', 'properties', 'required', 'nullable'] -%}
|
| 9 |
+
{%- set ns = namespace(found_first=false) -%}
|
| 10 |
+
{%- for key, value in properties | dictsort -%}
|
| 11 |
+
{%- set add_comma = false -%}
|
| 12 |
+
{%- if not filter_keys or key not in standard_keys -%}
|
| 13 |
+
{%- if ns.found_first %},{% endif -%}
|
| 14 |
+
{%- set ns.found_first = true -%}
|
| 15 |
+
{{ key }}:{
|
| 16 |
+
{%- if value['description'] -%}
|
| 17 |
+
description:<|"|>{{ value['description'] }}<|"|>
|
| 18 |
+
{%- set add_comma = true -%}
|
| 19 |
+
{%- endif -%}
|
| 20 |
+
{%- if value['type'] | upper == 'STRING' -%}
|
| 21 |
+
{%- if value['enum'] -%}
|
| 22 |
+
{%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
|
| 23 |
+
enum:{{ format_argument(value['enum']) }}
|
| 24 |
+
{%- endif -%}
|
| 25 |
+
{%- elif value['type'] | upper == 'ARRAY' -%}
|
| 26 |
+
{%- if value['items'] is mapping and value['items'] -%}
|
| 27 |
+
{%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
|
| 28 |
+
items:{
|
| 29 |
+
{%- set ns_items = namespace(found_first=false) -%}
|
| 30 |
+
{%- for item_key, item_value in value['items'] | dictsort -%}
|
| 31 |
+
{%- if item_value is not none -%}
|
| 32 |
+
{%- if ns_items.found_first %},{% endif -%}
|
| 33 |
+
{%- set ns_items.found_first = true -%}
|
| 34 |
+
{%- if item_key == 'properties' -%}
|
| 35 |
+
properties:{
|
| 36 |
+
{%- if item_value is mapping -%}
|
| 37 |
+
{{- format_parameters(item_value, value['items']['required'] | default([])) -}}
|
| 38 |
+
{%- endif -%}
|
| 39 |
+
}
|
| 40 |
+
{%- elif item_key == 'required' -%}
|
| 41 |
+
required:[
|
| 42 |
+
{%- for req_item in item_value -%}
|
| 43 |
+
<|"|>{{- req_item -}}<|"|>
|
| 44 |
+
{%- if not loop.last %},{% endif -%}
|
| 45 |
+
{%- endfor -%}
|
| 46 |
+
]
|
| 47 |
+
{%- elif item_key == 'type' -%}
|
| 48 |
+
{%- if item_value is string -%}
|
| 49 |
+
type:{{ format_argument(item_value | upper) }}
|
| 50 |
+
{%- else -%}
|
| 51 |
+
type:{{ format_argument(item_value | map('upper') | list) }}
|
| 52 |
+
{%- endif -%}
|
| 53 |
+
{%- else -%}
|
| 54 |
+
{{ item_key }}:{{ format_argument(item_value) }}
|
| 55 |
+
{%- endif -%}
|
| 56 |
+
{%- endif -%}
|
| 57 |
+
{%- endfor -%}
|
| 58 |
+
}
|
| 59 |
+
{%- endif -%}
|
| 60 |
+
{%- endif -%}
|
| 61 |
+
{%- if value['nullable'] %}
|
| 62 |
+
{%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
|
| 63 |
+
nullable:true
|
| 64 |
+
{%- endif -%}
|
| 65 |
+
{%- if value['type'] | upper == 'OBJECT' -%}
|
| 66 |
+
{%- if value['properties'] is defined and value['properties'] is mapping -%}
|
| 67 |
+
{%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
|
| 68 |
+
properties:{
|
| 69 |
+
{{- format_parameters(value['properties'], value['required'] | default([])) -}}
|
| 70 |
+
}
|
| 71 |
+
{%- elif value is mapping -%}
|
| 72 |
+
{%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
|
| 73 |
+
properties:{
|
| 74 |
+
{{- format_parameters(value, value['required'] | default([]), filter_keys=true) -}}
|
| 75 |
+
}
|
| 76 |
+
{%- endif -%}
|
| 77 |
+
{%- if value['required'] -%}
|
| 78 |
+
{%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
|
| 79 |
+
required:[
|
| 80 |
+
{%- for item in value['required'] | default([]) -%}
|
| 81 |
+
<|"|>{{- item -}}<|"|>
|
| 82 |
+
{%- if not loop.last %},{% endif -%}
|
| 83 |
+
{%- endfor -%}
|
| 84 |
+
]
|
| 85 |
+
{%- endif -%}
|
| 86 |
+
{%- endif -%}
|
| 87 |
+
{%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
|
| 88 |
+
type:<|"|>{{ value['type'] | upper }}<|"|>}
|
| 89 |
+
{%- endif -%}
|
| 90 |
+
{%- endfor -%}
|
| 91 |
+
{%- endmacro -%}
|
| 92 |
+
{%- macro format_function_declaration(tool_data) -%}
|
| 93 |
+
declaration:{{- tool_data['function']['name'] -}}{description:<|"|>{{- tool_data['function']['description'] -}}<|"|>
|
| 94 |
+
{%- set params = tool_data['function']['parameters'] -%}
|
| 95 |
+
{%- if params -%}
|
| 96 |
+
,parameters:{
|
| 97 |
+
{%- if params['properties'] -%}
|
| 98 |
+
properties:{ {{- format_parameters(params['properties'], params['required']) -}} },
|
| 99 |
+
{%- endif -%}
|
| 100 |
+
{%- if params['required'] -%}
|
| 101 |
+
required:[
|
| 102 |
+
{%- for item in params['required'] -%}
|
| 103 |
+
<|"|>{{- item -}}<|"|>
|
| 104 |
+
{{- ',' if not loop.last -}}
|
| 105 |
+
{%- endfor -%}
|
| 106 |
+
],
|
| 107 |
+
{%- endif -%}
|
| 108 |
+
{%- if params['type'] -%}
|
| 109 |
+
type:<|"|>{{- params['type'] | upper -}}<|"|>}
|
| 110 |
+
{%- endif -%}
|
| 111 |
+
{%- endif -%}
|
| 112 |
+
{%- if 'response' in tool_data['function'] -%}
|
| 113 |
+
{%- set response_declaration = tool_data['function']['response'] -%}
|
| 114 |
+
,response:{
|
| 115 |
+
{%- if response_declaration['description'] -%}
|
| 116 |
+
description:<|"|>{{- response_declaration['description'] -}}<|"|>,
|
| 117 |
+
{%- endif -%}
|
| 118 |
+
{%- if response_declaration['type'] | upper == 'OBJECT' -%}
|
| 119 |
+
type:<|"|>{{- response_declaration['type'] | upper -}}<|"|>}
|
| 120 |
+
{%- endif -%}
|
| 121 |
+
{%- endif -%}
|
| 122 |
+
}
|
| 123 |
+
{%- endmacro -%}
|
| 124 |
+
{%- macro format_argument(argument, escape_keys=True) -%}
|
| 125 |
+
{%- if argument is none -%}
|
| 126 |
+
{{- 'null' -}}
|
| 127 |
+
{%- elif argument is string -%}
|
| 128 |
+
{{- '<|"|>' + argument + '<|"|>' -}}
|
| 129 |
+
{%- elif argument is boolean -%}
|
| 130 |
+
{{- 'true' if argument else 'false' -}}
|
| 131 |
+
{%- elif argument is mapping -%}
|
| 132 |
+
{{- '{' -}}
|
| 133 |
+
{%- set ns = namespace(found_first=false) -%}
|
| 134 |
+
{%- for key, value in argument | dictsort -%}
|
| 135 |
+
{%- if ns.found_first %},{% endif -%}
|
| 136 |
+
{%- set ns.found_first = true -%}
|
| 137 |
+
{%- if escape_keys -%}
|
| 138 |
+
{{- '<|"|>' + key + '<|"|>' -}}
|
| 139 |
+
{%- else -%}
|
| 140 |
+
{{- key -}}
|
| 141 |
+
{%- endif -%}
|
| 142 |
+
:{{- format_argument(value, escape_keys=escape_keys) -}}
|
| 143 |
+
{%- endfor -%}
|
| 144 |
+
{{- '}' -}}
|
| 145 |
+
{%- elif argument is sequence -%}
|
| 146 |
+
{{- '[' -}}
|
| 147 |
+
{%- for item in argument -%}
|
| 148 |
+
{{- format_argument(item, escape_keys=escape_keys) -}}
|
| 149 |
+
{%- if not loop.last %},{% endif -%}
|
| 150 |
+
{%- endfor -%}
|
| 151 |
+
{{- ']' -}}
|
| 152 |
+
{%- else -%}
|
| 153 |
+
{{- argument -}}
|
| 154 |
+
{%- endif -%}
|
| 155 |
+
{%- endmacro -%}
|
| 156 |
+
{%- macro strip_thinking(text) -%}
|
| 157 |
+
{%- set ns = namespace(result='') -%}
|
| 158 |
+
{%- for part in text.split('<channel|>') -%}
|
| 159 |
+
{%- if '<|channel>' in part -%}
|
| 160 |
+
{%- set ns.result = ns.result + part.split('<|channel>')[0] -%}
|
| 161 |
+
{%- else -%}
|
| 162 |
+
{%- set ns.result = ns.result + part -%}
|
| 163 |
+
{%- endif -%}
|
| 164 |
+
{%- endfor -%}
|
| 165 |
+
{{- ns.result | trim -}}
|
| 166 |
+
{%- endmacro -%}
|
| 167 |
+
|
| 168 |
+
{%- macro format_tool_response_block(tool_name, response) -%}
|
| 169 |
+
{{- '<|tool_response>' -}}
|
| 170 |
+
{%- if response is mapping -%}
|
| 171 |
+
{{- 'response:' + tool_name + '{' -}}
|
| 172 |
+
{%- for key, value in response | dictsort -%}
|
| 173 |
+
{{- key -}}:{{- format_argument(value, escape_keys=False) -}}
|
| 174 |
+
{%- if not loop.last %},{% endif -%}
|
| 175 |
+
{%- endfor -%}
|
| 176 |
+
{{- '}' -}}
|
| 177 |
+
{%- else -%}
|
| 178 |
+
{{- 'response:' + tool_name + '{value:' + format_argument(response, escape_keys=False) + '}' -}}
|
| 179 |
+
{%- endif -%}
|
| 180 |
+
{{- '<tool_response|>' -}}
|
| 181 |
+
{%- endmacro -%}
|
| 182 |
+
|
| 183 |
+
{#- ===== SETUP ===== -#}
|
| 184 |
+
{%- set ns = namespace(prev_message_type=None, prev_non_tool_role=None) -%}
|
| 185 |
+
{%- set loop_messages = messages -%}
|
| 186 |
+
{%- set enable_thinking = enable_thinking | default(false) -%}
|
| 187 |
+
{%- set preserve_thinking = preserve_thinking | default(false) -%}
|
| 188 |
+
{{- bos_token -}}
|
| 189 |
+
{#- Handle System/Tool Definitions Block -#}
|
| 190 |
+
{%- if enable_thinking or tools or (messages and messages[0]['role'] in ['system', 'developer']) -%}
|
| 191 |
+
{{- '<|turn>system\n' -}}
|
| 192 |
+
{#- Inject Thinking token at the very top of the FIRST system turn -#}
|
| 193 |
+
{%- if enable_thinking -%}
|
| 194 |
+
{{- '<|think|>\n' -}}
|
| 195 |
+
{%- set ns.prev_message_type = 'think' -%}
|
| 196 |
+
{%- endif -%}
|
| 197 |
+
{%- if messages and messages[0]['role'] in ['system', 'developer'] -%}
|
| 198 |
+
{%- if messages[0]['content'] is string -%}
|
| 199 |
+
{{- messages[0]['content'] | trim -}}
|
| 200 |
+
{%- elif messages[0]['content'] is sequence -%}
|
| 201 |
+
{%- for item in messages[0]['content'] -%}
|
| 202 |
+
{{- item['text'] | trim + ' '-}}
|
| 203 |
+
{%- endfor -%}
|
| 204 |
+
{%- endif -%}
|
| 205 |
+
{%- set loop_messages = messages[1:] -%}
|
| 206 |
+
{%- endif -%}
|
| 207 |
+
{%- if tools -%}
|
| 208 |
+
{%- for tool in tools %}
|
| 209 |
+
{{- '<|tool>' -}}
|
| 210 |
+
{{- format_function_declaration(tool) | trim -}}
|
| 211 |
+
{{- '<tool|>' -}}
|
| 212 |
+
{%- endfor %}
|
| 213 |
+
{%- set ns.prev_message_type = 'tool' -%}
|
| 214 |
+
{%- endif -%}
|
| 215 |
+
{{- '<turn|>\n' -}}
|
| 216 |
+
{%- endif %}
|
| 217 |
+
|
| 218 |
+
{#- Pre-scan: find last user message index for reasoning guard -#}
|
| 219 |
+
{%- set ns_turn = namespace(last_user_idx=-1) -%}
|
| 220 |
+
{%- for i in range(loop_messages | length) -%}
|
| 221 |
+
{%- if loop_messages[i]['role'] == 'user' -%}
|
| 222 |
+
{%- set ns_turn.last_user_idx = i -%}
|
| 223 |
+
{%- endif -%}
|
| 224 |
+
{%- endfor -%}
|
| 225 |
+
|
| 226 |
+
{#- Loop through messages -#}
|
| 227 |
+
{%- for message in loop_messages -%}
|
| 228 |
+
{%- if message['role'] != 'tool' -%}
|
| 229 |
+
{%- set ns.prev_message_type = None -%}
|
| 230 |
+
{%- set role = 'model' if message['role'] == 'assistant' else message['role'] -%}
|
| 231 |
+
{#- Detect continuation using tracked state — O(1) instead of O(n) backward scan -#}
|
| 232 |
+
{%- set continue_same_model_turn = (role == 'model' and ns.prev_non_tool_role == 'assistant') -%}
|
| 233 |
+
{%- if not continue_same_model_turn -%}
|
| 234 |
+
{{- '<|turn>' + role + '\n' }}
|
| 235 |
+
|
| 236 |
+
{%- endif -%}
|
| 237 |
+
|
| 238 |
+
{#- Render reasoning/reasoning_content as thinking channel -#}
|
| 239 |
+
{%- set thinking_text = message.get('reasoning') or message.get('reasoning_content') -%}
|
| 240 |
+
{%- set thinking_gate = (loop.index0 > ns_turn.last_user_idx) or (preserve_thinking and message.get('tool_calls')) -%}
|
| 241 |
+
{%- if thinking_text and thinking_gate -%}
|
| 242 |
+
{{- '<|channel>thought\n' + thinking_text + '\n<channel|>' -}}
|
| 243 |
+
{%- endif -%}
|
| 244 |
+
|
| 245 |
+
{%- if message.get('tool_calls') -%}
|
| 246 |
+
{%- for tool_call in message.get('tool_calls') -%}
|
| 247 |
+
{%- set function = tool_call['function'] -%}
|
| 248 |
+
{{- '<|tool_call>call:' + function['name'] + '{' -}}
|
| 249 |
+
{%- if function['arguments'] is mapping -%}
|
| 250 |
+
{%- set ns_args = namespace(found_first=false) -%}
|
| 251 |
+
{%- for key, value in function['arguments'] | dictsort -%}
|
| 252 |
+
{%- if ns_args.found_first %},{% endif -%}
|
| 253 |
+
{%- set ns_args.found_first = true -%}
|
| 254 |
+
{{- key -}}:{{- format_argument(value, escape_keys=False) -}}
|
| 255 |
+
{%- endfor -%}
|
| 256 |
+
{%- elif function['arguments'] is none -%}
|
| 257 |
+
{%- else -%}
|
| 258 |
+
{{- raise_exception(
|
| 259 |
+
"chat_template: tool_calls[].function.arguments must be a "
|
| 260 |
+
"JSON object (mapping), not a string. Deserialize arguments "
|
| 261 |
+
"before passing to the template."
|
| 262 |
+
) -}}
|
| 263 |
+
{%- endif -%}
|
| 264 |
+
{{- '}<tool_call|>' -}}
|
| 265 |
+
{%- endfor -%}
|
| 266 |
+
{%- set ns.prev_message_type = 'tool_call' -%}
|
| 267 |
+
{%- endif -%}
|
| 268 |
+
|
| 269 |
+
{%- set ns_tr_out = namespace(flag=false) -%}
|
| 270 |
+
{%- if message.get('tool_responses') -%}
|
| 271 |
+
{#- Legacy: tool_responses embedded on the assistant message (Google/Gemma native) -#}
|
| 272 |
+
{%- for tool_response in message.get('tool_responses') -%}
|
| 273 |
+
{{- format_tool_response_block(tool_response['name'] | default('unknown', true), tool_response['response']) -}}
|
| 274 |
+
{%- set ns_tr_out.flag = true -%}
|
| 275 |
+
{%- set ns.prev_message_type = 'tool_response' -%}
|
| 276 |
+
{%- endfor -%}
|
| 277 |
+
{%- elif message.get('tool_calls') -%}
|
| 278 |
+
{#- OpenAI Chat Completions: forward-scan consecutive role:tool messages -#}
|
| 279 |
+
{%- set ns_tool_scan = namespace(stopped=false) -%}
|
| 280 |
+
{%- for k in range(loop.index0 + 1, loop_messages | length) -%}
|
| 281 |
+
{%- if ns_tool_scan.stopped -%}
|
| 282 |
+
{%- elif loop_messages[k]['role'] != 'tool' -%}
|
| 283 |
+
{%- set ns_tool_scan.stopped = true -%}
|
| 284 |
+
{%- else -%}
|
| 285 |
+
{%- set follow = loop_messages[k] -%}
|
| 286 |
+
{#- Resolve tool_call_id to function name -#}
|
| 287 |
+
{%- set ns_tname = namespace(name=follow.get('name') or 'unknown') -%}
|
| 288 |
+
{%- for tc in message.get('tool_calls') -%}
|
| 289 |
+
{%- if tc.get('id') == follow.get('tool_call_id') -%}
|
| 290 |
+
{%- set ns_tname.name = tc['function']['name'] -%}
|
| 291 |
+
{%- endif -%}
|
| 292 |
+
{%- endfor -%}
|
| 293 |
+
{#- Handle content as string or content-parts array -#}
|
| 294 |
+
{%- set tool_body = follow.get('content') -%}
|
| 295 |
+
{%- if tool_body is string -%}
|
| 296 |
+
{{- format_tool_response_block(ns_tname.name, tool_body) -}}
|
| 297 |
+
{%- elif tool_body is sequence and tool_body is not string -%}
|
| 298 |
+
{%- set ns_txt = namespace(s='') -%}
|
| 299 |
+
{%- for part in tool_body -%}
|
| 300 |
+
{%- if part.get('type') == 'text' -%}
|
| 301 |
+
{%- set ns_txt.s = ns_txt.s + (part.get('text') | default('')) -%}
|
| 302 |
+
{%- endif -%}
|
| 303 |
+
{%- endfor -%}
|
| 304 |
+
{{- format_tool_response_block(ns_tname.name, ns_txt.s) -}}
|
| 305 |
+
{%- for part in tool_body -%}
|
| 306 |
+
{%- if part.get('type') in ['image', 'image_url'] -%}
|
| 307 |
+
{{- '<|image|>' -}}
|
| 308 |
+
{%- elif part.get('type') in ['audio', 'input_audio'] -%}
|
| 309 |
+
{{- '<|audio|>' -}}
|
| 310 |
+
{%- elif part.get('type') == 'video' -%}
|
| 311 |
+
{{- '<|video|>' -}}
|
| 312 |
+
{%- endif -%}
|
| 313 |
+
{%- endfor -%}
|
| 314 |
+
{%- else -%}
|
| 315 |
+
{{- format_tool_response_block(ns_tname.name, tool_body) -}}
|
| 316 |
+
{%- endif -%}
|
| 317 |
+
{%- set ns_tr_out.flag = true -%}
|
| 318 |
+
{%- set ns.prev_message_type = 'tool_response' -%}
|
| 319 |
+
{%- endif -%}
|
| 320 |
+
{%- endfor -%}
|
| 321 |
+
{%- endif -%}
|
| 322 |
+
|
| 323 |
+
{%- set captured_content -%}
|
| 324 |
+
{%- if message.get('content') is string -%}
|
| 325 |
+
{%- if role == 'model' -%}
|
| 326 |
+
{{- strip_thinking(message['content']) -}}
|
| 327 |
+
{%- else -%}
|
| 328 |
+
{{- message['content'] | trim -}}
|
| 329 |
+
{%- endif -%}
|
| 330 |
+
{%- elif message.get('content') is sequence -%}
|
| 331 |
+
{%- for item in message['content'] -%}
|
| 332 |
+
{%- if item.get('type') == 'text' -%}
|
| 333 |
+
{%- if role == 'model' -%}
|
| 334 |
+
{{- strip_thinking(item['text']) -}}
|
| 335 |
+
{%- else -%}
|
| 336 |
+
{{- item['text'] | trim -}}
|
| 337 |
+
{%- endif -%}
|
| 338 |
+
{%- elif item.get('type') in ['image', 'image_url'] -%}
|
| 339 |
+
{{- '<|image|>' -}}
|
| 340 |
+
{%- elif item.get('type') in ['audio', 'input_audio'] -%}
|
| 341 |
+
{{- '<|audio|>' -}}
|
| 342 |
+
{%- elif item.get('type') == 'video' -%}
|
| 343 |
+
{{- '<|video|>' -}}
|
| 344 |
+
{%- endif -%}
|
| 345 |
+
{%- endfor -%}
|
| 346 |
+
{%- endif -%}
|
| 347 |
+
{%- endset -%}
|
| 348 |
+
|
| 349 |
+
{{- captured_content -}}
|
| 350 |
+
{%- set has_content = captured_content | trim | length > 0 -%}
|
| 351 |
+
|
| 352 |
+
{#- Forward-scan: find next non-tool message role for continuation detection -#}
|
| 353 |
+
{%- set next_nt = namespace(role=None, found=false) -%}
|
| 354 |
+
{%- for j in range(loop.index0 + 1, loop_messages | length) -%}
|
| 355 |
+
{%- if not next_nt.found -%}
|
| 356 |
+
{%- if loop_messages[j]['role'] != 'tool' -%}
|
| 357 |
+
{%- set next_nt.role = loop_messages[j]['role'] -%}
|
| 358 |
+
{%- set next_nt.found = true -%}
|
| 359 |
+
{%- endif -%}
|
| 360 |
+
{%- endif -%}
|
| 361 |
+
{%- endfor -%}
|
| 362 |
+
|
| 363 |
+
{%- set continues_into_next = (
|
| 364 |
+
role == 'model'
|
| 365 |
+
and next_nt.role == 'assistant'
|
| 366 |
+
and (not message.get('tool_calls') or ns_tr_out.flag)
|
| 367 |
+
) -%}
|
| 368 |
+
|
| 369 |
+
{%- if ns.prev_message_type == 'tool_call' and not ns_tr_out.flag -%}
|
| 370 |
+
{{- '<|tool_response>' -}}
|
| 371 |
+
{%- elif continues_into_next -%}
|
| 372 |
+
{%- elif not (ns_tr_out.flag and not has_content and not next_nt.found) -%}
|
| 373 |
+
{{- '<turn|>\n' -}}
|
| 374 |
+
{%- endif -%}
|
| 375 |
+
{%- endif -%}
|
| 376 |
+
|
| 377 |
+
{#- Track previous non-tool role for next iteration (avoids O(n) backward scan) -#}
|
| 378 |
+
{%- set ns.prev_non_tool_role = message['role'] -%}
|
| 379 |
+
{%- endfor -%}
|
| 380 |
+
|
| 381 |
+
{%- if add_generation_prompt -%}
|
| 382 |
+
{%- if ns.prev_message_type != 'tool_response' and ns.prev_message_type != 'tool_call' -%}
|
| 383 |
+
{{- '<|turn>model\n' -}}
|
| 384 |
+
{%- elif ns.prev_message_type == 'tool_response' and enable_thinking -%}
|
| 385 |
+
{{- '<|channel>thought\n' -}}
|
| 386 |
+
{%- endif -%}
|
| 387 |
+
{%- endif -%}
|
commit_policy.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2026 Modilify
|
| 2 |
+
# SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0
|
| 3 |
+
"""Confidence-and-entropy commit policy for inference."""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from collections.abc import Sequence
|
| 8 |
+
from dataclasses import dataclass
|
| 9 |
+
import math
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
|
| 13 |
+
from .latent_deliberation import (
|
| 14 |
+
advance_trajectory_clocks,
|
| 15 |
+
should_force_trajectory_jump,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
FUSED_EPS = 1e-6
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def fused_commit_confidence(
|
| 22 |
+
proposal_confidence: torch.Tensor,
|
| 23 |
+
token_entropy: torch.Tensor,
|
| 24 |
+
*,
|
| 25 |
+
vocab_size: int = 256000,
|
| 26 |
+
eps: float = FUSED_EPS,
|
| 27 |
+
) -> torch.Tensor:
|
| 28 |
+
"""Fuse proposal confidence with token entropy.
|
| 29 |
+
|
| 30 |
+
Effective confidence uses an excess-entropy sigmoid:
|
| 31 |
+
|
| 32 |
+
p = clamp(proposal_confidence, eps, 1 - eps)
|
| 33 |
+
h2 = -p * log(p) - (1 - p) * log(1 - p)
|
| 34 |
+
excess = max(token_entropy - h2, 0)
|
| 35 |
+
fused = sigmoid(logit(p) - excess) ** 2
|
| 36 |
+
|
| 37 |
+
When token entropy equals the binary entropy of ``p``, fused confidence
|
| 38 |
+
equals ``p ** 2``. Entropy above that binary entropy reduces confidence.
|
| 39 |
+
|
| 40 |
+
Args:
|
| 41 |
+
proposal_confidence: Sampled-token probabilities, shape ``[batch, canvas]``.
|
| 42 |
+
token_entropy: Token-level entropy, shape ``[batch, canvas]``.
|
| 43 |
+
vocab_size: Unused; retained so callers can pass the model vocabulary.
|
| 44 |
+
eps: Clamp that keeps logits finite.
|
| 45 |
+
|
| 46 |
+
Returns:
|
| 47 |
+
Fused commit confidence in ``(eps, 1 - eps)``.
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
del vocab_size
|
| 51 |
+
p = proposal_confidence.float().clamp(min=eps, max=1.0 - eps)
|
| 52 |
+
entropy = token_entropy.float().clamp(min=0.0)
|
| 53 |
+
binary_entropy = -p * torch.log(p) - (1.0 - p) * torch.log1p(-p)
|
| 54 |
+
excess = (entropy - binary_entropy).clamp(min=0.0)
|
| 55 |
+
logit_p = torch.log(p) - torch.log1p(-p)
|
| 56 |
+
fused = torch.sigmoid(logit_p - excess).square()
|
| 57 |
+
return fused.clamp(min=eps, max=1.0 - eps)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def fused_commit_failure_rate(
|
| 61 |
+
proposal_confidence: torch.Tensor,
|
| 62 |
+
token_entropy: torch.Tensor,
|
| 63 |
+
**kwargs: object,
|
| 64 |
+
) -> torch.Tensor:
|
| 65 |
+
"""Return ``1 - fused_commit_confidence``."""
|
| 66 |
+
|
| 67 |
+
return 1.0 - fused_commit_confidence(
|
| 68 |
+
proposal_confidence, token_entropy, **kwargs
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@dataclass(frozen=True)
|
| 73 |
+
class CommitPolicyDecision:
|
| 74 |
+
"""One inference transition from proposal to committed prefix."""
|
| 75 |
+
|
| 76 |
+
normal_lengths: torch.LongTensor
|
| 77 |
+
commit_lengths: torch.LongTensor
|
| 78 |
+
commit_token_ids: torch.LongTensor
|
| 79 |
+
jump_rows: torch.BoolTensor
|
| 80 |
+
ponder_steps: torch.IntTensor
|
| 81 |
+
stagnation_steps: torch.IntTensor
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def prefix_failure_commit_lengths(
|
| 85 |
+
failure_rate: torch.Tensor,
|
| 86 |
+
*,
|
| 87 |
+
failure_budget: float,
|
| 88 |
+
valid_mask: torch.BoolTensor | None = None,
|
| 89 |
+
) -> torch.LongTensor:
|
| 90 |
+
"""Return the longest prefix with ``cumsum(failure_rate) < budget``.
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
failure_rate: Per-token failure rates, shape ``[batch, canvas]``.
|
| 94 |
+
failure_budget: Strict cumulative risk limit.
|
| 95 |
+
valid_mask: Optional canvas mask with the same shape.
|
| 96 |
+
|
| 97 |
+
Returns:
|
| 98 |
+
Commit lengths of shape ``[batch]``.
|
| 99 |
+
"""
|
| 100 |
+
|
| 101 |
+
if failure_rate.ndim != 2:
|
| 102 |
+
raise ValueError("Failure rate must have shape [batch, canvas].")
|
| 103 |
+
if not math.isfinite(failure_budget) or failure_budget <= 0:
|
| 104 |
+
raise ValueError("Commit failure budget must be finite and positive.")
|
| 105 |
+
if valid_mask is None:
|
| 106 |
+
valid_mask = torch.ones_like(failure_rate, dtype=torch.bool)
|
| 107 |
+
if valid_mask.shape != failure_rate.shape:
|
| 108 |
+
raise ValueError("Commit validity mask must match failure rate.")
|
| 109 |
+
|
| 110 |
+
risk = failure_rate.float().clamp(0.0, 1.0) * valid_mask.to(torch.float32)
|
| 111 |
+
cumulative_risk = risk.cumsum(dim=-1)
|
| 112 |
+
contiguous_valid = valid_mask.long().cumprod(dim=-1).bool()
|
| 113 |
+
allowed = cumulative_risk.lt(float(failure_budget)) & contiguous_valid
|
| 114 |
+
return allowed.long().cumprod(dim=-1).sum(dim=-1)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def first_committed_token_lengths(
|
| 118 |
+
proposal: torch.LongTensor,
|
| 119 |
+
commit_lengths: torch.LongTensor,
|
| 120 |
+
token_id: int | Sequence[int],
|
| 121 |
+
) -> torch.LongTensor:
|
| 122 |
+
"""Clip each prefix immediately after its first stop token.
|
| 123 |
+
|
| 124 |
+
Args:
|
| 125 |
+
proposal: Token IDs, shape ``[batch, canvas]``.
|
| 126 |
+
commit_lengths: Unclipped prefix lengths, shape ``[batch]``.
|
| 127 |
+
token_id: One stop ID or a sequence of stop IDs.
|
| 128 |
+
|
| 129 |
+
Returns:
|
| 130 |
+
Clipped commit lengths of shape ``[batch]``.
|
| 131 |
+
"""
|
| 132 |
+
|
| 133 |
+
if proposal.ndim != 2 or commit_lengths.shape != proposal.shape[:1]:
|
| 134 |
+
raise ValueError("Proposal and commit lengths must share a batch dimension.")
|
| 135 |
+
positions = torch.arange(proposal.shape[1], device=proposal.device).unsqueeze(0)
|
| 136 |
+
committed = positions.lt(commit_lengths[:, None])
|
| 137 |
+
stop_token_ids = (
|
| 138 |
+
(int(token_id),)
|
| 139 |
+
if isinstance(token_id, int)
|
| 140 |
+
else tuple(dict.fromkeys(int(value) for value in token_id))
|
| 141 |
+
)
|
| 142 |
+
if not stop_token_ids:
|
| 143 |
+
raise ValueError("At least one stop token ID is required.")
|
| 144 |
+
matches = proposal.eq(stop_token_ids[0])
|
| 145 |
+
for value in stop_token_ids[1:]:
|
| 146 |
+
matches |= proposal.eq(value)
|
| 147 |
+
matches &= committed
|
| 148 |
+
sentinel = torch.full_like(positions, proposal.shape[1])
|
| 149 |
+
first = torch.where(matches, positions, sentinel).min(dim=-1).values
|
| 150 |
+
clipped = torch.where(first.lt(proposal.shape[1]), first + 1, commit_lengths)
|
| 151 |
+
return torch.minimum(clipped, commit_lengths)
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def bounded_prefix_failure_commit_lengths(
|
| 155 |
+
committed_token_ids: torch.LongTensor,
|
| 156 |
+
failure_rate: torch.Tensor,
|
| 157 |
+
*,
|
| 158 |
+
failure_budget: float,
|
| 159 |
+
remaining_lengths: torch.LongTensor,
|
| 160 |
+
stop_token_id: int | Sequence[int],
|
| 161 |
+
valid_mask: torch.BoolTensor | None = None,
|
| 162 |
+
) -> torch.LongTensor:
|
| 163 |
+
"""Apply remaining-length and stop-token bounds to the prefix policy."""
|
| 164 |
+
|
| 165 |
+
if committed_token_ids.shape != failure_rate.shape:
|
| 166 |
+
raise ValueError("Committed token IDs and failure rate must share [batch, canvas].")
|
| 167 |
+
if remaining_lengths.shape != committed_token_ids.shape[:1]:
|
| 168 |
+
raise ValueError("Remaining lengths must have shape [batch].")
|
| 169 |
+
commit_lengths = prefix_failure_commit_lengths(
|
| 170 |
+
failure_rate,
|
| 171 |
+
failure_budget=failure_budget,
|
| 172 |
+
valid_mask=valid_mask,
|
| 173 |
+
)
|
| 174 |
+
commit_lengths = torch.minimum(commit_lengths, remaining_lengths.clamp_min(0))
|
| 175 |
+
return first_committed_token_lengths(
|
| 176 |
+
committed_token_ids,
|
| 177 |
+
commit_lengths,
|
| 178 |
+
stop_token_id,
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def select_commit_lengths(
|
| 183 |
+
sampled_token_ids: torch.LongTensor,
|
| 184 |
+
normal_failure_rate: torch.Tensor,
|
| 185 |
+
previous_failure_rate: torch.Tensor,
|
| 186 |
+
greedy_token_ids: torch.LongTensor,
|
| 187 |
+
jump_failure_rate: torch.Tensor,
|
| 188 |
+
*,
|
| 189 |
+
ponder_steps: torch.Tensor,
|
| 190 |
+
stagnation_steps: torch.Tensor,
|
| 191 |
+
active_rows: torch.BoolTensor,
|
| 192 |
+
remaining_lengths: torch.LongTensor,
|
| 193 |
+
failure_budget: float,
|
| 194 |
+
jump_failure_budget: float,
|
| 195 |
+
stop_token_id: int | Sequence[int],
|
| 196 |
+
max_ponder_steps: int,
|
| 197 |
+
stagnation_threshold: int,
|
| 198 |
+
min_progress: float,
|
| 199 |
+
valid_mask: torch.BoolTensor | None = None,
|
| 200 |
+
) -> CommitPolicyDecision:
|
| 201 |
+
"""Select sampled commits or a greedy jump after stagnation.
|
| 202 |
+
|
| 203 |
+
Progress is the signed change in fused failure rate over the union of the
|
| 204 |
+
previous and current prefixes plus one blocking position.
|
| 205 |
+
|
| 206 |
+
Args:
|
| 207 |
+
sampled_token_ids: Temperature-sampled canvas tokens.
|
| 208 |
+
normal_failure_rate: Fused failure rates for the sampled tokens.
|
| 209 |
+
previous_failure_rate: Fused failure rates from the previous step.
|
| 210 |
+
greedy_token_ids: Greedy canvas tokens used for jumps.
|
| 211 |
+
jump_failure_rate: Fused failure rates for the greedy tokens.
|
| 212 |
+
ponder_steps: Per-row useful-ponder clocks.
|
| 213 |
+
stagnation_steps: Per-row stagnation clocks.
|
| 214 |
+
active_rows: Rows that are still generating.
|
| 215 |
+
remaining_lengths: Tokens still allowed on each row.
|
| 216 |
+
failure_budget: Normal commit budget.
|
| 217 |
+
jump_failure_budget: Forced-jump budget.
|
| 218 |
+
stop_token_id: Turn or EOS stop IDs.
|
| 219 |
+
max_ponder_steps: Watchdog on useful pondering.
|
| 220 |
+
stagnation_threshold: Watchdog on true stagnation.
|
| 221 |
+
min_progress: Minimum signed improvement counted as progress.
|
| 222 |
+
valid_mask: Optional canvas mask.
|
| 223 |
+
|
| 224 |
+
Returns:
|
| 225 |
+
Commit lengths, token IDs, jump flags, and updated clocks.
|
| 226 |
+
"""
|
| 227 |
+
|
| 228 |
+
if not (
|
| 229 |
+
sampled_token_ids.shape
|
| 230 |
+
== normal_failure_rate.shape
|
| 231 |
+
== previous_failure_rate.shape
|
| 232 |
+
== greedy_token_ids.shape
|
| 233 |
+
== jump_failure_rate.shape
|
| 234 |
+
):
|
| 235 |
+
raise ValueError("Sampled and greedy statistics must share [batch, canvas].")
|
| 236 |
+
|
| 237 |
+
normal = bounded_prefix_failure_commit_lengths(
|
| 238 |
+
sampled_token_ids,
|
| 239 |
+
normal_failure_rate,
|
| 240 |
+
failure_budget=failure_budget,
|
| 241 |
+
remaining_lengths=remaining_lengths,
|
| 242 |
+
stop_token_id=stop_token_id,
|
| 243 |
+
valid_mask=valid_mask,
|
| 244 |
+
)
|
| 245 |
+
canvas_length = normal_failure_rate.shape[1]
|
| 246 |
+
previous_prefix_length = prefix_failure_commit_lengths(
|
| 247 |
+
previous_failure_rate,
|
| 248 |
+
failure_budget=failure_budget,
|
| 249 |
+
valid_mask=valid_mask,
|
| 250 |
+
)
|
| 251 |
+
frontier_length = torch.maximum(previous_prefix_length, normal) + 1
|
| 252 |
+
valid_lengths = (
|
| 253 |
+
valid_mask.long().sum(dim=-1)
|
| 254 |
+
if valid_mask is not None
|
| 255 |
+
else torch.full_like(frontier_length, canvas_length)
|
| 256 |
+
)
|
| 257 |
+
frontier_length = torch.minimum(frontier_length, valid_lengths)
|
| 258 |
+
positions = torch.arange(canvas_length, device=normal_failure_rate.device)[None, :]
|
| 259 |
+
progress_mask = positions < frontier_length[:, None]
|
| 260 |
+
if valid_mask is not None:
|
| 261 |
+
progress_mask &= valid_mask
|
| 262 |
+
progress_mask &= active_rows[:, None]
|
| 263 |
+
signed_improvement = previous_failure_rate.float() - normal_failure_rate.float()
|
| 264 |
+
weights = progress_mask.float()
|
| 265 |
+
progress = (signed_improvement * weights).sum(dim=-1) / weights.sum(dim=-1).clamp_min(
|
| 266 |
+
1.0
|
| 267 |
+
)
|
| 268 |
+
next_ponder, next_stagnation = advance_trajectory_clocks(
|
| 269 |
+
ponder_steps,
|
| 270 |
+
stagnation_steps,
|
| 271 |
+
commit_lengths=normal,
|
| 272 |
+
active_rows=active_rows,
|
| 273 |
+
progress_scores=progress,
|
| 274 |
+
min_progress=min_progress,
|
| 275 |
+
)
|
| 276 |
+
jump_rows = normal.eq(0) & active_rows & should_force_trajectory_jump(
|
| 277 |
+
next_ponder,
|
| 278 |
+
next_stagnation,
|
| 279 |
+
max_ponder_steps=max_ponder_steps,
|
| 280 |
+
stagnation_threshold=stagnation_threshold,
|
| 281 |
+
)
|
| 282 |
+
jump_commit = bounded_prefix_failure_commit_lengths(
|
| 283 |
+
greedy_token_ids,
|
| 284 |
+
jump_failure_rate,
|
| 285 |
+
failure_budget=jump_failure_budget,
|
| 286 |
+
remaining_lengths=remaining_lengths,
|
| 287 |
+
stop_token_id=stop_token_id,
|
| 288 |
+
valid_mask=valid_mask,
|
| 289 |
+
)
|
| 290 |
+
committed = torch.where(jump_rows, jump_commit, normal)
|
| 291 |
+
commit_token_ids = torch.where(
|
| 292 |
+
jump_rows[:, None],
|
| 293 |
+
greedy_token_ids,
|
| 294 |
+
sampled_token_ids,
|
| 295 |
+
)
|
| 296 |
+
committed = first_committed_token_lengths(
|
| 297 |
+
commit_token_ids,
|
| 298 |
+
committed,
|
| 299 |
+
stop_token_id,
|
| 300 |
+
)
|
| 301 |
+
committed = torch.where(active_rows, committed, 0)
|
| 302 |
+
jump_rows &= committed.gt(0)
|
| 303 |
+
next_ponder = torch.where(committed.gt(0), 0, next_ponder).to(torch.int32)
|
| 304 |
+
next_stagnation = torch.where(committed.gt(0), 0, next_stagnation).to(torch.int32)
|
| 305 |
+
return CommitPolicyDecision(
|
| 306 |
+
normal_lengths=normal,
|
| 307 |
+
commit_lengths=committed,
|
| 308 |
+
commit_token_ids=commit_token_ids,
|
| 309 |
+
jump_rows=jump_rows,
|
| 310 |
+
ponder_steps=next_ponder,
|
| 311 |
+
stagnation_steps=next_stagnation,
|
| 312 |
+
)
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
__all__ = [
|
| 316 |
+
"CommitPolicyDecision",
|
| 317 |
+
"bounded_prefix_failure_commit_lengths",
|
| 318 |
+
"first_committed_token_lengths",
|
| 319 |
+
"fused_commit_confidence",
|
| 320 |
+
"fused_commit_failure_rate",
|
| 321 |
+
"prefix_failure_commit_lengths",
|
| 322 |
+
"select_commit_lengths",
|
| 323 |
+
]
|
config.json
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"ModilifyMk1ForBlockDiffusion"
|
| 4 |
+
],
|
| 5 |
+
"auto_map": {
|
| 6 |
+
"AutoConfig": "configuration_modilify_mk1.ModilifyMk1Config",
|
| 7 |
+
"AutoModel": "modeling_modilify_mk1.ModilifyMk1Model",
|
| 8 |
+
"AutoModelForCausalLM": "modeling_modilify_mk1.ModilifyMk1ForBlockDiffusion",
|
| 9 |
+
"AutoModelForMultimodalLM": "modeling_modilify_mk1.ModilifyMk1ForBlockDiffusion"
|
| 10 |
+
},
|
| 11 |
+
"boi_token_id": 255999,
|
| 12 |
+
"bos_token_id": 2,
|
| 13 |
+
"canvas_length": 256,
|
| 14 |
+
"commit_failure_budget": 0.2,
|
| 15 |
+
"denoise_temperature": 0.8,
|
| 16 |
+
"dtype": "bfloat16",
|
| 17 |
+
"eoi_token_id": 258882,
|
| 18 |
+
"eos_token_id": [
|
| 19 |
+
1,
|
| 20 |
+
106
|
| 21 |
+
],
|
| 22 |
+
"image_token_id": 258880,
|
| 23 |
+
"initializer_range": 0.02,
|
| 24 |
+
"jump_failure_budget": 2.0,
|
| 25 |
+
"jump_on_no_progress_after": 12,
|
| 26 |
+
"latent_dim": 1536,
|
| 27 |
+
"latent_dropout": 0.0,
|
| 28 |
+
"latent_local_attention_window": 128,
|
| 29 |
+
"latent_memory_slots": 64,
|
| 30 |
+
"latent_num_heads": 16,
|
| 31 |
+
"latent_num_layers": 4,
|
| 32 |
+
"max_ponder_steps": 64,
|
| 33 |
+
"min_trajectory_progress": 0.005,
|
| 34 |
+
"model_type": "modilify_mk1",
|
| 35 |
+
"pad_token_id": 0,
|
| 36 |
+
"text_config": {
|
| 37 |
+
"attention_bias": false,
|
| 38 |
+
"attention_dropout": 0.0,
|
| 39 |
+
"bos_token_id": 2,
|
| 40 |
+
"dtype": "bfloat16",
|
| 41 |
+
"eos_token_id": 1,
|
| 42 |
+
"final_logit_softcapping": 30.0,
|
| 43 |
+
"global_head_dim": 512,
|
| 44 |
+
"head_dim": 256,
|
| 45 |
+
"hidden_activation": "gelu_pytorch_tanh",
|
| 46 |
+
"hidden_size": 2816,
|
| 47 |
+
"initializer_range": 0.02,
|
| 48 |
+
"intermediate_size": 2112,
|
| 49 |
+
"layer_types": [
|
| 50 |
+
"sliding_attention",
|
| 51 |
+
"sliding_attention",
|
| 52 |
+
"sliding_attention",
|
| 53 |
+
"sliding_attention",
|
| 54 |
+
"sliding_attention",
|
| 55 |
+
"full_attention",
|
| 56 |
+
"sliding_attention",
|
| 57 |
+
"sliding_attention",
|
| 58 |
+
"sliding_attention",
|
| 59 |
+
"sliding_attention",
|
| 60 |
+
"sliding_attention",
|
| 61 |
+
"full_attention",
|
| 62 |
+
"sliding_attention",
|
| 63 |
+
"sliding_attention",
|
| 64 |
+
"sliding_attention",
|
| 65 |
+
"sliding_attention",
|
| 66 |
+
"sliding_attention",
|
| 67 |
+
"full_attention",
|
| 68 |
+
"sliding_attention",
|
| 69 |
+
"sliding_attention",
|
| 70 |
+
"sliding_attention",
|
| 71 |
+
"sliding_attention",
|
| 72 |
+
"sliding_attention",
|
| 73 |
+
"full_attention",
|
| 74 |
+
"sliding_attention",
|
| 75 |
+
"sliding_attention",
|
| 76 |
+
"sliding_attention",
|
| 77 |
+
"sliding_attention",
|
| 78 |
+
"sliding_attention",
|
| 79 |
+
"full_attention"
|
| 80 |
+
],
|
| 81 |
+
"max_position_embeddings": 262144,
|
| 82 |
+
"model_type": "modilify_mk1_text",
|
| 83 |
+
"moe_intermediate_size": 704,
|
| 84 |
+
"num_attention_heads": 16,
|
| 85 |
+
"num_experts": 128,
|
| 86 |
+
"num_global_key_value_heads": 2,
|
| 87 |
+
"num_hidden_layers": 30,
|
| 88 |
+
"num_key_value_heads": 8,
|
| 89 |
+
"pad_token_id": 0,
|
| 90 |
+
"rms_norm_eps": 1e-06,
|
| 91 |
+
"rope_parameters": {
|
| 92 |
+
"full_attention": {
|
| 93 |
+
"partial_rotary_factor": 0.25,
|
| 94 |
+
"rope_theta": 1000000.0,
|
| 95 |
+
"rope_type": "proportional"
|
| 96 |
+
},
|
| 97 |
+
"sliding_attention": {
|
| 98 |
+
"rope_theta": 10000.0,
|
| 99 |
+
"rope_type": "default"
|
| 100 |
+
}
|
| 101 |
+
},
|
| 102 |
+
"sliding_window": 1024,
|
| 103 |
+
"tie_word_embeddings": true,
|
| 104 |
+
"top_k_experts": 8,
|
| 105 |
+
"use_bidirectional_attention": "vision",
|
| 106 |
+
"vocab_size": 262144
|
| 107 |
+
},
|
| 108 |
+
"tie_word_embeddings": true,
|
| 109 |
+
"transformers_version": "5.14.1",
|
| 110 |
+
"turn_end_token_id": 106,
|
| 111 |
+
"vision_config": {
|
| 112 |
+
"_name_or_path": "",
|
| 113 |
+
"architectures": null,
|
| 114 |
+
"attention_bias": false,
|
| 115 |
+
"attention_dropout": 0.0,
|
| 116 |
+
"chunk_size_feed_forward": 0,
|
| 117 |
+
"default_output_length": 280,
|
| 118 |
+
"dtype": "bfloat16",
|
| 119 |
+
"global_head_dim": 72,
|
| 120 |
+
"head_dim": 72,
|
| 121 |
+
"hidden_activation": "gelu_pytorch_tanh",
|
| 122 |
+
"hidden_size": 1152,
|
| 123 |
+
"id2label": {
|
| 124 |
+
"0": "LABEL_0",
|
| 125 |
+
"1": "LABEL_1"
|
| 126 |
+
},
|
| 127 |
+
"initializer_range": 0.02,
|
| 128 |
+
"intermediate_size": 4304,
|
| 129 |
+
"is_encoder_decoder": false,
|
| 130 |
+
"label2id": {
|
| 131 |
+
"LABEL_0": 0,
|
| 132 |
+
"LABEL_1": 1
|
| 133 |
+
},
|
| 134 |
+
"max_position_embeddings": 131072,
|
| 135 |
+
"model_type": "gemma4_vision",
|
| 136 |
+
"num_attention_heads": 16,
|
| 137 |
+
"num_hidden_layers": 27,
|
| 138 |
+
"num_key_value_heads": 16,
|
| 139 |
+
"output_attentions": false,
|
| 140 |
+
"output_hidden_states": false,
|
| 141 |
+
"patch_size": 16,
|
| 142 |
+
"pooling_kernel_size": 3,
|
| 143 |
+
"position_embedding_size": 10240,
|
| 144 |
+
"problem_type": null,
|
| 145 |
+
"return_dict": true,
|
| 146 |
+
"rms_norm_eps": 1e-06,
|
| 147 |
+
"rope_parameters": {
|
| 148 |
+
"rope_theta": 100.0,
|
| 149 |
+
"rope_type": "default"
|
| 150 |
+
},
|
| 151 |
+
"standardize": true,
|
| 152 |
+
"use_clipped_linears": false
|
| 153 |
+
},
|
| 154 |
+
"repetition_penalty": 1.0
|
| 155 |
+
}
|
configuration_modilify_mk1.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2026 Modilify
|
| 2 |
+
# SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0
|
| 3 |
+
"""Configuration classes for Modilify Mk1."""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import math
|
| 8 |
+
from typing import Any
|
| 9 |
+
|
| 10 |
+
from transformers.models.diffusion_gemma import (
|
| 11 |
+
DiffusionGemmaConfig,
|
| 12 |
+
DiffusionGemmaTextConfig,
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class ModilifyMk1TextConfig(DiffusionGemmaTextConfig):
|
| 17 |
+
"""Text configuration for the Modilify Mk1 decoder.
|
| 18 |
+
|
| 19 |
+
This class preserves the standard DiffusionGemma text schema while giving
|
| 20 |
+
the exported model an independent, stable model type.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
model_type = "modilify_mk1_text"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class ModilifyMk1Config(DiffusionGemmaConfig):
|
| 27 |
+
"""Serializable multimodal inference configuration for Modilify Mk1.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
text_config: DiffusionGemma text configuration or its serialized form.
|
| 31 |
+
vision_config: Gemma 4 vision configuration or its serialized form.
|
| 32 |
+
denoise_temperature: Sampling temperature used at every denoising step.
|
| 33 |
+
commit_failure_budget: Maximum cumulative failure risk for normal commits.
|
| 34 |
+
jump_failure_budget: Maximum cumulative failure risk for forced jumps.
|
| 35 |
+
latent_dim: Width of the recurrent latent state.
|
| 36 |
+
latent_memory_slots: Number of persistent latent memory slots.
|
| 37 |
+
latent_num_layers: Number of latent Transformer blocks.
|
| 38 |
+
latent_num_heads: Number of latent attention heads.
|
| 39 |
+
latent_local_attention_window: Local token-attention radius.
|
| 40 |
+
latent_dropout: Latent Transformer dropout probability.
|
| 41 |
+
jump_on_no_progress_after: Stagnation steps before a forced jump.
|
| 42 |
+
max_ponder_steps: Maximum denoising iterations per requested token.
|
| 43 |
+
min_trajectory_progress: Minimum fused-risk improvement counted as progress.
|
| 44 |
+
turn_end_token_id: Native Gemma turn terminator.
|
| 45 |
+
repetition_penalty: Transformers-style repetition penalty. ``1.0`` disables
|
| 46 |
+
it.
|
| 47 |
+
kwargs: Standard DiffusionGemma configuration values.
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
model_type = "modilify_mk1"
|
| 51 |
+
sub_configs = {
|
| 52 |
+
"text_config": ModilifyMk1TextConfig,
|
| 53 |
+
**{
|
| 54 |
+
key: value
|
| 55 |
+
for key, value in DiffusionGemmaConfig.sub_configs.items()
|
| 56 |
+
if key != "text_config"
|
| 57 |
+
},
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
def __init__(
|
| 61 |
+
self,
|
| 62 |
+
text_config: (
|
| 63 |
+
ModilifyMk1TextConfig
|
| 64 |
+
| DiffusionGemmaTextConfig
|
| 65 |
+
| dict[str, Any]
|
| 66 |
+
| None
|
| 67 |
+
) = None,
|
| 68 |
+
vision_config: Any | dict[str, Any] | None = None,
|
| 69 |
+
*,
|
| 70 |
+
denoise_temperature: float = 0.8,
|
| 71 |
+
commit_failure_budget: float = 0.2,
|
| 72 |
+
jump_failure_budget: float = 2.0,
|
| 73 |
+
latent_dim: int = 1536,
|
| 74 |
+
latent_memory_slots: int = 64,
|
| 75 |
+
latent_num_layers: int = 4,
|
| 76 |
+
latent_num_heads: int = 16,
|
| 77 |
+
latent_local_attention_window: int = 128,
|
| 78 |
+
latent_dropout: float = 0.0,
|
| 79 |
+
jump_on_no_progress_after: int = 12,
|
| 80 |
+
max_ponder_steps: int = 64,
|
| 81 |
+
min_trajectory_progress: float = 0.005,
|
| 82 |
+
turn_end_token_id: int = 106,
|
| 83 |
+
repetition_penalty: float = 1.0,
|
| 84 |
+
**kwargs: Any,
|
| 85 |
+
) -> None:
|
| 86 |
+
kwargs.pop("model_type", None)
|
| 87 |
+
kwargs.pop("fused_entropy_weight", None)
|
| 88 |
+
kwargs.pop("vocab_chunk_size", None)
|
| 89 |
+
if isinstance(text_config, DiffusionGemmaTextConfig):
|
| 90 |
+
text_payload = text_config.to_dict()
|
| 91 |
+
text_payload.pop("model_type", None)
|
| 92 |
+
text_config = ModilifyMk1TextConfig(**text_payload)
|
| 93 |
+
elif isinstance(text_config, dict):
|
| 94 |
+
text_payload = dict(text_config)
|
| 95 |
+
text_payload.pop("model_type", None)
|
| 96 |
+
text_config = ModilifyMk1TextConfig(**text_payload)
|
| 97 |
+
elif text_config is None:
|
| 98 |
+
text_config = ModilifyMk1TextConfig()
|
| 99 |
+
|
| 100 |
+
self.denoise_temperature = float(denoise_temperature)
|
| 101 |
+
self.commit_failure_budget = float(commit_failure_budget)
|
| 102 |
+
self.jump_failure_budget = float(jump_failure_budget)
|
| 103 |
+
self.latent_dim = int(latent_dim)
|
| 104 |
+
self.latent_memory_slots = int(latent_memory_slots)
|
| 105 |
+
self.latent_num_layers = int(latent_num_layers)
|
| 106 |
+
self.latent_num_heads = int(latent_num_heads)
|
| 107 |
+
self.latent_local_attention_window = int(latent_local_attention_window)
|
| 108 |
+
self.latent_dropout = float(latent_dropout)
|
| 109 |
+
self.jump_on_no_progress_after = int(jump_on_no_progress_after)
|
| 110 |
+
self.max_ponder_steps = int(max_ponder_steps)
|
| 111 |
+
self.min_trajectory_progress = float(min_trajectory_progress)
|
| 112 |
+
self.turn_end_token_id = int(turn_end_token_id)
|
| 113 |
+
self.repetition_penalty = float(repetition_penalty)
|
| 114 |
+
super().__init__(
|
| 115 |
+
text_config=text_config,
|
| 116 |
+
vision_config=vision_config,
|
| 117 |
+
**kwargs,
|
| 118 |
+
)
|
| 119 |
+
self.model_type = type(self).model_type
|
| 120 |
+
if not hasattr(self, "eos_token_id"):
|
| 121 |
+
self.eos_token_id = self.text_config.eos_token_id
|
| 122 |
+
if not hasattr(self, "pad_token_id"):
|
| 123 |
+
self.pad_token_id = self.text_config.pad_token_id
|
| 124 |
+
if not hasattr(self, "bos_token_id"):
|
| 125 |
+
self.bos_token_id = self.text_config.bos_token_id
|
| 126 |
+
self._validate_modilify()
|
| 127 |
+
|
| 128 |
+
def _validate_modilify(self) -> None:
|
| 129 |
+
"""Validate inference extension values."""
|
| 130 |
+
|
| 131 |
+
policy_values = (
|
| 132 |
+
self.denoise_temperature,
|
| 133 |
+
self.commit_failure_budget,
|
| 134 |
+
self.jump_failure_budget,
|
| 135 |
+
self.min_trajectory_progress,
|
| 136 |
+
self.repetition_penalty,
|
| 137 |
+
)
|
| 138 |
+
if any(not math.isfinite(value) for value in policy_values):
|
| 139 |
+
raise ValueError("Modilify Mk1 policy values must be finite.")
|
| 140 |
+
positive = (
|
| 141 |
+
self.denoise_temperature,
|
| 142 |
+
self.commit_failure_budget,
|
| 143 |
+
self.jump_failure_budget,
|
| 144 |
+
self.latent_dim,
|
| 145 |
+
self.latent_memory_slots,
|
| 146 |
+
self.latent_num_layers,
|
| 147 |
+
self.latent_num_heads,
|
| 148 |
+
self.latent_local_attention_window,
|
| 149 |
+
self.jump_on_no_progress_after,
|
| 150 |
+
self.max_ponder_steps,
|
| 151 |
+
self.repetition_penalty,
|
| 152 |
+
)
|
| 153 |
+
if any(value <= 0 for value in positive):
|
| 154 |
+
raise ValueError(
|
| 155 |
+
"Modilify Mk1 dimensions, budgets, intervals, and "
|
| 156 |
+
"`repetition_penalty` must be positive."
|
| 157 |
+
)
|
| 158 |
+
if self.latent_dim % self.latent_num_heads:
|
| 159 |
+
raise ValueError("`latent_dim` must be divisible by `latent_num_heads`.")
|
| 160 |
+
if not 0.0 <= self.latent_dropout < 1.0:
|
| 161 |
+
raise ValueError("`latent_dropout` must be in [0, 1).")
|
| 162 |
+
if self.min_trajectory_progress < 0:
|
| 163 |
+
raise ValueError("`min_trajectory_progress` must be non-negative.")
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
ModilifyMk1Config.register_for_auto_class()
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
__all__ = ["ModilifyMk1Config", "ModilifyMk1TextConfig"]
|
generation_config.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"denoise_temperature": 0.8,
|
| 3 |
+
"eos_token_id": [
|
| 4 |
+
1,
|
| 5 |
+
106
|
| 6 |
+
],
|
| 7 |
+
"max_denoising_steps": null,
|
| 8 |
+
"max_new_tokens": 256,
|
| 9 |
+
"one_token_per_denoise_step": false,
|
| 10 |
+
"repetition_penalty": 1.0,
|
| 11 |
+
"return_dict_in_generate": true,
|
| 12 |
+
"t_max": 0.8,
|
| 13 |
+
"t_min": 0.8,
|
| 14 |
+
"transformers_version": "5.14.1",
|
| 15 |
+
"turn_end_token_id": 106
|
| 16 |
+
}
|
generation_modilify_mk1.py
ADDED
|
@@ -0,0 +1,835 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2026 Modilify
|
| 2 |
+
# SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0
|
| 3 |
+
"""Rolling generation for latent-memory Modilify Mk1."""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from collections.abc import Sequence
|
| 8 |
+
from dataclasses import dataclass, replace
|
| 9 |
+
import math
|
| 10 |
+
from typing import Any
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
from transformers.cache_utils import Cache
|
| 14 |
+
from transformers.generation import LogitsProcessorList
|
| 15 |
+
from transformers.generation.streamers import BaseStreamer
|
| 16 |
+
from transformers.modeling_outputs import ModelOutput
|
| 17 |
+
|
| 18 |
+
from transformers.models.diffusion_gemma import (
|
| 19 |
+
DiffusionGemmaGenerationConfig,
|
| 20 |
+
DiffusionGemmaGenerationMixin,
|
| 21 |
+
)
|
| 22 |
+
from .commit_policy import fused_commit_failure_rate, select_commit_lengths
|
| 23 |
+
from .latent_deliberation import LatentDeliberationState
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _flatten_token_ids(*values: object) -> set[int]:
|
| 27 |
+
"""Normalize scalar and sequence token-ID configuration values."""
|
| 28 |
+
|
| 29 |
+
token_ids: set[int] = set()
|
| 30 |
+
for value in values:
|
| 31 |
+
if value is None:
|
| 32 |
+
continue
|
| 33 |
+
if isinstance(value, int):
|
| 34 |
+
token_ids.add(int(value))
|
| 35 |
+
continue
|
| 36 |
+
if isinstance(value, (list, tuple, set)):
|
| 37 |
+
token_ids.update(int(token_id) for token_id in value if token_id is not None)
|
| 38 |
+
return token_ids
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _add_repetition_history(
|
| 42 |
+
history: torch.BoolTensor,
|
| 43 |
+
token_ids: torch.LongTensor,
|
| 44 |
+
eligible: torch.BoolTensor,
|
| 45 |
+
excluded_token_ids: set[int],
|
| 46 |
+
) -> None:
|
| 47 |
+
"""Add eligible row-local token IDs to a compact ``[batch, vocab]`` history."""
|
| 48 |
+
|
| 49 |
+
if token_ids.shape != eligible.shape or token_ids.shape[0] != history.shape[0]:
|
| 50 |
+
raise ValueError("Repetition history token and eligibility shapes must match.")
|
| 51 |
+
eligible = eligible.clone()
|
| 52 |
+
for token_id in excluded_token_ids:
|
| 53 |
+
eligible &= token_ids.ne(token_id)
|
| 54 |
+
if not bool(eligible.any()):
|
| 55 |
+
return
|
| 56 |
+
rows = torch.arange(history.shape[0], device=history.device)[:, None]
|
| 57 |
+
rows = rows.expand_as(token_ids)
|
| 58 |
+
history[rows[eligible], token_ids[eligible]] = True
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class ModilifyMk1GenerationConfig(DiffusionGemmaGenerationConfig):
|
| 62 |
+
"""Generation controls for the Modilify Mk1 commit policy.
|
| 63 |
+
|
| 64 |
+
Args:
|
| 65 |
+
turn_end_token_id: Token that closes a native Gemma turn.
|
| 66 |
+
denoise_temperature: Sampling temperature used for every canvas step.
|
| 67 |
+
repetition_penalty: Transformers-style repetition penalty. ``1.0``
|
| 68 |
+
disables it.
|
| 69 |
+
kwargs: Standard DiffusionGemma generation arguments.
|
| 70 |
+
"""
|
| 71 |
+
|
| 72 |
+
def __init__(
|
| 73 |
+
self,
|
| 74 |
+
*,
|
| 75 |
+
turn_end_token_id: int | None = None,
|
| 76 |
+
denoise_temperature: float = 0.8,
|
| 77 |
+
repetition_penalty: float = 1.0,
|
| 78 |
+
**kwargs: Any,
|
| 79 |
+
) -> None:
|
| 80 |
+
self.turn_end_token_id = turn_end_token_id
|
| 81 |
+
self.denoise_temperature = float(denoise_temperature)
|
| 82 |
+
self.repetition_penalty = float(repetition_penalty)
|
| 83 |
+
kwargs.pop("one_token_per_denoise_step", None)
|
| 84 |
+
kwargs["t_min"] = self.denoise_temperature
|
| 85 |
+
kwargs["t_max"] = self.denoise_temperature
|
| 86 |
+
super().__init__(**kwargs)
|
| 87 |
+
self.one_token_per_denoise_step = False
|
| 88 |
+
|
| 89 |
+
def update(self, **kwargs: Any) -> dict[str, Any]:
|
| 90 |
+
"""Apply standard generation overrides and supported local overrides."""
|
| 91 |
+
|
| 92 |
+
if "denoise_temperature" in kwargs:
|
| 93 |
+
self.denoise_temperature = float(kwargs.pop("denoise_temperature"))
|
| 94 |
+
if "repetition_penalty" in kwargs:
|
| 95 |
+
self.repetition_penalty = float(kwargs.pop("repetition_penalty"))
|
| 96 |
+
kwargs["t_min"] = self.denoise_temperature
|
| 97 |
+
kwargs["t_max"] = self.denoise_temperature
|
| 98 |
+
unused = super().update(**kwargs)
|
| 99 |
+
self.one_token_per_denoise_step = False
|
| 100 |
+
self.t_min = self.denoise_temperature
|
| 101 |
+
self.t_max = self.denoise_temperature
|
| 102 |
+
return unused
|
| 103 |
+
|
| 104 |
+
def validate(self, **kwargs: Any) -> None:
|
| 105 |
+
"""Validate generation values, including a single temperature.
|
| 106 |
+
|
| 107 |
+
DiffusionGemma's parent validator requires a non-empty temperature
|
| 108 |
+
interval. Modilify Mk1 uses one temperature, so ``t_min == t_max`` is
|
| 109 |
+
validated here.
|
| 110 |
+
"""
|
| 111 |
+
|
| 112 |
+
del kwargs
|
| 113 |
+
if (
|
| 114 |
+
not math.isfinite(self.denoise_temperature)
|
| 115 |
+
or self.denoise_temperature <= 0
|
| 116 |
+
):
|
| 117 |
+
raise ValueError("`denoise_temperature` must be positive.")
|
| 118 |
+
if not math.isfinite(self.repetition_penalty) or self.repetition_penalty <= 0:
|
| 119 |
+
raise ValueError("`repetition_penalty` must be a finite positive number.")
|
| 120 |
+
if self.max_denoising_steps is not None and (
|
| 121 |
+
not isinstance(self.max_denoising_steps, int)
|
| 122 |
+
or self.max_denoising_steps <= 0
|
| 123 |
+
):
|
| 124 |
+
raise ValueError("`max_denoising_steps` must be a positive integer.")
|
| 125 |
+
if self.turn_end_token_id is not None and (
|
| 126 |
+
not isinstance(self.turn_end_token_id, int) or self.turn_end_token_id < 0
|
| 127 |
+
):
|
| 128 |
+
raise ValueError("`turn_end_token_id` must be a non-negative integer.")
|
| 129 |
+
|
| 130 |
+
@classmethod
|
| 131 |
+
def from_model_config(cls, model_config: Any) -> "ModilifyMk1GenerationConfig":
|
| 132 |
+
"""Build generation defaults from a model configuration."""
|
| 133 |
+
|
| 134 |
+
return cls(
|
| 135 |
+
turn_end_token_id=model_config.turn_end_token_id,
|
| 136 |
+
denoise_temperature=model_config.denoise_temperature,
|
| 137 |
+
repetition_penalty=getattr(model_config, "repetition_penalty", 1.0),
|
| 138 |
+
eos_token_id=getattr(
|
| 139 |
+
model_config,
|
| 140 |
+
"eos_token_id",
|
| 141 |
+
model_config.text_config.eos_token_id,
|
| 142 |
+
),
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
@staticmethod
|
| 146 |
+
def _get_default_generation_params() -> dict[str, object]:
|
| 147 |
+
"""Return defaults with no inherited entropy or readiness controls."""
|
| 148 |
+
|
| 149 |
+
return {
|
| 150 |
+
"max_new_tokens": 256,
|
| 151 |
+
"max_denoising_steps": 48,
|
| 152 |
+
"t_min": 0.8,
|
| 153 |
+
"t_max": 0.8,
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
@dataclass
|
| 158 |
+
class ModilifyMk1GenerationOutput(ModelOutput):
|
| 159 |
+
"""Structured result returned by rolling block-diffusion generation."""
|
| 160 |
+
|
| 161 |
+
sequences: torch.LongTensor
|
| 162 |
+
generated_lengths: torch.LongTensor | None = None
|
| 163 |
+
tokens_per_forward: torch.FloatTensor | None = None
|
| 164 |
+
past_key_values: Cache | None = None
|
| 165 |
+
stop_reason: str | tuple[str, ...] | None = None
|
| 166 |
+
committed_tokens: int | torch.LongTensor | None = None
|
| 167 |
+
denoise_steps: int | torch.LongTensor | None = None
|
| 168 |
+
no_progress_steps: int | torch.LongTensor | None = None
|
| 169 |
+
jump_count: int | torch.LongTensor | None = None
|
| 170 |
+
forced_jump_bad_count: int | torch.LongTensor | None = None
|
| 171 |
+
heavy_forward_count: int | torch.LongTensor | None = None
|
| 172 |
+
latent_context_update_count: int | torch.LongTensor | None = None
|
| 173 |
+
average_commit_len: float | torch.FloatTensor | None = None
|
| 174 |
+
state_shift_count: int | torch.LongTensor | None = None
|
| 175 |
+
latent_memory_norm: float | torch.FloatTensor | None = None
|
| 176 |
+
state_retention_score: float | torch.FloatTensor | None = None
|
| 177 |
+
logits: None = None
|
| 178 |
+
scores: None = None
|
| 179 |
+
hidden_states: None = None
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
@dataclass
|
| 183 |
+
class _RollingState:
|
| 184 |
+
"""All real iterative state; no vocabulary-sized tensor is retained."""
|
| 185 |
+
|
| 186 |
+
canvas: torch.LongTensor
|
| 187 |
+
confidence: torch.FloatTensor
|
| 188 |
+
entropy: torch.FloatTensor
|
| 189 |
+
age: torch.IntTensor
|
| 190 |
+
latent_state: LatentDeliberationState
|
| 191 |
+
history_hidden_state: torch.FloatTensor | None
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def _retain_denoise_proposals(proposal: torch.LongTensor) -> torch.LongTensor:
|
| 195 |
+
"""Keep every latest denoise token; confidence controls commit, not writeback."""
|
| 196 |
+
|
| 197 |
+
if proposal.ndim != 2:
|
| 198 |
+
raise ValueError("Denoise proposals must have shape [batch, canvas].")
|
| 199 |
+
return proposal.clone()
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class _NoiseCanvasSampler:
|
| 203 |
+
"""Uniform diffusion noise source with no commit-policy responsibilities."""
|
| 204 |
+
|
| 205 |
+
def __init__(self, *, canvas_length: int, vocab_size: int) -> None:
|
| 206 |
+
self.canvas_length = int(canvas_length)
|
| 207 |
+
self.vocab_size = int(vocab_size)
|
| 208 |
+
self.initial_entropy = math.log(self.vocab_size)
|
| 209 |
+
|
| 210 |
+
def initialize_canvas(
|
| 211 |
+
self,
|
| 212 |
+
batch_size: int,
|
| 213 |
+
device: torch.device,
|
| 214 |
+
) -> torch.LongTensor:
|
| 215 |
+
"""Sample a uniformly random starting canvas.
|
| 216 |
+
|
| 217 |
+
Args:
|
| 218 |
+
batch_size: Number of canvases to create.
|
| 219 |
+
device: Device on which token IDs are allocated.
|
| 220 |
+
|
| 221 |
+
Returns:
|
| 222 |
+
Random token IDs with shape ``[batch_size, canvas_length]``.
|
| 223 |
+
"""
|
| 224 |
+
|
| 225 |
+
return torch.randint(
|
| 226 |
+
self.vocab_size,
|
| 227 |
+
(batch_size, self.canvas_length),
|
| 228 |
+
device=device,
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
class ModilifyMk1GenerationMixin(DiffusionGemmaGenerationMixin):
|
| 233 |
+
"""Transformers-compatible rolling latent-deliberation generator."""
|
| 234 |
+
|
| 235 |
+
def _prepare_sampler(
|
| 236 |
+
self,
|
| 237 |
+
generation_config: ModilifyMk1GenerationConfig,
|
| 238 |
+
canvas_length: int | None = None,
|
| 239 |
+
) -> _NoiseCanvasSampler:
|
| 240 |
+
del generation_config
|
| 241 |
+
return _NoiseCanvasSampler(
|
| 242 |
+
canvas_length=canvas_length or self.config.canvas_length,
|
| 243 |
+
vocab_size=self.config.text_config.vocab_size,
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
@staticmethod
|
| 247 |
+
def _shift_state_rows(
|
| 248 |
+
state: _RollingState,
|
| 249 |
+
commit_lengths: torch.LongTensor,
|
| 250 |
+
sampler: _NoiseCanvasSampler,
|
| 251 |
+
) -> _RollingState:
|
| 252 |
+
"""Shift every rolling row by its own committed prefix length."""
|
| 253 |
+
|
| 254 |
+
batch_size, canvas_length = state.canvas.shape
|
| 255 |
+
if commit_lengths.shape != (batch_size,):
|
| 256 |
+
raise ValueError("Commit lengths must have shape [batch].")
|
| 257 |
+
if not bool(commit_lengths.gt(0).any()):
|
| 258 |
+
return state
|
| 259 |
+
positions = torch.arange(canvas_length, device=state.canvas.device)[None, :]
|
| 260 |
+
source = positions + commit_lengths[:, None]
|
| 261 |
+
retained = source.lt(canvas_length)
|
| 262 |
+
|
| 263 |
+
def shift(value: torch.Tensor, fill_value: float | int = 0) -> torch.Tensor:
|
| 264 |
+
index = source.clamp_max(canvas_length - 1)
|
| 265 |
+
index = index.view(
|
| 266 |
+
batch_size, canvas_length, *([1] * (value.ndim - 2))
|
| 267 |
+
).expand_as(value)
|
| 268 |
+
gathered = value.gather(1, index)
|
| 269 |
+
mask = retained.view(
|
| 270 |
+
batch_size, canvas_length, *([1] * (value.ndim - 2))
|
| 271 |
+
)
|
| 272 |
+
fill = torch.as_tensor(fill_value, device=value.device, dtype=value.dtype)
|
| 273 |
+
return torch.where(mask, gathered, fill)
|
| 274 |
+
|
| 275 |
+
tail = sampler.initialize_canvas(batch_size, state.canvas.device)
|
| 276 |
+
canvas = torch.cat((state.canvas, tail), dim=1).gather(1, source)
|
| 277 |
+
unknown_entropy = float(sampler.initial_entropy)
|
| 278 |
+
latent = state.latent_state
|
| 279 |
+
committed = commit_lengths.gt(0)
|
| 280 |
+
shifted_latent = LatentDeliberationState(
|
| 281 |
+
token_latents=shift(latent.token_latents),
|
| 282 |
+
memory_slots=latent.memory_slots.clone(),
|
| 283 |
+
confidence=shift(latent.confidence),
|
| 284 |
+
entropy=shift(latent.entropy, unknown_entropy),
|
| 285 |
+
age=shift(latent.age),
|
| 286 |
+
token_changed=shift(latent.token_changed),
|
| 287 |
+
confidence_delta=shift(latent.confidence_delta),
|
| 288 |
+
entropy_delta=shift(latent.entropy_delta),
|
| 289 |
+
ponder_steps=torch.where(
|
| 290 |
+
committed, torch.zeros_like(latent.ponder_steps), latent.ponder_steps
|
| 291 |
+
),
|
| 292 |
+
stagnation_steps=torch.where(
|
| 293 |
+
committed,
|
| 294 |
+
torch.zeros_like(latent.stagnation_steps),
|
| 295 |
+
latent.stagnation_steps,
|
| 296 |
+
),
|
| 297 |
+
)
|
| 298 |
+
return _RollingState(
|
| 299 |
+
canvas=canvas,
|
| 300 |
+
confidence=shift(state.confidence),
|
| 301 |
+
entropy=shift(state.entropy, unknown_entropy),
|
| 302 |
+
age=shift(state.age),
|
| 303 |
+
latent_state=shifted_latent,
|
| 304 |
+
history_hidden_state=(
|
| 305 |
+
None
|
| 306 |
+
if state.history_hidden_state is None
|
| 307 |
+
else shift(state.history_hidden_state)
|
| 308 |
+
),
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
@staticmethod
|
| 312 |
+
def _merge_state_rows(
|
| 313 |
+
previous: _RollingState,
|
| 314 |
+
updated: _RollingState,
|
| 315 |
+
update_mask: torch.BoolTensor,
|
| 316 |
+
) -> _RollingState:
|
| 317 |
+
"""Advance active rows while leaving completed rows unchanged."""
|
| 318 |
+
|
| 319 |
+
def choose(old: torch.Tensor, new: torch.Tensor) -> torch.Tensor:
|
| 320 |
+
mask = update_mask.view(
|
| 321 |
+
update_mask.shape[0],
|
| 322 |
+
*([1] * (old.ndim - 1)),
|
| 323 |
+
)
|
| 324 |
+
return torch.where(mask, new, old)
|
| 325 |
+
|
| 326 |
+
old_latent = previous.latent_state
|
| 327 |
+
new_latent = updated.latent_state
|
| 328 |
+
latent = LatentDeliberationState(
|
| 329 |
+
token_latents=choose(old_latent.token_latents, new_latent.token_latents),
|
| 330 |
+
memory_slots=choose(old_latent.memory_slots, new_latent.memory_slots),
|
| 331 |
+
confidence=choose(old_latent.confidence, new_latent.confidence),
|
| 332 |
+
entropy=choose(old_latent.entropy, new_latent.entropy),
|
| 333 |
+
age=choose(old_latent.age, new_latent.age),
|
| 334 |
+
token_changed=choose(old_latent.token_changed, new_latent.token_changed),
|
| 335 |
+
confidence_delta=choose(
|
| 336 |
+
old_latent.confidence_delta, new_latent.confidence_delta
|
| 337 |
+
),
|
| 338 |
+
entropy_delta=choose(old_latent.entropy_delta, new_latent.entropy_delta),
|
| 339 |
+
ponder_steps=choose(old_latent.ponder_steps, new_latent.ponder_steps),
|
| 340 |
+
stagnation_steps=choose(
|
| 341 |
+
old_latent.stagnation_steps, new_latent.stagnation_steps
|
| 342 |
+
),
|
| 343 |
+
)
|
| 344 |
+
history = previous.history_hidden_state
|
| 345 |
+
if updated.history_hidden_state is not None:
|
| 346 |
+
history = (
|
| 347 |
+
updated.history_hidden_state
|
| 348 |
+
if history is None
|
| 349 |
+
else choose(history, updated.history_hidden_state)
|
| 350 |
+
)
|
| 351 |
+
return _RollingState(
|
| 352 |
+
canvas=choose(previous.canvas, updated.canvas),
|
| 353 |
+
confidence=choose(previous.confidence, updated.confidence),
|
| 354 |
+
entropy=choose(previous.entropy, updated.entropy),
|
| 355 |
+
age=choose(previous.age, updated.age),
|
| 356 |
+
latent_state=latent,
|
| 357 |
+
history_hidden_state=history,
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
@torch.inference_mode()
|
| 361 |
+
def generate(
|
| 362 |
+
self,
|
| 363 |
+
input_ids: torch.LongTensor | None = None,
|
| 364 |
+
past_key_values: Cache | None = None,
|
| 365 |
+
streamer: BaseStreamer | None = None,
|
| 366 |
+
generation_config: ModilifyMk1GenerationConfig | None = None,
|
| 367 |
+
logits_processor: LogitsProcessorList | None = None,
|
| 368 |
+
**kwargs,
|
| 369 |
+
) -> ModilifyMk1GenerationOutput:
|
| 370 |
+
"""Generate one or more responses with rolling block diffusion.
|
| 371 |
+
|
| 372 |
+
Args:
|
| 373 |
+
input_ids: Tokenized prompts with shape ``[batch, sequence]``.
|
| 374 |
+
past_key_values: Optional existing encoder cache.
|
| 375 |
+
streamer: Optional standard Transformers token streamer.
|
| 376 |
+
generation_config: Generation limits and token IDs.
|
| 377 |
+
logits_processor: Unsupported custom logits processors.
|
| 378 |
+
**kwargs: Standard multimodal encoder inputs and generation values.
|
| 379 |
+
|
| 380 |
+
Returns:
|
| 381 |
+
Generated sequences and diffusion diagnostics.
|
| 382 |
+
|
| 383 |
+
Raises:
|
| 384 |
+
ValueError: If inputs are invalid or unsupported logits processing
|
| 385 |
+
is requested.
|
| 386 |
+
"""
|
| 387 |
+
|
| 388 |
+
generation_config, model_kwargs = self._prepare_generation_config(
|
| 389 |
+
generation_config,
|
| 390 |
+
**kwargs,
|
| 391 |
+
)
|
| 392 |
+
if input_ids is None or input_ids.ndim != 2 or input_ids.shape[0] < 1:
|
| 393 |
+
raise ValueError(
|
| 394 |
+
"Modilify Mk1 generation requires `input_ids` with shape "
|
| 395 |
+
"[batch, sequence]."
|
| 396 |
+
)
|
| 397 |
+
if logits_processor:
|
| 398 |
+
raise ValueError(
|
| 399 |
+
"Modilify Mk1 samples the configured temperature distribution "
|
| 400 |
+
"and does not accept custom logits processors."
|
| 401 |
+
)
|
| 402 |
+
batch_size, input_width = input_ids.shape
|
| 403 |
+
if batch_size > 1 and streamer is not None:
|
| 404 |
+
raise ValueError("Streamers currently support batch size 1 only.")
|
| 405 |
+
if batch_size > 1 and past_key_values is not None:
|
| 406 |
+
raise ValueError("Batched generation requires a fresh KV cache.")
|
| 407 |
+
device = input_ids.device
|
| 408 |
+
dtype = self.model.decoder.embed_tokens.weight.dtype
|
| 409 |
+
canvas_length = self.config.canvas_length
|
| 410 |
+
cached_length = (
|
| 411 |
+
past_key_values.get_seq_length() if past_key_values is not None else 0
|
| 412 |
+
)
|
| 413 |
+
repetition_penalty = float(generation_config.repetition_penalty)
|
| 414 |
+
repetition_enabled = repetition_penalty != 1.0
|
| 415 |
+
if repetition_enabled and cached_length:
|
| 416 |
+
raise ValueError(
|
| 417 |
+
"Repetition penalty requires a fresh KV cache so the complete "
|
| 418 |
+
"prompt token history is available."
|
| 419 |
+
)
|
| 420 |
+
_, max_new_tokens = self._prepare_generated_length(
|
| 421 |
+
generation_config, cached_length + input_width
|
| 422 |
+
)
|
| 423 |
+
max_iterations = max(1, max_new_tokens * int(self.config.max_ponder_steps))
|
| 424 |
+
if past_key_values is None:
|
| 425 |
+
past_key_values = self._prepare_cache_for_generation(
|
| 426 |
+
generation_config,
|
| 427 |
+
batch_size=batch_size,
|
| 428 |
+
max_length=input_width + batch_size * max_new_tokens,
|
| 429 |
+
)
|
| 430 |
+
expected_mask_width = cached_length + input_width
|
| 431 |
+
cache_attention_mask = model_kwargs.pop(
|
| 432 |
+
"attention_mask",
|
| 433 |
+
torch.ones(
|
| 434 |
+
batch_size, expected_mask_width, dtype=torch.bool, device=device
|
| 435 |
+
),
|
| 436 |
+
).bool()
|
| 437 |
+
if cache_attention_mask.shape != (batch_size, expected_mask_width):
|
| 438 |
+
raise ValueError(
|
| 439 |
+
"`attention_mask` must have shape [batch, cached_length + sequence]."
|
| 440 |
+
)
|
| 441 |
+
provided_position_ids = model_kwargs.pop("position_ids", None)
|
| 442 |
+
if provided_position_ids is not None:
|
| 443 |
+
if provided_position_ids.shape != input_ids.shape:
|
| 444 |
+
raise ValueError("`position_ids` must have the same shape as `input_ids`.")
|
| 445 |
+
prompt_positions = provided_position_ids.to(device=device, dtype=torch.int32)
|
| 446 |
+
elif cached_length:
|
| 447 |
+
prompt_positions = torch.arange(
|
| 448 |
+
cached_length,
|
| 449 |
+
cached_length + input_width,
|
| 450 |
+
device=device,
|
| 451 |
+
dtype=torch.int32,
|
| 452 |
+
).unsqueeze(0)
|
| 453 |
+
else:
|
| 454 |
+
input_mask = cache_attention_mask[:, -input_width:]
|
| 455 |
+
prompt_positions = (
|
| 456 |
+
input_mask.long().cumsum(dim=-1).sub(1).clamp_min(0).to(torch.int32)
|
| 457 |
+
)
|
| 458 |
+
logical_lengths = cache_attention_mask.long().sum(dim=-1)
|
| 459 |
+
if input_width:
|
| 460 |
+
encoder_keys = ("pixel_values", "mm_token_type_ids", "image_position_ids")
|
| 461 |
+
encoder_kwargs = {
|
| 462 |
+
key: model_kwargs.pop(key)
|
| 463 |
+
for key in encoder_keys
|
| 464 |
+
if key in model_kwargs
|
| 465 |
+
}
|
| 466 |
+
past_key_values = self.model.encoder(
|
| 467 |
+
input_ids=input_ids,
|
| 468 |
+
attention_mask=cache_attention_mask,
|
| 469 |
+
past_key_values=past_key_values,
|
| 470 |
+
position_ids=prompt_positions,
|
| 471 |
+
**encoder_kwargs,
|
| 472 |
+
).past_key_values
|
| 473 |
+
|
| 474 |
+
sampler = self._prepare_sampler(generation_config, canvas_length)
|
| 475 |
+
latent = LatentDeliberationState.empty(
|
| 476 |
+
batch_size=batch_size,
|
| 477 |
+
canvas_length=canvas_length,
|
| 478 |
+
latent_dim=self.config.latent_dim,
|
| 479 |
+
memory_slots=self.config.latent_memory_slots,
|
| 480 |
+
device=device,
|
| 481 |
+
dtype=dtype,
|
| 482 |
+
)
|
| 483 |
+
state = _RollingState(
|
| 484 |
+
canvas=sampler.initialize_canvas(batch_size, device),
|
| 485 |
+
confidence=torch.zeros(
|
| 486 |
+
batch_size, canvas_length, device=device, dtype=torch.float32
|
| 487 |
+
),
|
| 488 |
+
entropy=torch.full(
|
| 489 |
+
(batch_size, canvas_length),
|
| 490 |
+
math.log(self.config.text_config.vocab_size),
|
| 491 |
+
device=device,
|
| 492 |
+
dtype=torch.float32,
|
| 493 |
+
),
|
| 494 |
+
age=torch.zeros(
|
| 495 |
+
batch_size, canvas_length, device=device, dtype=torch.int32
|
| 496 |
+
),
|
| 497 |
+
latent_state=latent,
|
| 498 |
+
history_hidden_state=None,
|
| 499 |
+
)
|
| 500 |
+
turn_end = (
|
| 501 |
+
self.config.turn_end_token_id
|
| 502 |
+
if generation_config.turn_end_token_id is None
|
| 503 |
+
else generation_config.turn_end_token_id
|
| 504 |
+
)
|
| 505 |
+
configured_eos = generation_config.eos_token_id
|
| 506 |
+
if configured_eos is None:
|
| 507 |
+
configured_eos = self.config.eos_token_id
|
| 508 |
+
if isinstance(configured_eos, int):
|
| 509 |
+
configured_eos = [configured_eos]
|
| 510 |
+
stop_token_ids = tuple(
|
| 511 |
+
dict.fromkeys((int(turn_end), *(int(value) for value in configured_eos or ())))
|
| 512 |
+
)
|
| 513 |
+
pad_token_id = generation_config.pad_token_id
|
| 514 |
+
if pad_token_id is None:
|
| 515 |
+
pad_token_id = getattr(self.config, "pad_token_id", None)
|
| 516 |
+
if isinstance(pad_token_id, (list, tuple)):
|
| 517 |
+
pad_token_id = pad_token_id[0]
|
| 518 |
+
pad_token_id = int(0 if pad_token_id is None else pad_token_id)
|
| 519 |
+
excluded_repetition_token_ids = _flatten_token_ids(
|
| 520 |
+
generation_config.pad_token_id,
|
| 521 |
+
generation_config.bos_token_id,
|
| 522 |
+
generation_config.eos_token_id,
|
| 523 |
+
generation_config.turn_end_token_id,
|
| 524 |
+
getattr(self.config, "image_token_id", None),
|
| 525 |
+
)
|
| 526 |
+
repetition_history = None
|
| 527 |
+
if repetition_enabled:
|
| 528 |
+
repetition_history = torch.zeros(
|
| 529 |
+
(batch_size, self.config.text_config.vocab_size),
|
| 530 |
+
dtype=torch.bool,
|
| 531 |
+
device=device,
|
| 532 |
+
)
|
| 533 |
+
_add_repetition_history(
|
| 534 |
+
repetition_history,
|
| 535 |
+
input_ids,
|
| 536 |
+
cache_attention_mask[:, -input_width:],
|
| 537 |
+
excluded_repetition_token_ids,
|
| 538 |
+
)
|
| 539 |
+
generated = torch.full(
|
| 540 |
+
(batch_size, max_new_tokens),
|
| 541 |
+
pad_token_id,
|
| 542 |
+
dtype=input_ids.dtype,
|
| 543 |
+
device=device,
|
| 544 |
+
)
|
| 545 |
+
committed = torch.zeros(batch_size, dtype=torch.long, device=device)
|
| 546 |
+
denoise_steps = torch.zeros_like(committed)
|
| 547 |
+
jumps = torch.zeros_like(committed)
|
| 548 |
+
forced_jump_tokens = torch.zeros_like(committed)
|
| 549 |
+
shifts = torch.zeros_like(committed)
|
| 550 |
+
retention_scores = torch.zeros(batch_size, dtype=torch.float32, device=device)
|
| 551 |
+
stop_codes = torch.zeros_like(committed)
|
| 552 |
+
active_rows = torch.ones(batch_size, dtype=torch.bool, device=device)
|
| 553 |
+
canvas_positions = torch.arange(canvas_length, device=device)[None, :]
|
| 554 |
+
if streamer is not None:
|
| 555 |
+
streamer.put(input_ids.cpu())
|
| 556 |
+
|
| 557 |
+
while bool(active_rows.any()):
|
| 558 |
+
decoder_positions = (logical_lengths[:, None] + canvas_positions).to(
|
| 559 |
+
torch.int32
|
| 560 |
+
)
|
| 561 |
+
denoise_steps += active_rows.long()
|
| 562 |
+
decoder_attention_mask = torch.cat(
|
| 563 |
+
(
|
| 564 |
+
cache_attention_mask,
|
| 565 |
+
torch.ones(
|
| 566 |
+
batch_size,
|
| 567 |
+
canvas_length,
|
| 568 |
+
dtype=torch.bool,
|
| 569 |
+
device=device,
|
| 570 |
+
),
|
| 571 |
+
),
|
| 572 |
+
dim=-1,
|
| 573 |
+
)
|
| 574 |
+
output = self(
|
| 575 |
+
input_ids=None,
|
| 576 |
+
past_key_values=past_key_values,
|
| 577 |
+
decoder_input_ids=state.canvas,
|
| 578 |
+
previous_confidence=state.confidence,
|
| 579 |
+
previous_entropy=state.entropy,
|
| 580 |
+
token_age=state.age,
|
| 581 |
+
latent_state=state.latent_state,
|
| 582 |
+
history_hidden_state=state.history_hidden_state,
|
| 583 |
+
decoder_position_ids=decoder_positions,
|
| 584 |
+
decoder_read_cache=True,
|
| 585 |
+
decoder_attention_mask=decoder_attention_mask,
|
| 586 |
+
return_proposal_statistics=True,
|
| 587 |
+
denoise_temperature=generation_config.denoise_temperature,
|
| 588 |
+
repetition_token_mask=repetition_history,
|
| 589 |
+
repetition_penalty=repetition_penalty,
|
| 590 |
+
**model_kwargs,
|
| 591 |
+
)
|
| 592 |
+
if any(
|
| 593 |
+
value is None
|
| 594 |
+
for value in (
|
| 595 |
+
output.proposal,
|
| 596 |
+
output.proposal_confidence,
|
| 597 |
+
output.token_entropy,
|
| 598 |
+
output.greedy_proposal,
|
| 599 |
+
output.greedy_confidence,
|
| 600 |
+
)
|
| 601 |
+
):
|
| 602 |
+
raise RuntimeError("Model forward did not return proposal statistics.")
|
| 603 |
+
proposal = output.proposal
|
| 604 |
+
proposal_confidence = output.proposal_confidence
|
| 605 |
+
token_entropy = output.token_entropy
|
| 606 |
+
greedy_proposal = output.greedy_proposal
|
| 607 |
+
greedy_confidence = output.greedy_confidence
|
| 608 |
+
next_canvas = _retain_denoise_proposals(proposal)
|
| 609 |
+
next_confidence = proposal_confidence.float()
|
| 610 |
+
next_latent = replace(
|
| 611 |
+
output.next_latent_state,
|
| 612 |
+
confidence=next_confidence.detach().float(),
|
| 613 |
+
entropy=token_entropy.detach().float(),
|
| 614 |
+
age=state.age + 1,
|
| 615 |
+
token_changed=next_canvas.ne(state.canvas).detach().float(),
|
| 616 |
+
confidence_delta=next_confidence.detach().float() - state.confidence,
|
| 617 |
+
entropy_delta=token_entropy.detach().float() - state.entropy,
|
| 618 |
+
)
|
| 619 |
+
next_state = _RollingState(
|
| 620 |
+
canvas=next_canvas,
|
| 621 |
+
confidence=next_confidence,
|
| 622 |
+
entropy=token_entropy,
|
| 623 |
+
age=state.age + 1,
|
| 624 |
+
latent_state=next_latent,
|
| 625 |
+
history_hidden_state=output.heavy_hidden_state,
|
| 626 |
+
)
|
| 627 |
+
next_state = self._merge_state_rows(state, next_state, active_rows)
|
| 628 |
+
remaining = torch.tensor(
|
| 629 |
+
max_new_tokens, device=device, dtype=torch.long
|
| 630 |
+
).sub(committed)
|
| 631 |
+
vocab_size = self.config.text_config.vocab_size
|
| 632 |
+
normal_failure_rate = fused_commit_failure_rate(
|
| 633 |
+
proposal_confidence, token_entropy, vocab_size=vocab_size
|
| 634 |
+
)
|
| 635 |
+
jump_failure_rate = fused_commit_failure_rate(
|
| 636 |
+
greedy_confidence, token_entropy, vocab_size=vocab_size
|
| 637 |
+
)
|
| 638 |
+
previous_failure_rate = fused_commit_failure_rate(
|
| 639 |
+
state.confidence, state.entropy, vocab_size=vocab_size
|
| 640 |
+
)
|
| 641 |
+
policy_decision = select_commit_lengths(
|
| 642 |
+
sampled_token_ids=proposal,
|
| 643 |
+
normal_failure_rate=normal_failure_rate,
|
| 644 |
+
previous_failure_rate=previous_failure_rate,
|
| 645 |
+
greedy_token_ids=greedy_proposal,
|
| 646 |
+
jump_failure_rate=jump_failure_rate,
|
| 647 |
+
ponder_steps=state.latent_state.ponder_steps,
|
| 648 |
+
stagnation_steps=state.latent_state.stagnation_steps,
|
| 649 |
+
active_rows=active_rows,
|
| 650 |
+
remaining_lengths=remaining,
|
| 651 |
+
failure_budget=self.config.commit_failure_budget,
|
| 652 |
+
jump_failure_budget=self.config.jump_failure_budget,
|
| 653 |
+
stop_token_id=stop_token_ids,
|
| 654 |
+
max_ponder_steps=self.config.max_ponder_steps,
|
| 655 |
+
stagnation_threshold=self.config.jump_on_no_progress_after,
|
| 656 |
+
min_progress=self.config.min_trajectory_progress,
|
| 657 |
+
)
|
| 658 |
+
next_ponder = policy_decision.ponder_steps
|
| 659 |
+
next_stagnation = policy_decision.stagnation_steps
|
| 660 |
+
commit_lengths = policy_decision.commit_lengths
|
| 661 |
+
jump_rows = policy_decision.jump_rows
|
| 662 |
+
jumps += jump_rows.long()
|
| 663 |
+
forced_jump_tokens += torch.where(
|
| 664 |
+
jump_rows, commit_lengths, torch.zeros_like(commit_lengths)
|
| 665 |
+
)
|
| 666 |
+
commit_positions = canvas_positions.lt(commit_lengths[:, None])
|
| 667 |
+
if bool(jump_rows.any()):
|
| 668 |
+
next_state = replace(
|
| 669 |
+
next_state,
|
| 670 |
+
canvas=torch.where(
|
| 671 |
+
commit_positions & jump_rows[:, None],
|
| 672 |
+
policy_decision.commit_token_ids,
|
| 673 |
+
next_state.canvas,
|
| 674 |
+
),
|
| 675 |
+
)
|
| 676 |
+
next_state = replace(
|
| 677 |
+
next_state,
|
| 678 |
+
latent_state=replace(
|
| 679 |
+
next_state.latent_state,
|
| 680 |
+
ponder_steps=next_ponder,
|
| 681 |
+
stagnation_steps=next_stagnation,
|
| 682 |
+
),
|
| 683 |
+
)
|
| 684 |
+
commit_token_ids = policy_decision.commit_token_ids
|
| 685 |
+
before = committed.clone()
|
| 686 |
+
write_rows = torch.arange(batch_size, device=device)[:, None].expand_as(
|
| 687 |
+
commit_token_ids
|
| 688 |
+
)
|
| 689 |
+
write_positions = before[:, None] + canvas_positions
|
| 690 |
+
generated[
|
| 691 |
+
write_rows[commit_positions], write_positions[commit_positions]
|
| 692 |
+
] = commit_token_ids[commit_positions]
|
| 693 |
+
if repetition_history is not None:
|
| 694 |
+
_add_repetition_history(
|
| 695 |
+
repetition_history,
|
| 696 |
+
commit_token_ids,
|
| 697 |
+
commit_positions,
|
| 698 |
+
excluded_repetition_token_ids,
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
+
commit_width = int(commit_lengths.max())
|
| 702 |
+
if commit_width:
|
| 703 |
+
block_mask = torch.arange(commit_width, device=device)[None, :].lt(
|
| 704 |
+
commit_lengths[:, None]
|
| 705 |
+
)
|
| 706 |
+
committed_block = torch.where(
|
| 707 |
+
block_mask,
|
| 708 |
+
commit_token_ids[:, :commit_width],
|
| 709 |
+
torch.full(
|
| 710 |
+
(batch_size, commit_width),
|
| 711 |
+
pad_token_id,
|
| 712 |
+
device=device,
|
| 713 |
+
dtype=input_ids.dtype,
|
| 714 |
+
),
|
| 715 |
+
)
|
| 716 |
+
block_positions = (
|
| 717 |
+
logical_lengths[:, None] + canvas_positions[:, :commit_width]
|
| 718 |
+
).to(torch.int32)
|
| 719 |
+
block_positions = torch.where(
|
| 720 |
+
block_mask, block_positions, torch.zeros_like(block_positions)
|
| 721 |
+
)
|
| 722 |
+
cache_attention_mask = torch.cat(
|
| 723 |
+
(cache_attention_mask, block_mask), dim=-1
|
| 724 |
+
)
|
| 725 |
+
past_key_values = self.model.encoder(
|
| 726 |
+
input_ids=committed_block,
|
| 727 |
+
attention_mask=cache_attention_mask,
|
| 728 |
+
past_key_values=past_key_values,
|
| 729 |
+
position_ids=block_positions,
|
| 730 |
+
).past_key_values
|
| 731 |
+
if streamer is not None:
|
| 732 |
+
streamer.put(committed_block.cpu())
|
| 733 |
+
committed += commit_lengths
|
| 734 |
+
logical_lengths += commit_lengths
|
| 735 |
+
committed_rows = commit_lengths.gt(0)
|
| 736 |
+
shifts += committed_rows.long()
|
| 737 |
+
state = self._shift_state_rows(next_state, commit_lengths, sampler)
|
| 738 |
+
retention_scores += committed_rows.float()
|
| 739 |
+
|
| 740 |
+
turn_hits = (commit_token_ids.eq(turn_end) & commit_positions).any(dim=-1)
|
| 741 |
+
eos_hits = torch.zeros_like(turn_hits)
|
| 742 |
+
for token_id in stop_token_ids:
|
| 743 |
+
if token_id != turn_end:
|
| 744 |
+
eos_hits |= (commit_token_ids.eq(token_id) & commit_positions).any(
|
| 745 |
+
dim=-1
|
| 746 |
+
)
|
| 747 |
+
stop_codes = torch.where(
|
| 748 |
+
stop_codes.eq(0) & turn_hits,
|
| 749 |
+
torch.ones_like(stop_codes),
|
| 750 |
+
stop_codes,
|
| 751 |
+
)
|
| 752 |
+
stop_codes = torch.where(
|
| 753 |
+
stop_codes.eq(0) & eos_hits,
|
| 754 |
+
torch.full_like(stop_codes, 2),
|
| 755 |
+
stop_codes,
|
| 756 |
+
)
|
| 757 |
+
stop_codes = torch.where(
|
| 758 |
+
stop_codes.eq(0) & committed.ge(max_new_tokens),
|
| 759 |
+
torch.full_like(stop_codes, 3),
|
| 760 |
+
stop_codes,
|
| 761 |
+
)
|
| 762 |
+
if generation_config.max_denoising_steps is not None:
|
| 763 |
+
stop_codes = torch.where(
|
| 764 |
+
stop_codes.eq(0)
|
| 765 |
+
& denoise_steps.ge(generation_config.max_denoising_steps),
|
| 766 |
+
torch.full_like(stop_codes, 4),
|
| 767 |
+
stop_codes,
|
| 768 |
+
)
|
| 769 |
+
stop_codes = torch.where(
|
| 770 |
+
stop_codes.eq(0) & denoise_steps.ge(max_iterations),
|
| 771 |
+
torch.full_like(stop_codes, 5),
|
| 772 |
+
stop_codes,
|
| 773 |
+
)
|
| 774 |
+
active_rows = stop_codes.eq(0)
|
| 775 |
+
|
| 776 |
+
output_width = int(committed.max())
|
| 777 |
+
sequences = torch.cat((input_ids, generated[:, :output_width]), dim=-1)
|
| 778 |
+
if streamer is not None:
|
| 779 |
+
streamer.end()
|
| 780 |
+
reason_names = {
|
| 781 |
+
1: "turn_end",
|
| 782 |
+
2: "eos",
|
| 783 |
+
3: "max_new_tokens",
|
| 784 |
+
4: "max_denoising_steps",
|
| 785 |
+
5: "episode_watchdog",
|
| 786 |
+
}
|
| 787 |
+
stop_reasons = tuple(
|
| 788 |
+
reason_names.get(code, "unknown")
|
| 789 |
+
for code in stop_codes.detach().cpu().tolist()
|
| 790 |
+
)
|
| 791 |
+
tokens_per_forward = committed.float() / denoise_steps.clamp_min(1).float()
|
| 792 |
+
average_commit_len = committed.float() / shifts.clamp_min(1).float()
|
| 793 |
+
latent_memory_norm = (
|
| 794 |
+
state.latent_state.memory_slots.float().norm(dim=-1).mean(dim=-1)
|
| 795 |
+
)
|
| 796 |
+
state_retention_score = retention_scores / shifts.clamp_min(1).float()
|
| 797 |
+
|
| 798 |
+
def scalar_or_tensor(
|
| 799 |
+
value: torch.Tensor,
|
| 800 |
+
*,
|
| 801 |
+
floating: bool = False,
|
| 802 |
+
) -> int | float | torch.Tensor:
|
| 803 |
+
if batch_size > 1:
|
| 804 |
+
return value
|
| 805 |
+
item = value[0].item()
|
| 806 |
+
return float(item) if floating else int(item)
|
| 807 |
+
|
| 808 |
+
return ModilifyMk1GenerationOutput(
|
| 809 |
+
sequences=sequences,
|
| 810 |
+
generated_lengths=committed.clone(),
|
| 811 |
+
tokens_per_forward=tokens_per_forward,
|
| 812 |
+
past_key_values=past_key_values,
|
| 813 |
+
stop_reason=stop_reasons[0] if batch_size == 1 else stop_reasons,
|
| 814 |
+
committed_tokens=scalar_or_tensor(committed),
|
| 815 |
+
denoise_steps=scalar_or_tensor(denoise_steps),
|
| 816 |
+
no_progress_steps=scalar_or_tensor(state.latent_state.stagnation_steps),
|
| 817 |
+
jump_count=scalar_or_tensor(jumps),
|
| 818 |
+
forced_jump_bad_count=scalar_or_tensor(forced_jump_tokens),
|
| 819 |
+
heavy_forward_count=scalar_or_tensor(denoise_steps),
|
| 820 |
+
latent_context_update_count=scalar_or_tensor(denoise_steps),
|
| 821 |
+
average_commit_len=scalar_or_tensor(average_commit_len, floating=True),
|
| 822 |
+
state_shift_count=scalar_or_tensor(shifts),
|
| 823 |
+
latent_memory_norm=scalar_or_tensor(latent_memory_norm, floating=True),
|
| 824 |
+
state_retention_score=scalar_or_tensor(
|
| 825 |
+
state_retention_score,
|
| 826 |
+
floating=True,
|
| 827 |
+
),
|
| 828 |
+
)
|
| 829 |
+
|
| 830 |
+
|
| 831 |
+
__all__ = [
|
| 832 |
+
"ModilifyMk1GenerationConfig",
|
| 833 |
+
"ModilifyMk1GenerationMixin",
|
| 834 |
+
"ModilifyMk1GenerationOutput",
|
| 835 |
+
]
|
latent_deliberation.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2026 Modilify
|
| 2 |
+
# SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0
|
| 3 |
+
"""Fixed-shape latent deliberation state for Modilify Mk1 decoding.
|
| 4 |
+
|
| 5 |
+
The state contains no vocabulary-sized tensors. Per-canvas information stays in
|
| 6 |
+
a compact latent space so iterative diffusion does not retain one logits
|
| 7 |
+
allocation per denoise pass.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
from dataclasses import dataclass
|
| 13 |
+
import math
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
from torch import nn
|
| 17 |
+
from torch.nn import functional as F
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@dataclass
|
| 21 |
+
class LatentDeliberationState:
|
| 22 |
+
"""Persistent, fixed-size state for one or more canvas episodes."""
|
| 23 |
+
|
| 24 |
+
token_latents: torch.Tensor
|
| 25 |
+
memory_slots: torch.Tensor
|
| 26 |
+
confidence: torch.Tensor
|
| 27 |
+
entropy: torch.Tensor
|
| 28 |
+
age: torch.Tensor
|
| 29 |
+
token_changed: torch.Tensor
|
| 30 |
+
confidence_delta: torch.Tensor
|
| 31 |
+
entropy_delta: torch.Tensor
|
| 32 |
+
ponder_steps: torch.Tensor
|
| 33 |
+
stagnation_steps: torch.Tensor
|
| 34 |
+
|
| 35 |
+
@classmethod
|
| 36 |
+
def empty(
|
| 37 |
+
cls,
|
| 38 |
+
*,
|
| 39 |
+
batch_size: int,
|
| 40 |
+
canvas_length: int,
|
| 41 |
+
latent_dim: int,
|
| 42 |
+
memory_slots: int,
|
| 43 |
+
device: torch.device,
|
| 44 |
+
dtype: torch.dtype,
|
| 45 |
+
) -> "LatentDeliberationState":
|
| 46 |
+
"""Create a zero-initialized recurrent state.
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
batch_size: Number of independent sequences.
|
| 50 |
+
canvas_length: Number of rolling canvas positions.
|
| 51 |
+
latent_dim: Width of each latent token and memory slot.
|
| 52 |
+
memory_slots: Number of persistent memory slots.
|
| 53 |
+
device: Allocation device.
|
| 54 |
+
dtype: Floating-point dtype for latent tensors.
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
A zero-initialized state with integer progress clocks.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
return cls(
|
| 61 |
+
token_latents=torch.zeros(
|
| 62 |
+
batch_size, canvas_length, latent_dim, device=device, dtype=dtype
|
| 63 |
+
),
|
| 64 |
+
memory_slots=torch.zeros(
|
| 65 |
+
batch_size, memory_slots, latent_dim, device=device, dtype=dtype
|
| 66 |
+
),
|
| 67 |
+
confidence=torch.zeros(
|
| 68 |
+
batch_size, canvas_length, device=device, dtype=torch.float32
|
| 69 |
+
),
|
| 70 |
+
entropy=torch.zeros(
|
| 71 |
+
batch_size, canvas_length, device=device, dtype=torch.float32
|
| 72 |
+
),
|
| 73 |
+
age=torch.zeros(
|
| 74 |
+
batch_size, canvas_length, device=device, dtype=torch.int32
|
| 75 |
+
),
|
| 76 |
+
token_changed=torch.zeros(
|
| 77 |
+
batch_size, canvas_length, device=device, dtype=torch.float32
|
| 78 |
+
),
|
| 79 |
+
confidence_delta=torch.zeros(
|
| 80 |
+
batch_size, canvas_length, device=device, dtype=torch.float32
|
| 81 |
+
),
|
| 82 |
+
entropy_delta=torch.zeros(
|
| 83 |
+
batch_size, canvas_length, device=device, dtype=torch.float32
|
| 84 |
+
),
|
| 85 |
+
ponder_steps=torch.zeros(batch_size, device=device, dtype=torch.int32),
|
| 86 |
+
stagnation_steps=torch.zeros(batch_size, device=device, dtype=torch.int32),
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
def shift(
|
| 90 |
+
self, committed: int, *, entropy_fill_value: float = 0.0
|
| 91 |
+
) -> "LatentDeliberationState":
|
| 92 |
+
"""Drop committed canvas positions without changing long-term memory.
|
| 93 |
+
|
| 94 |
+
Args:
|
| 95 |
+
committed: Number of leading canvas positions to drop.
|
| 96 |
+
entropy_fill_value: Fill value for newly exposed entropy slots.
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
A shifted state with unchanged memory slots.
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
canvas_length = self.token_latents.shape[1]
|
| 103 |
+
if not 0 <= committed <= canvas_length:
|
| 104 |
+
raise ValueError("`committed` must be in [0, canvas_length].")
|
| 105 |
+
if committed == 0:
|
| 106 |
+
return self
|
| 107 |
+
|
| 108 |
+
def shifted(tensor: torch.Tensor, fill_value: float | int = 0) -> torch.Tensor:
|
| 109 |
+
result = torch.full_like(tensor, fill_value)
|
| 110 |
+
if committed < canvas_length:
|
| 111 |
+
result[:, : canvas_length - committed] = tensor[:, committed:]
|
| 112 |
+
return result
|
| 113 |
+
|
| 114 |
+
return LatentDeliberationState(
|
| 115 |
+
token_latents=shifted(self.token_latents),
|
| 116 |
+
memory_slots=self.memory_slots.clone(),
|
| 117 |
+
confidence=shifted(self.confidence),
|
| 118 |
+
entropy=shifted(self.entropy, entropy_fill_value),
|
| 119 |
+
age=shifted(self.age),
|
| 120 |
+
token_changed=shifted(self.token_changed),
|
| 121 |
+
confidence_delta=shifted(self.confidence_delta),
|
| 122 |
+
entropy_delta=shifted(self.entropy_delta),
|
| 123 |
+
ponder_steps=torch.zeros_like(self.ponder_steps),
|
| 124 |
+
stagnation_steps=torch.zeros_like(self.stagnation_steps),
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def advance_trajectory_clocks(
|
| 129 |
+
ponder_steps: torch.Tensor,
|
| 130 |
+
stagnation_steps: torch.Tensor,
|
| 131 |
+
*,
|
| 132 |
+
commit_lengths: torch.LongTensor,
|
| 133 |
+
active_rows: torch.BoolTensor,
|
| 134 |
+
progress_scores: torch.Tensor,
|
| 135 |
+
min_progress: float,
|
| 136 |
+
) -> tuple[torch.IntTensor, torch.IntTensor]:
|
| 137 |
+
"""Advance useful-ponder and true-stagnation clocks for each row.
|
| 138 |
+
|
| 139 |
+
Args:
|
| 140 |
+
ponder_steps: Current useful-ponder clocks, shape ``[batch]``.
|
| 141 |
+
stagnation_steps: Current stagnation clocks, shape ``[batch]``.
|
| 142 |
+
commit_lengths: Tokens committed this step.
|
| 143 |
+
active_rows: Rows that are still generating.
|
| 144 |
+
progress_scores: Signed fused-risk improvement.
|
| 145 |
+
min_progress: Minimum improvement counted as progress.
|
| 146 |
+
|
| 147 |
+
Returns:
|
| 148 |
+
Updated ponder and stagnation clocks.
|
| 149 |
+
"""
|
| 150 |
+
|
| 151 |
+
if min_progress < 0:
|
| 152 |
+
raise ValueError("`min_progress` must be non-negative.")
|
| 153 |
+
if not (
|
| 154 |
+
ponder_steps.shape
|
| 155 |
+
== stagnation_steps.shape
|
| 156 |
+
== commit_lengths.shape
|
| 157 |
+
== active_rows.shape
|
| 158 |
+
== progress_scores.shape
|
| 159 |
+
):
|
| 160 |
+
raise ValueError("Trajectory clock inputs must share shape [batch].")
|
| 161 |
+
committed = commit_lengths.gt(0)
|
| 162 |
+
waiting = active_rows & ~committed
|
| 163 |
+
improving = progress_scores.ge(min_progress)
|
| 164 |
+
next_ponder = torch.where(
|
| 165 |
+
committed, torch.zeros_like(ponder_steps), ponder_steps + waiting.to(torch.int32)
|
| 166 |
+
)
|
| 167 |
+
next_stagnation = torch.where(
|
| 168 |
+
committed,
|
| 169 |
+
torch.zeros_like(stagnation_steps),
|
| 170 |
+
torch.where(
|
| 171 |
+
waiting & improving,
|
| 172 |
+
torch.zeros_like(stagnation_steps),
|
| 173 |
+
stagnation_steps + waiting.to(torch.int32),
|
| 174 |
+
),
|
| 175 |
+
)
|
| 176 |
+
return next_ponder.to(torch.int32), next_stagnation.to(torch.int32)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def should_force_trajectory_jump(
|
| 180 |
+
ponder_steps: torch.Tensor,
|
| 181 |
+
stagnation_steps: torch.Tensor,
|
| 182 |
+
*,
|
| 183 |
+
max_ponder_steps: int,
|
| 184 |
+
stagnation_threshold: int,
|
| 185 |
+
) -> torch.BoolTensor:
|
| 186 |
+
"""Return whether a row has exhausted ponder or stagnation budget."""
|
| 187 |
+
|
| 188 |
+
if max_ponder_steps <= 0 or stagnation_threshold <= 0:
|
| 189 |
+
raise ValueError("Trajectory jump limits must be positive.")
|
| 190 |
+
return ponder_steps.ge(max_ponder_steps) | stagnation_steps.ge(stagnation_threshold)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
class _TemporalTransformerCell(nn.Module):
|
| 194 |
+
"""One-step recurrent token update with fixed-slot memory attention."""
|
| 195 |
+
|
| 196 |
+
def __init__(
|
| 197 |
+
self,
|
| 198 |
+
latent_dim: int,
|
| 199 |
+
num_heads: int,
|
| 200 |
+
dropout: float,
|
| 201 |
+
local_attention_window: int,
|
| 202 |
+
) -> None:
|
| 203 |
+
super().__init__()
|
| 204 |
+
self.state_norm = nn.LayerNorm(latent_dim)
|
| 205 |
+
self.observation_norm = nn.LayerNorm(latent_dim)
|
| 206 |
+
self.memory_address_norm = nn.LayerNorm(latent_dim)
|
| 207 |
+
self.memory_value_norm = nn.LayerNorm(latent_dim)
|
| 208 |
+
self.temporal_update = nn.Linear(2 * latent_dim, 2 * latent_dim)
|
| 209 |
+
self.local_attention = nn.MultiheadAttention(
|
| 210 |
+
latent_dim, num_heads, dropout=dropout, batch_first=True
|
| 211 |
+
)
|
| 212 |
+
self.local_attention_window = local_attention_window
|
| 213 |
+
self.register_buffer("_local_attention_mask", torch.empty(0), persistent=False)
|
| 214 |
+
self.token_memory_attention = nn.MultiheadAttention(
|
| 215 |
+
latent_dim, num_heads, dropout=dropout, batch_first=True
|
| 216 |
+
)
|
| 217 |
+
self.memory_token_attention = nn.MultiheadAttention(
|
| 218 |
+
latent_dim, num_heads, dropout=dropout, batch_first=True
|
| 219 |
+
)
|
| 220 |
+
self.token_ff_norm = nn.LayerNorm(latent_dim)
|
| 221 |
+
self.memory_ff_norm = nn.LayerNorm(latent_dim)
|
| 222 |
+
self.stored_token_norm = nn.LayerNorm(latent_dim)
|
| 223 |
+
self.stored_memory_norm = nn.LayerNorm(latent_dim)
|
| 224 |
+
expansion = latent_dim * 4
|
| 225 |
+
self.token_ff = nn.Sequential(
|
| 226 |
+
nn.Linear(latent_dim, expansion),
|
| 227 |
+
nn.SiLU(),
|
| 228 |
+
nn.Linear(expansion, latent_dim),
|
| 229 |
+
)
|
| 230 |
+
self.memory_ff = nn.Sequential(
|
| 231 |
+
nn.Linear(latent_dim, expansion),
|
| 232 |
+
nn.SiLU(),
|
| 233 |
+
nn.Linear(expansion, latent_dim),
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
def forward(
|
| 237 |
+
self,
|
| 238 |
+
previous_tokens: torch.Tensor,
|
| 239 |
+
observation: torch.Tensor,
|
| 240 |
+
memory: torch.Tensor,
|
| 241 |
+
memory_slot_identity: torch.Tensor,
|
| 242 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 243 |
+
"""Update token latents and persistent memory for one cell.
|
| 244 |
+
|
| 245 |
+
Slot identity is an addressing key only. After the memory residual is
|
| 246 |
+
normalized, the same identity is added back so persistent content stays
|
| 247 |
+
slot-specific and invariant to canvas commits.
|
| 248 |
+
|
| 249 |
+
Args:
|
| 250 |
+
previous_tokens: Previous token latents, shape ``[batch, canvas, dim]``.
|
| 251 |
+
observation: Current observation, same shape as ``previous_tokens``.
|
| 252 |
+
memory: Persistent memory slots, shape ``[batch, slots, dim]``.
|
| 253 |
+
memory_slot_identity: Scaled slot addresses, same shape as ``memory``.
|
| 254 |
+
|
| 255 |
+
Returns:
|
| 256 |
+
Updated token latents and memory slots.
|
| 257 |
+
"""
|
| 258 |
+
|
| 259 |
+
gate_logits, candidate = self.temporal_update(
|
| 260 |
+
torch.cat(
|
| 261 |
+
(self.state_norm(previous_tokens), self.observation_norm(observation)),
|
| 262 |
+
dim=-1,
|
| 263 |
+
)
|
| 264 |
+
).chunk(2, dim=-1)
|
| 265 |
+
gate = torch.sigmoid(gate_logits)
|
| 266 |
+
tokens = gate * previous_tokens + (1.0 - gate) * torch.nn.functional.silu(candidate)
|
| 267 |
+
if (
|
| 268 |
+
self._local_attention_mask.shape != (tokens.shape[1], tokens.shape[1])
|
| 269 |
+
or self._local_attention_mask.device != tokens.device
|
| 270 |
+
or self._local_attention_mask.dtype != tokens.dtype
|
| 271 |
+
):
|
| 272 |
+
positions = torch.arange(tokens.shape[1], device=tokens.device)
|
| 273 |
+
allowed = (positions[:, None] - positions[None, :]).abs() < self.local_attention_window
|
| 274 |
+
self._local_attention_mask = torch.zeros(
|
| 275 |
+
tokens.shape[1], tokens.shape[1], device=tokens.device, dtype=tokens.dtype
|
| 276 |
+
).masked_fill(~allowed, torch.finfo(tokens.dtype).min)
|
| 277 |
+
local_update, _ = self.local_attention(
|
| 278 |
+
self.state_norm(tokens),
|
| 279 |
+
self.state_norm(tokens),
|
| 280 |
+
self.state_norm(tokens),
|
| 281 |
+
attn_mask=self._local_attention_mask,
|
| 282 |
+
need_weights=False,
|
| 283 |
+
)
|
| 284 |
+
tokens = tokens + local_update
|
| 285 |
+
|
| 286 |
+
addressed_memory = self.memory_address_norm(memory + memory_slot_identity)
|
| 287 |
+
memory_values = self.memory_value_norm(memory)
|
| 288 |
+
token_memory_update, _ = self.token_memory_attention(
|
| 289 |
+
self.state_norm(tokens), addressed_memory, memory_values, need_weights=False
|
| 290 |
+
)
|
| 291 |
+
tokens = tokens + token_memory_update
|
| 292 |
+
tokens = tokens + self.token_ff(self.token_ff_norm(tokens))
|
| 293 |
+
|
| 294 |
+
memory_token_update, _ = self.memory_token_attention(
|
| 295 |
+
addressed_memory,
|
| 296 |
+
self.state_norm(tokens),
|
| 297 |
+
self.state_norm(tokens),
|
| 298 |
+
need_weights=False,
|
| 299 |
+
)
|
| 300 |
+
memory = memory + memory_token_update
|
| 301 |
+
memory = memory + self.memory_ff(self.memory_ff_norm(memory))
|
| 302 |
+
return (
|
| 303 |
+
self.stored_token_norm(tokens),
|
| 304 |
+
self.stored_memory_norm(memory) + memory_slot_identity,
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
class LatentDeliberationTransformer(nn.Module):
|
| 309 |
+
"""Small recurrent Transformer that compresses repeated denoise context."""
|
| 310 |
+
|
| 311 |
+
def __init__(
|
| 312 |
+
self,
|
| 313 |
+
*,
|
| 314 |
+
hidden_size: int,
|
| 315 |
+
latent_dim: int = 512,
|
| 316 |
+
memory_slots: int = 16,
|
| 317 |
+
num_layers: int = 2,
|
| 318 |
+
num_heads: int = 8,
|
| 319 |
+
local_attention_window: int = 32,
|
| 320 |
+
dropout: float = 0.0,
|
| 321 |
+
) -> None:
|
| 322 |
+
super().__init__()
|
| 323 |
+
if latent_dim % num_heads:
|
| 324 |
+
raise ValueError("`latent_dim` must be divisible by `num_heads`.")
|
| 325 |
+
if local_attention_window <= 0:
|
| 326 |
+
raise ValueError("`local_attention_window` must be positive.")
|
| 327 |
+
self.hidden_size = hidden_size
|
| 328 |
+
self.latent_dim = latent_dim
|
| 329 |
+
self.memory_slots = memory_slots
|
| 330 |
+
self.heavy_projection = nn.Linear(hidden_size, latent_dim, bias=False)
|
| 331 |
+
self.embedding_projection = nn.Linear(hidden_size, latent_dim, bias=False)
|
| 332 |
+
self.scalar_projection = nn.Linear(11, latent_dim, bias=False)
|
| 333 |
+
self.blocks = nn.ModuleList(
|
| 334 |
+
[
|
| 335 |
+
_TemporalTransformerCell(
|
| 336 |
+
latent_dim, num_heads, dropout, local_attention_window
|
| 337 |
+
)
|
| 338 |
+
for _ in range(num_layers)
|
| 339 |
+
]
|
| 340 |
+
)
|
| 341 |
+
self.output_norm = nn.LayerNorm(latent_dim)
|
| 342 |
+
self.output_projection = nn.Linear(latent_dim, hidden_size, bias=False)
|
| 343 |
+
self.memory_slot_identity = nn.Parameter(torch.empty(memory_slots, latent_dim))
|
| 344 |
+
self.reset_memory_slot_identity()
|
| 345 |
+
|
| 346 |
+
@torch.no_grad()
|
| 347 |
+
def reset_memory_slot_identity(self) -> None:
|
| 348 |
+
"""Restore orthonormal slot addresses after generic initialization."""
|
| 349 |
+
|
| 350 |
+
workspace = torch.empty_like(self.memory_slot_identity, dtype=torch.float32)
|
| 351 |
+
if self.memory_slots <= self.latent_dim:
|
| 352 |
+
nn.init.orthogonal_(workspace)
|
| 353 |
+
else:
|
| 354 |
+
nn.init.normal_(workspace, mean=0.0, std=1.0)
|
| 355 |
+
workspace = F.normalize(workspace, dim=-1)
|
| 356 |
+
self.memory_slot_identity.copy_(workspace.to(dtype=self.memory_slot_identity.dtype))
|
| 357 |
+
|
| 358 |
+
def scaled_memory_slot_identity(
|
| 359 |
+
self,
|
| 360 |
+
*,
|
| 361 |
+
batch_size: int,
|
| 362 |
+
device: torch.device,
|
| 363 |
+
dtype: torch.dtype,
|
| 364 |
+
) -> torch.Tensor:
|
| 365 |
+
"""Return unit directions scaled to LayerNorm RMS.
|
| 366 |
+
|
| 367 |
+
Args:
|
| 368 |
+
batch_size: Number of sequences to broadcast over.
|
| 369 |
+
device: Output device.
|
| 370 |
+
dtype: Output dtype.
|
| 371 |
+
|
| 372 |
+
Returns:
|
| 373 |
+
Slot identities of shape ``[batch, slots, latent_dim]``.
|
| 374 |
+
"""
|
| 375 |
+
|
| 376 |
+
identity = F.normalize(self.memory_slot_identity.float(), dim=-1)
|
| 377 |
+
identity = identity * math.sqrt(self.latent_dim)
|
| 378 |
+
return identity.to(device=device, dtype=dtype).unsqueeze(0).expand(
|
| 379 |
+
batch_size, -1, -1
|
| 380 |
+
)
|
| 381 |
+
|
| 382 |
+
def project_context(self, token_latents: torch.Tensor) -> torch.Tensor:
|
| 383 |
+
"""Translate latent state into a self-conditioning embedding."""
|
| 384 |
+
|
| 385 |
+
return self.output_projection(self.output_norm(token_latents))
|
| 386 |
+
|
| 387 |
+
def forward(
|
| 388 |
+
self,
|
| 389 |
+
*,
|
| 390 |
+
heavy_hidden: torch.Tensor,
|
| 391 |
+
token_embeddings: torch.Tensor,
|
| 392 |
+
confidence: torch.Tensor,
|
| 393 |
+
entropy: torch.Tensor,
|
| 394 |
+
state: LatentDeliberationState,
|
| 395 |
+
) -> tuple[torch.Tensor, LatentDeliberationState]:
|
| 396 |
+
"""Advance latent memory and produce decoder self-conditioning.
|
| 397 |
+
|
| 398 |
+
Args:
|
| 399 |
+
heavy_hidden: Hidden states from the previous decoder pass.
|
| 400 |
+
token_embeddings: Embeddings of current noisy canvas tokens.
|
| 401 |
+
confidence: Proposal confidence for each canvas position.
|
| 402 |
+
entropy: Proposal entropy for each canvas position.
|
| 403 |
+
state: Persistent latent state from the preceding pass.
|
| 404 |
+
|
| 405 |
+
Returns:
|
| 406 |
+
Self-conditioning embeddings and the next compact latent state.
|
| 407 |
+
"""
|
| 408 |
+
|
| 409 |
+
if heavy_hidden.ndim != 3:
|
| 410 |
+
raise ValueError("`heavy_hidden` must have shape [batch, canvas, hidden].")
|
| 411 |
+
if heavy_hidden.shape != token_embeddings.shape:
|
| 412 |
+
raise ValueError("`heavy_hidden` and `token_embeddings` must have the same shape.")
|
| 413 |
+
batch_size, canvas_length, hidden_size = heavy_hidden.shape
|
| 414 |
+
if hidden_size != self.hidden_size:
|
| 415 |
+
raise ValueError("Unexpected hidden size for latent deliberation.")
|
| 416 |
+
expected_state = (batch_size, canvas_length, self.latent_dim)
|
| 417 |
+
if state.token_latents.shape != expected_state:
|
| 418 |
+
raise ValueError("State token latents do not match the current canvas.")
|
| 419 |
+
if state.memory_slots.shape != (batch_size, self.memory_slots, self.latent_dim):
|
| 420 |
+
raise ValueError("State memory slots do not match this module.")
|
| 421 |
+
if state.age.dtype is not torch.int32:
|
| 422 |
+
raise TypeError("Latent deliberation ages must use int32.")
|
| 423 |
+
|
| 424 |
+
scalars = torch.stack(
|
| 425 |
+
(
|
| 426 |
+
confidence.to(dtype=heavy_hidden.dtype),
|
| 427 |
+
entropy.to(dtype=heavy_hidden.dtype).log1p(),
|
| 428 |
+
state.age.to(dtype=heavy_hidden.dtype).clamp_max(32767).log1p(),
|
| 429 |
+
torch.linspace(
|
| 430 |
+
-1.0,
|
| 431 |
+
1.0,
|
| 432 |
+
canvas_length,
|
| 433 |
+
device=heavy_hidden.device,
|
| 434 |
+
dtype=heavy_hidden.dtype,
|
| 435 |
+
)
|
| 436 |
+
.unsqueeze(0)
|
| 437 |
+
.expand(batch_size, -1),
|
| 438 |
+
state.token_changed.to(dtype=heavy_hidden.dtype),
|
| 439 |
+
state.confidence_delta.to(dtype=heavy_hidden.dtype),
|
| 440 |
+
state.entropy_delta.to(dtype=heavy_hidden.dtype).sign()
|
| 441 |
+
* state.entropy_delta.to(dtype=heavy_hidden.dtype).abs().log1p(),
|
| 442 |
+
state.ponder_steps.to(dtype=heavy_hidden.dtype).log1p()[:, None].expand(
|
| 443 |
+
-1, canvas_length
|
| 444 |
+
),
|
| 445 |
+
state.stagnation_steps.to(dtype=heavy_hidden.dtype).log1p()[:, None].expand(
|
| 446 |
+
-1, canvas_length
|
| 447 |
+
),
|
| 448 |
+
confidence.to(dtype=heavy_hidden.dtype)
|
| 449 |
+
* torch.exp(-entropy.to(dtype=heavy_hidden.dtype).clamp_min(0.0)),
|
| 450 |
+
state.confidence_delta.to(dtype=heavy_hidden.dtype).clamp_min(0.0)
|
| 451 |
+
+ (-state.entropy_delta.to(dtype=heavy_hidden.dtype)).clamp_min(0.0).log1p(),
|
| 452 |
+
),
|
| 453 |
+
dim=-1,
|
| 454 |
+
)
|
| 455 |
+
observation = (
|
| 456 |
+
self.heavy_projection(heavy_hidden)
|
| 457 |
+
+ self.embedding_projection(token_embeddings)
|
| 458 |
+
+ self.scalar_projection(scalars)
|
| 459 |
+
)
|
| 460 |
+
tokens = state.token_latents
|
| 461 |
+
memory = state.memory_slots
|
| 462 |
+
slot_identity = self.scaled_memory_slot_identity(
|
| 463 |
+
batch_size=batch_size,
|
| 464 |
+
device=memory.device,
|
| 465 |
+
dtype=memory.dtype,
|
| 466 |
+
)
|
| 467 |
+
for block in self.blocks:
|
| 468 |
+
tokens, memory = block(tokens, observation, memory, slot_identity)
|
| 469 |
+
observation = tokens
|
| 470 |
+
next_state = LatentDeliberationState(
|
| 471 |
+
token_latents=tokens,
|
| 472 |
+
memory_slots=memory,
|
| 473 |
+
confidence=confidence.to(dtype=torch.float32),
|
| 474 |
+
entropy=entropy.to(dtype=torch.float32),
|
| 475 |
+
age=state.age,
|
| 476 |
+
token_changed=state.token_changed,
|
| 477 |
+
confidence_delta=state.confidence_delta,
|
| 478 |
+
entropy_delta=state.entropy_delta,
|
| 479 |
+
ponder_steps=state.ponder_steps,
|
| 480 |
+
stagnation_steps=state.stagnation_steps,
|
| 481 |
+
)
|
| 482 |
+
return self.project_context(tokens), next_state
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
__all__ = [
|
| 486 |
+
"LatentDeliberationState",
|
| 487 |
+
"LatentDeliberationTransformer",
|
| 488 |
+
"advance_trajectory_clocks",
|
| 489 |
+
"should_force_trajectory_jump",
|
| 490 |
+
]
|
model-00001-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:990b322d47df486108e725ff6884842b4b6512e98e57202d04be8b57ecd2b456
|
| 3 |
+
size 5363460572
|
model-00002-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b2b7137b04fad66c8654441a9535b257862ee9f97ddf9d4b635ac3fe09b6366d
|
| 3 |
+
size 4884577974
|
model-00003-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4c58d6983f7796596da51974362aefc6f578aea6c572eb1ff404c76d10ae6d69
|
| 3 |
+
size 4913414718
|
model-00004-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:89ff79354e9cbb2912756be80fd9dfc487fdb226f25e78248397080c89f3d830
|
| 3 |
+
size 4884577998
|
model-00005-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:41a2615fab0008f86cd27ea11d04edda0cea8f8e92b3f579ee2f21c4fc642357
|
| 3 |
+
size 4913414782
|
model-00006-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8059cb7856f4bc999bfbba2ef805bbfca7caa4857f07388f75369064cfa01209
|
| 3 |
+
size 4884578038
|
model-00007-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4e5e17d862209d68ef619895f1ac31b1dded2a3c151738e8241d9c85d147c764
|
| 3 |
+
size 4913414782
|
model-00008-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7f553a5906c249c4c2181fed3fa6b59ea6b8e8bfe6a0844b5a375c7e81957edd
|
| 3 |
+
size 4884578038
|
model-00009-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:46bdb5a161862beeebe862d5dba46562b8d0c511c84aac87355433b45a30ae4c
|
| 3 |
+
size 4913414782
|
model-00010-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b1d6491992dfe7b4ccf4ee266594bc754c90dace3c8c7fb21e5c149e64b57841
|
| 3 |
+
size 4884578038
|
model-00011-of-00011.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b60ca12117cdb0165501adf18ecb7fef85310db6565cf6004bc72aebfe726573
|
| 3 |
+
size 2838371094
|
model.safetensors.index.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
modeling_modilify_mk1.py
ADDED
|
@@ -0,0 +1,688 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2026 Modilify
|
| 2 |
+
# SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0
|
| 3 |
+
"""Standard PyTorch multimodal model implementation for Modilify Mk1."""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from collections.abc import Sequence
|
| 8 |
+
from dataclasses import dataclass, replace
|
| 9 |
+
import math
|
| 10 |
+
from typing import Any
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
from torch import nn
|
| 14 |
+
from torch.nn import functional as F
|
| 15 |
+
from transformers.cache_utils import Cache
|
| 16 |
+
from transformers.masking_utils import (
|
| 17 |
+
ALL_MASK_ATTENTION_FUNCTIONS,
|
| 18 |
+
bidirectional_mask_function,
|
| 19 |
+
)
|
| 20 |
+
from transformers.modeling_outputs import BaseModelOutputWithPast
|
| 21 |
+
from transformers.utils import ModelOutput
|
| 22 |
+
from transformers.models.diffusion_gemma import (
|
| 23 |
+
DiffusionGemmaDecoderModel,
|
| 24 |
+
DiffusionGemmaEncoderModel,
|
| 25 |
+
DiffusionGemmaPreTrainedModel,
|
| 26 |
+
)
|
| 27 |
+
from transformers.models.diffusion_gemma.modeling_diffusion_gemma import (
|
| 28 |
+
DiffusionGemmaRMSNorm,
|
| 29 |
+
DiffusionGemmaTextRouter,
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
from .configuration_modilify_mk1 import ModilifyMk1Config
|
| 33 |
+
from .generation_modilify_mk1 import (
|
| 34 |
+
ModilifyMk1GenerationConfig,
|
| 35 |
+
ModilifyMk1GenerationMixin,
|
| 36 |
+
)
|
| 37 |
+
from .latent_deliberation import (
|
| 38 |
+
LatentDeliberationState,
|
| 39 |
+
LatentDeliberationTransformer,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@dataclass
|
| 44 |
+
class ModilifyMk1DecoderOutput(BaseModelOutputWithPast):
|
| 45 |
+
"""Decoder hidden states and latent-context diagnostics."""
|
| 46 |
+
|
| 47 |
+
token_embeddings: torch.FloatTensor | None = None
|
| 48 |
+
latent_residual_diagnostics: dict[str, torch.Tensor] | None = None
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@dataclass
|
| 52 |
+
class ModilifyMk1ModelOutput(BaseModelOutputWithPast):
|
| 53 |
+
"""Combined multimodal encoder and diffusion decoder output."""
|
| 54 |
+
|
| 55 |
+
token_embeddings: torch.FloatTensor | None = None
|
| 56 |
+
encoder_last_hidden_state: torch.FloatTensor | None = None
|
| 57 |
+
latent_residual_diagnostics: dict[str, torch.Tensor] | None = None
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
@dataclass
|
| 61 |
+
class ModilifyMk1BlockDiffusionOutput(ModelOutput):
|
| 62 |
+
"""Inference output used by the rolling diffusion generator."""
|
| 63 |
+
|
| 64 |
+
logits: torch.FloatTensor | None = None
|
| 65 |
+
heavy_hidden_state: torch.FloatTensor | None = None
|
| 66 |
+
next_latent_state: LatentDeliberationState | None = None
|
| 67 |
+
past_key_values: Cache | None = None
|
| 68 |
+
encoder_last_hidden_state: torch.FloatTensor | None = None
|
| 69 |
+
temporal_context: torch.FloatTensor | None = None
|
| 70 |
+
latent_residual_diagnostics: dict[str, torch.Tensor] | None = None
|
| 71 |
+
proposal: torch.LongTensor | None = None
|
| 72 |
+
proposal_confidence: torch.FloatTensor | None = None
|
| 73 |
+
token_entropy: torch.FloatTensor | None = None
|
| 74 |
+
greedy_proposal: torch.LongTensor | None = None
|
| 75 |
+
greedy_confidence: torch.FloatTensor | None = None
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class ModilifyMk1RMSNorm(DiffusionGemmaRMSNorm):
|
| 79 |
+
"""Official RMSNorm parameters with a same-dtype residual forward."""
|
| 80 |
+
|
| 81 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 82 |
+
"""Normalize ``hidden_states`` and restore the input dtype."""
|
| 83 |
+
|
| 84 |
+
normed_output = self._norm(hidden_states)
|
| 85 |
+
if self.with_scale:
|
| 86 |
+
normed_output = normed_output * self.weight.to(dtype=normed_output.dtype)
|
| 87 |
+
return normed_output.type_as(hidden_states)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class ModilifyMk1TextRouter(DiffusionGemmaTextRouter):
|
| 91 |
+
"""Official router parameters with a log-softmax top-k route."""
|
| 92 |
+
|
| 93 |
+
def __init__(self, config: Any) -> None:
|
| 94 |
+
super().__init__(config)
|
| 95 |
+
self.norm = ModilifyMk1RMSNorm(self.hidden_size, eps=self.eps, with_scale=False)
|
| 96 |
+
|
| 97 |
+
def forward(
|
| 98 |
+
self, hidden_states: torch.Tensor
|
| 99 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 100 |
+
"""Return route probabilities, top-k weights, and expert indices."""
|
| 101 |
+
|
| 102 |
+
hidden_states = self.norm(hidden_states)
|
| 103 |
+
hidden_states = hidden_states * self.scale * self.scalar_root_size
|
| 104 |
+
expert_scores = self.proj(hidden_states)
|
| 105 |
+
router_probabilities = F.log_softmax(expert_scores, dim=-1).exp()
|
| 106 |
+
top_k_weights, top_k_index = torch.topk(
|
| 107 |
+
router_probabilities,
|
| 108 |
+
k=self.config.top_k_experts,
|
| 109 |
+
dim=-1,
|
| 110 |
+
)
|
| 111 |
+
top_k_weights = top_k_weights / top_k_weights.sum(dim=-1, keepdim=True)
|
| 112 |
+
top_k_weights = top_k_weights * self.per_expert_scale[top_k_index]
|
| 113 |
+
return router_probabilities, top_k_weights, top_k_index
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def install_modilify_mk1_trunk_semantics(module: nn.Module) -> None:
|
| 117 |
+
"""Replace official leaf modules on this instance only.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
module: Encoder, decoder, or parent module whose children should be
|
| 121 |
+
swapped to the instance-scoped RMSNorm and router implementations.
|
| 122 |
+
"""
|
| 123 |
+
|
| 124 |
+
for name, child in list(module.named_children()):
|
| 125 |
+
if type(child) is DiffusionGemmaRMSNorm:
|
| 126 |
+
dim = int(child.weight.shape[0]) if child.with_scale else 1
|
| 127 |
+
replacement = ModilifyMk1RMSNorm(
|
| 128 |
+
dim, eps=child.eps, with_scale=child.with_scale
|
| 129 |
+
)
|
| 130 |
+
replacement.load_state_dict(child.state_dict())
|
| 131 |
+
setattr(module, name, replacement)
|
| 132 |
+
elif type(child) is DiffusionGemmaTextRouter:
|
| 133 |
+
replacement = ModilifyMk1TextRouter(child.config)
|
| 134 |
+
replacement.load_state_dict(child.state_dict())
|
| 135 |
+
setattr(module, name, replacement)
|
| 136 |
+
else:
|
| 137 |
+
install_modilify_mk1_trunk_semantics(child)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class ModilifyMk1EncoderModel(DiffusionGemmaEncoderModel):
|
| 141 |
+
"""Unmodified Transformers DiffusionGemma multimodal encoder."""
|
| 142 |
+
|
| 143 |
+
config_class = ModilifyMk1Config
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
class ModilifyMk1DecoderModel(DiffusionGemmaDecoderModel):
|
| 147 |
+
"""DiffusionGemma decoder conditioned by recurrent latent embeddings."""
|
| 148 |
+
|
| 149 |
+
config_class = ModilifyMk1Config
|
| 150 |
+
latent_residual_rms_ratio_cap = 0.5
|
| 151 |
+
|
| 152 |
+
@staticmethod
|
| 153 |
+
def create_diffusion_decoder_attention_mask(
|
| 154 |
+
config: Any,
|
| 155 |
+
inputs_embeds: torch.Tensor,
|
| 156 |
+
past_key_values: Cache,
|
| 157 |
+
decoder_attention_mask: torch.Tensor | dict | None = None,
|
| 158 |
+
) -> dict[str, torch.Tensor | None]:
|
| 159 |
+
"""Build bidirectional canvas masks without skipping sliding layers.
|
| 160 |
+
|
| 161 |
+
Args:
|
| 162 |
+
config: Text configuration used for layer types and window size.
|
| 163 |
+
inputs_embeds: Canvas embeddings that define query length and dtype.
|
| 164 |
+
past_key_values: Prefix cache used to size the key/value axis.
|
| 165 |
+
decoder_attention_mask: Optional 2-D mask or precomputed 4-D maps.
|
| 166 |
+
|
| 167 |
+
Returns:
|
| 168 |
+
A mapping from layer pattern to attention mask.
|
| 169 |
+
"""
|
| 170 |
+
|
| 171 |
+
if past_key_values is None:
|
| 172 |
+
raise ValueError(
|
| 173 |
+
"The diffusion mask requires `past_key_values` to construct the "
|
| 174 |
+
"next attention mask correctly."
|
| 175 |
+
)
|
| 176 |
+
if (
|
| 177 |
+
decoder_attention_mask is None
|
| 178 |
+
or config._attn_implementation
|
| 179 |
+
not in ALL_MASK_ATTENTION_FUNCTIONS._global_mapping
|
| 180 |
+
):
|
| 181 |
+
return {"full_attention": None, "sliding_attention": None}
|
| 182 |
+
if isinstance(decoder_attention_mask, dict) and all(
|
| 183 |
+
mask.ndim == 4 for mask in decoder_attention_mask.values()
|
| 184 |
+
):
|
| 185 |
+
return decoder_attention_mask
|
| 186 |
+
|
| 187 |
+
text_config = config.get_text_config() if hasattr(config, "get_text_config") else config
|
| 188 |
+
q_length = inputs_embeds.shape[1]
|
| 189 |
+
q_offset = past_key_values.get_seq_length()
|
| 190 |
+
if isinstance(q_offset, torch.Tensor):
|
| 191 |
+
q_offset = q_offset.to(inputs_embeds.device)
|
| 192 |
+
additional_kv_length = (
|
| 193 |
+
getattr(config, "canvas_length", 0) if past_key_values.is_compileable else 0
|
| 194 |
+
)
|
| 195 |
+
mask_mapping: dict[str, torch.Tensor | None] = {}
|
| 196 |
+
for layer_pattern in set(text_config.layer_types):
|
| 197 |
+
layer_idx = past_key_values.is_sliding.index(
|
| 198 |
+
layer_pattern == "sliding_attention"
|
| 199 |
+
)
|
| 200 |
+
kv_length, kv_offset = past_key_values.get_mask_sizes(q_length, layer_idx)
|
| 201 |
+
kv_length += additional_kv_length
|
| 202 |
+
if layer_pattern == "sliding_attention" and past_key_values.is_compileable:
|
| 203 |
+
sliding_layer = past_key_values.layers[layer_idx]
|
| 204 |
+
max_length = sliding_layer.get_max_length() + additional_kv_length
|
| 205 |
+
if kv_length >= max_length:
|
| 206 |
+
kv_length = max_length
|
| 207 |
+
mask_mapping[layer_pattern] = ALL_MASK_ATTENTION_FUNCTIONS[
|
| 208 |
+
config._attn_implementation
|
| 209 |
+
](
|
| 210 |
+
batch_size=inputs_embeds.shape[0],
|
| 211 |
+
q_length=q_length,
|
| 212 |
+
kv_length=kv_length,
|
| 213 |
+
q_offset=q_offset,
|
| 214 |
+
kv_offset=kv_offset,
|
| 215 |
+
mask_function=bidirectional_mask_function,
|
| 216 |
+
attention_mask=decoder_attention_mask,
|
| 217 |
+
allow_is_causal_skip=False,
|
| 218 |
+
allow_is_bidirectional_skip=True,
|
| 219 |
+
local_size=getattr(text_config, "sliding_window", None),
|
| 220 |
+
dtype=inputs_embeds.dtype,
|
| 221 |
+
config=text_config,
|
| 222 |
+
use_vmap=False,
|
| 223 |
+
device=inputs_embeds.device,
|
| 224 |
+
)
|
| 225 |
+
return mask_mapping
|
| 226 |
+
|
| 227 |
+
def merge_latent_context(
|
| 228 |
+
self,
|
| 229 |
+
token_embeddings: torch.Tensor,
|
| 230 |
+
latent_context: torch.Tensor | None,
|
| 231 |
+
) -> tuple[torch.Tensor, dict[str, torch.Tensor]]:
|
| 232 |
+
"""Apply the native self-conditioning bridge to latent context.
|
| 233 |
+
|
| 234 |
+
Args:
|
| 235 |
+
token_embeddings: Embedded noisy canvas tokens.
|
| 236 |
+
latent_context: Context emitted by the latent Transformer.
|
| 237 |
+
|
| 238 |
+
Returns:
|
| 239 |
+
Merged embeddings and scalar diagnostic tensors.
|
| 240 |
+
"""
|
| 241 |
+
|
| 242 |
+
context = (
|
| 243 |
+
torch.zeros_like(token_embeddings)
|
| 244 |
+
if latent_context is None
|
| 245 |
+
else latent_context.to(token_embeddings)
|
| 246 |
+
)
|
| 247 |
+
if context.shape != token_embeddings.shape:
|
| 248 |
+
raise ValueError("Latent context must match the canvas embedding shape.")
|
| 249 |
+
mapper = self.self_conditioning
|
| 250 |
+
normalized = mapper.pre_norm(context)
|
| 251 |
+
mapped = mapper.down_proj(
|
| 252 |
+
mapper.act_fn(mapper.gate_proj(normalized)) * mapper.up_proj(normalized)
|
| 253 |
+
)
|
| 254 |
+
mapped_rms_per_token = mapped.float().square().mean(dim=-1, keepdim=True).sqrt()
|
| 255 |
+
token_rms_per_token = (
|
| 256 |
+
token_embeddings.float().square().mean(dim=-1, keepdim=True).sqrt()
|
| 257 |
+
)
|
| 258 |
+
cap = self.latent_residual_rms_ratio_cap * token_rms_per_token
|
| 259 |
+
scale = cap / torch.sqrt(mapped_rms_per_token.square() + cap.square() + 1.0e-12)
|
| 260 |
+
mapped = mapped * scale.to(mapped)
|
| 261 |
+
combined = mapper.post_norm(token_embeddings + mapped)
|
| 262 |
+
token_rms = token_embeddings.detach().float().square().mean().sqrt()
|
| 263 |
+
mapped_rms = mapped.detach().float().square().mean().sqrt()
|
| 264 |
+
diagnostics = {
|
| 265 |
+
"token_embedding_rms": token_rms,
|
| 266 |
+
"latent_context_rms": context.detach().float().square().mean().sqrt(),
|
| 267 |
+
"mapped_context_rms": mapped_rms,
|
| 268 |
+
"latent_to_embedding_rms_ratio": mapped_rms / token_rms.clamp_min(1.0e-12),
|
| 269 |
+
}
|
| 270 |
+
return combined, diagnostics
|
| 271 |
+
|
| 272 |
+
def forward(
|
| 273 |
+
self,
|
| 274 |
+
decoder_input_ids: torch.LongTensor,
|
| 275 |
+
past_key_values: Cache | None = None,
|
| 276 |
+
temporal_context_embeddings: torch.FloatTensor | None = None,
|
| 277 |
+
decoder_attention_mask: torch.Tensor | dict | None = None,
|
| 278 |
+
decoder_position_ids: torch.LongTensor | None = None,
|
| 279 |
+
**kwargs: Any,
|
| 280 |
+
) -> ModilifyMk1DecoderOutput:
|
| 281 |
+
"""Decode one noisy canvas using Transformers and PyTorch operations."""
|
| 282 |
+
|
| 283 |
+
token_embeddings = self.embed_tokens(decoder_input_ids)
|
| 284 |
+
inputs_embeds, diagnostics = self.merge_latent_context(
|
| 285 |
+
token_embeddings,
|
| 286 |
+
temporal_context_embeddings,
|
| 287 |
+
)
|
| 288 |
+
if decoder_position_ids is None:
|
| 289 |
+
prefix = past_key_values.get_seq_length(0) if past_key_values is not None else 0
|
| 290 |
+
decoder_position_ids = torch.arange(
|
| 291 |
+
prefix,
|
| 292 |
+
prefix + inputs_embeds.shape[1],
|
| 293 |
+
device=inputs_embeds.device,
|
| 294 |
+
).unsqueeze(0)
|
| 295 |
+
if not isinstance(mask_mapping := decoder_attention_mask, dict):
|
| 296 |
+
mask_mapping = self.create_diffusion_decoder_attention_mask(
|
| 297 |
+
config=self.text_config,
|
| 298 |
+
inputs_embeds=inputs_embeds,
|
| 299 |
+
past_key_values=past_key_values,
|
| 300 |
+
decoder_attention_mask=decoder_attention_mask,
|
| 301 |
+
)
|
| 302 |
+
position_embeddings = {
|
| 303 |
+
layer_type: self.rotary_emb(inputs_embeds, decoder_position_ids, layer_type)
|
| 304 |
+
for layer_type in self.unique_layer_types
|
| 305 |
+
}
|
| 306 |
+
hidden_states = inputs_embeds
|
| 307 |
+
for index, layer in enumerate(self.layers[: self.text_config.num_hidden_layers]):
|
| 308 |
+
layer_type = self.text_config.layer_types[index]
|
| 309 |
+
hidden_states = layer(
|
| 310 |
+
hidden_states,
|
| 311 |
+
position_embeddings=position_embeddings[layer_type],
|
| 312 |
+
attention_mask=mask_mapping[layer_type],
|
| 313 |
+
position_ids=decoder_position_ids,
|
| 314 |
+
past_key_values=past_key_values,
|
| 315 |
+
**kwargs,
|
| 316 |
+
)
|
| 317 |
+
return ModilifyMk1DecoderOutput(
|
| 318 |
+
last_hidden_state=self.norm(hidden_states),
|
| 319 |
+
past_key_values=past_key_values,
|
| 320 |
+
token_embeddings=token_embeddings,
|
| 321 |
+
latent_residual_diagnostics=diagnostics,
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
class ModilifyMk1Model(DiffusionGemmaPreTrainedModel):
|
| 326 |
+
"""Multimodal encoder plus latent-conditioned block diffusion decoder."""
|
| 327 |
+
|
| 328 |
+
config_class = ModilifyMk1Config
|
| 329 |
+
_tied_weights_keys = {
|
| 330 |
+
"encoder.language_model.norm.weight": "decoder.norm.weight",
|
| 331 |
+
r"encoder.language_model.layers\.(?:[^.]+\.)*weight": r"decoder.layers\.(?:[^.]+\.)*weight",
|
| 332 |
+
r"encoder.language_model.layers\.(?:[^.]+\.)*scale": r"decoder.layers\.(?:[^.]+\.)*scale",
|
| 333 |
+
(
|
| 334 |
+
r"encoder.language_model.layers\.(?:[^.]+\.)*per_expert_scale"
|
| 335 |
+
): r"decoder.layers\.(?:[^.]+\.)*per_expert_scale",
|
| 336 |
+
(
|
| 337 |
+
r"encoder.language_model.layers\.(?:[^.]+\.)*gate_up_proj"
|
| 338 |
+
): r"decoder.layers\.(?:[^.]+\.)*gate_up_proj",
|
| 339 |
+
(
|
| 340 |
+
r"encoder.language_model.layers\.(?:[^.]+\.)*down_proj"
|
| 341 |
+
): r"decoder.layers\.(?:[^.]+\.)*down_proj",
|
| 342 |
+
"encoder.language_model.embed_tokens.weight": "decoder.embed_tokens.weight",
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
def __init__(self, config: ModilifyMk1Config) -> None:
|
| 346 |
+
super().__init__(config)
|
| 347 |
+
self.encoder = ModilifyMk1EncoderModel(config)
|
| 348 |
+
self.decoder = ModilifyMk1DecoderModel(config)
|
| 349 |
+
install_modilify_mk1_trunk_semantics(self)
|
| 350 |
+
self.post_init()
|
| 351 |
+
|
| 352 |
+
def get_encoder(self) -> ModilifyMk1EncoderModel:
|
| 353 |
+
"""Return the multimodal encoder."""
|
| 354 |
+
|
| 355 |
+
return self.encoder
|
| 356 |
+
|
| 357 |
+
def get_decoder(self) -> ModilifyMk1DecoderModel:
|
| 358 |
+
"""Return the diffusion decoder."""
|
| 359 |
+
|
| 360 |
+
return self.decoder
|
| 361 |
+
|
| 362 |
+
def get_input_embeddings(self) -> nn.Module:
|
| 363 |
+
"""Return the shared text embedding module."""
|
| 364 |
+
|
| 365 |
+
return self.encoder.get_input_embeddings()
|
| 366 |
+
|
| 367 |
+
def set_input_embeddings(self, value: nn.Module) -> None:
|
| 368 |
+
"""Set the shared text embedding module."""
|
| 369 |
+
|
| 370 |
+
self.encoder.set_input_embeddings(value)
|
| 371 |
+
self.decoder.embed_tokens = value
|
| 372 |
+
|
| 373 |
+
def forward(
|
| 374 |
+
self,
|
| 375 |
+
*,
|
| 376 |
+
input_ids: torch.LongTensor | None = None,
|
| 377 |
+
attention_mask: torch.Tensor | dict | None = None,
|
| 378 |
+
past_key_values: Cache | None = None,
|
| 379 |
+
position_ids: torch.LongTensor | None = None,
|
| 380 |
+
decoder_input_ids: torch.LongTensor,
|
| 381 |
+
temporal_context_embeddings: torch.FloatTensor | None = None,
|
| 382 |
+
decoder_attention_mask: torch.Tensor | dict | None = None,
|
| 383 |
+
decoder_position_ids: torch.LongTensor | None = None,
|
| 384 |
+
**kwargs: Any,
|
| 385 |
+
) -> ModilifyMk1ModelOutput:
|
| 386 |
+
"""Encode multimodal context and decode one canvas."""
|
| 387 |
+
|
| 388 |
+
encoder_hidden_state = None
|
| 389 |
+
encoder_keys = ("pixel_values", "mm_token_type_ids", "image_position_ids", "inputs_embeds")
|
| 390 |
+
encoder_kwargs = {key: kwargs.pop(key) for key in encoder_keys if key in kwargs}
|
| 391 |
+
if input_ids is not None:
|
| 392 |
+
encoded = self.encoder(
|
| 393 |
+
input_ids=input_ids,
|
| 394 |
+
attention_mask=attention_mask,
|
| 395 |
+
past_key_values=past_key_values,
|
| 396 |
+
position_ids=position_ids,
|
| 397 |
+
**encoder_kwargs,
|
| 398 |
+
)
|
| 399 |
+
past_key_values = encoded.past_key_values
|
| 400 |
+
encoder_hidden_state = encoded.last_hidden_state
|
| 401 |
+
elif past_key_values is None:
|
| 402 |
+
raise ValueError("Either `input_ids` or `past_key_values` is required.")
|
| 403 |
+
decoded = self.decoder(
|
| 404 |
+
decoder_input_ids=decoder_input_ids,
|
| 405 |
+
past_key_values=past_key_values,
|
| 406 |
+
temporal_context_embeddings=temporal_context_embeddings,
|
| 407 |
+
decoder_attention_mask=decoder_attention_mask,
|
| 408 |
+
decoder_position_ids=decoder_position_ids,
|
| 409 |
+
**kwargs,
|
| 410 |
+
)
|
| 411 |
+
return ModilifyMk1ModelOutput(
|
| 412 |
+
last_hidden_state=decoded.last_hidden_state,
|
| 413 |
+
past_key_values=past_key_values,
|
| 414 |
+
token_embeddings=decoded.token_embeddings,
|
| 415 |
+
encoder_last_hidden_state=encoder_hidden_state,
|
| 416 |
+
latent_residual_diagnostics=decoded.latent_residual_diagnostics,
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
class ModilifyMk1ForBlockDiffusion(
|
| 421 |
+
DiffusionGemmaPreTrainedModel,
|
| 422 |
+
ModilifyMk1GenerationMixin,
|
| 423 |
+
):
|
| 424 |
+
"""Inference-only multimodal Modilify Mk1 model."""
|
| 425 |
+
|
| 426 |
+
config_class = ModilifyMk1Config
|
| 427 |
+
_tied_weights_keys = {"lm_head.weight": "model.decoder.embed_tokens.weight"}
|
| 428 |
+
generation_config_class = ModilifyMk1GenerationConfig
|
| 429 |
+
|
| 430 |
+
@torch.no_grad()
|
| 431 |
+
def _init_weights(self, module: nn.Module) -> None:
|
| 432 |
+
super()._init_weights(module)
|
| 433 |
+
if isinstance(module, LatentDeliberationTransformer):
|
| 434 |
+
module.reset_memory_slot_identity()
|
| 435 |
+
|
| 436 |
+
def __init__(self, config: ModilifyMk1Config) -> None:
|
| 437 |
+
super().__init__(config)
|
| 438 |
+
self.model = ModilifyMk1Model(config)
|
| 439 |
+
self.latent_deliberation = LatentDeliberationTransformer(
|
| 440 |
+
hidden_size=config.text_config.hidden_size,
|
| 441 |
+
latent_dim=config.latent_dim,
|
| 442 |
+
memory_slots=config.latent_memory_slots,
|
| 443 |
+
num_layers=config.latent_num_layers,
|
| 444 |
+
num_heads=config.latent_num_heads,
|
| 445 |
+
local_attention_window=config.latent_local_attention_window,
|
| 446 |
+
dropout=config.latent_dropout,
|
| 447 |
+
)
|
| 448 |
+
self.lm_head = nn.Linear(
|
| 449 |
+
config.text_config.hidden_size,
|
| 450 |
+
config.text_config.vocab_size,
|
| 451 |
+
bias=False,
|
| 452 |
+
)
|
| 453 |
+
self.final_logit_softcapping = config.text_config.final_logit_softcapping
|
| 454 |
+
self.post_init()
|
| 455 |
+
|
| 456 |
+
def _prepare_latent_context(
|
| 457 |
+
self,
|
| 458 |
+
decoder_input_ids: torch.LongTensor,
|
| 459 |
+
*,
|
| 460 |
+
history_hidden_state: torch.Tensor | None,
|
| 461 |
+
confidence: torch.Tensor | None,
|
| 462 |
+
entropy: torch.Tensor | None,
|
| 463 |
+
age: torch.Tensor | None,
|
| 464 |
+
latent_state: LatentDeliberationState | None,
|
| 465 |
+
) -> tuple[torch.Tensor, LatentDeliberationState]:
|
| 466 |
+
"""Advance recurrent latent state for the current canvas."""
|
| 467 |
+
|
| 468 |
+
batch_size, canvas_length = decoder_input_ids.shape
|
| 469 |
+
dtype = self.model.decoder.embed_tokens.weight.dtype
|
| 470 |
+
if latent_state is None:
|
| 471 |
+
latent_state = LatentDeliberationState.empty(
|
| 472 |
+
batch_size=batch_size,
|
| 473 |
+
canvas_length=canvas_length,
|
| 474 |
+
latent_dim=self.config.latent_dim,
|
| 475 |
+
memory_slots=self.config.latent_memory_slots,
|
| 476 |
+
device=decoder_input_ids.device,
|
| 477 |
+
dtype=dtype,
|
| 478 |
+
)
|
| 479 |
+
confidence = (
|
| 480 |
+
latent_state.confidence
|
| 481 |
+
if confidence is None
|
| 482 |
+
else confidence.squeeze(-1).float()
|
| 483 |
+
)
|
| 484 |
+
entropy = latent_state.entropy if entropy is None else entropy.squeeze(-1).float()
|
| 485 |
+
if age is not None:
|
| 486 |
+
latent_state = replace(
|
| 487 |
+
latent_state,
|
| 488 |
+
age=age.to(device=decoder_input_ids.device, dtype=torch.int32),
|
| 489 |
+
)
|
| 490 |
+
token_embeddings = self.model.decoder.embed_tokens(decoder_input_ids)
|
| 491 |
+
history = (
|
| 492 |
+
torch.zeros_like(token_embeddings)
|
| 493 |
+
if history_hidden_state is None
|
| 494 |
+
else history_hidden_state
|
| 495 |
+
)
|
| 496 |
+
return self.latent_deliberation(
|
| 497 |
+
heavy_hidden=history,
|
| 498 |
+
token_embeddings=token_embeddings,
|
| 499 |
+
confidence=confidence,
|
| 500 |
+
entropy=entropy,
|
| 501 |
+
state=latent_state,
|
| 502 |
+
)
|
| 503 |
+
|
| 504 |
+
def _apply_repetition_penalty(
|
| 505 |
+
self,
|
| 506 |
+
logits: torch.Tensor,
|
| 507 |
+
*,
|
| 508 |
+
repetition_token_mask: torch.BoolTensor | None,
|
| 509 |
+
repetition_penalty: float,
|
| 510 |
+
) -> torch.Tensor:
|
| 511 |
+
"""Apply a sign-aware Transformers repetition penalty.
|
| 512 |
+
|
| 513 |
+
Args:
|
| 514 |
+
logits: Soft-capped scores, shape ``[batch, canvas, vocab]``.
|
| 515 |
+
repetition_token_mask: Tokens already seen, shape ``[batch, vocab]``.
|
| 516 |
+
repetition_penalty: Penalty factor. ``1.0`` leaves logits unchanged.
|
| 517 |
+
|
| 518 |
+
Returns:
|
| 519 |
+
Penalized logits with the same shape as ``logits``.
|
| 520 |
+
"""
|
| 521 |
+
|
| 522 |
+
if (
|
| 523 |
+
repetition_token_mask is None
|
| 524 |
+
or not math.isfinite(repetition_penalty)
|
| 525 |
+
or repetition_penalty == 1.0
|
| 526 |
+
):
|
| 527 |
+
return logits
|
| 528 |
+
if repetition_penalty <= 0:
|
| 529 |
+
raise ValueError("`repetition_penalty` must be a positive finite number.")
|
| 530 |
+
if repetition_token_mask.shape != (logits.shape[0], logits.shape[-1]):
|
| 531 |
+
raise ValueError(
|
| 532 |
+
"`repetition_token_mask` must have shape [batch, vocab]."
|
| 533 |
+
)
|
| 534 |
+
scores = logits.float()
|
| 535 |
+
penalized = torch.where(scores < 0, scores * repetition_penalty, scores / repetition_penalty)
|
| 536 |
+
mask = repetition_token_mask.to(device=scores.device).unsqueeze(1)
|
| 537 |
+
return torch.where(mask, penalized, scores).to(dtype=logits.dtype)
|
| 538 |
+
|
| 539 |
+
def _proposal_statistics(
|
| 540 |
+
self,
|
| 541 |
+
logits: torch.Tensor,
|
| 542 |
+
*,
|
| 543 |
+
denoise_temperature: float | None = None,
|
| 544 |
+
repetition_token_mask: torch.BoolTensor | None = None,
|
| 545 |
+
repetition_penalty: float = 1.0,
|
| 546 |
+
sampling_generators: Sequence[torch.Generator] | None = None,
|
| 547 |
+
) -> tuple[
|
| 548 |
+
torch.LongTensor,
|
| 549 |
+
torch.Tensor,
|
| 550 |
+
torch.Tensor,
|
| 551 |
+
torch.LongTensor,
|
| 552 |
+
torch.Tensor,
|
| 553 |
+
]:
|
| 554 |
+
"""Compute exact proposal statistics with standard PyTorch operations."""
|
| 555 |
+
|
| 556 |
+
temperature = (
|
| 557 |
+
self.config.denoise_temperature
|
| 558 |
+
if denoise_temperature is None
|
| 559 |
+
else float(denoise_temperature)
|
| 560 |
+
)
|
| 561 |
+
if not math.isfinite(temperature) or temperature <= 0.0:
|
| 562 |
+
raise ValueError("`denoise_temperature` must be positive.")
|
| 563 |
+
scores = self._apply_repetition_penalty(
|
| 564 |
+
logits,
|
| 565 |
+
repetition_token_mask=repetition_token_mask,
|
| 566 |
+
repetition_penalty=repetition_penalty,
|
| 567 |
+
).float() / temperature
|
| 568 |
+
probabilities = torch.softmax(scores, dim=-1)
|
| 569 |
+
if sampling_generators is None:
|
| 570 |
+
proposal = torch.multinomial(
|
| 571 |
+
probabilities.reshape(-1, probabilities.shape[-1]),
|
| 572 |
+
num_samples=1,
|
| 573 |
+
).view(logits.shape[:-1])
|
| 574 |
+
else:
|
| 575 |
+
if len(sampling_generators) != logits.shape[0]:
|
| 576 |
+
raise ValueError("Sampling requires one generator per batch row.")
|
| 577 |
+
rows = []
|
| 578 |
+
for row, generator in enumerate(sampling_generators):
|
| 579 |
+
rows.append(
|
| 580 |
+
torch.multinomial(
|
| 581 |
+
probabilities[row],
|
| 582 |
+
num_samples=1,
|
| 583 |
+
generator=generator,
|
| 584 |
+
).squeeze(-1)
|
| 585 |
+
)
|
| 586 |
+
proposal = torch.stack(rows, dim=0)
|
| 587 |
+
proposal_confidence = probabilities.gather(-1, proposal.unsqueeze(-1)).squeeze(-1)
|
| 588 |
+
greedy_proposal = probabilities.argmax(dim=-1)
|
| 589 |
+
greedy_confidence = probabilities.gather(
|
| 590 |
+
-1, greedy_proposal.unsqueeze(-1)
|
| 591 |
+
).squeeze(-1)
|
| 592 |
+
token_entropy = -(
|
| 593 |
+
probabilities * probabilities.clamp_min(1.0e-30).log()
|
| 594 |
+
).sum(dim=-1)
|
| 595 |
+
return (
|
| 596 |
+
proposal,
|
| 597 |
+
proposal_confidence,
|
| 598 |
+
token_entropy,
|
| 599 |
+
greedy_proposal,
|
| 600 |
+
greedy_confidence,
|
| 601 |
+
)
|
| 602 |
+
|
| 603 |
+
def forward(
|
| 604 |
+
self,
|
| 605 |
+
*,
|
| 606 |
+
input_ids: torch.LongTensor | None = None,
|
| 607 |
+
attention_mask: torch.Tensor | dict | None = None,
|
| 608 |
+
past_key_values: Cache | None = None,
|
| 609 |
+
position_ids: torch.LongTensor | None = None,
|
| 610 |
+
decoder_input_ids: torch.LongTensor,
|
| 611 |
+
previous_confidence: torch.FloatTensor | None = None,
|
| 612 |
+
previous_entropy: torch.FloatTensor | None = None,
|
| 613 |
+
token_age: torch.Tensor | None = None,
|
| 614 |
+
latent_state: LatentDeliberationState | None = None,
|
| 615 |
+
history_hidden_state: torch.FloatTensor | None = None,
|
| 616 |
+
decoder_attention_mask: torch.Tensor | dict | None = None,
|
| 617 |
+
decoder_position_ids: torch.LongTensor | None = None,
|
| 618 |
+
return_proposal_statistics: bool = False,
|
| 619 |
+
denoise_temperature: float | None = None,
|
| 620 |
+
repetition_token_mask: torch.BoolTensor | None = None,
|
| 621 |
+
repetition_penalty: float = 1.0,
|
| 622 |
+
sampling_generators: Sequence[torch.Generator] | None = None,
|
| 623 |
+
**kwargs: Any,
|
| 624 |
+
) -> ModilifyMk1BlockDiffusionOutput:
|
| 625 |
+
"""Run one inference step over a noisy diffusion canvas."""
|
| 626 |
+
|
| 627 |
+
latent_context, next_state = self._prepare_latent_context(
|
| 628 |
+
decoder_input_ids,
|
| 629 |
+
history_hidden_state=history_hidden_state,
|
| 630 |
+
confidence=previous_confidence,
|
| 631 |
+
entropy=previous_entropy,
|
| 632 |
+
age=token_age,
|
| 633 |
+
latent_state=latent_state,
|
| 634 |
+
)
|
| 635 |
+
outputs = self.model(
|
| 636 |
+
input_ids=input_ids,
|
| 637 |
+
attention_mask=attention_mask,
|
| 638 |
+
past_key_values=past_key_values,
|
| 639 |
+
position_ids=position_ids,
|
| 640 |
+
decoder_input_ids=decoder_input_ids,
|
| 641 |
+
temporal_context_embeddings=latent_context,
|
| 642 |
+
decoder_attention_mask=decoder_attention_mask,
|
| 643 |
+
decoder_position_ids=decoder_position_ids,
|
| 644 |
+
**kwargs,
|
| 645 |
+
)
|
| 646 |
+
logits = self.lm_head(outputs.last_hidden_state)
|
| 647 |
+
logits = (
|
| 648 |
+
torch.tanh(logits / self.final_logit_softcapping)
|
| 649 |
+
* self.final_logit_softcapping
|
| 650 |
+
)
|
| 651 |
+
statistics = (None, None, None, None, None)
|
| 652 |
+
if return_proposal_statistics:
|
| 653 |
+
statistics = self._proposal_statistics(
|
| 654 |
+
logits,
|
| 655 |
+
denoise_temperature=denoise_temperature,
|
| 656 |
+
repetition_token_mask=repetition_token_mask,
|
| 657 |
+
repetition_penalty=repetition_penalty,
|
| 658 |
+
sampling_generators=sampling_generators,
|
| 659 |
+
)
|
| 660 |
+
return ModilifyMk1BlockDiffusionOutput(
|
| 661 |
+
logits=None if return_proposal_statistics else logits,
|
| 662 |
+
heavy_hidden_state=outputs.last_hidden_state,
|
| 663 |
+
next_latent_state=next_state,
|
| 664 |
+
past_key_values=outputs.past_key_values,
|
| 665 |
+
encoder_last_hidden_state=outputs.encoder_last_hidden_state,
|
| 666 |
+
temporal_context=latent_context,
|
| 667 |
+
latent_residual_diagnostics=outputs.latent_residual_diagnostics,
|
| 668 |
+
proposal=statistics[0],
|
| 669 |
+
proposal_confidence=statistics[1],
|
| 670 |
+
token_entropy=statistics[2],
|
| 671 |
+
greedy_proposal=statistics[3],
|
| 672 |
+
greedy_confidence=statistics[4],
|
| 673 |
+
)
|
| 674 |
+
|
| 675 |
+
|
| 676 |
+
ModilifyMk1Model.register_for_auto_class("AutoModel")
|
| 677 |
+
ModilifyMk1ForBlockDiffusion.register_for_auto_class("AutoModelForCausalLM")
|
| 678 |
+
ModilifyMk1ForBlockDiffusion.register_for_auto_class("AutoModelForMultimodalLM")
|
| 679 |
+
|
| 680 |
+
|
| 681 |
+
__all__ = [
|
| 682 |
+
"ModilifyMk1BlockDiffusionOutput",
|
| 683 |
+
"ModilifyMk1Config",
|
| 684 |
+
"ModilifyMk1DecoderModel",
|
| 685 |
+
"ModilifyMk1EncoderModel",
|
| 686 |
+
"ModilifyMk1ForBlockDiffusion",
|
| 687 |
+
"ModilifyMk1Model",
|
| 688 |
+
]
|
processor_config.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"audio_ms_per_token": 40,
|
| 3 |
+
"audio_seq_length": 750,
|
| 4 |
+
"feature_extractor": {
|
| 5 |
+
"dither": 0.0,
|
| 6 |
+
"feature_extractor_type": "Gemma4AudioFeatureExtractor",
|
| 7 |
+
"feature_size": 128,
|
| 8 |
+
"fft_length": 512,
|
| 9 |
+
"fft_overdrive": false,
|
| 10 |
+
"frame_length": 320,
|
| 11 |
+
"hop_length": 160,
|
| 12 |
+
"input_scale_factor": 1.0,
|
| 13 |
+
"max_frequency": 8000.0,
|
| 14 |
+
"mel_floor": 0.001,
|
| 15 |
+
"min_frequency": 0.0,
|
| 16 |
+
"padding_side": "right",
|
| 17 |
+
"padding_value": 0.0,
|
| 18 |
+
"per_bin_mean": null,
|
| 19 |
+
"per_bin_stddev": null,
|
| 20 |
+
"preemphasis": 0.0,
|
| 21 |
+
"preemphasis_htk_flavor": true,
|
| 22 |
+
"return_attention_mask": true,
|
| 23 |
+
"sampling_rate": 16000
|
| 24 |
+
},
|
| 25 |
+
"image_processor": {
|
| 26 |
+
"do_convert_rgb": true,
|
| 27 |
+
"do_normalize": false,
|
| 28 |
+
"do_rescale": true,
|
| 29 |
+
"do_resize": true,
|
| 30 |
+
"image_mean": [
|
| 31 |
+
0.0,
|
| 32 |
+
0.0,
|
| 33 |
+
0.0
|
| 34 |
+
],
|
| 35 |
+
"image_processor_type": "Gemma4ImageProcessor",
|
| 36 |
+
"image_seq_length": 280,
|
| 37 |
+
"image_std": [
|
| 38 |
+
1.0,
|
| 39 |
+
1.0,
|
| 40 |
+
1.0
|
| 41 |
+
],
|
| 42 |
+
"max_soft_tokens": 280,
|
| 43 |
+
"patch_size": 16,
|
| 44 |
+
"pooling_kernel_size": 3,
|
| 45 |
+
"resample": 3,
|
| 46 |
+
"rescale_factor": 0.00392156862745098
|
| 47 |
+
},
|
| 48 |
+
"image_seq_length": 280,
|
| 49 |
+
"processor_class": "Gemma4Processor",
|
| 50 |
+
"video_processor": {
|
| 51 |
+
"do_convert_rgb": true,
|
| 52 |
+
"do_normalize": true,
|
| 53 |
+
"do_rescale": true,
|
| 54 |
+
"do_resize": true,
|
| 55 |
+
"do_sample_frames": true,
|
| 56 |
+
"image_mean": [
|
| 57 |
+
0.0,
|
| 58 |
+
0.0,
|
| 59 |
+
0.0
|
| 60 |
+
],
|
| 61 |
+
"image_std": [
|
| 62 |
+
1.0,
|
| 63 |
+
1.0,
|
| 64 |
+
1.0
|
| 65 |
+
],
|
| 66 |
+
"max_soft_tokens": 70,
|
| 67 |
+
"num_frames": 32,
|
| 68 |
+
"patch_size": 16,
|
| 69 |
+
"pooling_kernel_size": 3,
|
| 70 |
+
"resample": 3,
|
| 71 |
+
"rescale_factor": 0.00392156862745098,
|
| 72 |
+
"return_metadata": false,
|
| 73 |
+
"video_processor_type": "Gemma4VideoProcessor"
|
| 74 |
+
}
|
| 75 |
+
}
|
tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cc8d3a0ce36466ccc1278bf987df5f71db1719b9ca6b4118264f45cb627bfe0f
|
| 3 |
+
size 32169626
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"audio_token": "<|audio|>",
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"boa_token": "<|audio>",
|
| 5 |
+
"boi_token": "<|image>",
|
| 6 |
+
"bos_token": "<bos>",
|
| 7 |
+
"eoa_token": "<audio|>",
|
| 8 |
+
"eoc_token": "<channel|>",
|
| 9 |
+
"eoi_token": "<image|>",
|
| 10 |
+
"eos_token": "<eos>",
|
| 11 |
+
"eot_token": "<turn|>",
|
| 12 |
+
"escape_token": "<|\"|>",
|
| 13 |
+
"etc_token": "<tool_call|>",
|
| 14 |
+
"etd_token": "<tool|>",
|
| 15 |
+
"etr_token": "<tool_response|>",
|
| 16 |
+
"extra_special_tokens": [
|
| 17 |
+
"<|video|>"
|
| 18 |
+
],
|
| 19 |
+
"image_token": "<|image|>",
|
| 20 |
+
"is_local": true,
|
| 21 |
+
"local_files_only": false,
|
| 22 |
+
"mask_token": "<mask>",
|
| 23 |
+
"model_max_length": 1000000000000000019884624838656,
|
| 24 |
+
"model_specific_special_tokens": {
|
| 25 |
+
"audio_token": "<|audio|>",
|
| 26 |
+
"boa_token": "<|audio>",
|
| 27 |
+
"boi_token": "<|image>",
|
| 28 |
+
"eoa_token": "<audio|>",
|
| 29 |
+
"eoc_token": "<channel|>",
|
| 30 |
+
"eoi_token": "<image|>",
|
| 31 |
+
"eot_token": "<turn|>",
|
| 32 |
+
"escape_token": "<|\"|>",
|
| 33 |
+
"etc_token": "<tool_call|>",
|
| 34 |
+
"etd_token": "<tool|>",
|
| 35 |
+
"etr_token": "<tool_response|>",
|
| 36 |
+
"image_token": "<|image|>",
|
| 37 |
+
"soc_token": "<|channel>",
|
| 38 |
+
"sot_token": "<|turn>",
|
| 39 |
+
"stc_token": "<|tool_call>",
|
| 40 |
+
"std_token": "<|tool>",
|
| 41 |
+
"str_token": "<|tool_response>",
|
| 42 |
+
"think_token": "<|think|>"
|
| 43 |
+
},
|
| 44 |
+
"pad_token": "<pad>",
|
| 45 |
+
"padding_side": "left",
|
| 46 |
+
"processor_class": "Gemma4Processor",
|
| 47 |
+
"response_schema": {
|
| 48 |
+
"properties": {
|
| 49 |
+
"content": {
|
| 50 |
+
"type": "string"
|
| 51 |
+
},
|
| 52 |
+
"role": {
|
| 53 |
+
"const": "assistant"
|
| 54 |
+
},
|
| 55 |
+
"thinking": {
|
| 56 |
+
"type": "string"
|
| 57 |
+
},
|
| 58 |
+
"tool_calls": {
|
| 59 |
+
"items": {
|
| 60 |
+
"properties": {
|
| 61 |
+
"function": {
|
| 62 |
+
"properties": {
|
| 63 |
+
"arguments": {
|
| 64 |
+
"additionalProperties": {},
|
| 65 |
+
"type": "object",
|
| 66 |
+
"x-parser": "gemma4-tool-call"
|
| 67 |
+
},
|
| 68 |
+
"name": {
|
| 69 |
+
"type": "string"
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"type": "object",
|
| 73 |
+
"x-regex": "call\\:(?P<name>\\w+)(?P<arguments>\\{.*\\})"
|
| 74 |
+
},
|
| 75 |
+
"type": {
|
| 76 |
+
"const": "function"
|
| 77 |
+
}
|
| 78 |
+
},
|
| 79 |
+
"type": "object"
|
| 80 |
+
},
|
| 81 |
+
"type": "array",
|
| 82 |
+
"x-regex-iterator": "<\\|tool_call>(.*?)<tool_call\\|>"
|
| 83 |
+
}
|
| 84 |
+
},
|
| 85 |
+
"type": "object",
|
| 86 |
+
"x-regex": "(\\<\\|channel\\>thought\\n(?P<thinking>.*?)\\<channel\\|\\>)?(?P<tool_calls>\\<\\|tool_call\\>.*\\<tool_call\\|\\>)?(?P<content>(?:(?!\\<turn\\|\\>)(?!\\<\\|tool_response\\>).)+)?(?:\\<turn\\|\\>|\\<\\|tool_response\\>)?"
|
| 87 |
+
},
|
| 88 |
+
"soc_token": "<|channel>",
|
| 89 |
+
"sot_token": "<|turn>",
|
| 90 |
+
"stc_token": "<|tool_call>",
|
| 91 |
+
"std_token": "<|tool>",
|
| 92 |
+
"str_token": "<|tool_response>",
|
| 93 |
+
"think_token": "<|think|>",
|
| 94 |
+
"tokenizer_class": "GemmaTokenizer",
|
| 95 |
+
"unk_token": "<unk>"
|
| 96 |
+
}
|