Author SHA1 Message Date
Owen Qwen e0665b1042 Add tokenizer longhaul cache 2026-07-30 23:32:52 -05:00
49 changed files with 1723 additions and 2215 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Extensions
.taskman/
*.a
*.bat
*.bin
+600 -5
View File
@@ -1,9 +1,604 @@
# Longhaul.cpp
# llama.cpp
This is a private fork that I decided to make public due to it working better than I expected.
![llama](https://raw.githubusercontent.com/ggml-org/llama.brand/refs/heads/master/cover/llama-cpp/cover-llama-cpp-dark.svg)
Do I recommend you use this for anything important? No. Is it cool? I like to think so.
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Release](https://img.shields.io/github/v/release/ggml-org/llama.cpp)](https://github.com/ggml-org/llama.cpp/releases)
[![Server](https://github.com/ggml-org/llama.cpp/actions/workflows/server.yml/badge.svg)](https://github.com/ggml-org/llama.cpp/actions/workflows/server.yml)
[![Docker](https://github.com/ggml-org/llama.cpp/actions/workflows/docker.yml/badge.svg)](https://github.com/ggml-org/llama.cpp/actions/workflows/docker.yml)
[![Winget](https://github.com/ggml-org/llama.cpp/actions/workflows/winget.yml/badge.svg)](https://github.com/ggml-org/llama.cpp/actions/workflows/winget.yml)
It only supports Metal at the moment.
[Manifesto](https://github.com/ggml-org/llama.cpp/discussions/205) / [ggml](https://github.com/ggml-org/ggml) / [ops](https://github.com/ggml-org/llama.cpp/blob/master/docs/ops.md) / [maintainer PRs](https://github.com/ggml-org/llama.cpp/issues?q=is%3Apr%20is%3Aopen%20draft%3AFalse%20(author%3Argerganov%20OR%20author%3AKitaitiMakoto%20OR%20author%3Adanbev%20OR%20author%3Aaldehir%20OR%20author%3Amax-krasnyansky%20OR%20author%3ACISC%20OR%20author%3Aggerganov%20OR%20author%3Aam17an%20OR%20author%3Abartowski1182%20OR%20author%3Ahipudding%20OR%20author%3AServeurpersoCom%20OR%20author%3Apwilkin%20OR%20author%3Areeselevine%20OR%20author%3Angxson%20OR%20author%3Ajeffbolznv%20OR%20author%3A0cc4m%20OR%20author%3Aangt%20OR%20author%3AIMbackK%20OR%20author%3Aarthw%20OR%20author%3AJohannesGaessler%20OR%20author%3AORippler%20OR%20author%3Aruixiang63%20OR%20author%3Axctan%20OR%20author%3Aallozaur%20OR%20author%3Ayomaytk%20OR%20author%3Aaendk%20OR%20author%3Agaugarg-nv%20OR%20author%3Ataronaeo%20OR%20author%3Aforforever73%20OR%20author%3Alhez%20OR%20author%3Anetrunnereve%20OR%20author%3Afairydreaming)%20sort%3Aupdated-desc)
This is a personal project and because of this I have disabled open PRs. If you want to contribute, you can open an issue.
LLM inference in C/C++
## Recent API changes
- [Changelog for `libllama` API](https://github.com/ggml-org/llama.cpp/issues/9289)
- [Changelog for `llama-server` REST API](https://github.com/ggml-org/llama.cpp/issues/9291)
## Hot topics
- **Hugging Face cache migration: models downloaded with `-hf` are now stored in the standard Hugging Face cache directory, enabling sharing with other HF tools.**
- **[guide : using the new WebUI of llama.cpp](https://github.com/ggml-org/llama.cpp/discussions/16938)**
- [guide : running gpt-oss with llama.cpp](https://github.com/ggml-org/llama.cpp/discussions/15396)
- [[FEEDBACK] Better packaging for llama.cpp to support downstream consumers 🤗](https://github.com/ggml-org/llama.cpp/discussions/15313)
- Support for the `gpt-oss` model with native MXFP4 format has been added | [PR](https://github.com/ggml-org/llama.cpp/pull/15091) | [Collaboration with NVIDIA](https://blogs.nvidia.com/blog/rtx-ai-garage-openai-oss) | [Comment](https://github.com/ggml-org/llama.cpp/discussions/15095)
- Multimodal support arrived in `llama-server`: [#12898](https://github.com/ggml-org/llama.cpp/pull/12898) | [documentation](./docs/multimodal.md)
- VS Code extension for FIM completions: https://github.com/ggml-org/llama.vscode
- Vim/Neovim plugin for FIM completions: https://github.com/ggml-org/llama.vim
- Hugging Face Inference Endpoints now support GGUF out of the box! https://github.com/ggml-org/llama.cpp/discussions/9669
- Hugging Face GGUF editor: [discussion](https://github.com/ggml-org/llama.cpp/discussions/9268) | [tool](https://huggingface.co/spaces/CISCai/gguf-editor)
- WebGPU support is now available in the browser, see a blog/demo introducing it [here](https://reeselevine.github.io/llamas-on-the-web/).
----
## Quick start
Getting started with llama.cpp is straightforward. Here are several ways to install it on your machine:
- Install `llama.cpp` using [brew, nix, winget, or conda-forge](docs/install.md)
- Run with Docker - see our [Docker documentation](docs/docker.md)
- Download pre-built binaries from the [releases page](https://github.com/ggml-org/llama.cpp/releases)
- Build from source by cloning this repository - check out [our build guide](docs/build.md)
Once installed, you'll need a model to work with. Head to the [Obtaining and quantizing models](#obtaining-and-quantizing-models) section to learn more.
Example command:
```sh
# Use a local model file
llama-cli -m my_model.gguf
# Or download and run a model directly from Hugging Face
llama-cli -hf ggml-org/gemma-3-1b-it-GGUF
# Launch OpenAI-compatible API server
llama-server -hf ggml-org/gemma-3-1b-it-GGUF
```
## Description
The main goal of `llama.cpp` is to enable LLM inference with minimal setup and state-of-the-art performance on a wide
range of hardware - locally and in the cloud.
- Plain C/C++ implementation without any dependencies
- Apple silicon is a first-class citizen - optimized via ARM NEON, Accelerate and Metal frameworks
- AVX, AVX2, AVX512 and AMX support for x86 architectures
- RVV, ZVFH, ZFH, ZICBOP and ZIHINTPAUSE support for RISC-V architectures
- 1.5-bit, 2-bit, 3-bit, 4-bit, 5-bit, 6-bit, and 8-bit integer quantization for faster inference and reduced memory use
- Custom CUDA kernels for running LLMs on NVIDIA GPUs (support for AMD GPUs via HIP and Moore Threads GPUs via MUSA)
- Vulkan and SYCL backend support
- CPU+GPU hybrid inference to partially accelerate models larger than the total VRAM capacity
The `llama.cpp` project is the main playground for developing new features for the [ggml](https://github.com/ggml-org/ggml) library.
<details>
<summary>Models</summary>
Typically finetunes of the base models below are supported as well.
Instructions for adding support for new models: [HOWTO-add-model.md](docs/development/HOWTO-add-model.md)
#### Text-only
- [X] LLaMA 🦙
- [x] LLaMA 2 🦙🦙
- [x] LLaMA 3 🦙🦙🦙
- [X] [Mistral 7B](https://huggingface.co/mistralai/Mistral-7B-v0.1)
- [x] [Mixtral MoE](https://huggingface.co/models?search=mistral-ai/Mixtral)
- [x] [DBRX](https://huggingface.co/databricks/dbrx-instruct)
- [x] [Jamba](https://huggingface.co/ai21labs)
- [X] [Falcon](https://huggingface.co/models?search=tiiuae/falcon)
- [X] [Chinese LLaMA / Alpaca](https://github.com/ymcui/Chinese-LLaMA-Alpaca) and [Chinese LLaMA-2 / Alpaca-2](https://github.com/ymcui/Chinese-LLaMA-Alpaca-2)
- [X] [Vigogne (French)](https://github.com/bofenghuang/vigogne)
- [X] [BERT](https://github.com/ggml-org/llama.cpp/pull/5423)
- [X] [Koala](https://bair.berkeley.edu/blog/2023/04/03/koala/)
- [X] [Baichuan 1 & 2](https://huggingface.co/models?search=baichuan-inc/Baichuan) + [derivations](https://huggingface.co/hiyouga/baichuan-7b-sft)
- [X] [Aquila 1 & 2](https://huggingface.co/models?search=BAAI/Aquila)
- [X] [Starcoder models](https://github.com/ggml-org/llama.cpp/pull/3187)
- [X] [Refact](https://huggingface.co/smallcloudai/Refact-1_6B-fim)
- [X] [MPT](https://github.com/ggml-org/llama.cpp/pull/3417)
- [X] [Bloom](https://github.com/ggml-org/llama.cpp/pull/3553)
- [x] [Yi models](https://huggingface.co/models?search=01-ai/Yi)
- [X] [StableLM models](https://huggingface.co/stabilityai)
- [x] [Deepseek models](https://huggingface.co/models?search=deepseek-ai/deepseek)
- [x] [Qwen models](https://huggingface.co/models?search=Qwen/Qwen)
- [x] [PLaMo-13B](https://github.com/ggml-org/llama.cpp/pull/3557)
- [x] [Phi models](https://huggingface.co/models?search=microsoft/phi)
- [x] [PhiMoE](https://github.com/ggml-org/llama.cpp/pull/11003)
- [x] [GPT-2](https://huggingface.co/gpt2)
- [x] [Orion 14B](https://github.com/ggml-org/llama.cpp/pull/5118)
- [x] [InternLM2](https://huggingface.co/models?search=internlm2)
- [x] [CodeShell](https://github.com/WisdomShell/codeshell)
- [x] [Gemma](https://ai.google.dev/gemma)
- [x] [Mamba](https://github.com/state-spaces/mamba)
- [x] [Grok-1](https://huggingface.co/keyfan/grok-1-hf)
- [x] [Xverse](https://huggingface.co/models?search=xverse)
- [x] [Command-R models](https://huggingface.co/models?search=CohereForAI/c4ai-command-r)
- [x] [SEA-LION](https://huggingface.co/models?search=sea-lion)
- [x] [GritLM-7B](https://huggingface.co/GritLM/GritLM-7B) + [GritLM-8x7B](https://huggingface.co/GritLM/GritLM-8x7B)
- [x] [OLMo](https://allenai.org/olmo)
- [x] [OLMo 2](https://allenai.org/olmo)
- [x] [OLMoE](https://huggingface.co/allenai/OLMoE-1B-7B-0924)
- [x] [Granite models](https://huggingface.co/collections/ibm-granite/granite-code-models-6624c5cec322e4c148c8b330)
- [x] [GPT-NeoX](https://github.com/EleutherAI/gpt-neox) + [Pythia](https://github.com/EleutherAI/pythia)
- [x] [Snowflake-Arctic MoE](https://huggingface.co/collections/Snowflake/arctic-66290090abe542894a5ac520)
- [x] [Smaug](https://huggingface.co/models?search=Smaug)
- [x] [Poro 34B](https://huggingface.co/LumiOpen/Poro-34B)
- [x] [Bitnet b1.58 models](https://huggingface.co/1bitLLM)
- [x] [Flan T5](https://huggingface.co/models?search=flan-t5)
- [x] [Open Elm models](https://huggingface.co/collections/apple/openelm-instruct-models-6619ad295d7ae9f868b759ca)
- [x] [ChatGLM3-6b](https://huggingface.co/THUDM/chatglm3-6b) + [ChatGLM4-9b](https://huggingface.co/THUDM/glm-4-9b) + [GLMEdge-1.5b](https://huggingface.co/THUDM/glm-edge-1.5b-chat) + [GLMEdge-4b](https://huggingface.co/THUDM/glm-edge-4b-chat)
- [x] [GLM-4-0414](https://huggingface.co/collections/THUDM/glm-4-0414-67f3cbcb34dd9d252707cb2e)
- [x] [SmolLM](https://huggingface.co/collections/HuggingFaceTB/smollm-6695016cad7167254ce15966)
- [x] [EXAONE-3.0-7.8B-Instruct](https://huggingface.co/LGAI-EXAONE/EXAONE-3.0-7.8B-Instruct)
- [x] [FalconMamba Models](https://huggingface.co/collections/tiiuae/falconmamba-7b-66b9a580324dd1598b0f6d4a)
- [x] [Jais](https://huggingface.co/inceptionai/jais-13b-chat)
- [x] [Bielik-11B-v2.3](https://huggingface.co/collections/speakleash/bielik-11b-v23-66ee813238d9b526a072408a)
- [x] [RWKV-7](https://huggingface.co/collections/shoumenchougou/rwkv7-gxx-gguf)
- [x] [RWKV-6](https://github.com/BlinkDL/RWKV-LM)
- [x] [QRWKV-6](https://huggingface.co/recursal/QRWKV6-32B-Instruct-Preview-v0.1)
- [x] [GigaChat-20B-A3B](https://huggingface.co/ai-sage/GigaChat-20B-A3B-instruct)
- [X] [Trillion-7B-preview](https://huggingface.co/trillionlabs/Trillion-7B-preview)
- [x] [Ling models](https://huggingface.co/collections/inclusionAI/ling-67c51c85b34a7ea0aba94c32)
- [x] [Liquid LFM2 models](https://huggingface.co/collections/LiquidAI/lfm2)
- [x] [Liquid LFM2.5 models](https://huggingface.co/collections/LiquidAI/lfm25)
- [x] [Liquid Nanos](https://huggingface.co/collections/LiquidAI/liquid-nanos)
- [x] [Hunyuan models](https://huggingface.co/collections/tencent/hunyuan-dense-model-6890632cda26b19119c9c5e7)
- [x] [BailingMoeV2 (Ring/Ling 2.0) models](https://huggingface.co/collections/inclusionAI/ling-v2-68bf1dd2fc34c306c1fa6f86)
- [x] [Mellum models](https://huggingface.co/JetBrains/models?search=mellum)
#### Multimodal
- [x] [LLaVA 1.5 models](https://huggingface.co/collections/liuhaotian/llava-15-653aac15d994e992e2677a7e), [LLaVA 1.6 models](https://huggingface.co/collections/liuhaotian/llava-16-65b9e40155f60fd046a5ccf2)
- [x] [BakLLaVA](https://huggingface.co/models?search=SkunkworksAI/Bakllava)
- [x] [Obsidian](https://huggingface.co/NousResearch/Obsidian-3B-V0.5)
- [x] [ShareGPT4V](https://huggingface.co/models?search=Lin-Chen/ShareGPT4V)
- [x] [MobileVLM 1.7B/3B models](https://huggingface.co/models?search=mobileVLM)
- [x] [Yi-VL](https://huggingface.co/models?search=Yi-VL)
- [x] [Mini CPM](https://huggingface.co/models?search=MiniCPM)
- [x] [Moondream](https://huggingface.co/vikhyatk/moondream2)
- [x] [Bunny](https://github.com/BAAI-DCAI/Bunny)
- [x] [GLM-EDGE](https://huggingface.co/models?search=glm-edge)
- [x] [Qwen2-VL](https://huggingface.co/collections/Qwen/qwen2-vl-66cee7455501d7126940800d)
- [x] [LFM2-VL](https://huggingface.co/collections/LiquidAI/lfm2-vl-68963bbc84a610f7638d5ffa)
</details>
<details>
<summary>Bindings</summary>
- Python: [ddh0/easy-llama](https://github.com/ddh0/easy-llama)
- Python: [abetlen/llama-cpp-python](https://github.com/abetlen/llama-cpp-python)
- Go: [go-skynet/go-llama.cpp](https://github.com/go-skynet/go-llama.cpp)
- Node.js: [withcatai/node-llama-cpp](https://github.com/withcatai/node-llama-cpp)
- JS/TS (llama.cpp server client): [lgrammel/modelfusion](https://modelfusion.dev/integration/model-provider/llamacpp)
- JS/TS (Programmable Prompt Engine CLI): [offline-ai/cli](https://github.com/offline-ai/cli)
- JavaScript/Wasm (works in browser): [tangledgroup/llama-cpp-wasm](https://github.com/tangledgroup/llama-cpp-wasm)
- Typescript/Wasm (nicer API, available on npm): [ngxson/wllama](https://github.com/ngxson/wllama)
- Ruby: [yoshoku/llama_cpp.rb](https://github.com/yoshoku/llama_cpp.rb)
- Ruby: [docusealco/rllama](https://github.com/docusealco/rllama)
- Rust (more features): [edgenai/llama_cpp-rs](https://github.com/edgenai/llama_cpp-rs)
- Rust (nicer API): [mdrokz/rust-llama.cpp](https://github.com/mdrokz/rust-llama.cpp)
- Rust (more direct bindings): [utilityai/llama-cpp-rs](https://github.com/utilityai/llama-cpp-rs)
- Rust (automated build from crates.io): [ShelbyJenkins/llm_client](https://github.com/ShelbyJenkins/llm_client)
- C#/.NET: [SciSharp/LLamaSharp](https://github.com/SciSharp/LLamaSharp)
- C#/VB.NET (more features - community license): [LM-Kit.NET](https://docs.lm-kit.com/lm-kit-net/index.html)
- Scala 3: [donderom/llm4s](https://github.com/donderom/llm4s)
- Clojure: [phronmophobic/llama.clj](https://github.com/phronmophobic/llama.clj)
- React Native: [mybigday/llama.rn](https://github.com/mybigday/llama.rn)
- Java: [kherud/java-llama.cpp](https://github.com/kherud/java-llama.cpp)
- Java: [QuasarByte/llama-cpp-jna](https://github.com/QuasarByte/llama-cpp-jna)
- Zig: [deins/llama.cpp.zig](https://github.com/Deins/llama.cpp.zig)
- Flutter/Dart: [netdur/llama_cpp_dart](https://github.com/netdur/llama_cpp_dart)
- Flutter: [xuegao-tzx/Fllama](https://github.com/xuegao-tzx/Fllama)
- PHP (API bindings and features built on top of llama.cpp): [distantmagic/resonance](https://github.com/distantmagic/resonance) [(more info)](https://github.com/ggml-org/llama.cpp/pull/6326)
- Guile Scheme: [guile_llama_cpp](https://savannah.nongnu.org/projects/guile-llama-cpp)
- Swift [srgtuszy/llama-cpp-swift](https://github.com/srgtuszy/llama-cpp-swift)
- Swift [ShenghaiWang/SwiftLlama](https://github.com/ShenghaiWang/SwiftLlama)
- Delphi [Embarcadero/llama-cpp-delphi](https://github.com/Embarcadero/llama-cpp-delphi)
- Go (no CGo needed): [hybridgroup/yzma](https://github.com/hybridgroup/yzma)
- Android: [llama.android](/examples/llama.android)
</details>
<details>
<summary>UIs</summary>
*(to have a project listed here, it should clearly state that it depends on `llama.cpp`)*
- [AI Sublime Text plugin](https://github.com/yaroslavyaroslav/OpenAI-sublime-text) (MIT)
- [BonzAI App](https://apps.apple.com/us/app/bonzai-your-local-ai-agent/id6752847988) (proprietary)
- [cztomsik/ava](https://github.com/cztomsik/ava) (MIT)
- [Dot](https://github.com/alexpinel/Dot) (GPL)
- [eva](https://github.com/ylsdamxssjxxdd/eva) (MIT)
- [iohub/collama](https://github.com/iohub/coLLaMA) (Apache-2.0)
- [janhq/jan](https://github.com/janhq/jan) (AGPL)
- [johnbean393/Sidekick](https://github.com/johnbean393/Sidekick) (MIT)
- [KanTV](https://github.com/zhouwg/kantv?tab=readme-ov-file) (Apache-2.0)
- [KodiBot](https://github.com/firatkiral/kodibot) (GPL)
- [llama.vim](https://github.com/ggml-org/llama.vim) (MIT)
- [LARS](https://github.com/abgulati/LARS) (AGPL)
- [Llama Assistant](https://github.com/vietanhdev/llama-assistant) (GPL)
- [LlamaLib](https://github.com/undreamai/LlamaLib) (Apache-2.0)
- [LLMFarm](https://github.com/guinmoon/LLMFarm?tab=readme-ov-file) (MIT)
- [LLMUnity](https://github.com/undreamai/LLMUnity) (MIT)
- [LMStudio](https://lmstudio.ai/) (proprietary)
- [LocalAI](https://github.com/mudler/LocalAI) (MIT)
- [LostRuins/koboldcpp](https://github.com/LostRuins/koboldcpp) (AGPL)
- [MindMac](https://mindmac.app) (proprietary)
- [MindWorkAI/AI-Studio](https://github.com/MindWorkAI/AI-Studio) (FSL-1.1-MIT)
- [Mobile-Artificial-Intelligence/maid](https://github.com/Mobile-Artificial-Intelligence/maid) (MIT)
- [Mozilla-Ocho/llamafile](https://github.com/Mozilla-Ocho/llamafile) (Apache-2.0)
- [nat/openplayground](https://github.com/nat/openplayground) (MIT)
- [nomic-ai/gpt4all](https://github.com/nomic-ai/gpt4all) (MIT)
- [ollama/ollama](https://github.com/ollama/ollama) (MIT)
- [oobabooga/text-generation-webui](https://github.com/oobabooga/text-generation-webui) (AGPL)
- [PocketPal AI](https://github.com/a-ghorbani/pocketpal-ai) (MIT)
- [psugihara/FreeChat](https://github.com/psugihara/FreeChat) (MIT)
- [ptsochantaris/emeltal](https://github.com/ptsochantaris/emeltal) (MIT)
- [pythops/tenere](https://github.com/pythops/tenere) (AGPL)
- [ramalama](https://github.com/containers/ramalama) (MIT)
- [semperai/amica](https://github.com/semperai/amica) (MIT)
- [withcatai/catai](https://github.com/withcatai/catai) (MIT)
- [Autopen](https://github.com/blackhole89/autopen) (GPL)
</details>
<details>
<summary>Tools</summary>
- [akx/ggify](https://github.com/akx/ggify) – download PyTorch models from Hugging Face Hub and convert them to GGML
- [akx/ollama-dl](https://github.com/akx/ollama-dl) – download models from the Ollama library to be used directly with llama.cpp
- [crashr/gppm](https://github.com/crashr/gppm) – launch llama.cpp instances utilizing NVIDIA Tesla P40 or P100 GPUs with reduced idle power consumption
- [gpustack/gguf-parser](https://github.com/gpustack/gguf-parser-go/tree/main/cmd/gguf-parser) - review/check the GGUF file and estimate the memory usage
- [Styled Lines](https://marketplace.unity.com/packages/tools/generative-ai/styled-lines-llama-cpp-model-292902) (proprietary licensed, async wrapper of inference part for game development in Unity3d with pre-built Mobile and Web platform wrappers and a model example)
- [unslothai/unsloth](https://github.com/unslothai/unsloth) – 🦥 exports/saves fine-tuned and trained models to GGUF (Apache-2.0)
</details>
<details>
<summary>Infrastructure</summary>
- [Paddler](https://github.com/intentee/paddler) - Open-source LLMOps platform for hosting and scaling AI in your own infrastructure
- [GPUStack](https://github.com/gpustack/gpustack) - Manage GPU clusters for running LLMs
- [llama_cpp_canister](https://github.com/onicai/llama_cpp_canister) - llama.cpp as a smart contract on the Internet Computer, using WebAssembly
- [llama-swap](https://github.com/mostlygeek/llama-swap) - transparent proxy that adds automatic model switching with llama-server
- [Kalavai](https://github.com/kalavai-net/kalavai-client) - Crowdsource end to end LLM deployment at any scale
- [llmaz](https://github.com/InftyAI/llmaz) - ☸️ Easy, advanced inference platform for large language models on Kubernetes.
- [LLMKube](https://github.com/defilantech/llmkube) - Kubernetes operator for llama.cpp with multi-GPU and Apple Silicon Metal
support"
</details>
<details>
<summary>Games</summary>
- [Lucy's Labyrinth](https://github.com/MorganRO8/Lucys_Labyrinth) - A simple maze game where agents controlled by an AI model will try to trick you.
</details>
## Supported backends
| Backend | Target devices |
| --- | --- |
| [Metal](docs/build.md#metal-build) | Apple Silicon |
| [BLAS](docs/build.md#blas-build) | All |
| [BLIS](docs/backend/BLIS.md) | All |
| [SYCL](docs/backend/SYCL.md) | Intel GPU |
| [OpenVINO [In Progress]](docs/backend/OPENVINO.md) | Intel CPUs, GPUs, and NPUs |
| [MUSA](docs/build.md#musa) | Moore Threads GPU |
| [CUDA](docs/build.md#cuda) | Nvidia GPU |
| [HIP](docs/build.md#hip) | AMD GPU |
| [ZenDNN](docs/build.md#zendnn) | AMD CPU |
| [Vulkan](docs/build.md#vulkan) | GPU |
| [CANN](docs/build.md#cann) | Ascend NPU |
| [OpenCL](docs/backend/OPENCL.md) | Adreno GPU |
| [IBM zDNN](docs/backend/zDNN.md) | IBM Z & LinuxONE |
| [WebGPU](docs/build.md#webgpu) | All |
| [RPC](https://github.com/ggml-org/llama.cpp/tree/master/tools/rpc) | All |
| [Hexagon [In Progress]](docs/backend/snapdragon/README.md) | Snapdragon |
| [VirtGPU](docs/backend/VirtGPU.md) | VirtGPU APIR |
## Obtaining and quantizing models
The [Hugging Face](https://huggingface.co) platform hosts a [number of LLMs](https://huggingface.co/models?library=gguf&sort=trending) compatible with `llama.cpp`:
- [Trending](https://huggingface.co/models?library=gguf&sort=trending)
- [LLaMA](https://huggingface.co/models?sort=trending&search=llama+gguf)
You can either manually download the GGUF file or directly use any `llama.cpp`-compatible models from [Hugging Face](https://huggingface.co/) or other model hosting sites, by using this CLI argument: `-hf <user>/<model>[:quant]`. For example:
```sh
llama-cli -hf ggml-org/gemma-3-1b-it-GGUF
```
By default, the CLI would download from Hugging Face, you can switch to other options with the environment variable `MODEL_ENDPOINT`. The `MODEL_ENDPOINT` must point to a Hugging Face compatible API endpoint.
After downloading a model, use the CLI tools to run it locally - see below.
`llama.cpp` requires the model to be stored in the [GGUF](https://github.com/ggml-org/ggml/blob/master/docs/gguf.md) file format. Models in other data formats can be converted to GGUF using the `convert_*.py` Python scripts in this repo.
The Hugging Face platform provides a variety of online tools for converting, quantizing and hosting models with `llama.cpp`:
- Use the [GGUF-my-repo space](https://huggingface.co/spaces/ggml-org/gguf-my-repo) to convert to GGUF format and quantize model weights to smaller sizes
- Use the [GGUF-my-LoRA space](https://huggingface.co/spaces/ggml-org/gguf-my-lora) to convert LoRA adapters to GGUF format (more info: https://github.com/ggml-org/llama.cpp/discussions/10123)
- Use the [GGUF-editor space](https://huggingface.co/spaces/CISCai/gguf-editor) to edit GGUF meta data in the browser (more info: https://github.com/ggml-org/llama.cpp/discussions/9268)
- Use the [Inference Endpoints](https://ui.endpoints.huggingface.co/) to directly host `llama.cpp` in the cloud (more info: https://github.com/ggml-org/llama.cpp/discussions/9669)
To learn more about model quantization, [read this documentation](tools/quantize/README.md)
## [`llama-cli`](tools/cli)
#### A CLI tool for accessing and experimenting with most of `llama.cpp`'s functionality.
- <details open>
<summary>Run in conversation mode</summary>
Models with a built-in chat template will automatically activate conversation mode. If this doesn't occur, you can manually enable it by adding `-cnv` and specifying a suitable chat template with `--chat-template NAME`
```bash
llama-cli -m model.gguf
# > hi, who are you?
# Hi there! I'm your helpful assistant! I'm an AI-powered chatbot designed to assist and provide information to users like you. I'm here to help answer your questions, provide guidance, and offer support on a wide range of topics. I'm a friendly and knowledgeable AI, and I'm always happy to help with anything you need. What's on your mind, and how can I assist you today?
#
# > what is 1+1?
# Easy peasy! The answer to 1+1 is... 2!
```
</details>
- <details>
<summary>Run in conversation mode with custom chat template</summary>
```bash
# use the "chatml" template (use -h to see the list of supported templates)
llama-cli -m model.gguf -cnv --chat-template chatml
# use a custom template
llama-cli -m model.gguf -cnv --in-prefix 'User: ' --reverse-prompt 'User:'
```
</details>
- <details>
<summary>Constrain the output with a custom grammar</summary>
```bash
llama-cli -m model.gguf -n 256 --grammar-file grammars/json.gbnf -p 'Request: schedule a call at 8pm; Command:'
# {"appointmentTime": "8pm", "appointmentDetails": "schedule a a call"}
```
The [grammars/](grammars/) folder contains a handful of sample grammars. To write your own, check out the [GBNF Guide](grammars/README.md).
For authoring more complex JSON grammars, check out https://grammar.intrinsiclabs.ai/
</details>
## [`llama-server`](tools/server)
#### A lightweight, [OpenAI API](https://github.com/openai/openai-openapi) compatible, HTTP server for serving LLMs.
- <details open>
<summary>Start a local HTTP server with default configuration on port 8080</summary>
```bash
llama-server -m model.gguf --port 8080
# Basic web UI can be accessed via browser: http://localhost:8080
# Chat completion endpoint: http://localhost:8080/v1/chat/completions
```
</details>
- <details>
<summary>Support multiple-users and parallel decoding</summary>
```bash
# up to 4 concurrent requests, each with 4096 max context
llama-server -m model.gguf -c 16384 -np 4
```
</details>
- <details>
<summary>Enable speculative decoding</summary>
```bash
# the draft.gguf model should be a small variant of the target model.gguf
llama-server -m model.gguf -md draft.gguf
```
</details>
- <details>
<summary>Serve an embedding model</summary>
```bash
# use the /embedding endpoint
llama-server -m model.gguf --embedding --pooling cls -ub 8192
```
</details>
- <details>
<summary>Serve a reranking model</summary>
```bash
# use the /reranking endpoint
llama-server -m model.gguf --reranking
```
</details>
- <details>
<summary>Constrain all outputs with a grammar</summary>
```bash
# custom grammar
llama-server -m model.gguf --grammar-file grammar.gbnf
# JSON
llama-server -m model.gguf --grammar-file grammars/json.gbnf
```
</details>
## [`llama-perplexity`](tools/perplexity)
#### A tool for measuring the [perplexity](tools/perplexity/README.md) [^1] (and other quality metrics) of a model over a given text.
- <details open>
<summary>Measure the perplexity over a text file</summary>
```bash
llama-perplexity -m model.gguf -f file.txt
# [1]15.2701,[2]5.4007,[3]5.3073,[4]6.2965,[5]5.8940,[6]5.6096,[7]5.7942,[8]4.9297, ...
# Final estimate: PPL = 5.4007 +/- 0.67339
```
</details>
- <details>
<summary>Measure KL divergence</summary>
```bash
# TODO
```
</details>
[^1]: [https://huggingface.co/docs/transformers/perplexity](https://huggingface.co/docs/transformers/perplexity)
## [`llama-bench`](tools/llama-bench)
#### Benchmark the performance of the inference for various parameters.
- <details open>
<summary>Run default benchmark</summary>
```bash
llama-bench -m model.gguf
# Output:
# | model | size | params | backend | threads | test | t/s |
# | ------------------- | ---------: | ---------: | ---------- | ------: | ------------: | -------------------: |
# | qwen2 1.5B Q4_0 | 885.97 MiB | 1.54 B | Metal,BLAS | 16 | pp512 | 5765.41 ± 20.55 |
# | qwen2 1.5B Q4_0 | 885.97 MiB | 1.54 B | Metal,BLAS | 16 | tg128 | 197.71 ± 0.81 |
#
# build: 3e0ba0e60 (4229)
```
</details>
## [`llama-simple`](examples/simple)
#### A minimal example for implementing apps with `llama.cpp`. Useful for developers.
- <details>
<summary>Basic text completion</summary>
```bash
llama-simple -m model.gguf
# Hello my name is Kaitlyn and I am a 16 year old girl. I am a junior in high school and I am currently taking a class called "The Art of
```
</details>
## Contributing
- Contributors can open PRs
- Collaborators will be invited based on contributions
- Maintainers can push to branches in the `llama.cpp` repo and merge PRs into the `master` branch
- Any help with managing issues, PRs and projects is very appreciated!
- See [good first issues](https://github.com/ggml-org/llama.cpp/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) for tasks suitable for first contributions
- Read the [CONTRIBUTING.md](CONTRIBUTING.md) for more information
- Make sure to read this: [Inference at the edge](https://github.com/ggml-org/llama.cpp/discussions/205)
- A bit of backstory for those who are interested: [Changelog podcast](https://changelog.com/podcast/532)
## Other documentation
- [cli](tools/cli/README.md)
- [completion](tools/completion/README.md)
- [server](tools/server/README.md)
- [GBNF grammars](grammars/README.md)
#### Development documentation
- [How to build](docs/build.md)
- [Running on Docker](docs/docker.md)
- [Build on Android](docs/android.md)
- [Multi-GPU usage](docs/multi-gpu.md)
- [Performance troubleshooting](docs/development/token_generation_performance_tips.md)
- [GGML tips & tricks](https://github.com/ggml-org/llama.cpp/wiki/GGML-Tips-&-Tricks)
#### Seminal papers and background on the models
If your issue is with model generation quality, then please at least scan the following links and papers to understand the limitations of LLaMA models. This is especially important when choosing an appropriate model size and appreciating both the significant and subtle differences between LLaMA models and ChatGPT:
- LLaMA:
- [Introducing LLaMA: A foundational, 65-billion-parameter large language model](https://ai.facebook.com/blog/large-language-model-llama-meta-ai/)
- [LLaMA: Open and Efficient Foundation Language Models](https://arxiv.org/abs/2302.13971)
- GPT-3
- [Language Models are Few-Shot Learners](https://arxiv.org/abs/2005.14165)
- GPT-3.5 / InstructGPT / ChatGPT:
- [Aligning language models to follow instructions](https://openai.com/research/instruction-following)
- [Training language models to follow instructions with human feedback](https://arxiv.org/abs/2203.02155)
## XCFramework
The XCFramework is a precompiled version of the library for iOS, visionOS, tvOS,
and macOS. It can be used in Swift projects without the need to compile the
library from source. For example:
```swift
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MyLlamaPackage",
targets: [
.executableTarget(
name: "MyLlamaPackage",
dependencies: [
"LlamaFramework"
]),
.binaryTarget(
name: "LlamaFramework",
url: "https://github.com/ggml-org/llama.cpp/releases/download/b5046/llama-b5046-xcframework.zip",
checksum: "c19be78b5f00d8d29a25da41042cb7afa094cbf6280a225abe614b03b20029ab"
)
]
)
```
The above example is using an intermediate build `b5046` of the library. This can be modified
to use a different version by changing the URL and checksum.
## Completions
Command-line completion is available for some environments.
#### Bash Completion
```bash
$ build/bin/llama-cli --completion-bash > ~/.llama-completion.bash
$ source ~/.llama-completion.bash
```
Optionally this can be added to your `.bashrc` or `.bash_profile` to load it
automatically. For example:
```console
$ echo "source ~/.llama-completion.bash" >> ~/.bashrc
```
## Dependencies
- [yhirose/cpp-httplib](https://github.com/yhirose/cpp-httplib) - Single-header HTTP server, used by `llama-server` - MIT license
- [stb-image](https://github.com/nothings/stb) - Single-header image format decoder, used by multimodal subsystem - Public domain
- [nlohmann/json](https://github.com/nlohmann/json) - Single-header JSON library, used by various tools/examples - MIT License
- [miniaudio.h](https://github.com/mackron/miniaudio) - Single-header audio format decoder, used by multimodal subsystem - Public domain
- [subprocess.h](https://github.com/sheredom/subprocess.h) - Single-header process launching solution for C and C++ - Public domain
+27 -1
View File
@@ -817,6 +817,15 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context
if (params.load_mode == LLAMA_LOAD_MODE_LONGHAUL && params.longhaul_cache_bytes == 0) {
throw std::invalid_argument("error: --longhaul requires --longhaul-cache N\n");
}
if (params.tokenizer_longhaul != (params.tokenizer_longhaul_cache_bytes != 0)) {
throw std::invalid_argument(
params.tokenizer_longhaul
? "error: --tokenizer-longhaul requires --tokenizer-longhaul-cache N\n"
: "error: --tokenizer-longhaul-cache requires --tokenizer-longhaul\n");
}
if (params.tokenizer_longhaul && params.token_cache_size_mib == 0) {
throw std::invalid_argument("error: --tokenizer-longhaul requires an enabled persistent token cache\n");
}
postprocess_cpu_params(params.cpuparams, nullptr);
postprocess_cpu_params(params.cpuparams_batch, &params.cpuparams);
@@ -2631,7 +2640,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
).set_env("LLAMA_ARG_LOAD_MODE"));
add_opt(common_arg(
{"--longhaul"},
"stream routed Qwen3.5 MoE, Laguna, Inkling, or Ling 3.0 experts through a bounded CPU or Metal cache",
"stream routed Qwen3.5 MoE experts through a bounded Metal cache",
[](common_params & params) {
params.load_mode = LLAMA_LOAD_MODE_LONGHAUL;
}
@@ -2646,6 +2655,23 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
params.longhaul_cache_bytes = uint64_t(value) * 1024 * 1024 * 1024;
}
).set_env("LLAMA_ARG_LONGHAUL_CACHE"));
add_opt(common_arg(
{"--tokenizer-longhaul"},
"build and page the Qwen3.5/Laguna tokenizer through a bounded cache",
[](common_params & params) {
params.tokenizer_longhaul = true;
}
).set_env("LLAMA_ARG_TOKENIZER_LONGHAUL"));
add_opt(common_arg(
{"--tokenizer-longhaul-cache"}, "N",
"tokenizer-longhaul RAM cache size in MiB",
[](common_params & params, int value) {
if (value <= 0) {
throw std::invalid_argument("tokenizer-longhaul cache size must be positive");
}
params.tokenizer_longhaul_cache_bytes = uint64_t(value) * 1024 * 1024;
}
).set_env("LLAMA_ARG_TOKENIZER_LONGHAUL_CACHE"));
add_opt(common_arg(
{"--numa"}, "TYPE",
"attempt optimizations that help on some NUMA systems\n"
-119
View File
@@ -2530,118 +2530,6 @@ static common_chat_params common_chat_params_init_minimax_m3(const common_chat_t
return data;
}
// Inkling's TML format can emit multiple typed content blocks in one turn.
// <|end_message|> closes a block while content_model_end_sampling closes the
// generation, so the generic single-block parser is not sufficient.
static common_chat_params common_chat_params_init_inkling(
const common_chat_template & tmpl,
const autoparser::generation_params & inputs) {
common_chat_params data;
const std::string MSG_MODEL = "<|message_model|>";
const std::string MSG_USER = "<|message_user|>";
const std::string MSG_SYSTEM = "<|message_system|>";
const std::string MSG_TOOL = "<|message_tool|>";
const std::string THINK = "<|content_thinking|>";
const std::string TEXT = "<|content_text|>";
const std::string END_MESSAGE = "<|end_message|>";
const std::string END_SAMPLING = "<|content_model_end_sampling|>";
const std::string INVOKE_TOOL = "<|content_invoke_tool_json|>";
data.prompt = common_chat_template_direct_apply_impl(tmpl, inputs);
data.generation_prompt = common_chat_template_generation_prompt_impl(tmpl, inputs);
data.format = COMMON_CHAT_FORMAT_PEG_NATIVE;
data.supports_thinking = true;
data.thinking_start_tag = THINK;
data.thinking_end_tags = {END_MESSAGE};
data.preserved_tokens = {
MSG_MODEL, MSG_USER, MSG_SYSTEM, MSG_TOOL,
THINK, TEXT, END_MESSAGE, END_SAMPLING, INVOKE_TOOL,
};
data.message_delimiters = {
{ COMMON_CHAT_ROLE_ASSISTANT, MSG_MODEL },
{ COMMON_CHAT_ROLE_USER, MSG_USER },
{ COMMON_CHAT_ROLE_SYSTEM, MSG_SYSTEM },
{ COMMON_CHAT_ROLE_TOOL, MSG_TOOL },
};
const bool has_tools = inputs.tools.is_array() && !inputs.tools.empty();
const bool extract_reasoning =
inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
if (inputs.has_continuation()) {
const auto & msg = inputs.continue_msg;
data.generation_prompt = MSG_MODEL + THINK + msg.reasoning_content;
if (inputs.continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT) {
data.generation_prompt += END_MESSAGE + TEXT + msg.render_content();
}
data.prompt += data.generation_prompt;
}
auto parser = build_chat_peg_parser([&](common_chat_peg_builder & p) {
auto generation_prompt = p.literal(MSG_MODEL);
auto end = p.end();
common_peg_parser reasoning_block = p.eps();
if (extract_reasoning) {
reasoning_block =
p.literal(THINK) +
p.reasoning(p.until_one_of({ END_MESSAGE, TEXT, END_SAMPLING })) +
p.optional(p.literal(END_MESSAGE));
} else {
reasoning_block = p.content(
p.literal(THINK) +
p.until_one_of({ END_MESSAGE, TEXT, END_SAMPLING }) +
p.optional(p.literal(END_MESSAGE)));
}
auto reasoning = p.optional(reasoning_block);
auto text_block =
p.optional(p.literal(MSG_MODEL)) +
p.optional(p.literal(TEXT)) +
p.content(p.until_one_of({ THINK, END_MESSAGE, END_SAMPLING })) +
p.optional(p.literal(END_MESSAGE));
auto text_content =
p.one_or_more(p.choice({ reasoning_block, text_block }));
if (!has_tools || inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_NONE) {
return generation_prompt + reasoning + text_content +
p.optional(p.literal(END_SAMPLING)) + end;
}
auto tool_section = p.standard_json_tools(
INVOKE_TOOL, END_MESSAGE, inputs.tools,
/* parallel_tool_calls = */ false,
/* force_tool_calls = */ true,
/* name_key = */ "name",
/* args_key = */ "args",
/* array_wrapped = */ false,
/* function_is_key = */ false,
/* call_id_key = */ "",
/* gen_call_id_key = */ "",
/* parameters_order = */ {},
/* accept_openai_wrapper = */ false);
auto tool_block =
p.optional(p.literal(MSG_MODEL)) +
p.until_one_of({ INVOKE_TOOL, TEXT, THINK, END_MESSAGE, END_SAMPLING }) +
tool_section;
auto tool_calls = inputs.parallel_tool_calls
? p.one_or_more(tool_block)
: tool_block;
auto mixed_body =
p.one_or_more(p.choice({ tool_block, reasoning_block, text_block }));
auto body = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED
? tool_calls
: mixed_body;
return generation_prompt + reasoning + body +
p.optional(p.literal(END_SAMPLING)) + end;
});
data.parser = parser.save();
return data;
}
namespace workaround {
static void map_developer_role_to_system(json & messages) {
@@ -3059,13 +2947,6 @@ std::optional<common_chat_params> common_chat_try_specialized_template(
return common_chat_params_init_cohere2moe(tmpl, params);
}
if (src.find("<|content_thinking|>") != std::string::npos &&
src.find("<|content_text|>") != std::string::npos &&
src.find("<|message_model|>") != std::string::npos) {
LOG_DBG("Using specialized template: Inkling\n");
return common_chat_params_init_inkling(tmpl, params);
}
if (is_lfm2_template(src)) {
LOG_DBG("Using specialized template: LFM2\n");
return common_chat_params_init_lfm2(tmpl, params, /* tool_list_tokens = */ true);
+2
View File
@@ -1592,6 +1592,8 @@ struct llama_model_params common_model_params_to_llama(common_params & params) {
mparams.split_mode = params.split_mode;
mparams.load_mode = params.load_mode;
mparams.longhaul_cache_bytes = params.longhaul_cache_bytes;
mparams.tokenizer_longhaul = params.tokenizer_longhaul;
mparams.tokenizer_longhaul_cache_bytes = params.tokenizer_longhaul_cache_bytes;
mparams.tensor_split = params.tensor_split;
mparams.check_tensors = params.check_tensors;
mparams.use_extra_bufts = !params.no_extra_bufts;
+2
View File
@@ -486,6 +486,8 @@ struct common_params {
enum llama_split_mode split_mode = LLAMA_SPLIT_MODE_LAYER; // how to split the model across GPUs
enum llama_load_mode load_mode = LLAMA_LOAD_MODE_MMAP; // how to load the model
uint64_t longhaul_cache_bytes = 0;
bool tokenizer_longhaul = false;
uint64_t tokenizer_longhaul_cache_bytes = 0;
common_cpu_params cpuparams;
common_cpu_params cpuparams_batch;
-1
View File
@@ -27,7 +27,6 @@ TEXT_MODEL_MAP: dict[str, str] = {
"BaichuanForCausalLM": "baichuan",
"BailingMoeForCausalLM": "bailingmoe",
"BailingMoeV2ForCausalLM": "bailingmoe",
"BailingMoeV3ForCausalLM": "bailingmoe",
"BambaForCausalLM": "granite",
"BertForMaskedLM": "bert",
"BertForSequenceClassification": "bert",
-180
View File
@@ -1,7 +1,5 @@
from __future__ import annotations
import math
from typing import Callable, Iterable, TYPE_CHECKING
import torch
@@ -190,184 +188,6 @@ class BailingMoeV2Model(TextModel):
raise ValueError(f"Unprocessed experts: {experts}")
@ModelBase.register("BailingMoeV3ForCausalLM")
class BailingMoeV3Model(TextModel):
"""Ling 3.0 - hybrid KDA (linear) + gated MLA (full) attention with a bailing MoE"""
model_arch = gguf.MODEL_ARCH.BAILINGMOE3
_experts: list[dict[str, Tensor]] | None = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# the MTP block sits right after the last decoder layer and is not converted
self.block_count = self.hparams["num_hidden_layers"]
self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)
def is_mla_layer(self, il: int) -> bool:
# every layer_group_size-th layer is a full attention (MLA) layer, the rest are KDA
return (il + 1) % self.hparams["layer_group_size"] == 0
def set_vocab(self):
# identical tokenizer to Ling 2.0, the bailingmoe2 pre-tokenizer hash matches
self._set_vocab_gpt2()
def set_gguf_parameters(self):
hparams = self.hparams
# note: to enable the MLA KV cache, attention is converted into MQA (ie: GQA with 1 group)
hparams["num_key_value_heads"] = 1
super().set_gguf_parameters()
self.gguf_writer.add_vocab_size(hparams["vocab_size"])
# n_head_kv == 0 marks a KDA (recurrent) layer, > 0 marks an MLA (attention) layer
self.gguf_writer.add_head_count_kv([
1 if self.is_mla_layer(il) else 0 for il in range(self.block_count)
])
# KDA
self.gguf_writer.add_ssm_conv_kernel(hparams["short_conv_kernel_size"])
self.gguf_writer.add_kda_head_dim(hparams["head_dim"])
# safe gate: g = lower_bound * sigmoid(exp(A_log) * (f_proj(x) + dt_bias))
assert hparams.get("kda_safe_gate", False), "only the safe gate form is implemented"
self.gguf_writer.add_kda_gate_lower_bound(hparams["kda_lower_bound"])
# MLA - converted into MQA with larger heads, then decompressed to MHA
kv_lora_rank = hparams["kv_lora_rank"]
qk_rope_head_dim = hparams["qk_rope_head_dim"]
self.gguf_writer.add_kv_lora_rank(kv_lora_rank)
self.gguf_writer.add_key_length(kv_lora_rank + qk_rope_head_dim)
self.gguf_writer.add_value_length(kv_lora_rank)
self.gguf_writer.add_key_length_mla(hparams["qk_nope_head_dim"] + qk_rope_head_dim)
self.gguf_writer.add_value_length_mla(hparams["v_head_dim"])
self.gguf_writer.add_rope_dimension_count(qk_rope_head_dim)
# MoE
self.gguf_writer.add_leading_dense_block_count(hparams["first_k_dense_replace"])
self.gguf_writer.add_expert_feed_forward_length(hparams["moe_intermediate_size"])
self.gguf_writer.add_expert_shared_feed_forward_length(
hparams["moe_shared_expert_intermediate_size"] * hparams["num_shared_experts"])
self.gguf_writer.add_expert_shared_count(hparams["num_shared_experts"])
self.gguf_writer.add_expert_weights_scale(hparams["routed_scaling_factor"])
self.gguf_writer.add_expert_weights_norm(hparams["norm_topk_prob"])
self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID)
self.gguf_writer.add_expert_group_count(hparams["n_group"])
self.gguf_writer.add_expert_group_used_count(hparams["topk_group"])
# Optional per-layer SwiGLU clamps (vLLM SwigluStepAndMul):
# out = silu(gate).clamp(max=limit) * up.clamp(-limit, limit)
# 0.0 (or a missing/null entry) means no clamping for that layer.
def _clamp_limits(key: str) -> list[float] | None:
if (limits := hparams.get(key)) is None:
return None
limits = [0.0 if v is None else float(v) for v in limits[:self.block_count]]
return limits + [0.0] * (self.block_count - len(limits))
if (limits := _clamp_limits("expert_swiglu_limit_list")) is not None:
self.gguf_writer.add_swiglu_clamp_exp(limits)
if (limits := _clamp_limits("share_expert_swiglu_limit_list")) is not None:
self.gguf_writer.add_swiglu_clamp_shexp(limits)
if (nextn_layers := hparams.get("num_nextn_predict_layers")) is not None:
self.gguf_writer.add_nextn_predict_layers(nextn_layers)
@classmethod
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
name, gen = item
if name.endswith(".expert_bias"):
name = name.replace(".expert_bias", ".expert_bias.bias")
return super().filter_tensors((name, gen))
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
# drop the MTP block
if bid is not None and bid >= self.block_count:
return
n_head = self.hparams["num_attention_heads"]
head_dim = self.hparams["head_dim"]
if name.endswith(".A_log"):
# the safe gate uses -exp(A_log) only through exp(A_log), see the graph:
# g = kda_lower_bound * sigmoid(exp(A_log) * (f_proj(x) + dt_bias))
# {n_head} -> ggml ne = [1, n_head]
data_torch = torch.exp(data_torch.float())
data_torch = data_torch.reshape(-1, 1)
elif name.endswith(".dt_bias"):
name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias"
elif name.endswith((".q_conv1d.weight", ".k_conv1d.weight", ".v_conv1d.weight")):
# HF {d_inner, [1,] d_conv} -> numpy (1, d_inner, 1, d_conv) -> ggml ne = [d_conv, 1, d_inner, 1]
d_conv = data_torch.shape[-1]
d_inner = math.prod(data_torch.shape[:-1])
data_torch = data_torch.reshape(1, d_inner, 1, d_conv)
elif name.endswith("attention.g_proj.weight"):
assert bid is not None
# the very same HF name is the KDA output gate on linear layers and the
# attention output gate on MLA layers
tensor = gguf.MODEL_TENSOR.ATTN_GATE if self.is_mla_layer(bid) else gguf.MODEL_TENSOR.SSM_G
yield from super().modify_tensors(data_torch, self.format_tensor_name(tensor, bid), bid)
return
elif name.endswith("attention.kv_b_proj.weight"):
assert bid is not None
# MLA with the absorption optimization needs these two split and k_b transposed
v_head_dim = self.hparams["v_head_dim"]
qk_nope_head_dim = self.hparams["qk_nope_head_dim"]
assert data_torch.shape[0] == n_head * (v_head_dim + qk_nope_head_dim)
kv_b = data_torch.view(n_head, qk_nope_head_dim + v_head_dim, data_torch.shape[-1])
k_b, v_b = torch.split(kv_b, [qk_nope_head_dim, v_head_dim], dim=1)
k_b = k_b.transpose(1, 2)
yield from super().modify_tensors(k_b, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K_B, bid), bid)
yield from super().modify_tensors(v_b, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V_B, bid), bid)
return
elif name.endswith("attention.dense.weight"):
assert bid is not None
# MLA output projection (KDA layers call it o_proj)
yield from super().modify_tensors(data_torch, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_OUT, bid), bid)
return
elif "mlp.experts" in name:
n_experts = self.hparams["num_experts"]
assert bid is not None
if self._experts is None:
self._experts = [{} for _ in range(self.block_count)]
self._experts[bid][name] = data_torch
if len(self._experts[bid]) >= n_experts * 3:
# merge the experts into a single 3d tensor
for w_name in ["down_proj", "gate_proj", "up_proj"]:
datas: list[Tensor] = []
for xid in range(n_experts):
ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight"
datas.append(self._experts[bid][ename])
del self._experts[bid][ename]
data_torch = torch.stack(datas, dim=0)
merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight"
yield from super().modify_tensors(data_torch, merged_name, bid)
return
del head_dim # only used for the asserts above
yield from super().modify_tensors(data_torch, name, bid)
def prepare_tensors(self):
super().prepare_tensors()
if self._experts is not None:
experts = [k for d in self._experts for k in d.keys()]
if len(experts) > 0:
raise ValueError(f"Unprocessed experts: {experts}")
@ModelBase.register("SarvamMoEForCausalLM", "modeling_sarvam_moe.SarvamMoEForCausalLM")
class SarvamMoEModel(BailingMoeV2Model):
model_arch = gguf.MODEL_ARCH.BAILINGMOE2
+10 -65
View File
@@ -11,17 +11,11 @@ llama-cli \
--model /path/to/model.gguf \
--longhaul \
--longhaul-cache 2 \
--n-gpu-layers 0
--n-gpu-layers 99
```
`--longhaul-cache` is the expert cache budget in GiB. It is required when `--longhaul` is used. The same options are accepted by `llama-server`.
The command above runs every repeating layer on CPU and is supported on macOS,
Linux, and Windows. On macOS, the existing all-Metal mode remains available by
using `--n-gpu-layers 99` instead. Longhaul does not silently change device
placement: an unsupported GPU or mixed CPU/GPU repeating-layer placement fails
with guidance to use `--n-gpu-layers 0`.
Longhaul may reduce `--ubatch-size` so that every expert selected by one graph segment can be present in the cache at the same time. The effective value is logged during context creation. If one token selects more experts than the cache has slots, the routed MoE computation is split into multiple stages and the partial results are summed. This permits smaller caches at the cost of additional graph work.
The normal startup warmup is skipped automatically in longhaul mode. Routed expert weights are not read until the first real decode.
@@ -30,72 +24,23 @@ The normal startup warmup is skipped automatically in longhaul mode. Routed expe
Longhaul currently requires:
- all repeating layers on CPU, or all repeating layers on Metal
- Qwen3.5 MoE, Laguna, Inkling, or Ling 3.0 (`bailingmoe3`) architecture
- macOS with the Metal backend
- Qwen3.5 MoE or Laguna architecture
- all repeating model layers assigned to Metal
- text generation without embeddings or LoRA adapters
CPU mode is available wherever the CPU backend is supported. Metal mode requires
macOS. Longhaul does not restrict the GGUF quantization type; individual tensor
types must still be supported by the selected compute backend.
Longhaul does not restrict the GGUF quantization type. Individual tensor types must still be supported by Metal.
MTP/speculative decoding, tensor validation during loading, vocabulary-only
loading, and mixed CPU/GPU repeating-layer placement are not supported. Both
single-file and split GGUF models are supported; routed expert tensors are read
from the shard that owns each tensor.
MTP/speculative decoding, tensor validation during loading, vocabulary-only loading, and CPU or mixed CPU/Metal layer placement are not supported. Both single-file and split GGUF models are supported; routed expert tensors are read from the shard that owns each tensor.
The cache budget covers the compact routed-expert tensors. It does not include
dense weights, attention weights, the KV cache, graph allocations, or temporary
staging buffers. CPU expert caches use the standard directly writable tensor
layout so individual slots can be replaced; optimized CPU buffer selection
continues to apply to all non-streamed weights.
File-cache control is best effort and is outside the explicit cache budget.
macOS disables caching for streamed files where supported, and Linux advises the
kernel to discard completed reads. Windows may retain file data in its system
cache. Windows reads from one GGUF shard are serialized to preserve positional
read correctness, while POSIX systems retain concurrent `pread` operations.
The cache budget covers the compact routed-expert tensors. It does not include dense weights, attention weights, the KV cache, graph allocations, or the temporary buffer used for one expert read. Disk reads bypass the macOS unified file cache where supported, avoiding a second long-lived copy of streamed weights in system RAM.
This implementation synchronizes at each routed MoE layer to discover the selected experts, populate missing cache slots, and continue execution with cache-local expert IDs. Storage speed and expert reuse therefore have a large effect on generation speed.
Expert IDs are planned as a batch at each synchronization point. Experts already
needed by that batch are protected from eviction, duplicate IDs are loaded only
once, and independent expert slices are read concurrently where the platform
supports positional reads. CPU and shared Metal buffers are populated directly;
private Metal buffers use a staged fallback.
Ling 3.0 support covers its hybrid KDA/MLA transformer and sigmoid-routed MoE.
For Ling-3.0-flash, the router, score-correction bias, and shared expert remain
resident while the 512 routed experts are streamed. Each token selects eight
routed experts, so cache budgets with fewer than eight slots execute an MoE
layer in multiple stages. AtomicChat's Ling GGUFs can be loaded directly, and
the HF converter also recognizes `BailingMoeV3ForCausalLM` checkpoints. The
converter omits the auxiliary MTP tensor block because Longhaul does not support
MTP tensors.
Inkling keeps its two shared experts resident and streams only the routed
256-expert banks. Inkling-Small selects six routed experts per token. In the
seven-shard Q8_0 model, one cache slot across all 40 MoE layers uses about
0.996 GiB, so a 6 GiB cache provides six slots and executes each MoE layer in
one stage. The non-routed tensors, including both shared experts, require about
6.02 GiB in addition to the expert cache:
```sh
llama-cli \
--model /path/to/Inkling-Small-Q8_0-00001-of-00007.gguf \
--longhaul \
--longhaul-cache 6 \
--n-gpu-layers 99 \
--flash-attn off \
--jinja
```
Budgets from 1 through 5 GiB are supported by splitting the six selected
experts into multiple stages. Add `--n-gpu-layers 0` to run entirely on CPU.
Inkling currently forces flash attention off because its relative-position
bias is evaluated by the unfused attention path. This support covers text and
tool-call generation from the language-model GGUF; multimodal projection files
are not loaded by longhaul. Use `--jinja` with `llama-cli`, `llama-completion`,
or `llama-server` so the model's embedded TML chat template is accepted.
once, and independent expert slices are read concurrently on shared-memory Metal
devices. Private Metal buffers use a staged fallback.
## Benchmarking prompt processing
@@ -106,7 +51,7 @@ llama-bench \
--model /path/to/model.gguf \
--load-mode longhaul \
--longhaul-cache 2 \
--n-gpu-layers 0 \
--n-gpu-layers 99 \
--n-prompt 2048 \
--n-gen 0
```
+21
View File
@@ -30,3 +30,24 @@ call `llama_token_cache_flush()`.
The 5 GiB limit accounts for logical entry payloads. SQLite metadata and bounded
journal files are not included.
## Tokenizer longhaul
Qwen3.5 MoE and Laguna models can move their reverse-token and BPE-merge indexes
into a persistent, disk-backed index while keeping a bounded lookup working set:
```text
--tokenizer-longhaul
--tokenizer-longhaul-cache N
```
`N` is the application-owned lookup cache budget in MiB and is required when
the mode is enabled. The mode is independent of model `--longhaul`, so the two
can be used together. The finite tokenizer index builds in the background while
model loading continues. If tokenizer work arrives first, it waits for atomic
index publication; build or storage failures are reported instead of silently
falling back to an unbounded tokenizer.
The persistent index uses the directory selected by `--token-cache-dir` and is
subject to `--token-cache-size`. Tokenized-text results continue to be cached
on demand and therefore do not have a finite completion point.
+1 -45
View File
@@ -244,8 +244,7 @@ class Keys:
DT_B_C_RMS = "{arch}.ssm.dt_b_c_rms"
class KDA:
HEAD_DIM = "{arch}.kda.head_dim"
GATE_LOWER_BOUND = "{arch}.kda.gate_lower_bound"
HEAD_DIM = "{arch}.kda.head_dim"
class WKV:
HEAD_SIZE = "{arch}.wkv.head_size"
@@ -518,7 +517,6 @@ class MODEL_ARCH(IntEnum):
PLM = auto()
BAILINGMOE = auto()
BAILINGMOE2 = auto()
BAILINGMOE3 = auto()
DOTS1 = auto()
ARCEE = auto()
AFMOE = auto()
@@ -666,8 +664,6 @@ class MODEL_TENSOR(IntEnum):
SSM_CONV1D_Q = auto() # Kimi Linear
SSM_CONV1D_K = auto() # Kimi Linear
SSM_CONV1D_V = auto() # Kimi Linear
SSM_F = auto() # bailingmoe3 (no_kda_lora)
SSM_G = auto() # bailingmoe3 (no_kda_lora)
SSM_F_A = auto() # Kimi Linear
SSM_F_B = auto() # Kimi Linear
SSM_BETA = auto() # Kimi Linear qwen3.5
@@ -1132,7 +1128,6 @@ MODEL_ARCH_NAMES: dict[MODEL_ARCH, str] = {
MODEL_ARCH.PLM: "plm",
MODEL_ARCH.BAILINGMOE: "bailingmoe",
MODEL_ARCH.BAILINGMOE2: "bailingmoe2",
MODEL_ARCH.BAILINGMOE3: "bailingmoe3",
MODEL_ARCH.DOTS1: "dots1",
MODEL_ARCH.ARCEE: "arcee",
MODEL_ARCH.AFMOE: "afmoe",
@@ -1279,8 +1274,6 @@ TENSOR_NAMES: dict[MODEL_TENSOR, str] = {
MODEL_TENSOR.SSM_CONV1D_Q: "blk.{bid}.ssm_conv1d_q", # Kimi Linear
MODEL_TENSOR.SSM_CONV1D_K: "blk.{bid}.ssm_conv1d_k", # Kimi Linear
MODEL_TENSOR.SSM_CONV1D_V: "blk.{bid}.ssm_conv1d_v", # Kimi Linear
MODEL_TENSOR.SSM_F: "blk.{bid}.ssm_f", # bailingmoe3
MODEL_TENSOR.SSM_G: "blk.{bid}.ssm_g", # bailingmoe3
MODEL_TENSOR.SSM_F_A: "blk.{bid}.ssm_f_a", # Kimi Linear
MODEL_TENSOR.SSM_F_B: "blk.{bid}.ssm_f_b", # Kimi Linear
MODEL_TENSOR.SSM_BETA: "blk.{bid}.ssm_beta", # Kimi Linear qwen3.5
@@ -3856,43 +3849,6 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM,
MODEL_TENSOR.LAYER_OUT_NORM,
],
MODEL_ARCH.BAILINGMOE3: [
MODEL_TENSOR.TOKEN_EMBD,
MODEL_TENSOR.OUTPUT_NORM,
MODEL_TENSOR.OUTPUT,
MODEL_TENSOR.ATTN_NORM,
MODEL_TENSOR.ATTN_Q,
MODEL_TENSOR.ATTN_K,
MODEL_TENSOR.ATTN_V,
MODEL_TENSOR.ATTN_OUT,
MODEL_TENSOR.ATTN_GATE,
MODEL_TENSOR.ATTN_KV_A_MQA,
MODEL_TENSOR.ATTN_KV_B,
MODEL_TENSOR.ATTN_K_B,
MODEL_TENSOR.ATTN_V_B,
MODEL_TENSOR.ATTN_KV_A_NORM,
MODEL_TENSOR.SSM_CONV1D_Q,
MODEL_TENSOR.SSM_CONV1D_K,
MODEL_TENSOR.SSM_CONV1D_V,
MODEL_TENSOR.SSM_F,
MODEL_TENSOR.SSM_G,
MODEL_TENSOR.SSM_BETA,
MODEL_TENSOR.SSM_A,
MODEL_TENSOR.SSM_DT,
MODEL_TENSOR.SSM_NORM,
MODEL_TENSOR.FFN_NORM,
MODEL_TENSOR.FFN_GATE,
MODEL_TENSOR.FFN_DOWN,
MODEL_TENSOR.FFN_UP,
MODEL_TENSOR.FFN_GATE_INP,
MODEL_TENSOR.FFN_EXP_PROBS_B,
MODEL_TENSOR.FFN_GATE_EXP,
MODEL_TENSOR.FFN_DOWN_EXP,
MODEL_TENSOR.FFN_UP_EXP,
MODEL_TENSOR.FFN_GATE_SHEXP,
MODEL_TENSOR.FFN_DOWN_SHEXP,
MODEL_TENSOR.FFN_UP_SHEXP,
],
MODEL_ARCH.DOTS1: [
MODEL_TENSOR.TOKEN_EMBD,
MODEL_TENSOR.OUTPUT_NORM,
-3
View File
@@ -1087,9 +1087,6 @@ class GGUFWriter:
def add_kda_head_dim(self, value: int) -> None:
self.add_uint32(Keys.KDA.HEAD_DIM.format(arch=self.arch), value)
def add_kda_gate_lower_bound(self, value: float) -> None:
self.add_float32(Keys.KDA.GATE_LOWER_BOUND.format(arch=self.arch), value)
def add_tokenizer_model(self, model: str) -> None:
self.add_string(Keys.Tokenizer.MODEL, model)
-19
View File
@@ -253,7 +253,6 @@ class TensorNameMap:
# Attention query
MODEL_TENSOR.ATTN_Q: (
"model.layers.{bid}.attention.q_proj", # bailingmoe3
"model.layers.{bid}.self_attn.q_proj", # llama-hf nemotron olmoe olmo2 phimoe
"layers.{bid}.self_attn.q_proj", # embeddinggemma
"model.layers.{bid}.self_attn.q_proj_no_perm", # llama-custom
@@ -274,7 +273,6 @@ class TensorNameMap:
# Attention key
MODEL_TENSOR.ATTN_K: (
"model.layers.{bid}.attention.k_proj", # bailingmoe3
"model.layers.{bid}.self_attn.k_proj", # llama-hf nemotron olmoe olmo2 phimoe
"layers.{bid}.self_attn.k_proj", # embeddinggemma
"model.layers.{bid}.self_attn.k_proj_no_perm", # llama-custom
@@ -296,7 +294,6 @@ class TensorNameMap:
# Attention value
MODEL_TENSOR.ATTN_V: (
"model.layers.{bid}.attention.v_proj", # bailingmoe3
"model.layers.{bid}.self_attn.v_proj", # llama-hf nemotron olmoe olmo2 phimoe
"layers.{bid}.self_attn.v_proj", # embeddinggemma
"layers.{bid}.attention.wv", # llama-pth
@@ -317,7 +314,6 @@ class TensorNameMap:
# Attention output
MODEL_TENSOR.ATTN_OUT: (
"model.layers.{bid}.attention.o_proj", # bailingmoe3
"gpt_neox.layers.{bid}.attention.dense", # gptneox
"transformer.h.{bid}.attn.c_proj", # gpt2 refact qwen jais
"transformer.blocks.{bid}.attn.out_proj", # mpt
@@ -829,7 +825,6 @@ class TensorNameMap:
),
MODEL_TENSOR.SSM_DT: (
"model.layers.{bid}.attention.dt_proj", # bailingmoe3
"model.layers.{bid}.dt_proj", # mamba-hf
"backbone.layers.{bid}.mixer.dt_proj", # mamba
"model.layers.{bid}.mamba.dt_proj", # jamba falcon-h1 granite-hybrid
@@ -845,7 +840,6 @@ class TensorNameMap:
),
MODEL_TENSOR.SSM_A: (
"model.layers.{bid}.attention.A_log", # bailingmoe3
"model.layers.{bid}.A_log", # mamba-hf
"backbone.layers.{bid}.mixer.A_log", # mamba
"model.layers.{bid}.mamba.A_log", # jamba falcon-h1 granite-hybrid
@@ -878,7 +872,6 @@ class TensorNameMap:
"model.layers.{bid}.linear_attn.norm", # qwen3next
"backbone.layers.{bid}.mixer.norm", # mamba2
"model.layers.{bid}.self_attn.o_norm", # kimi
"model.layers.{bid}.attention.o_norm", # bailingmoe3
),
MODEL_TENSOR.SSM_OUT: (
@@ -900,21 +893,12 @@ class TensorNameMap:
# Kimi Linear KDA (using SSM_ prefix for consistency)
MODEL_TENSOR.SSM_CONV1D_Q: (
"model.layers.{bid}.self_attn.q_conv1d",
"model.layers.{bid}.attention.q_conv1d", # bailingmoe3
),
MODEL_TENSOR.SSM_CONV1D_K: (
"model.layers.{bid}.self_attn.k_conv1d",
"model.layers.{bid}.attention.k_conv1d", # bailingmoe3
),
MODEL_TENSOR.SSM_CONV1D_V: (
"model.layers.{bid}.self_attn.v_conv1d",
"model.layers.{bid}.attention.v_conv1d", # bailingmoe3
),
MODEL_TENSOR.SSM_F: (
"model.layers.{bid}.attention.f_proj", # bailingmoe3
),
MODEL_TENSOR.SSM_G: (
"model.layers.{bid}.attention.g_proj", # bailingmoe3
),
MODEL_TENSOR.SSM_F_A: (
"model.layers.{bid}.self_attn.f_a_proj",
@@ -925,7 +909,6 @@ class TensorNameMap:
MODEL_TENSOR.SSM_BETA: (
"model.layers.{bid}.linear_attn.in_proj_b", # qwen3.5
"model.layers.{bid}.self_attn.b_proj", # Kimi Linear
"model.layers.{bid}.attention.b_proj", # bailingmoe3
),
MODEL_TENSOR.SSM_G_A: (
"model.layers.{bid}.self_attn.g_a_proj",
@@ -1114,7 +1097,6 @@ class TensorNameMap:
),
MODEL_TENSOR.ATTN_KV_A_MQA: (
"model.layers.{bid}.attention.kv_a_proj_with_mqa", # bailingmoe3
"model.layers.{bid}.self_attn.kv_a_proj_with_mqa", # deepseek2
"layers.{bid}.attention.wkv_a_with_mqa", # mistral-large
),
@@ -1139,7 +1121,6 @@ class TensorNameMap:
),
MODEL_TENSOR.ATTN_KV_A_NORM: (
"model.layers.{bid}.attention.kv_a_layernorm", # bailingmoe3
"model.layers.{bid}.self_attn.kv_a_layernorm", # deepseek2
"layers.{bid}.attention.kv_a_norm", # mistral-large
),
+2
View File
@@ -360,6 +360,8 @@ extern "C" {
bool no_alloc; // only load metadata and simulate memory allocations
uint64_t longhaul_cache_bytes; // routed expert cache size for LLAMA_LOAD_MODE_LONGHAUL
bool tokenizer_longhaul; // page Qwen3.5/Laguna tokenizer indexes through a bounded cache
uint64_t tokenizer_longhaul_cache_bytes; // tokenizer-longhaul RAM cache budget
};
struct llama_sampler_seq_config {
+1
View File
@@ -38,6 +38,7 @@ add_library(llama
llama-quant.cpp
llama-sampler.cpp
llama-token-cache.cpp
llama-tokenizer-longhaul.cpp
llama-vocab.cpp
unicode-data.cpp
unicode.cpp
+1 -41
View File
@@ -105,7 +105,6 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
{ LLM_ARCH_PLM, "plm" },
{ LLM_ARCH_BAILINGMOE, "bailingmoe" },
{ LLM_ARCH_BAILINGMOE2, "bailingmoe2" },
{ LLM_ARCH_BAILINGMOE3, "bailingmoe3" },
{ LLM_ARCH_DOTS1, "dots1" },
{ LLM_ARCH_ARCEE, "arcee" },
{ LLM_ARCH_AFMOE, "afmoe" },
@@ -145,7 +144,6 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
{ LLM_ARCH_TALKIE, "talkie" },
{ LLM_ARCH_MELLUM, "mellum" },
{ LLM_ARCH_NANBEIGE, "nanbeige" },
{ LLM_ARCH_INKLING, "inkling" },
{ LLM_ARCH_UNKNOWN, "(unknown)" },
};
@@ -304,8 +302,7 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
{ LLM_KV_SSM_GROUP_COUNT, "%s.ssm.group_count" },
{ LLM_KV_SSM_DT_B_C_RMS, "%s.ssm.dt_b_c_rms" },
{ LLM_KV_KDA_HEAD_DIM, "%s.kda.head_dim" },
{ LLM_KV_KDA_GATE_LOWER_BOUND, "%s.kda.gate_lower_bound" },
{ LLM_KV_KDA_HEAD_DIM, "%s.kda.head_dim" },
{ LLM_KV_WKV_HEAD_SIZE, "%s.wkv.head_size" },
@@ -323,15 +320,6 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
{ LLM_KV_NORM_BEFORE_FC, "%s.norm_before_fc" },
{ LLM_KV_SHORTCONV_L_CACHE, "%s.shortconv.l_cache" },
{ LLM_KV_INKLING_D_REL, "%s.d_rel" },
{ LLM_KV_INKLING_REL_EXTENT, "%s.rel_extent" },
{ LLM_KV_INKLING_REL_EXTENT_SWA, "%s.rel_extent_swa" },
{ LLM_KV_INKLING_SHORTCONV_KERNEL, "%s.shortconv_kernel" },
{ LLM_KV_INKLING_DENSE_BLOCK_COUNT, "%s.dense_block_count" },
{ LLM_KV_INKLING_LOGIT_SCALE_DENOM, "%s.logit_scale_denom" },
{ LLM_KV_INKLING_LOG_SCALING_N_FLOOR, "%s.log_scaling_n_floor" },
{ LLM_KV_INKLING_LOG_SCALING_ALPHA, "%s.log_scaling_alpha" },
{ LLM_KV_INKLING_UNPADDED_VOCAB_SIZE, "%s.unpadded_vocab_size" },
// sentence-transformers dense modules feature dims
{ LLM_KV_DENSE_2_FEAT_IN, "%s.dense_2_feat_in" },
{ LLM_KV_DENSE_2_FEAT_OUT, "%s.dense_2_feat_out" },
@@ -461,8 +449,6 @@ static const std::map<llm_tensor, const char *> LLM_TENSOR_NAMES = {
{ LLM_TENSOR_SSM_CONV1D_Q, "blk.%d.ssm_conv1d_q" },
{ LLM_TENSOR_SSM_CONV1D_K, "blk.%d.ssm_conv1d_k" },
{ LLM_TENSOR_SSM_CONV1D_V, "blk.%d.ssm_conv1d_v" },
{ LLM_TENSOR_SSM_F, "blk.%d.ssm_f" },
{ LLM_TENSOR_SSM_G, "blk.%d.ssm_g" },
{ LLM_TENSOR_SSM_F_A, "blk.%d.ssm_f_a" },
{ LLM_TENSOR_SSM_F_B, "blk.%d.ssm_f_b" },
{ LLM_TENSOR_SSM_BETA, "blk.%d.ssm_beta" },
@@ -606,19 +592,9 @@ static const std::map<llm_tensor, const char *> LLM_TENSOR_NAMES = {
{ LLM_TENSOR_SHORTCONV_CONV, "blk.%d.shortconv.conv" },
{ LLM_TENSOR_SHORTCONV_INPROJ, "blk.%d.shortconv.in_proj" },
{ LLM_TENSOR_SHORTCONV_OUTPROJ, "blk.%d.shortconv.out_proj" },
{ LLM_TENSOR_ATTN_R, "blk.%d.attn_r" },
{ LLM_TENSOR_ATTN_REL_PROJ, "blk.%d.attn_rel_proj" },
{ LLM_TENSOR_SHORTCONV_K, "blk.%d.shortconv_k" },
{ LLM_TENSOR_SHORTCONV_V, "blk.%d.shortconv_v" },
{ LLM_TENSOR_SHORTCONV_ATTN, "blk.%d.shortconv_attn" },
{ LLM_TENSOR_SHORTCONV_MLP, "blk.%d.shortconv_mlp" },
{ LLM_TENSOR_FFN_GSCALE, "blk.%d.ffn_gscale" },
{ LLM_TENSOR_FFN_GATE_CHEXPS, "blk.%d.ffn_gate_chexps" },
{ LLM_TENSOR_FFN_DOWN_CHEXPS, "blk.%d.ffn_down_chexps" },
{ LLM_TENSOR_FFN_UP_CHEXPS, "blk.%d.ffn_up_chexps" },
{ LLM_TENSOR_FFN_GATE_SHEXPS, "blk.%d.ffn_gate_shexp" },
{ LLM_TENSOR_FFN_DOWN_SHEXPS, "blk.%d.ffn_down_shexp" },
{ LLM_TENSOR_FFN_UP_SHEXPS, "blk.%d.ffn_up_shexp" },
{ LLM_TENSOR_VISEXP_ATTN_QKV, "blk.%d.vis_attn_qkv" },
{ LLM_TENSOR_VISEXP_ATTN_OUT, "blk.%d.vis_attn_output" },
{ LLM_TENSOR_VISEXP_FFN_GATE, "blk.%d.vis_gate" },
@@ -766,8 +742,6 @@ static const std::map<llm_tensor, llm_tensor_info> LLM_TENSOR_INFOS = {
{LLM_TENSOR_SSM_CONV1D_Q, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
{LLM_TENSOR_SSM_CONV1D_K, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
{LLM_TENSOR_SSM_CONV1D_V, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
{LLM_TENSOR_SSM_F, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_SSM_G, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_SSM_F_A, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_SSM_F_B, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_SSM_BETA, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
@@ -821,9 +795,6 @@ static const std::map<llm_tensor, llm_tensor_info> LLM_TENSOR_INFOS = {
{LLM_TENSOR_FFN_UP_EXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_GATE_UP_EXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_DOWN_CHEXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_DOWN_SHEXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_GATE_SHEXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_UP_SHEXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_GATE_CHEXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_UP_CHEXPS, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT_ID}},
{LLM_TENSOR_FFN_EXP_PROBS_B, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_ADD}},
@@ -865,13 +836,6 @@ static const std::map<llm_tensor, llm_tensor_info> LLM_TENSOR_INFOS = {
{LLM_TENSOR_SHORTCONV_CONV, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_SSM_CONV}},
{LLM_TENSOR_SHORTCONV_INPROJ, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_SHORTCONV_OUTPROJ, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_ATTN_R, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_ATTN_REL_PROJ, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_SHORTCONV_K, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_SSM_CONV}},
{LLM_TENSOR_SHORTCONV_V, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_SSM_CONV}},
{LLM_TENSOR_SHORTCONV_ATTN, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_SSM_CONV}},
{LLM_TENSOR_SHORTCONV_MLP, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_SSM_CONV}},
{LLM_TENSOR_FFN_GSCALE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
{LLM_TENSOR_VISEXP_ATTN_QKV, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_VISEXP_ATTN_OUT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
{LLM_TENSOR_VISEXP_FFN_GATE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
@@ -1001,11 +965,9 @@ bool llm_arch_is_hybrid(const llm_arch & arch) {
case LLM_ARCH_NEMOTRON_H:
case LLM_ARCH_NEMOTRON_H_MOE:
case LLM_ARCH_QWEN3NEXT:
case LLM_ARCH_BAILINGMOE3:
case LLM_ARCH_KIMI_LINEAR:
case LLM_ARCH_QWEN35:
case LLM_ARCH_QWEN35MOE:
case LLM_ARCH_INKLING:
return true;
default:
return false;
@@ -1061,9 +1023,7 @@ bool llm_arch_supports_sm_tensor(const llm_arch & arch) {
case LLM_ARCH_MINIMAX_M2:
case LLM_ARCH_MINIMAX_M3:
case LLM_ARCH_MISTRAL4:
case LLM_ARCH_BAILINGMOE3:
case LLM_ARCH_KIMI_LINEAR:
case LLM_ARCH_INKLING:
return false;
default:
return true;
-24
View File
@@ -110,7 +110,6 @@ enum llm_arch {
LLM_ARCH_PLM,
LLM_ARCH_BAILINGMOE,
LLM_ARCH_BAILINGMOE2,
LLM_ARCH_BAILINGMOE3,
LLM_ARCH_DOTS1,
LLM_ARCH_ARCEE,
LLM_ARCH_AFMOE,
@@ -150,7 +149,6 @@ enum llm_arch {
LLM_ARCH_MINIMAX_M3,
LLM_ARCH_DFLASH,
LLM_ARCH_NANBEIGE,
LLM_ARCH_INKLING,
LLM_ARCH_UNKNOWN,
};
@@ -310,7 +308,6 @@ enum llm_kv {
LLM_KV_SSM_DT_B_C_RMS,
LLM_KV_KDA_HEAD_DIM,
LLM_KV_KDA_GATE_LOWER_BOUND,
LLM_KV_WKV_HEAD_SIZE,
@@ -369,15 +366,6 @@ enum llm_kv {
LLM_KV_NORM_BEFORE_FC,
LLM_KV_SHORTCONV_L_CACHE,
LLM_KV_INKLING_D_REL,
LLM_KV_INKLING_REL_EXTENT,
LLM_KV_INKLING_REL_EXTENT_SWA,
LLM_KV_INKLING_SHORTCONV_KERNEL,
LLM_KV_INKLING_DENSE_BLOCK_COUNT,
LLM_KV_INKLING_LOGIT_SCALE_DENOM,
LLM_KV_INKLING_LOG_SCALING_N_FLOOR,
LLM_KV_INKLING_LOG_SCALING_ALPHA,
LLM_KV_INKLING_UNPADDED_VOCAB_SIZE,
LLM_KV_XIELU_ALPHA_N,
LLM_KV_XIELU_ALPHA_P,
@@ -446,9 +434,6 @@ enum llm_tensor {
LLM_TENSOR_FFN_DOWN_CHEXPS,
LLM_TENSOR_FFN_GATE_CHEXPS,
LLM_TENSOR_FFN_UP_CHEXPS,
LLM_TENSOR_FFN_DOWN_SHEXPS,
LLM_TENSOR_FFN_GATE_SHEXPS,
LLM_TENSOR_FFN_UP_SHEXPS,
LLM_TENSOR_FFN_EXP_PROBS_B,
LLM_TENSOR_FFN_LATENT_DOWN,
LLM_TENSOR_FFN_LATENT_UP,
@@ -492,8 +477,6 @@ enum llm_tensor {
LLM_TENSOR_SSM_CONV1D_Q, // kimi: Q conv1d weight
LLM_TENSOR_SSM_CONV1D_K, // kimi: K conv1d weight
LLM_TENSOR_SSM_CONV1D_V, // kimi: V conv1d weight
LLM_TENSOR_SSM_F, // bailingmoe3: full-rank forget gate projection
LLM_TENSOR_SSM_G, // bailingmoe3: full-rank output gate projection
LLM_TENSOR_SSM_F_A, // kimi: forget gate projection A
LLM_TENSOR_SSM_F_B, // kimi: forget gate projection B
LLM_TENSOR_SSM_BETA, // kimi: beta mixing coefficient and qwen3.5
@@ -612,13 +595,6 @@ enum llm_tensor {
LLM_TENSOR_SHORTCONV_CONV,
LLM_TENSOR_SHORTCONV_INPROJ,
LLM_TENSOR_SHORTCONV_OUTPROJ,
LLM_TENSOR_ATTN_R,
LLM_TENSOR_ATTN_REL_PROJ,
LLM_TENSOR_SHORTCONV_K,
LLM_TENSOR_SHORTCONV_V,
LLM_TENSOR_SHORTCONV_ATTN,
LLM_TENSOR_SHORTCONV_MLP,
LLM_TENSOR_FFN_GSCALE,
LLM_TENSOR_VISEXP_ATTN_QKV,
LLM_TENSOR_VISEXP_ATTN_OUT,
LLM_TENSOR_VISEXP_FFN_GATE,
+2 -4
View File
@@ -3586,10 +3586,8 @@ llama_context * llama_init_from_model(
return nullptr;
}
if (params.flash_attn_type != LLAMA_FLASH_ATTN_TYPE_DISABLED &&
(model->arch == LLM_ARCH_GROK || model->arch == LLM_ARCH_INKLING)) {
LLAMA_LOG_WARN("%s: flash_attn is not compatible with %s - forcing off\n",
__func__, model->arch == LLM_ARCH_GROK ? "Grok" : "Inkling");
if (params.flash_attn_type != LLAMA_FLASH_ATTN_TYPE_DISABLED && model->arch == LLM_ARCH_GROK) {
LLAMA_LOG_WARN("%s: flash_attn is not compatible with Grok - forcing off\n", __func__);
params.flash_attn_type = LLAMA_FLASH_ATTN_TYPE_DISABLED;
}
-113
View File
@@ -1765,119 +1765,6 @@ ggml_tensor * llm_graph_context::build_ffn(
return cur;
}
ggml_tensor * llm_graph_context::build_moe_experts(
ggml_tensor * cur,
ggml_tensor * selected_experts,
ggml_tensor * weights,
ggml_tensor * up_exps,
ggml_tensor * gate_exps,
ggml_tensor * down_exps,
int64_t n_expert_used,
llm_ffn_op_type type_op,
int il) const {
GGML_ASSERT(cur != nullptr);
GGML_ASSERT(selected_experts != nullptr);
GGML_ASSERT(weights != nullptr);
GGML_ASSERT(up_exps != nullptr && down_exps != nullptr);
const int64_t n_embd = cur->ne[0];
const int64_t n_tokens = cur->ne[1];
// Ensure routing and weights are available before a longhaul cache
// callback resolves and loads the first expert chunk.
ggml_build_forward_expand(gf, weights);
ggml_tensor * moe_inp = ggml_reshape_3d(ctx0, cur, n_embd, 1, n_tokens);
const int64_t chunk_size = longhaul
? std::min<int64_t>(n_expert_used, longhaul->capacity())
: n_expert_used;
GGML_ASSERT(chunk_size > 0);
ggml_tensor * moe_out = nullptr;
int chunk = 0;
for (int64_t first = 0; first < n_expert_used; first += chunk_size, ++chunk) {
const int64_t count = std::min<int64_t>(chunk_size, n_expert_used - first);
ggml_tensor * selected_chunk = selected_experts;
ggml_tensor * weights_chunk = weights;
if (count != n_expert_used) {
selected_chunk = ggml_view_2d(
ctx0, selected_experts, count, n_tokens,
selected_experts->nb[1], first*selected_experts->nb[0]);
weights_chunk = ggml_view_3d(
ctx0, weights, 1, count, n_tokens,
weights->nb[1], weights->nb[2], first*weights->nb[1]);
}
ggml_tensor * selected_moe = selected_chunk;
if (longhaul) {
selected_moe = ggml_dup(ctx0, selected_chunk);
ggml_format_name(selected_moe, "longhaul.remap.%d.%d", il, chunk);
ggml_build_forward_expand(gf, selected_moe);
}
ggml_tensor * up = build_lora_mm_id(up_exps, moe_inp, selected_moe);
ggml_tensor * act = gate_exps
? build_lora_mm_id(gate_exps, moe_inp, selected_moe)
: up;
switch (type_op) {
case LLM_FFN_SILU:
act = gate_exps
? ggml_swiglu_split(ctx0, act, up)
: ggml_silu(ctx0, act);
break;
case LLM_FFN_GELU:
act = gate_exps
? ggml_geglu_split(ctx0, act, up)
: ggml_gelu(ctx0, act);
break;
case LLM_FFN_RELU:
act = gate_exps
? ggml_reglu_split(ctx0, act, up)
: ggml_relu(ctx0, act);
break;
default:
GGML_ABORT("unsupported routed expert activation");
}
ggml_tensor * experts = build_lora_mm_id(
down_exps, act, selected_moe);
experts = ggml_mul(ctx0, experts, weights_chunk);
ggml_build_forward_expand(gf, experts);
ggml_tensor * chunk_out = nullptr;
for (int64_t i = 0; i < count; ++i) {
ggml_tensor * expert = ggml_view_2d(
ctx0, experts, n_embd, n_tokens,
experts->nb[2], i*experts->nb[1]);
ggml_build_forward_expand(gf, expert);
chunk_out = chunk_out ? ggml_add(ctx0, chunk_out, expert) : expert;
ggml_build_forward_expand(gf, chunk_out);
}
if (count == 1) {
chunk_out = ggml_cont(ctx0, chunk_out);
}
ggml_tensor * contribution = chunk_out;
if (longhaul) {
ggml_tensor * release = ggml_cont(ctx0, chunk_out);
ggml_format_name(release, "longhaul.release.%d.%d", il, chunk);
ggml_build_forward_expand(gf, release);
if (chunk_size < n_expert_used) {
contribution = release;
}
}
moe_out = moe_out ? ggml_add(ctx0, moe_out, contribution) : contribution;
ggml_build_forward_expand(gf, moe_out);
}
cb(moe_out, "ffn_moe_out", il);
return moe_out;
}
ggml_tensor * llm_graph_context::build_moe_ffn(
ggml_tensor * cur,
ggml_tensor * gate_inp,
-14
View File
@@ -1000,20 +1000,6 @@ struct llm_graph_context {
llm_ffn_gate_type type_gate,
int il) const;
// Execute experts for an architecture that supplies its own selected IDs
// and weights. In longhaul mode this transparently stages those experts
// through the bounded cache.
ggml_tensor * build_moe_experts(
ggml_tensor * cur,
ggml_tensor * selected_experts,
ggml_tensor * weights,
ggml_tensor * up_exps,
ggml_tensor * gate_exps,
ggml_tensor * down_exps,
int64_t n_expert_used,
llm_ffn_op_type type_op,
int il) const;
// build MoE FFN without bias tensors
ggml_tensor * build_moe_ffn(
ggml_tensor * cur,
-4
View File
@@ -191,10 +191,6 @@ uint32_t llama_hparams::n_embd_k_idx(uint32_t il) const {
}
uint32_t llama_hparams::n_embd_r() const {
if (n_embd_r_impl != 0) {
return n_embd_r_impl;
}
if (wkv_head_size != 0) {
// for RWKV models
return token_shift_count * n_embd;
-13
View File
@@ -80,18 +80,6 @@ struct llama_hparams {
uint32_t n_shortconv_l_cache = 0;
// Explicit recurrent-state size override. Inkling packs four independent
// short-convolution histories into the recurrent cache for every layer.
uint32_t n_embd_r_impl = 0;
// Inkling relative-attention and output metadata.
uint32_t inkling_d_rel = 0;
uint32_t inkling_rel_extent = 0;
uint32_t inkling_rel_extent_swa = 0;
uint32_t inkling_log_n_floor = 0;
float inkling_log_alpha = 0.0f;
uint32_t inkling_unpadded_n_vocab = 0;
std::array<uint32_t, LLAMA_MAX_LAYERS> n_head_arr;
std::array<uint32_t, LLAMA_MAX_LAYERS> n_head_kv_arr;
std::array<uint32_t, LLAMA_MAX_LAYERS> n_ff_arr;
@@ -174,7 +162,6 @@ struct llama_hparams {
// for Kimi Linear KDA
uint32_t n_embd_head_kda = 0;
float kda_gate_lower_bound = 0.0f;
bool ssm_dt_b_c_rms = false;
-38
View File
@@ -1931,37 +1931,6 @@ void llama_kv_cache::set_input_pos_bucket(ggml_tensor * dst, const llama_ubatch
}
}
void llama_kv_cache::set_input_pos_rel_flat(
ggml_tensor * dst,
const llama_ubatch * ubatch,
uint32_t extent) const {
GGML_ASSERT(ggml_backend_buffer_is_host(dst->buffer));
GGML_ASSERT(dst->ne[1] == (int64_t) ubatch->n_tokens);
int32_t * data = (int32_t *) dst->data;
const int64_t n_kv = dst->ne[0];
// Each query owns an extent+1-row block in the flattened relative-logit
// table. The final row is the zero-bias sentinel for empty/out-of-band KV
// cells.
for (int64_t i = 0; i < (int64_t) ubatch->n_tokens; ++i) {
const llama_seq_id seq_id = ubatch->seq_id[i][0];
const auto & cells = v_cells[seq_to_stream[seq_id]];
const llama_pos p1 = ubatch->pos[i];
for (int64_t j = 0; j < n_kv; ++j) {
int32_t rel = (int32_t) extent;
if (!cells.is_empty(j)) {
const llama_pos d = p1 - cells.pos_get(j);
if (d >= 0 && d < (llama_pos) extent) {
rel = (int32_t) d;
}
}
data[i*n_kv + j] = (int32_t) (i*(extent + 1)) + rel;
}
}
}
void llama_kv_cache::set_input_k_rot(ggml_tensor * dst) const {
GGML_ASSERT(ggml_backend_buffer_is_host(dst->buffer));
@@ -2927,13 +2896,6 @@ void llama_kv_cache_context::set_input_pos_bucket(ggml_tensor * dst, const llama
kv->set_input_pos_bucket(dst, ubatch);
}
void llama_kv_cache_context::set_input_pos_rel_flat(
ggml_tensor * dst,
const llama_ubatch * ubatch,
uint32_t extent) const {
kv->set_input_pos_rel_flat(dst, ubatch, extent);
}
void llama_kv_cache_context::set_input_k_rot(ggml_tensor * dst) const {
kv->set_input_k_rot(dst);
}
-4
View File
@@ -215,8 +215,6 @@ public:
void set_input_kq_mask (ggml_tensor * dst, const llama_ubatch * ubatch, bool causal_attn) const;
void set_input_pos_bucket(ggml_tensor * dst, const llama_ubatch * ubatch) const;
void set_input_pos_rel_flat(
ggml_tensor * dst, const llama_ubatch * ubatch, uint32_t extent) const;
void set_input_k_rot(ggml_tensor * dst) const;
void set_input_v_rot(ggml_tensor * dst) const;
@@ -407,8 +405,6 @@ public:
void set_input_k_shift (ggml_tensor * dst) const;
void set_input_kq_mask (ggml_tensor * dst, const llama_ubatch * ubatch, bool causal_attn) const;
void set_input_pos_bucket(ggml_tensor * dst, const llama_ubatch * ubatch) const;
void set_input_pos_rel_flat(
ggml_tensor * dst, const llama_ubatch * ubatch, uint32_t extent) const;
void set_input_k_rot(ggml_tensor * dst) const;
void set_input_v_rot(ggml_tensor * dst) const;
-22
View File
@@ -9,7 +9,6 @@
#include <stdexcept>
#include <cerrno>
#include <algorithm>
#include <mutex>
#ifdef __has_include
#if __has_include(<unistd.h>)
@@ -67,12 +66,8 @@ static std::string llama_format_win_err(DWORD err) {
// llama_file
struct llama_file::impl {
bool no_cache = false;
#if defined(_WIN32)
HANDLE fp_win32;
std::mutex read_at_mutex;
std::string GetErrorMessageWin32(DWORD error_code) const {
std::string ret;
LPSTR lpMsgBuf = NULL;
@@ -438,10 +433,6 @@ void llama_file::read_raw_unsafe(void * ptr, size_t len) { pimpl->read_raw_unsaf
void llama_file::read_at(void * ptr, size_t len, size_t offset) {
#ifdef _WIN32
// The Windows FILE pointer is shared by all longhaul I/O workers. Keep the
// seek and read together so concurrent jobs cannot change each other's
// offsets. POSIX uses pread below and does not need this serialization.
std::lock_guard<std::mutex> lock(pimpl->read_at_mutex);
seek(offset, SEEK_SET);
read_raw(ptr, len);
#else
@@ -457,13 +448,6 @@ void llama_file::read_at(void * ptr, size_t len, size_t offset) {
}
n_read += result;
}
#if defined(__linux__) && defined(POSIX_FADV_DONTNEED)
if (pimpl->no_cache) {
// Longhaul owns a bounded expert cache, so avoid retaining a second
// long-lived copy of completed reads in the kernel page cache.
(void) posix_fadvise(file_id(), (off_t) offset, (off_t) len, POSIX_FADV_DONTNEED);
}
#endif
#endif
}
@@ -472,12 +456,6 @@ void llama_file::set_no_cache() const {
if (fcntl(file_id(), F_NOCACHE, 1) != 0) {
throw std::runtime_error(format("failed to disable file cache: %s", strerror(errno)));
}
#elif defined(__linux__) && defined(POSIX_FADV_RANDOM)
pimpl->no_cache = true;
const int err = posix_fadvise(file_id(), 0, 0, POSIX_FADV_RANDOM);
if (err != 0) {
LLAMA_LOG_WARN("%s: failed to set random-access file advice: %s\n", __func__, strerror(err));
}
#endif
}
-21
View File
@@ -1220,7 +1220,6 @@ struct ggml_tensor * llama_model_loader::create_tensor(
}
ggml_backend_buffer_type_t buft = nullptr;
ggml_backend_buffer_type_t requested_override = nullptr;
// check overrides
if (tensor_buft_overrides) {
@@ -1228,7 +1227,6 @@ struct ggml_tensor * llama_model_loader::create_tensor(
for (const auto * overrides = tensor_buft_overrides; overrides->pattern != nullptr; ++overrides) {
std::regex pattern(overrides->pattern);
if (std::regex_search(tensor_name, pattern)) {
requested_override = overrides->buft;
if (overrides->buft == ggml_backend_cpu_buffer_type()) {
// when overriding to a CPU buffer, consider the extra buffer types
buft = select_weight_buft(hparams, t_meta, op, buft_list_cpu);
@@ -1258,25 +1256,6 @@ struct ggml_tensor * llama_model_loader::create_tensor(
}
}
// Streamed CPU experts are updated one expert slice at a time. CPU
// repack and accelerator buffers only accept whole-tensor updates, so
// keep these cache tensors in the standard, directly writable layout.
if ((flags & TENSOR_LONGHAUL) && buft_list == buft_list_cpu) {
auto * cpu_dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU);
if (!cpu_dev) {
throw std::runtime_error("no CPU backend found");
}
ggml_backend_buffer_type_t cpu_buft = ggml_backend_dev_buffer_type(cpu_dev);
if (requested_override != nullptr &&
requested_override != ggml_backend_cpu_buffer_type() &&
requested_override != cpu_buft) {
throw std::runtime_error(format(
"longhaul CPU expert tensor '%s' has an incompatible buffer override",
tn.str().c_str()));
}
buft = cpu_buft;
}
// avoid using a host buffer when using mmap
auto * buft_dev = ggml_backend_buft_get_device(buft);
if (use_mmap && buft_dev && buft == ggml_backend_dev_host_buffer_type(buft_dev)) {
+2 -5
View File
@@ -29,7 +29,6 @@ bool llama_model_saver_supports_arch(llm_arch arch) {
case LLM_ARCH_STEP35:
case LLM_ARCH_MELLUM:
case LLM_ARCH_LAGUNA:
case LLM_ARCH_INKLING:
return false;
default:
return true;
@@ -121,7 +120,6 @@ void llama_model_saver::add_kv(const enum llm_kv key, const Container & value, c
}
// instantiate for external usage:
template void llama_model_saver::add_kv<std::vector<uint32_t>>(const enum llm_kv, const std::vector<uint32_t> &, const bool);
template void llama_model_saver::add_kv<std::vector<float>>(const enum llm_kv, const std::vector<float> &, const bool);
void llama_model_saver::add_kv(const enum llm_kv key, const std::vector<std::string> & value) {
std::vector<const char *> tmp(value.size());
@@ -216,8 +214,8 @@ void llama_model_saver::add_kv_from_model() {
add_kv(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp);
add_kv(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_shexp);
add_kv(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_chexp);
add_kv(LLM_KV_SWIGLU_CLAMP_EXP, hparams.swiglu_clamp_exp, true);
add_kv(LLM_KV_SWIGLU_CLAMP_SHEXP, hparams.swiglu_clamp_shexp, true);
add_kv(LLM_KV_SWIGLU_CLAMP_EXP, hparams.swiglu_clamp_exp);
add_kv(LLM_KV_SWIGLU_CLAMP_SHEXP, hparams.swiglu_clamp_shexp);
add_kv(LLM_KV_USE_PARALLEL_RESIDUAL, hparams.use_par_res);
// add_kv(LLM_KV_TENSOR_DATA_LAYOUT, ???);
add_kv(LLM_KV_EXPERT_COUNT, hparams.n_expert);
@@ -320,7 +318,6 @@ void llama_model_saver::add_kv_from_model() {
add_kv(LLM_KV_SSM_DT_B_C_RMS, hparams.ssm_dt_b_c_rms);
add_kv(LLM_KV_KDA_HEAD_DIM, hparams.n_embd_head_kda);
add_kv(LLM_KV_KDA_GATE_LOWER_BOUND, hparams.kda_gate_lower_bound);
add_kv(LLM_KV_WKV_HEAD_SIZE, hparams.wkv_head_size);
+13 -29
View File
@@ -308,14 +308,10 @@ static llama_model * llama_model_mapping(llm_arch arch, const llama_model_params
return new llama_model_dflash(params);
case LLM_ARCH_MIMO2:
return new llama_model_mimo2(params);
case LLM_ARCH_BAILINGMOE3:
return new llama_model_bailingmoe3(params);
case LLM_ARCH_KIMI_LINEAR:
return new llama_model_kimi_linear(params);
case LLM_ARCH_STEP35:
return new llama_model_step35(params);
case LLM_ARCH_INKLING:
return new llama_model_inkling(params);
default:
throw std::runtime_error(std::string("unsupported model architecture: '") + llm_arch_name(arch) + "'");
}
@@ -816,7 +812,6 @@ const char * llm_type_name(llm_type type) {
case LLM_TYPE_24B_A2B: return "24B.A2B";
case LLM_TYPE_26B_A4B: return "26B.A4B";
case LLM_TYPE_30B_A3B: return "30B.A3B";
case LLM_TYPE_124B_A5B: return "124B.A5B";
case LLM_TYPE_31B_A3_5B: return "31B.A3.5B";
case LLM_TYPE_35B_A3B: return "35B.A3B";
case LLM_TYPE_48B_A3B: return "48B.A3B";
@@ -1254,7 +1249,7 @@ void llama_model_base::load_hparams(llama_model_loader & ml) {
void llama_model_base::load_vocab(llama_model_loader & ml) {
const auto kv = LLM_KV(arch);
vocab.load(ml, kv);
vocab.load(ml, kv, params.tokenizer_longhaul, params.tokenizer_longhaul_cache_bytes);
}
bool llama_model_base::load_tensors(llama_model_loader & ml) {
@@ -1361,8 +1356,11 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) {
}
if (params.load_mode == LLAMA_LOAD_MODE_LONGHAUL) {
if (arch != LLM_ARCH_QWEN35MOE && arch != LLM_ARCH_LAGUNA && arch != LLM_ARCH_INKLING && arch != LLM_ARCH_BAILINGMOE3) {
throw std::runtime_error("longhaul currently requires a qwen35moe, laguna, inkling, or bailingmoe3 model");
#if !defined(__APPLE__)
throw std::runtime_error("longhaul is only supported on macOS");
#endif
if (arch != LLM_ARCH_QWEN35MOE && arch != LLM_ARCH_LAGUNA) {
throw std::runtime_error("longhaul currently requires a qwen35moe or laguna model");
}
if (hparams.n_layer_nextn != 0) {
throw std::runtime_error("longhaul does not support MTP tensors");
@@ -1370,27 +1368,13 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) {
if (params.check_tensors || params.no_alloc || params.vocab_only) {
throw std::runtime_error("longhaul does not support check-tensors, no-alloc, or vocab-only loading");
}
bool all_cpu = true;
bool all_metal = true;
for (int il = 0; il < n_layer_all; ++il) {
ggml_backend_dev_t dev = pimpl->dev_layer[il].dev;
const char * backend = ggml_backend_reg_name(ggml_backend_dev_backend_reg(dev));
all_cpu = all_cpu && ggml_backend_dev_type(dev) == GGML_BACKEND_DEVICE_TYPE_CPU;
all_metal = all_metal &&
ggml_backend_dev_type(dev) == GGML_BACKEND_DEVICE_TYPE_GPU &&
strcmp(backend, "MTL") == 0;
if (ggml_backend_dev_type(dev) != GGML_BACKEND_DEVICE_TYPE_GPU ||
strcmp(ggml_backend_reg_name(ggml_backend_dev_backend_reg(dev)), "MTL") != 0) {
throw std::runtime_error("longhaul requires every model layer on Metal");
}
}
if (!all_cpu && !all_metal) {
throw std::runtime_error(
"longhaul requires every repeating model layer on CPU or Metal; "
"use --n-gpu-layers 0 for CPU mode");
}
#if !defined(__APPLE__)
if (all_metal) {
throw std::runtime_error("longhaul Metal mode is only supported on macOS");
}
#endif
LLAMA_LOG_INFO("%s: longhaul using %s expert cache\n", __func__, all_cpu ? "CPU" : "Metal");
ml.configure_longhaul(params.longhaul_cache_bytes, n_expert, n_layer_all);
if (ml.longhaul_slots < (size_t) n_expert_used) {
LLAMA_LOG_WARN("%s: longhaul cache has %zu slots for %lld selected experts; "
@@ -2170,7 +2154,7 @@ llama_memory_i * llama_model::create_memory(const llama_memory_params & params,
// layer filters, so pick the right one here
llama_memory_hybrid::layer_filter_cb filter_attn = nullptr;
llama_memory_hybrid::layer_filter_cb filter_recr = nullptr;
if (arch == LLM_ARCH_FALCON_H1 || arch == LLM_ARCH_INKLING) {
if (arch == LLM_ARCH_FALCON_H1) {
filter_attn = [&](uint32_t) { return true; };
filter_recr = [&](uint32_t) { return true; };
} else if (arch == LLM_ARCH_NEMOTRON_H || arch == LLM_ARCH_NEMOTRON_H_MOE) {
@@ -2396,6 +2380,8 @@ llama_model_params llama_model_default_params() {
/*.no_host =*/ false,
/*.no_alloc =*/ false,
/*.longhaul_cache_bytes =*/ 0,
/*.tokenizer_longhaul =*/ false,
/*.tokenizer_longhaul_cache_bytes =*/ 0,
};
return result;
@@ -2511,7 +2497,6 @@ llama_rope_type llama_model_rope_type(const llama_model * model) {
case LLM_ARCH_NEMOTRON_H:
case LLM_ARCH_NEMOTRON_H_MOE:
case LLM_ARCH_KIMI_LINEAR:
case LLM_ARCH_INKLING:
return LLAMA_ROPE_TYPE_NONE;
// use what we call a normal RoPE, operating on pairs of consecutive head values
@@ -2541,7 +2526,6 @@ llama_rope_type llama_model_rope_type(const llama_model * model) {
case LLM_ARCH_GRANITE_HYBRID:
case LLM_ARCH_CHAMELEON:
case LLM_ARCH_BAILINGMOE:
case LLM_ARCH_BAILINGMOE3:
case LLM_ARCH_NEO_BERT:
case LLM_ARCH_SMOLLM3:
case LLM_ARCH_ARCEE:
-12
View File
@@ -124,7 +124,6 @@ enum llm_type {
LLM_TYPE_24B_A2B, // lfm2moe
LLM_TYPE_26B_A4B, // Gemma4
LLM_TYPE_30B_A3B,
LLM_TYPE_124B_A5B, // Ling 3.0 flash
LLM_TYPE_31B_A3_5B,
LLM_TYPE_35B_A3B, // Qwen3.5
LLM_TYPE_48B_A3B, // Kimi Linear
@@ -505,8 +504,6 @@ struct llama_layer {
struct ggml_tensor * ssm_q_conv = nullptr;
struct ggml_tensor * ssm_k_conv = nullptr;
struct ggml_tensor * ssm_v_conv = nullptr;
struct ggml_tensor * ssm_f = nullptr;
struct ggml_tensor * ssm_g = nullptr;
struct ggml_tensor * ssm_f_a = nullptr;
struct ggml_tensor * ssm_f_b = nullptr;
struct ggml_tensor * ssm_beta = nullptr;
@@ -537,15 +534,6 @@ struct llama_layer {
struct llama_layer_shortconv shortconv;
struct llama_layer_nextn nextn;
// Inkling relative attention and short-convolution weights.
struct ggml_tensor * wr = nullptr;
struct ggml_tensor * attn_rel_proj = nullptr;
struct ggml_tensor * shortconv_k = nullptr;
struct ggml_tensor * shortconv_v = nullptr;
struct ggml_tensor * shortconv_attn = nullptr;
struct ggml_tensor * shortconv_mlp = nullptr;
struct ggml_tensor * ffn_gscale = nullptr;
};
struct llama_device {
+77 -1
View File
@@ -214,6 +214,10 @@ public:
if (!ensure_open()) return params.capacity_bytes == 0;
std::lock_guard<std::mutex> db_lock(db_mutex);
if (sqlite3_exec(db, "DELETE FROM entries; PRAGMA incremental_vacuum;", nullptr, nullptr, nullptr) != SQLITE_OK) return db_error();
std::error_code ec;
fs::remove_all(fs::path(path).parent_path() / "tokenizer-indexes", ec);
if (ec) return false;
sqlite3_exec(db, "INSERT OR REPLACE INTO meta(key,value) VALUES('external_bytes',0)", nullptr, nullptr, nullptr);
return true;
}
@@ -239,6 +243,11 @@ public:
result.bytes_used = sqlite3_column_int64(stmt, 1);
}
sqlite3_finalize(stmt);
if (sqlite3_prepare_v2(db, "SELECT value FROM meta WHERE key='external_bytes'", -1, &stmt, nullptr) == SQLITE_OK &&
sqlite3_step(stmt) == SQLITE_ROW) {
result.bytes_used += static_cast<uint64_t>(sqlite3_column_int64(stmt, 0));
}
sqlite3_finalize(stmt);
}
return result;
}
@@ -247,6 +256,45 @@ public:
if (hit) ++stats.tokenizer_hits; else ++stats.tokenizer_misses;
}
std::string database_path() {
return ensure_open() ? path : std::string();
}
uint64_t capacity() const {
return params.capacity_bytes;
}
void set_external_bytes(uint64_t bytes) {
if (!ensure_open()) return;
std::lock_guard<std::mutex> db_lock(db_mutex);
sqlite3_exec(db, "BEGIN IMMEDIATE", nullptr, nullptr, nullptr);
sqlite3_stmt * stmt = nullptr;
sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO meta(key,value) VALUES('external_bytes',?1)", -1, &stmt, nullptr);
sqlite3_bind_int64(stmt, 1, static_cast<sqlite3_int64>(bytes));
sqlite3_step(stmt);
sqlite3_finalize(stmt);
uint64_t total = 0;
sqlite3_prepare_v2(db, "SELECT COALESCE(SUM(size),0) FROM entries", -1, &stmt, nullptr);
if (sqlite3_step(stmt) == SQLITE_ROW) total = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
const uint64_t entry_capacity = bytes >= params.capacity_bytes ? 0 : params.capacity_bytes - bytes;
while (total > entry_capacity) {
uint64_t victim_size = 0;
sqlite3_prepare_v2(db, "SELECT size FROM entries ORDER BY last_access LIMIT 1", -1, &stmt, nullptr);
if (sqlite3_step(stmt) == SQLITE_ROW) victim_size = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
if (victim_size == 0 || sqlite3_exec(db,
"DELETE FROM entries WHERE rowid=(SELECT rowid FROM entries ORDER BY last_access LIMIT 1)",
nullptr, nullptr, nullptr) != SQLITE_OK) {
break;
}
total -= std::min(total, victim_size);
++stats.evictions;
}
sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
}
private:
static int64_t now_tick() {
return std::chrono::duration_cast<std::chrono::microseconds>(
@@ -311,13 +359,22 @@ private:
sqlite3_bind_int64(stmt, 1, static_cast<sqlite3_int64>(params.capacity_bytes));
sqlite3_step(stmt);
sqlite3_finalize(stmt);
uint64_t external_bytes = 0;
sqlite3_prepare_v2(db, "SELECT value FROM meta WHERE key='external_bytes'", -1, &stmt, nullptr);
if (sqlite3_step(stmt) == SQLITE_ROW) {
external_bytes = static_cast<uint64_t>(sqlite3_column_int64(stmt, 0));
}
sqlite3_finalize(stmt);
const uint64_t entry_capacity = external_bytes >= params.capacity_bytes
? 0
: params.capacity_bytes - external_bytes;
sqlite3_exec(db, "BEGIN IMMEDIATE", nullptr, nullptr, nullptr);
while (true) {
sqlite3_prepare_v2(db, "SELECT COALESCE(SUM(size),0) FROM entries", -1, &stmt, nullptr);
uint64_t total = 0;
if (sqlite3_step(stmt) == SQLITE_ROW) total = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
if (total <= params.capacity_bytes) break;
if (total <= entry_capacity) break;
if (sqlite3_exec(db,
"DELETE FROM entries WHERE rowid=(SELECT rowid FROM entries ORDER BY last_access LIMIT 1)",
nullptr, nullptr, nullptr) != SQLITE_OK) {
@@ -355,12 +412,19 @@ private:
std::lock_guard<std::mutex> db_lock(db_mutex);
if (sqlite3_exec(db, "BEGIN IMMEDIATE", nullptr, nullptr, nullptr) != SQLITE_OK) { db_error(); return; }
uint64_t capacity = params.capacity_bytes;
uint64_t external_bytes = 0;
sqlite3_stmt * capacity_stmt = nullptr;
if (sqlite3_prepare_v2(db, "SELECT value FROM meta WHERE key='capacity'", -1, &capacity_stmt, nullptr) == SQLITE_OK &&
sqlite3_step(capacity_stmt) == SQLITE_ROW) {
capacity = static_cast<uint64_t>(sqlite3_column_int64(capacity_stmt, 0));
}
sqlite3_finalize(capacity_stmt);
if (sqlite3_prepare_v2(db, "SELECT value FROM meta WHERE key='external_bytes'", -1, &capacity_stmt, nullptr) == SQLITE_OK &&
sqlite3_step(capacity_stmt) == SQLITE_ROW) {
external_bytes = static_cast<uint64_t>(sqlite3_column_int64(capacity_stmt, 0));
}
sqlite3_finalize(capacity_stmt);
capacity = external_bytes >= capacity ? 0 : capacity - external_bytes;
if (item.payload.size() > capacity) {
sqlite3_exec(db, "ROLLBACK", nullptr, nullptr, nullptr);
return;
@@ -492,6 +556,18 @@ void llama_token_cache_note_tokenizer_hit(bool hit) {
cache().note_tokenizer(hit);
}
std::string llama_token_cache_database_path() {
return cache().database_path();
}
uint64_t llama_token_cache_capacity() {
return cache().capacity();
}
void llama_token_cache_set_external_bytes(uint64_t bytes) {
cache().set_external_bytes(bytes);
}
llama_token_cache_params llama_token_cache_default_params() {
return {nullptr, DEFAULT_CAPACITY};
}
+5
View File
@@ -27,3 +27,8 @@ void llama_token_cache_store(
void llama_token_cache_note_tokenizer_hit(bool hit);
// Internal helpers used by tokenizer-longhaul artifacts.
// Opening the cache here also fixes the configured path for the process.
std::string llama_token_cache_database_path();
uint64_t llama_token_cache_capacity();
void llama_token_cache_set_external_bytes(uint64_t bytes);
+471
View File
@@ -0,0 +1,471 @@
#include "llama-tokenizer-longhaul.h"
#include "llama-token-cache.h"
#include <sqlite3.h>
#include <algorithm>
#include <chrono>
#include <filesystem>
#include <list>
#include <mutex>
#include <thread>
#include <unordered_map>
#if defined(_WIN32)
# include <process.h>
#else
# include <unistd.h>
#endif
namespace fs = std::filesystem;
namespace {
constexpr int INDEX_VERSION = 1;
int process_id() {
#if defined(_WIN32)
return _getpid();
#else
return getpid();
#endif
}
bool exec_sql(sqlite3 * db, const char * sql, std::string & error) {
char * message = nullptr;
if (sqlite3_exec(db, sql, nullptr, nullptr, &message) == SQLITE_OK) {
return true;
}
error = message ? message : sqlite3_errmsg(db);
sqlite3_free(message);
return false;
}
struct lock_directory {
fs::path path;
bool owned = false;
~lock_directory() {
if (owned) {
std::error_code ec;
fs::remove(path, ec);
}
}
};
uint64_t tokenizer_artifact_bytes(const fs::path & directory) {
uint64_t total = 0;
std::error_code ec;
for (const auto & entry : fs::directory_iterator(directory, ec)) {
if (ec) break;
if (!entry.is_regular_file(ec) || entry.path().extension() != ".sqlite3") continue;
total += entry.file_size(ec);
if (ec) {
ec.clear();
}
}
return total;
}
} // namespace
struct llama_tokenizer_longhaul_index::impl {
struct cache_entry {
int value;
uint64_t bytes;
std::list<std::string>::iterator position;
};
std::string source_key;
uint64_t capacity;
fs::path artifact;
sqlite3 * db = nullptr;
std::string id;
uint64_t merge_count = 0;
mutable std::mutex mutex;
mutable uint64_t used = 0;
mutable std::list<std::string> lru;
mutable std::unordered_map<std::string, cache_entry> values;
impl(std::string source_key, uint64_t capacity)
: source_key(std::move(source_key)), capacity(capacity) {
}
~impl() {
if (db) {
sqlite3_close(db);
}
}
bool open_existing(std::string & error) {
if (!fs::exists(artifact)) {
return false;
}
sqlite3 * opened = nullptr;
if (sqlite3_open_v2(artifact.string().c_str(), &opened,
SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) {
error = opened ? sqlite3_errmsg(opened) : "failed to open tokenizer index";
if (opened) sqlite3_close(opened);
return false;
}
sqlite3_stmt * stmt = nullptr;
const char * sql =
"SELECT version, tokenizer_id, n_merges FROM metadata WHERE complete=1 LIMIT 1";
bool valid = sqlite3_prepare_v2(opened, sql, -1, &stmt, nullptr) == SQLITE_OK &&
sqlite3_step(stmt) == SQLITE_ROW &&
sqlite3_column_int(stmt, 0) == INDEX_VERSION;
if (valid) {
const unsigned char * value = sqlite3_column_text(stmt, 1);
id = value ? reinterpret_cast<const char *>(value) : "";
merge_count = static_cast<uint64_t>(sqlite3_column_int64(stmt, 2));
valid = !id.empty();
}
sqlite3_finalize(stmt);
if (!valid) {
error = "tokenizer index is incomplete or has an unsupported version";
sqlite3_close(opened);
return false;
}
db = opened;
sqlite3_busy_timeout(db, 5000);
return true;
}
bool build(
const std::vector<llama_tokenizer_longhaul_reverse> & reverse,
const std::vector<llama_tokenizer_longhaul_merge> & merges,
const std::string & tokenizer_id,
std::string & error) {
const fs::path temp = artifact.string() + ".tmp." + std::to_string(process_id());
std::error_code ec;
fs::remove(temp, ec);
sqlite3 * output = nullptr;
if (sqlite3_open_v2(temp.string().c_str(), &output,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE, nullptr) != SQLITE_OK) {
error = output ? sqlite3_errmsg(output) : "failed to create tokenizer index";
if (output) sqlite3_close(output);
return false;
}
auto fail = [&](const std::string & message) {
error = message;
sqlite3_close(output);
output = nullptr;
std::error_code remove_ec;
fs::remove(temp, remove_ec);
return false;
};
if (!exec_sql(output,
"PRAGMA journal_mode=OFF;"
"PRAGMA synchronous=OFF;"
"PRAGMA locking_mode=EXCLUSIVE;"
"CREATE TABLE metadata("
" version INTEGER NOT NULL,complete INTEGER NOT NULL,tokenizer_id TEXT NOT NULL,"
" n_reverse INTEGER NOT NULL,n_merges INTEGER NOT NULL);"
"CREATE TABLE reverse_tokens(text BLOB PRIMARY KEY,id INTEGER NOT NULL) WITHOUT ROWID;"
"CREATE TABLE merges(left_text BLOB NOT NULL,right_text BLOB NOT NULL,rank INTEGER NOT NULL,"
" PRIMARY KEY(left_text,right_text)) WITHOUT ROWID;"
"BEGIN IMMEDIATE;", error)) {
return fail(error);
}
sqlite3_stmt * insert_reverse = nullptr;
sqlite3_stmt * insert_merge = nullptr;
if (sqlite3_prepare_v2(output,
"INSERT INTO reverse_tokens(text,id) VALUES(?1,?2)", -1, &insert_reverse, nullptr) != SQLITE_OK ||
sqlite3_prepare_v2(output,
"INSERT INTO merges(left_text,right_text,rank) VALUES(?1,?2,?3)",
-1, &insert_merge, nullptr) != SQLITE_OK) {
if (insert_reverse) sqlite3_finalize(insert_reverse);
if (insert_merge) sqlite3_finalize(insert_merge);
return fail(sqlite3_errmsg(output));
}
for (const auto & item : reverse) {
sqlite3_bind_blob(insert_reverse, 1, item.text.data(), int(item.text.size()), SQLITE_STATIC);
sqlite3_bind_int(insert_reverse, 2, item.id);
if (sqlite3_step(insert_reverse) != SQLITE_DONE) {
const std::string message = sqlite3_errmsg(output);
sqlite3_finalize(insert_reverse);
sqlite3_finalize(insert_merge);
return fail(message);
}
sqlite3_reset(insert_reverse);
sqlite3_clear_bindings(insert_reverse);
}
for (const auto & item : merges) {
sqlite3_bind_blob(insert_merge, 1, item.left.data(), int(item.left.size()), SQLITE_STATIC);
sqlite3_bind_blob(insert_merge, 2, item.right.data(), int(item.right.size()), SQLITE_STATIC);
sqlite3_bind_int(insert_merge, 3, item.rank);
if (sqlite3_step(insert_merge) != SQLITE_DONE) {
const std::string message = sqlite3_errmsg(output);
sqlite3_finalize(insert_reverse);
sqlite3_finalize(insert_merge);
return fail(message);
}
sqlite3_reset(insert_merge);
sqlite3_clear_bindings(insert_merge);
}
sqlite3_finalize(insert_reverse);
sqlite3_finalize(insert_merge);
sqlite3_stmt * metadata = nullptr;
if (sqlite3_prepare_v2(output,
"INSERT INTO metadata VALUES(?1,1,?2,?3,?4)", -1, &metadata, nullptr) != SQLITE_OK) {
return fail(sqlite3_errmsg(output));
}
sqlite3_bind_int(metadata, 1, INDEX_VERSION);
sqlite3_bind_text(metadata, 2, tokenizer_id.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int64(metadata, 3, static_cast<sqlite3_int64>(reverse.size()));
sqlite3_bind_int64(metadata, 4, static_cast<sqlite3_int64>(merges.size()));
const bool metadata_ok = sqlite3_step(metadata) == SQLITE_DONE;
sqlite3_finalize(metadata);
if (!metadata_ok || !exec_sql(output, "COMMIT;", error)) {
return fail(metadata_ok ? error : sqlite3_errmsg(output));
}
sqlite3_close(output);
output = nullptr;
const uint64_t persistent_capacity = llama_token_cache_capacity();
const uint64_t artifact_size = fs::file_size(temp, ec);
if (ec || artifact_size > persistent_capacity) {
fs::remove(temp, ec);
error = "completed tokenizer index does not fit in --token-cache-size";
return false;
}
struct artifact_candidate {
fs::path path;
fs::file_time_type modified;
uint64_t size;
};
std::vector<artifact_candidate> candidates;
uint64_t existing_bytes = 0;
for (const auto & entry : fs::directory_iterator(artifact.parent_path(), ec)) {
if (ec) break;
if (!entry.is_regular_file(ec) || entry.path().extension() != ".sqlite3" ||
entry.path() == artifact || entry.path() == temp) {
continue;
}
const uint64_t size = entry.file_size(ec);
if (ec) {
ec.clear();
continue;
}
existing_bytes += size;
candidates.push_back({entry.path(), entry.last_write_time(ec), size});
if (ec) ec.clear();
}
std::sort(candidates.begin(), candidates.end(),
[](const artifact_candidate & a, const artifact_candidate & b) {
return a.modified < b.modified;
});
for (const auto & candidate : candidates) {
if (existing_bytes + artifact_size <= persistent_capacity) break;
fs::remove(candidate.path, ec);
if (!ec) existing_bytes -= std::min(existing_bytes, candidate.size);
ec.clear();
}
if (existing_bytes + artifact_size > persistent_capacity) {
fs::remove(temp, ec);
error = "tokenizer indexes do not fit in --token-cache-size";
return false;
}
fs::rename(temp, artifact, ec);
if (ec) {
const std::string rename_error = ec.message();
// A competing process may have published the same immutable artifact.
std::error_code remove_ec;
fs::remove(temp, remove_ec);
std::string ignored;
if (open_existing(ignored)) {
return true;
}
error = "failed to publish tokenizer index: " + rename_error;
return false;
}
const bool opened = open_existing(error);
if (opened) {
llama_token_cache_set_external_bytes(tokenizer_artifact_bytes(artifact.parent_path()));
}
return opened;
}
int lookup(const std::string & cache_key, const char * sql,
const std::string & first, const std::string * second, int missing) const {
std::lock_guard<std::mutex> lock(mutex);
auto found = values.find(cache_key);
if (found != values.end()) {
lru.splice(lru.begin(), lru, found->second.position);
return found->second.value;
}
sqlite3_stmt * stmt = nullptr;
int value = missing;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) {
sqlite3_bind_blob(stmt, 1, first.data(), int(first.size()), SQLITE_STATIC);
if (second) {
sqlite3_bind_blob(stmt, 2, second->data(), int(second->size()), SQLITE_STATIC);
}
if (sqlite3_step(stmt) == SQLITE_ROW) {
value = sqlite3_column_int(stmt, 0);
}
}
sqlite3_finalize(stmt);
const uint64_t bytes = cache_key.size() + sizeof(cache_entry) + sizeof(std::string);
if (bytes <= capacity) {
while (!lru.empty() && used + bytes > capacity) {
auto victim = values.find(lru.back());
if (victim != values.end()) {
used -= victim->second.bytes;
values.erase(victim);
}
lru.pop_back();
}
lru.push_front(cache_key);
values.emplace(cache_key, cache_entry{value, bytes, lru.begin()});
used += bytes;
}
return value;
}
};
llama_tokenizer_longhaul_index::llama_tokenizer_longhaul_index(
std::string source_key, uint64_t cache_bytes)
: pimpl(std::make_unique<impl>(std::move(source_key), cache_bytes)) {
}
llama_tokenizer_longhaul_index::~llama_tokenizer_longhaul_index() = default;
bool llama_tokenizer_longhaul_index::prepare(
const std::vector<llama_tokenizer_longhaul_reverse> & reverse,
const std::vector<llama_tokenizer_longhaul_merge> & merges,
const std::string & tokenizer_id,
std::string & error) {
const std::string cache_db = llama_token_cache_database_path();
if (cache_db.empty()) {
error = "persistent token cache is disabled or unavailable";
return false;
}
const fs::path directory = fs::path(cache_db).parent_path() / "tokenizer-indexes";
std::error_code ec;
fs::create_directories(directory, ec);
if (ec) {
error = "failed to create tokenizer index directory: " + ec.message();
return false;
}
pimpl->artifact = directory / (llama_token_cache_hash(pimpl->source_key) + ".sqlite3");
if (pimpl->open_existing(error)) {
if (pimpl->id == tokenizer_id) {
llama_token_cache_set_external_bytes(tokenizer_artifact_bytes(directory));
return true;
}
sqlite3_close(pimpl->db);
pimpl->db = nullptr;
std::error_code stale_ec;
fs::remove(pimpl->artifact, stale_ec);
}
lock_directory lock{pimpl->artifact.string() + ".lock", false};
lock.owned = fs::create_directory(lock.path, ec);
if (!lock.owned) {
// Another process is building. Wait for its atomic publication.
for (int i = 0; i < 24000; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(25));
std::string ignored;
if (pimpl->open_existing(ignored)) {
if (pimpl->id == tokenizer_id) {
llama_token_cache_set_external_bytes(tokenizer_artifact_bytes(directory));
return true;
}
sqlite3_close(pimpl->db);
pimpl->db = nullptr;
}
if (!fs::exists(lock.path)) {
lock.owned = fs::create_directory(lock.path, ec);
if (lock.owned) break;
}
}
if (!lock.owned) {
error = "timed out waiting for another tokenizer index builder";
return false;
}
}
error.clear();
if (pimpl->open_existing(error)) {
if (pimpl->id == tokenizer_id) {
llama_token_cache_set_external_bytes(tokenizer_artifact_bytes(directory));
return true;
}
sqlite3_close(pimpl->db);
pimpl->db = nullptr;
}
std::error_code remove_ec;
fs::remove(pimpl->artifact, remove_ec);
return pimpl->build(reverse, merges, tokenizer_id, error);
}
llama_token llama_tokenizer_longhaul_index::text_to_token(const std::string & text) const {
const std::string key = "t" + text;
return pimpl->lookup(key,
"SELECT id FROM reverse_tokens WHERE text=?1", text, nullptr, LLAMA_TOKEN_NULL);
}
int llama_tokenizer_longhaul_index::find_bpe_rank(
const std::string & left, const std::string & right) const {
std::string key;
key.reserve(left.size() + right.size() + 2);
key.push_back('m');
key += left;
key.push_back('\0');
key += right;
return pimpl->lookup(key,
"SELECT rank FROM merges WHERE left_text=?1 AND right_text=?2", left, &right, -1);
}
std::vector<std::string> llama_tokenizer_longhaul_index::get_bpe_merges() const {
std::lock_guard<std::mutex> lock(pimpl->mutex);
std::vector<std::string> result;
result.reserve(pimpl->merge_count);
sqlite3_stmt * stmt = nullptr;
if (sqlite3_prepare_v2(pimpl->db,
"SELECT left_text,right_text FROM merges ORDER BY rank", -1, &stmt, nullptr) != SQLITE_OK) {
return result;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
const char * left = static_cast<const char *>(sqlite3_column_blob(stmt, 0));
const int left_size = sqlite3_column_bytes(stmt, 0);
const char * right = static_cast<const char *>(sqlite3_column_blob(stmt, 1));
const int right_size = sqlite3_column_bytes(stmt, 1);
std::string item(left, left_size);
item.push_back(' ');
item.append(right, right_size);
result.push_back(std::move(item));
}
sqlite3_finalize(stmt);
return result;
}
uint64_t llama_tokenizer_longhaul_index::cache_bytes_used() const {
std::lock_guard<std::mutex> lock(pimpl->mutex);
return pimpl->used;
}
uint64_t llama_tokenizer_longhaul_index::cache_capacity() const {
return pimpl->capacity;
}
uint64_t llama_tokenizer_longhaul_index::n_merges() const {
return pimpl->merge_count;
}
const std::string & llama_tokenizer_longhaul_index::tokenizer_id() const {
return pimpl->id;
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "llama.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
struct llama_tokenizer_longhaul_reverse {
std::string text;
llama_token id;
};
struct llama_tokenizer_longhaul_merge {
std::string left;
std::string right;
int rank;
};
class llama_tokenizer_longhaul_index {
public:
llama_tokenizer_longhaul_index(std::string source_key, uint64_t cache_bytes);
~llama_tokenizer_longhaul_index();
llama_tokenizer_longhaul_index(const llama_tokenizer_longhaul_index &) = delete;
llama_tokenizer_longhaul_index & operator=(const llama_tokenizer_longhaul_index &) = delete;
bool prepare(
const std::vector<llama_tokenizer_longhaul_reverse> & reverse,
const std::vector<llama_tokenizer_longhaul_merge> & merges,
const std::string & tokenizer_id,
std::string & error);
llama_token text_to_token(const std::string & text) const;
int find_bpe_rank(const std::string & left, const std::string & right) const;
std::vector<std::string> get_bpe_merges() const;
uint64_t cache_bytes_used() const;
uint64_t cache_capacity() const;
uint64_t n_merges() const;
const std::string & tokenizer_id() const;
private:
struct impl;
std::unique_ptr<impl> pimpl;
};
+194 -33
View File
@@ -1,5 +1,6 @@
#include "llama-vocab.h"
#include "llama-token-cache.h"
#include "llama-tokenizer-longhaul.h"
#include "ggml.h"
#include "gguf.h"
@@ -13,13 +14,16 @@
#include <cctype>
#include <cfloat>
#include <cmath>
#include <condition_variable>
#include <cstdarg>
#include <cstring>
#include <forward_list>
#include <limits>
#include <map>
#include <mutex>
#include <queue>
#include <set>
#include <thread>
#include <unordered_map>
//
@@ -434,13 +438,6 @@ struct llm_tokenizer_bpe : llm_tokenizer {
"[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))*((?=[\\p{L}])([^A-Z]))+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))+((?=[\\p{L}])([^A-Z]))*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
};
break;
case LLAMA_VOCAB_PRE_TYPE_INKLING:
// o200k-family expression with combining marks included in
// letter lookaheads, as used by the Inkling tokenizer.
regex_exprs = {
"[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))*((?=[\\p{L}\\p{M}])([^A-Z]))+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))+((?=[\\p{L}\\p{M}])([^A-Z]))*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
};
break;
case LLAMA_VOCAB_PRE_TYPE_GRANITE_EMB_MULTI:
// Same lookaheads as GPT4O but with \p{M} added so combining marks
// (diacritics) attach to their base letters. Avoids excessive
@@ -1853,12 +1850,37 @@ struct llama_vocab::impl {
std::vector<char> precompiled_charsmap;
enum class longhaul_state {
disabled,
building,
ready,
failed,
};
bool tokenizer_longhaul = false;
uint64_t tokenizer_longhaul_cache_bytes = 0;
uint64_t tokenizer_longhaul_n_merges = 0;
mutable std::mutex tokenizer_longhaul_mutex;
mutable std::condition_variable tokenizer_longhaul_ready;
mutable longhaul_state tokenizer_longhaul_state = longhaul_state::disabled;
mutable std::string tokenizer_longhaul_error;
mutable std::unique_ptr<llama_tokenizer_longhaul_index> tokenizer_longhaul_index;
std::thread tokenizer_longhaul_worker;
impl(const llama_vocab & vocab) : vocab(vocab) {
}
~impl() = default;
~impl() {
if (tokenizer_longhaul_worker.joinable()) {
tokenizer_longhaul_worker.join();
}
}
void load(llama_model_loader & ml, const LLM_KV & kv);
void load(
llama_model_loader & ml,
const LLM_KV & kv,
bool tokenizer_longhaul,
uint64_t tokenizer_longhaul_cache_bytes);
void wait_for_tokenizer_longhaul() const;
bool load_cache_snapshot(const std::vector<uint8_t> & snapshot);
void reset_cache_snapshot_state();
@@ -1915,7 +1937,7 @@ struct llama_vocab::impl {
bool special) const;
// use cached data
const std::string & token_to_piece(llama_token token) const;
std::string token_to_piece(llama_token token) const;
int32_t detokenize(
const llama_token * tokens,
@@ -1935,11 +1957,29 @@ private:
const llama_vocab & vocab;
};
void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
void llama_vocab::impl::load(
llama_model_loader & ml,
const LLM_KV & kv,
bool tokenizer_longhaul_value,
uint64_t tokenizer_longhaul_cache_bytes_value) {
struct gguf_context * ctx = ml.metadata;
tokenizer_longhaul = tokenizer_longhaul_value;
tokenizer_longhaul_cache_bytes = tokenizer_longhaul_cache_bytes_value;
if (tokenizer_longhaul) {
if (tokenizer_longhaul_cache_bytes == 0) {
throw std::runtime_error("tokenizer-longhaul cache budget must be positive");
}
if (ml.get_arch() != LLM_ARCH_QWEN35MOE && ml.get_arch() != LLM_ARCH_LAGUNA) {
throw std::runtime_error("tokenizer-longhaul currently requires a qwen35moe or laguna model");
}
if (ml.get_token_cache_source_key().empty()) {
throw std::runtime_error("tokenizer-longhaul requires a file-backed GGUF model");
}
}
const std::string & source_cache_key = ml.get_token_cache_source_key();
if (!source_cache_key.empty()) {
if (!tokenizer_longhaul && !source_cache_key.empty()) {
std::vector<uint8_t> snapshot;
bool found = llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, source_cache_key, snapshot);
if (found && snapshot.size() == 65 && snapshot[0] == 'A') {
@@ -1965,6 +2005,9 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
ml.get_key(LLM_KV_TOKENIZER_TOKEN_TYPE_COUNT, n_token_types, false);
if (tokenizer_model == "no_vocab" || tokenizer_model == "none") {
if (tokenizer_longhaul) {
throw std::runtime_error("tokenizer-longhaul requires a BPE tokenizer");
}
type = LLAMA_VOCAB_TYPE_NONE;
// default special tokens
@@ -2331,10 +2374,6 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
tokenizer_pre == "talkie") {
pre_type = LLAMA_VOCAB_PRE_TYPE_GPT4O;
clean_spaces = false;
} else if (
tokenizer_pre == "inkling") {
pre_type = LLAMA_VOCAB_PRE_TYPE_INKLING;
clean_spaces = false;
} else if (
tokenizer_pre == "granite-embed-multi-97m") {
pre_type = LLAMA_VOCAB_PRE_TYPE_GRANITE_EMB_MULTI;
@@ -2618,12 +2657,7 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
if (suppress_idx != -1) {
const int n = gguf_get_arr_n(ctx, suppress_idx);
const int32_t * data = (const int32_t *) gguf_get_arr_data(ctx, suppress_idx);
suppress_tokens.reserve(n);
for (int i = 0; i < n; ++i) {
if (data[i] >= 0 && data[i] < (int32_t) id_to_token.size()) {
suppress_tokens.push_back(data[i]);
}
}
suppress_tokens.assign(data, data + n);
}
}
@@ -3079,6 +3113,69 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
}
}
if (tokenizer_longhaul) {
if (type != LLAMA_VOCAB_TYPE_BPE ||
(tokenizer_pre != "qwen35" && tokenizer_pre != "laguna")) {
throw std::runtime_error(
"tokenizer-longhaul currently supports only qwen35 and laguna BPE tokenizers");
}
std::vector<llama_tokenizer_longhaul_reverse> reverse;
reverse.reserve(token_to_id.size());
while (!token_to_id.empty()) {
auto node = token_to_id.extract(token_to_id.begin());
reverse.push_back({std::move(node.key()), node.mapped()});
}
token_to_id.rehash(0);
std::vector<llama_tokenizer_longhaul_merge> merges;
merges.reserve(bpe_ranks.size());
while (!bpe_ranks.empty()) {
auto node = bpe_ranks.extract(bpe_ranks.begin());
auto key = std::move(node.key());
merges.push_back({std::move(key.first), std::move(key.second), node.mapped()});
}
bpe_ranks.rehash(0);
tokenizer_longhaul_n_merges = merges.size();
cache_token_to_piece.clear();
cache_token_to_piece.shrink_to_fit();
token_cache_id = llama_token_cache_hash(
std::string("tokenizer-longhaul-v1\n") + source_cache_key);
{
std::lock_guard<std::mutex> lock(tokenizer_longhaul_mutex);
tokenizer_longhaul_state = longhaul_state::building;
}
const uint64_t cache_bytes = tokenizer_longhaul_cache_bytes;
const std::string source_key = source_cache_key;
const std::string tokenizer_id = token_cache_id;
tokenizer_longhaul_worker = std::thread(
[this, source_key, cache_bytes, tokenizer_id,
reverse = std::move(reverse), merges = std::move(merges)]() mutable {
auto index = std::make_unique<llama_tokenizer_longhaul_index>(source_key, cache_bytes);
std::string error;
const bool ok = index->prepare(reverse, merges, tokenizer_id, error);
{
std::lock_guard<std::mutex> lock(tokenizer_longhaul_mutex);
if (ok) {
tokenizer_longhaul_index = std::move(index);
tokenizer_longhaul_state = longhaul_state::ready;
LLAMA_LOG_INFO("%s: tokenizer-longhaul index is ready\n", "llama_vocab");
} else {
tokenizer_longhaul_error = error.empty()
? "unknown tokenizer-longhaul build error"
: std::move(error);
tokenizer_longhaul_state = longhaul_state::failed;
LLAMA_LOG_ERROR("%s: tokenizer-longhaul index failed: %s\n",
"llama_vocab", tokenizer_longhaul_error.c_str());
}
}
tokenizer_longhaul_ready.notify_all();
});
LLAMA_LOG_INFO("%s: tokenizer-longhaul index is building in the background\n", __func__);
return;
}
// Persist a versioned canonical snapshot of the effective tokenizer state.
// The snapshot also serves as the namespace for text-to-token cache entries.
{
@@ -3167,6 +3264,23 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
}
}
void llama_vocab::impl::wait_for_tokenizer_longhaul() const {
if (!tokenizer_longhaul) {
return;
}
std::unique_lock<std::mutex> lock(tokenizer_longhaul_mutex);
if (tokenizer_longhaul_state == longhaul_state::disabled) {
return;
}
tokenizer_longhaul_ready.wait(lock, [this] {
return tokenizer_longhaul_state == longhaul_state::ready ||
tokenizer_longhaul_state == longhaul_state::failed;
});
if (tokenizer_longhaul_state == longhaul_state::failed) {
throw std::runtime_error("tokenizer-longhaul failed: " + tokenizer_longhaul_error);
}
}
void llama_vocab::impl::reset_cache_snapshot_state() {
n_token_types = 0;
tokenizer_model.clear();
@@ -3298,36 +3412,43 @@ std::string llama_vocab::impl::type_name() const{
}
bool llama_vocab::impl::is_normal(llama_token id) const {
wait_for_tokenizer_longhaul();
GGML_ASSERT(type != LLAMA_VOCAB_TYPE_NONE);
return id_to_token[id].attr & LLAMA_TOKEN_ATTR_NORMAL;
}
bool llama_vocab::impl::is_unknown(llama_token id) const {
wait_for_tokenizer_longhaul();
GGML_ASSERT(type != LLAMA_VOCAB_TYPE_NONE);
return id_to_token[id].attr & LLAMA_TOKEN_ATTR_UNKNOWN;
}
bool llama_vocab::impl::is_control(llama_token id) const {
wait_for_tokenizer_longhaul();
GGML_ASSERT(type != LLAMA_VOCAB_TYPE_NONE);
return id_to_token[id].attr & LLAMA_TOKEN_ATTR_CONTROL;
}
bool llama_vocab::impl::is_byte(llama_token id) const {
wait_for_tokenizer_longhaul();
GGML_ASSERT(type != LLAMA_VOCAB_TYPE_NONE);
return id_to_token[id].attr & LLAMA_TOKEN_ATTR_BYTE;
}
bool llama_vocab::impl::is_user_defined(llama_token id) const {
wait_for_tokenizer_longhaul();
GGML_ASSERT(type != LLAMA_VOCAB_TYPE_NONE);
return id_to_token[id].attr & LLAMA_TOKEN_ATTR_USER_DEFINED;
}
bool llama_vocab::impl::is_unused(llama_token id) const {
wait_for_tokenizer_longhaul();
GGML_ASSERT(type != LLAMA_VOCAB_TYPE_NONE);
return id_to_token[id].attr & LLAMA_TOKEN_ATTR_UNUSED;
}
bool llama_vocab::impl::is_eog(llama_token id) const {
wait_for_tokenizer_longhaul();
return id != LLAMA_TOKEN_NULL && special_eog_ids.count(id) > 0;
}
@@ -3355,6 +3476,7 @@ uint8_t llama_vocab::impl::token_to_byte(llama_token id) const {
}
llama_token_attr llama_vocab::impl::token_get_attr(llama_token id) const {
wait_for_tokenizer_longhaul();
GGML_ASSERT(type != LLAMA_VOCAB_TYPE_NONE);
return id_to_token.at(id).attr;
}
@@ -3765,6 +3887,7 @@ std::vector<llama_token> llama_vocab::impl::tokenize(
const std::string & raw_text,
bool add_special,
bool parse_special) const {
wait_for_tokenizer_longhaul();
if (raw_text.empty() || token_cache_id.empty()) {
return tokenize_uncached(raw_text, add_special, parse_special);
}
@@ -3808,6 +3931,7 @@ std::vector<llama_token> llama_vocab::impl::tokenize(
}
int32_t llama_vocab::impl::token_to_piece(llama_token token, char * buf, int32_t length, int32_t lstrip, bool special) const {
wait_for_tokenizer_longhaul();
// ref: https://github.com/ggml-org/llama.cpp/pull/7587#discussion_r1620983843
static const int attr_special = LLAMA_TOKEN_ATTR_UNKNOWN | LLAMA_TOKEN_ATTR_CONTROL;
const llama_token_attr attr = token_get_attr(token);
@@ -3924,7 +4048,11 @@ int32_t llama_vocab::impl::token_to_piece(llama_token token, char * buf, int32_t
return 0;
}
const std::string & llama_vocab::impl::token_to_piece(llama_token token) const {
std::string llama_vocab::impl::token_to_piece(llama_token token) const {
wait_for_tokenizer_longhaul();
if (tokenizer_longhaul) {
return token_to_piece_for_cache(token, true);
}
return cache_token_to_piece.at(token);
}
@@ -4043,7 +4171,15 @@ int32_t llama_vocab::impl::detokenize(
void llama_vocab::impl::print_info() const {
LLAMA_LOG_INFO("%s: vocab type = %s\n", __func__, type_name().c_str());
LLAMA_LOG_INFO("%s: n_vocab = %u\n", __func__, vocab.n_tokens());
LLAMA_LOG_INFO("%s: n_merges = %u\n", __func__, (uint32_t) bpe_ranks.size());
LLAMA_LOG_INFO("%s: n_merges = %u\n", __func__,
(uint32_t) (tokenizer_longhaul ? tokenizer_longhaul_n_merges : bpe_ranks.size()));
if (tokenizer_longhaul) {
std::lock_guard<std::mutex> lock(tokenizer_longhaul_mutex);
LLAMA_LOG_INFO("%s: tokenizer longhaul = %s, cache = %.2f MiB\n", __func__,
tokenizer_longhaul_state == longhaul_state::ready ? "ready" :
tokenizer_longhaul_state == longhaul_state::failed ? "failed" : "building",
tokenizer_longhaul_cache_bytes / 1024.0 / 1024.0);
}
// special tokens
if (special_bos_id != LLAMA_TOKEN_NULL) { LLAMA_LOG_INFO( "%s: BOS token = %d '%s'\n", __func__, special_bos_id, id_to_token.at(special_bos_id).text.c_str() ); }
@@ -4076,8 +4212,12 @@ llama_vocab::llama_vocab() : pimpl(new impl(*this)) {
llama_vocab::~llama_vocab() = default;
void llama_vocab::load(llama_model_loader & ml, const LLM_KV & kv) {
pimpl->load(ml, kv);
void llama_vocab::load(
llama_model_loader & ml,
const LLM_KV & kv,
bool tokenizer_longhaul,
uint64_t tokenizer_longhaul_cache_bytes) {
pimpl->load(ml, kv, tokenizer_longhaul, tokenizer_longhaul_cache_bytes);
}
std::string llama_vocab::get_tokenizer_model() const {
@@ -4147,23 +4287,29 @@ llama_token llama_vocab::byte_to_token(uint8_t ch) const {
case LLAMA_VOCAB_TYPE_SPM:
case LLAMA_VOCAB_TYPE_UGM: {
const char buf[7] = { '<', '0', 'x', hex[ch >> 4], hex[ch & 15], '>', 0 };
auto token = pimpl->token_to_id.find(buf);
if (token != pimpl->token_to_id.end()) {
return (*token).second;
const auto token = text_to_token(buf);
if (token != LLAMA_TOKEN_NULL) {
return token;
}
// Try to fall back to just the byte as a string
const char buf2[2] = { (char)ch, 0 };
return pimpl->token_to_id.at(buf2);
const auto fallback = text_to_token(buf2);
if (fallback == LLAMA_TOKEN_NULL) throw std::out_of_range("byte token not found");
return fallback;
}
case LLAMA_VOCAB_TYPE_WPM:
case LLAMA_VOCAB_TYPE_BPE: {
return pimpl->token_to_id.at(unicode_byte_to_utf8(ch));
const auto token = text_to_token(unicode_byte_to_utf8(ch));
if (token == LLAMA_TOKEN_NULL) throw std::out_of_range("byte token not found");
return token;
}
case LLAMA_VOCAB_TYPE_PLAMO2: {
// PLaMo-2 uses byte tokens in format <0xXX>
char hex_str[8];
snprintf(hex_str, sizeof(hex_str), "<0x%02X>", ch);
return pimpl->token_to_id.at(hex_str);
const auto token = text_to_token(hex_str);
if (token == LLAMA_TOKEN_NULL) throw std::out_of_range("byte token not found");
return token;
}
default:
GGML_ABORT("fatal error");
@@ -4172,6 +4318,10 @@ llama_token llama_vocab::byte_to_token(uint8_t ch) const {
llama_token llama_vocab::text_to_token(const std::string & text) const {
GGML_ASSERT(pimpl->type != LLAMA_VOCAB_TYPE_NONE);
pimpl->wait_for_tokenizer_longhaul();
if (pimpl->tokenizer_longhaul_index) {
return pimpl->tokenizer_longhaul_index->text_to_token(text);
}
auto it = pimpl->token_to_id.find(text);
if (it != pimpl->token_to_id.end()) {
return (*it).second;
@@ -4181,16 +4331,19 @@ llama_token llama_vocab::text_to_token(const std::string & text) const {
const llama_vocab::token_data & llama_vocab::get_token_data(llama_token id) const {
GGML_ASSERT(pimpl->type != LLAMA_VOCAB_TYPE_NONE);
pimpl->wait_for_tokenizer_longhaul();
return pimpl->id_to_token.at(id);
}
const char * llama_vocab::token_get_text(llama_token id) const {
GGML_ASSERT(pimpl->type != LLAMA_VOCAB_TYPE_NONE);
pimpl->wait_for_tokenizer_longhaul();
return pimpl->id_to_token.at(id).text.c_str();
}
float llama_vocab::token_get_score(llama_token id) const {
GGML_ASSERT(pimpl->type != LLAMA_VOCAB_TYPE_NONE);
pimpl->wait_for_tokenizer_longhaul();
return pimpl->id_to_token.at(id).score;
}
@@ -4322,6 +4475,10 @@ int llama_vocab::find_bpe_rank(const std::string & token_left, const std::string
GGML_ASSERT(token_left.find(' ') == std::string::npos);
GGML_ASSERT(token_right.find(' ') == std::string::npos);
pimpl->wait_for_tokenizer_longhaul();
if (pimpl->tokenizer_longhaul_index) {
return pimpl->tokenizer_longhaul_index->find_bpe_rank(token_left, token_right);
}
auto it = pimpl->bpe_ranks.find(std::make_pair(token_left, token_right));
if (it == pimpl->bpe_ranks.end()) {
return -1;
@@ -4331,6 +4488,10 @@ int llama_vocab::find_bpe_rank(const std::string & token_left, const std::string
}
std::vector<std::string> llama_vocab::get_bpe_merges() const {
pimpl->wait_for_tokenizer_longhaul();
if (pimpl->tokenizer_longhaul_index) {
return pimpl->tokenizer_longhaul_index->get_bpe_merges();
}
int max_rank = -1;
for (const auto & pair : pimpl->bpe_ranks) {
max_rank = std::max(max_rank, pair.second);
@@ -4380,7 +4541,7 @@ std::vector<llama_token> llama_vocab::tokenize(
return pimpl->tokenize(raw_text, add_special, parse_special);
}
const std::string & llama_vocab::token_to_piece(llama_token token) const {
std::string llama_vocab::token_to_piece(llama_token token) const {
return pimpl->token_to_piece(token);
}
+6 -3
View File
@@ -65,7 +65,6 @@ enum llama_vocab_pre_type {
LLAMA_VOCAB_PRE_TYPE_GRANITE_EMB_MULTI = 54,
LLAMA_VOCAB_PRE_TYPE_MELLUM2 = 55,
LLAMA_VOCAB_PRE_TYPE_LAGUNA = 56,
LLAMA_VOCAB_PRE_TYPE_INKLING = 57,
};
struct LLM_KV;
@@ -87,7 +86,11 @@ struct llama_vocab {
llama_vocab();
~llama_vocab();
void load(llama_model_loader & ml, const LLM_KV & kv);
void load(
llama_model_loader & ml,
const LLM_KV & kv,
bool tokenizer_longhaul = false,
uint64_t tokenizer_longhaul_cache_bytes = 0);
std::string get_tokenizer_model() const;
std::string get_tokenizer_pre() const;
@@ -182,7 +185,7 @@ struct llama_vocab {
bool special) const;
// use cached data
const std::string & token_to_piece(llama_token token) const;
std::string token_to_piece(llama_token token) const;
int32_t detokenize(
const llama_token * tokens,
-461
View File
@@ -1,461 +0,0 @@
#include "models.h"
#include "llama-memory-recurrent.h"
// Ling 3.0 (BailingMoeV3): hybrid linear architecture, 5 KDA layers per 1 gated MLA layer,
// on top of a bailing MoE (sigmoid router with expert bias and group-limited routing).
//
// Differences to Kimi-Linear, which shares the KDA + MLA layout:
// - KDA forget/output gates are full-rank projections (config: no_kda_lora = true)
// - the KDA gate uses the safe-gate form (config: kda_safe_gate, kda_lower_bound):
// lower_bound * sigmoid(exp(A_log) * (f_proj(x) + dt_bias)) instead of
// -exp(A_log) * softplus(f_proj(x) + dt_bias)
// - MLA layers do use RoPE (partial, interleaved -> ggml NORM) and carry a head-wise
// sigmoid output gate applied before the output projection
void llama_model_bailingmoe3::load_arch_hparams(llama_model_loader & ml) {
ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps);
ml.get_key(LLM_KV_ATTENTION_KEY_LENGTH_MLA, hparams.n_embd_head_k_mla_impl);
ml.get_key(LLM_KV_ATTENTION_VALUE_LENGTH_MLA, hparams.n_embd_head_v_mla_impl);
ml.get_key(LLM_KV_ATTENTION_KV_LORA_RANK, hparams.n_lora_kv);
ml.get_key(LLM_KV_SSM_CONV_KERNEL, hparams.ssm_d_conv);
ml.get_key(LLM_KV_KDA_HEAD_DIM, hparams.n_embd_head_kda);
ml.get_key(LLM_KV_KDA_GATE_LOWER_BOUND, hparams.kda_gate_lower_bound);
// n_head_kv == 0 marks the KDA (recurrent) layers, the rest are full attention
for (uint32_t i = 0; i < hparams.n_layer(); ++i) {
hparams.is_recr_impl[i] = hparams.n_head_kv(i) == 0;
}
ml.get_key(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp);
ml.get_key(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_shexp, false);
ml.get_key(LLM_KV_EXPERT_SHARED_COUNT, hparams.n_expert_shared);
ml.get_key(LLM_KV_LEADING_DENSE_BLOCK_COUNT, hparams.n_layer_dense_lead, false);
ml.get_key(LLM_KV_EXPERT_WEIGHTS_SCALE, hparams.expert_weights_scale, false);
ml.get_key(LLM_KV_EXPERT_WEIGHTS_NORM, hparams.expert_weights_norm, false);
ml.get_key(LLM_KV_EXPERT_GATING_FUNC, hparams.expert_gating_func);
ml.get_key(LLM_KV_EXPERT_GROUP_COUNT, hparams.n_expert_groups, false);
ml.get_key(LLM_KV_EXPERT_GROUP_USED_COUNT, hparams.n_group_used, false);
// trained per-layer SwiGLU clamps (config: expert_swiglu_limit_list / share_expert_swiglu_limit_list)
ml.get_key_or_arr(LLM_KV_SWIGLU_CLAMP_EXP, hparams.swiglu_clamp_exp, hparams.n_layer_all, false);
ml.get_key_or_arr(LLM_KV_SWIGLU_CLAMP_SHEXP, hparams.swiglu_clamp_shexp, hparams.n_layer_all, false);
switch (hparams.n_layer()) {
case 42: type = LLM_TYPE_124B_A5B; break; // Ling-3.0-flash
default: type = LLM_TYPE_UNKNOWN;
}
}
void llama_model_bailingmoe3::load_arch_tensors(llama_model_loader &) {
LLAMA_LOAD_LOCALS;
tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0);
output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0);
output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, 0);
const int64_t head_dim_kda = hparams.n_embd_head_kda;
const int64_t d_inner = head_dim_kda * n_head;
const int64_t ssm_d_conv = hparams.ssm_d_conv;
const int expert_flags = params.load_mode == LLAMA_LOAD_MODE_LONGHAUL ? TENSOR_LONGHAUL : 0;
for (int i = 0; i < n_layer; ++i) {
auto & layer = layers[i];
layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
if (hparams.is_recr(i)) {
// === KDA (linear attention) layer ===
// conv1d weights are 4D in GGUF, quantization may drop the trailing 1
layer.ssm_q_conv = create_tensor(tn(LLM_TENSOR_SSM_CONV1D_Q, "weight", i), {ssm_d_conv, 1, d_inner, 1}, TENSOR_NOT_REQUIRED);
if (!layer.ssm_q_conv) {
layer.ssm_q_conv = create_tensor(tn(LLM_TENSOR_SSM_CONV1D_Q, "weight", i), {ssm_d_conv, 1, d_inner}, 0);
}
layer.ssm_k_conv = create_tensor(tn(LLM_TENSOR_SSM_CONV1D_K, "weight", i), {ssm_d_conv, 1, d_inner, 1}, TENSOR_NOT_REQUIRED);
if (!layer.ssm_k_conv) {
layer.ssm_k_conv = create_tensor(tn(LLM_TENSOR_SSM_CONV1D_K, "weight", i), {ssm_d_conv, 1, d_inner}, 0);
}
layer.ssm_v_conv = create_tensor(tn(LLM_TENSOR_SSM_CONV1D_V, "weight", i), {ssm_d_conv, 1, d_inner, 1}, TENSOR_NOT_REQUIRED);
if (!layer.ssm_v_conv) {
layer.ssm_v_conv = create_tensor(tn(LLM_TENSOR_SSM_CONV1D_V, "weight", i), {ssm_d_conv, 1, d_inner}, 0);
}
create_tensor_qkv(layer, i, n_embd, d_inner, d_inner, d_inner, 0);
// full-rank forget/output gate projections (no_kda_lora)
layer.ssm_f = create_tensor(tn(LLM_TENSOR_SSM_F, "weight", i), {n_embd, d_inner}, 0);
layer.ssm_g = create_tensor(tn(LLM_TENSOR_SSM_G, "weight", i), {n_embd, d_inner}, 0);
layer.ssm_beta = create_tensor(tn(LLM_TENSOR_SSM_BETA, "weight", i), {n_embd, n_head}, 0);
// note: the conversion script stores exp(A_log)
layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A, i), {1, n_head, 1, 1}, TENSOR_NOT_REQUIRED);
if (!layer.ssm_a) {
layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A, i), {1, n_head}, 0);
}
layer.ssm_dt_b = create_tensor(tn(LLM_TENSOR_SSM_DT, "bias", i), {d_inner}, 0);
layer.ssm_o_norm = create_tensor(tn(LLM_TENSOR_SSM_NORM, "weight", i), {head_dim_kda}, 0);
layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), {d_inner, n_embd}, 0);
} else {
// === gated MLA layer ===
const int64_t kv_lora_rank = hparams.n_lora_kv;
const int64_t n_embd_head_k_mla = hparams.n_embd_head_k_mla();
const int64_t n_embd_head_v_mla = hparams.n_embd_head_v_mla();
const int64_t qk_rope_head_dim = hparams.n_rot();
// Ling 3.0 has no query compression (q_lora_rank = null)
layer.wq = create_tensor(tn(LLM_TENSOR_ATTN_Q, "weight", i), {n_embd, n_head * n_embd_head_k_mla}, 0);
layer.attn_kv_a_norm = create_tensor(tn(LLM_TENSOR_ATTN_KV_A_NORM, "weight", i), {kv_lora_rank}, 0);
layer.wkv_a_mqa = create_tensor(tn(LLM_TENSOR_ATTN_KV_A_MQA, "weight", i), {n_embd, kv_lora_rank + qk_rope_head_dim}, 0);
layer.wkv_b = create_tensor(tn(LLM_TENSOR_ATTN_KV_B, "weight", i),
{kv_lora_rank, n_head * (n_embd_head_k_mla - qk_rope_head_dim + n_embd_head_v_mla)}, TENSOR_NOT_REQUIRED | TENSOR_SKIP_IF_VIRTUAL);
if (!layer.wkv_b) { // MLA KV cache enabled
layer.wk_b = create_tensor(tn(LLM_TENSOR_ATTN_K_B, "weight", i), {n_embd_head_k_mla - qk_rope_head_dim, kv_lora_rank, n_head}, 0);
layer.wv_b = create_tensor(tn(LLM_TENSOR_ATTN_V_B, "weight", i), {kv_lora_rank, n_embd_head_v_mla, n_head}, 0);
}
// head-wise output gate (gated_attention_proj_granularity_type = "head_wise")
layer.wqkv_gate = create_tensor(tn(LLM_TENSOR_ATTN_GATE, "weight", i), {n_embd, n_head}, 0);
layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), {n_head * n_embd_head_v_mla, n_embd}, 0);
}
layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
if (i < (int) hparams.n_layer_dense_lead) {
layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), {n_embd, n_ff}, 0);
layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), {n_ff, n_embd}, 0);
layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), {n_embd, n_ff}, 0);
} else {
const int64_t n_ff_exp = hparams.n_ff_exp;
const int64_t n_ff_shexp = hparams.n_ff_shexp > 0 ? hparams.n_ff_shexp : n_ff_exp * hparams.n_expert_shared;
layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), {n_embd, n_expert}, 0);
layer.ffn_exp_probs_b = create_tensor(tn(LLM_TENSOR_FFN_EXP_PROBS_B, "bias", i), {n_expert}, 0);
layer.ffn_gate_exps = create_tensor(tn(LLM_TENSOR_FFN_GATE_EXPS, "weight", i), {n_embd, n_ff_exp, n_expert}, expert_flags);
layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), {n_ff_exp, n_embd, n_expert}, expert_flags);
layer.ffn_up_exps = create_tensor(tn(LLM_TENSOR_FFN_UP_EXPS, "weight", i), {n_embd, n_ff_exp, n_expert}, expert_flags);
layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), {n_embd, n_ff_shexp}, 0);
layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), {n_ff_shexp, n_embd}, 0);
layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), {n_embd, n_ff_shexp}, 0);
}
}
}
std::unique_ptr<llm_graph_context> llama_model_bailingmoe3::build_arch_graph(const llm_graph_params & params) const {
return std::make_unique<graph>(*this, params);
}
// projection + causal conv1d + silu for one of Q, K, V (qkv: 0 = Q, 1 = K, 2 = V)
static ggml_tensor * bailingmoe3_causal_conv1d(
ggml_cgraph * gf, ggml_context * ctx0,
ggml_tensor * conv_states_all, ggml_tensor * conv_state_all,
int64_t qkv, ggml_tensor * x, ggml_tensor * proj_w, ggml_tensor * conv_w,
int64_t d_conv, int64_t head_dim, int64_t n_head,
int64_t n_seq_tokens, int64_t n_seqs, int64_t n_tokens, int64_t kv_head) {
const int64_t d_inner = head_dim * n_head;
const int64_t conv_state_size = (d_conv - 1) * d_inner;
const int64_t n_embd_r_total = 3 * conv_state_size; // Q + K + V
ggml_tensor * conv_state_x = ggml_view_3d(ctx0, conv_state_all, d_conv - 1, d_inner, n_seqs,
(d_conv - 1) * ggml_element_size(conv_state_all),
n_embd_r_total * ggml_element_size(conv_state_all),
qkv * conv_state_size * ggml_element_size(conv_state_all));
ggml_tensor * x_proj = ggml_mul_mat(ctx0, proj_w, x);
ggml_tensor * x_3d = ggml_reshape_3d(ctx0, x_proj, d_inner, n_seq_tokens, n_seqs);
ggml_tensor * conv_x = ggml_concat(ctx0, conv_state_x, ggml_transpose(ctx0, x_3d), 0);
ggml_tensor * last_conv_x = ggml_view_3d(ctx0, conv_x, d_conv - 1, d_inner, n_seqs,
conv_x->nb[1], conv_x->nb[2], n_seq_tokens * conv_x->nb[0]);
ggml_build_forward_expand(gf,
ggml_cpy(ctx0, last_conv_x,
ggml_view_3d(ctx0, conv_states_all,
d_conv - 1, d_inner, n_seqs,
(d_conv - 1) * ggml_element_size(conv_states_all),
n_embd_r_total * ggml_element_size(conv_states_all),
(kv_head * n_embd_r_total + qkv * conv_state_size) * ggml_element_size(conv_states_all))));
ggml_tensor * conv_weight = ggml_reshape_2d(ctx0, conv_w, d_conv, d_inner);
ggml_tensor * Xcur = ggml_ssm_conv(ctx0, conv_x, conv_weight);
Xcur = ggml_reshape_2d(ctx0, Xcur, d_inner, n_tokens);
Xcur = ggml_silu(ctx0, Xcur);
return ggml_reshape_4d(ctx0, Xcur, head_dim, n_head, n_seq_tokens, n_seqs);
}
llama_model_bailingmoe3::graph::graph(const llama_model & model, const llm_graph_params & params) :
llm_build_delta_net_base(params), model(model) {
ggml_tensor * cur;
ggml_tensor * inpL;
inpL = build_inp_embd(model.tok_embd);
cb(inpL, "inp_embd", -1);
// MLA layers use RoPE, so positions are needed
ggml_tensor * inp_pos = build_inp_pos();
auto * inp_kv = !hparams.is_mla() ? build_inp_mem_hybrid() : nullptr;
auto * inp_k = hparams.is_mla() ? build_inp_mem_hybrid_k() : nullptr;
auto * inp_rs = hparams.is_mla() ? inp_k->get_recr() : inp_kv->get_recr();
auto * inp_attn_kv = !hparams.is_mla() ? inp_kv->get_attn() : nullptr;
auto * inp_attn_k = hparams.is_mla() ? inp_k->get_attn() : nullptr;
ggml_tensor * inp_out_ids = build_inp_out_ids();
const int64_t n_head = hparams.n_head();
const int64_t head_dim = hparams.n_embd_head_kda;
const int64_t d_conv = hparams.ssm_d_conv;
const int64_t d_inner = n_head * head_dim;
const int64_t n_seqs = ubatch.n_seqs;
const int64_t n_seq_tokens = ubatch.n_seq_tokens;
GGML_ASSERT(n_seqs != 0);
GGML_ASSERT(ubatch.equal_seqs());
GGML_ASSERT(ubatch.n_tokens == n_seq_tokens * n_seqs);
const int64_t n_embd_head_k_mla = hparams.n_embd_head_k_mla();
const int64_t n_embd_head_v_mla = hparams.n_embd_head_v_mla();
const int64_t kv_lora_rank = hparams.n_lora_kv;
const int64_t n_embd_head_qk_rope = hparams.n_rot();
const int64_t n_embd_head_qk_nope = n_embd_head_k_mla - n_embd_head_qk_rope;
const float kq_scale_mla = 1.0f / sqrtf(float(n_embd_head_k_mla));
for (int il = 0; il < n_layer; ++il) {
const auto & layer = model.layers[il];
ggml_tensor * inpSA = inpL;
cur = build_norm(inpL, layer.attn_norm, NULL, LLM_NORM_RMS, il);
cb(cur, "attn_norm", il);
// the MLA output gate is computed from this same normed hidden state
ggml_tensor * attn_inp = cur;
if (hparams.is_recr(il)) {
// === KDA ===
const auto * mctx_cur = inp_rs->mctx;
const auto kv_head = mctx_cur->get_head();
ggml_tensor * conv_states_all = mctx_cur->get_r_l(il);
ggml_tensor * conv_state_all = build_rs(inp_rs, conv_states_all, hparams.n_embd_r(), n_seqs);
ggml_tensor * Qcur = bailingmoe3_causal_conv1d(gf, ctx0, conv_states_all, conv_state_all, 0, cur, layer.wq, layer.ssm_q_conv, d_conv, head_dim, n_head, n_seq_tokens, n_seqs, n_tokens, kv_head);
ggml_tensor * Kcur = bailingmoe3_causal_conv1d(gf, ctx0, conv_states_all, conv_state_all, 1, cur, layer.wk, layer.ssm_k_conv, d_conv, head_dim, n_head, n_seq_tokens, n_seqs, n_tokens, kv_head);
ggml_tensor * Vcur = bailingmoe3_causal_conv1d(gf, ctx0, conv_states_all, conv_state_all, 2, cur, layer.wv, layer.ssm_v_conv, d_conv, head_dim, n_head, n_seq_tokens, n_seqs, n_tokens, kv_head);
// safe gate: g1 = lower_bound * sigmoid(exp(A_log) * (f_proj(x) + dt_bias))
// note: ssm_a holds exp(A_log), applied by the conversion script
ggml_tensor * g1 = ggml_mul_mat(ctx0, layer.ssm_f, cur);
g1 = ggml_add(ctx0, g1, layer.ssm_dt_b);
g1 = ggml_reshape_3d(ctx0, g1, head_dim, n_head, n_tokens);
ggml_tensor * A = ggml_reshape_3d(ctx0, layer.ssm_a, 1, n_head, 1);
g1 = ggml_mul(ctx0, g1, A);
g1 = ggml_sigmoid(ctx0, g1);
g1 = ggml_scale(ctx0, g1, hparams.kda_gate_lower_bound);
cb(g1, "kda_g1", il);
g1 = ggml_reshape_4d(ctx0, g1, head_dim, n_head, n_seq_tokens, n_seqs);
ggml_tensor * beta = ggml_mul_mat(ctx0, layer.ssm_beta, cur);
beta = ggml_reshape_4d(ctx0, beta, 1, n_head, n_seq_tokens, n_seqs);
beta = ggml_sigmoid(ctx0, beta);
cb(beta, "kda_beta", il);
cur = ggml_reshape_3d(ctx0, cur, cur->ne[0], n_seq_tokens, n_seqs);
ggml_tensor * ssm_states_all = mctx_cur->get_s_l(il);
ggml_tensor * state = build_rs(inp_rs, ssm_states_all, hparams.n_embd_s(), n_seqs);
state = ggml_reshape_4d(ctx0, state, head_dim, head_dim, n_head, n_seqs);
const float eps_norm = hparams.f_norm_rms_eps;
Qcur = ggml_l2_norm(ctx0, Qcur, eps_norm);
Kcur = ggml_l2_norm(ctx0, Kcur, eps_norm);
auto attn_out = build_delta_net(Qcur, Kcur, Vcur, g1, beta, state, il);
ggml_tensor * output = ggml_cont(ctx0, attn_out.first);
ggml_tensor * new_state = attn_out.second;
ggml_build_forward_expand(gf,
ggml_cpy(ctx0, new_state,
ggml_view_1d(ctx0, ssm_states_all, hparams.n_embd_s() * n_seqs,
kv_head * hparams.n_embd_s() * ggml_element_size(ssm_states_all))));
// output gate: RMSNorm(o) * sigmoid(g_proj(x))
ggml_tensor * cur_2d = ggml_reshape_2d(ctx0, cur, cur->ne[0], n_seq_tokens * n_seqs);
ggml_tensor * g2 = ggml_mul_mat(ctx0, layer.ssm_g, cur_2d);
g2 = ggml_reshape_3d(ctx0, g2, head_dim, n_head, n_seq_tokens * n_seqs);
ggml_tensor * attn_out_final = ggml_reshape_3d(ctx0, output, head_dim, n_head, n_seq_tokens * n_seqs);
ggml_tensor * normed = build_norm(attn_out_final, layer.ssm_o_norm, nullptr, LLM_NORM_RMS, il);
ggml_tensor * gated = ggml_mul(ctx0, normed, ggml_sigmoid(ctx0, g2));
gated = ggml_cont_2d(ctx0, gated, d_inner, n_tokens);
cur = ggml_mul_mat(ctx0, layer.wo, gated);
cb(cur, "kda_out", il);
} else {
// === gated MLA ===
ggml_tensor * q = ggml_mul_mat(ctx0, layer.wq, cur);
cb(q, "q", il);
ggml_tensor * q_nope = ggml_view_3d(ctx0, q, n_embd_head_qk_nope, n_head, n_tokens,
ggml_row_size(q->type, n_embd_head_k_mla),
ggml_row_size(q->type, n_embd_head_k_mla) * n_head, 0);
ggml_tensor * q_pe = ggml_view_3d(ctx0, q, n_embd_head_qk_rope, n_head, n_tokens,
ggml_row_size(q->type, n_embd_head_k_mla),
ggml_row_size(q->type, n_embd_head_k_mla) * n_head,
ggml_row_size(q->type, n_embd_head_qk_nope));
ggml_tensor * kv_cmpr_pe = ggml_mul_mat(ctx0, layer.wkv_a_mqa, cur);
ggml_tensor * kv_cmpr = ggml_view_2d(ctx0, kv_cmpr_pe, kv_lora_rank, n_tokens,
ggml_row_size(kv_cmpr_pe->type, kv_lora_rank + n_embd_head_qk_rope), 0);
ggml_tensor * k_pe = ggml_view_3d(ctx0, kv_cmpr_pe, n_embd_head_qk_rope, 1, n_tokens,
ggml_row_size(kv_cmpr_pe->type, kv_lora_rank + n_embd_head_qk_rope),
ggml_row_size(kv_cmpr_pe->type, kv_lora_rank + n_embd_head_qk_rope),
ggml_row_size(kv_cmpr_pe->type, kv_lora_rank));
// note: HF applies rope on de-interleaved pairs, which is ggml's NORM rope
q_pe = ggml_rope_ext(ctx0, q_pe, inp_pos, nullptr, n_rot, rope_type, n_ctx_orig,
freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow);
cb(q_pe, "q_pe", il);
k_pe = ggml_rope_ext(ctx0, k_pe, inp_pos, nullptr, n_rot, rope_type, n_ctx_orig,
freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow);
cb(k_pe, "k_pe", il);
kv_cmpr = build_norm(kv_cmpr, layer.attn_kv_a_norm, nullptr, LLM_NORM_RMS, il);
cb(kv_cmpr, "kv_cmpr", il);
if (layer.wk_b && layer.wv_b) { // MLA KV cache enabled
q_nope = ggml_permute(ctx0, q_nope, 0, 2, 1, 3);
ggml_tensor * q_nope_absorbed = ggml_mul_mat(ctx0, layer.wk_b, q_nope);
q_nope_absorbed = ggml_permute(ctx0, q_nope_absorbed, 0, 2, 1, 3);
// note: rope must go first for in-place context shifting in build_rope_shift()
ggml_tensor * Qcur = ggml_concat(ctx0, q_nope_absorbed, q_pe, 0);
kv_cmpr = ggml_reshape_3d(ctx0, kv_cmpr, kv_lora_rank, 1, n_tokens);
ggml_tensor * Kcur = ggml_concat(ctx0, kv_cmpr, k_pe, 0);
ggml_tensor * Vcur = kv_cmpr;
// no output projection here - the gate is applied first
cur = build_attn(inp_attn_k, nullptr, NULL, nullptr,
Qcur, Kcur, Vcur, nullptr, nullptr, layer.wv_b, kq_scale_mla, il);
} else { // MLA KV cache disabled, fall back to MHA
ggml_tensor * Qcur = ggml_reshape_3d(ctx0, ggml_cont(ctx0, q), n_embd_head_k_mla, n_head, n_tokens);
ggml_tensor * kv = ggml_mul_mat(ctx0, layer.wkv_b, kv_cmpr);
const int64_t kv_per_head = n_embd_head_qk_nope + n_embd_head_v_mla;
ggml_tensor * k_nope = ggml_view_3d(ctx0, kv, n_embd_head_qk_nope, n_head, n_tokens,
ggml_row_size(kv->type, kv_per_head),
ggml_row_size(kv->type, kv_per_head * n_head), 0);
ggml_tensor * Vcur = ggml_view_3d(ctx0, kv, n_embd_head_v_mla, n_head, n_tokens,
ggml_row_size(kv->type, kv_per_head),
ggml_row_size(kv->type, kv_per_head * n_head),
ggml_row_size(kv->type, n_embd_head_qk_nope));
Vcur = ggml_cont(ctx0, Vcur);
ggml_tensor * k_pe_target = ggml_new_tensor_3d(ctx0, k_pe->type, n_embd_head_qk_rope, n_head, n_tokens);
ggml_tensor * k_pe_repeated = ggml_repeat(ctx0, k_pe, k_pe_target);
ggml_tensor * Kcur = ggml_concat(ctx0, k_nope, k_pe_repeated, 0);
cur = build_attn(inp_attn_kv, nullptr, NULL, nullptr,
Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale_mla, il);
}
cb(cur, "attn_out_pre_gate", il);
// head-wise sigmoid gate, computed from the same normed hidden state as q/kv
ggml_tensor * gate = ggml_mul_mat(ctx0, layer.wqkv_gate, attn_inp);
gate = ggml_sigmoid(ctx0, gate);
cb(gate, "attn_gate", il);
cur = ggml_reshape_3d(ctx0, cur, n_embd_head_v_mla, n_head, n_tokens);
gate = ggml_reshape_3d(ctx0, gate, 1, n_head, n_tokens);
cur = ggml_mul(ctx0, cur, gate);
cur = ggml_reshape_2d(ctx0, cur, n_embd_head_v_mla * n_head, n_tokens);
cb(cur, "attn_gated", il);
cur = ggml_mul_mat(ctx0, layer.wo, cur);
cb(cur, "attn_out", il);
}
if (il == n_layer - 1 && inp_out_ids) {
cur = ggml_get_rows(ctx0, cur, inp_out_ids);
inpSA = ggml_get_rows(ctx0, inpSA, inp_out_ids);
}
ggml_tensor * ffn_inp = ggml_add(ctx0, cur, inpSA);
cb(ffn_inp, "ffn_inp", il);
cur = build_norm(ffn_inp, layer.ffn_norm, NULL, LLM_NORM_RMS, il);
cb(cur, "ffn_norm", il);
if ((uint32_t) il < hparams.n_layer_dense_lead) {
cur = build_ffn(cur,
layer.ffn_up, NULL, NULL,
layer.ffn_gate, NULL, NULL,
layer.ffn_down, NULL, NULL,
NULL, LLM_FFN_SILU, LLM_FFN_PAR, il);
cb(cur, "ffn_out", il);
} else {
ggml_tensor * moe_out = build_moe_ffn(cur,
layer.ffn_gate_inp,
layer.ffn_up_exps,
layer.ffn_gate_exps,
layer.ffn_down_exps,
layer.ffn_exp_probs_b,
hparams.n_expert,
hparams.n_expert_used,
LLM_FFN_SILU, hparams.expert_weights_norm,
hparams.expert_weights_scale,
(llama_expert_gating_func_type) hparams.expert_gating_func,
il);
cb(moe_out, "ffn_moe_out", il);
ggml_tensor * ffn_shexp = build_ffn(cur,
layer.ffn_up_shexp, NULL, NULL,
layer.ffn_gate_shexp, NULL, NULL,
layer.ffn_down_shexp, NULL, NULL,
NULL, LLM_FFN_SILU, LLM_FFN_PAR, il);
cb(ffn_shexp, "ffn_shexp", il);
cur = ggml_add(ctx0, moe_out, ffn_shexp);
cb(cur, "ffn_out", il);
}
cur = ggml_add(ctx0, cur, ffn_inp);
cur = build_cvec(cur, il);
cb(cur, "l_out", il);
inpL = cur;
}
cur = inpL;
cur = build_norm(cur, model.output_norm, NULL, LLM_NORM_RMS, -1);
cb(cur, "result_norm", -1);
res->t_embd = cur;
cur = ggml_mul_mat(ctx0, model.output, cur);
cb(cur, "result_output", -1);
res->t_logits = cur;
ggml_build_forward_expand(gf, cur);
}
-490
View File
@@ -1,490 +0,0 @@
// Inkling: relative-position attention with per-layer packed short-convolution
// state and a mixture of routed and shared experts.
#include "models.h"
#include "../llama-memory-hybrid-iswa.h"
#include "../llama-memory-recurrent.h"
#include <algorithm>
#include <cmath>
void llama_model_inkling::load_arch_hparams(llama_model_loader & ml) {
ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps);
ml.get_key(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp);
ml.get_key(LLM_KV_EXPERT_SHARED_COUNT, hparams.n_expert_shared);
ml.get_key(LLM_KV_EXPERT_WEIGHTS_SCALE, hparams.expert_weights_scale);
ml.get_key(LLM_KV_EXPERT_GATING_FUNC, hparams.expert_gating_func, false);
ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa);
hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.is_swa_impl, hparams.n_layer());
for (uint32_t il = 0; il < hparams.n_layer(); ++il) {
hparams.is_recr_impl[il] = 1;
}
ml.get_key(LLM_KV_INKLING_D_REL, hparams.inkling_d_rel);
ml.get_key(LLM_KV_INKLING_REL_EXTENT, hparams.inkling_rel_extent);
ml.get_key(LLM_KV_INKLING_REL_EXTENT_SWA, hparams.inkling_rel_extent_swa);
ml.get_key(LLM_KV_INKLING_SHORTCONV_KERNEL, hparams.n_shortconv_l_cache);
ml.get_key(LLM_KV_INKLING_DENSE_BLOCK_COUNT, hparams.n_layer_dense_lead);
float logit_scale_denom = 0.0f;
ml.get_key(LLM_KV_INKLING_LOGIT_SCALE_DENOM, logit_scale_denom);
GGML_ASSERT(logit_scale_denom != 0.0f);
hparams.f_logit_scale = 1.0f/logit_scale_denom;
ml.get_key(LLM_KV_INKLING_LOG_SCALING_N_FLOOR, hparams.inkling_log_n_floor, false);
ml.get_key(LLM_KV_INKLING_LOG_SCALING_ALPHA, hparams.inkling_log_alpha, false);
ml.get_key(LLM_KV_INKLING_UNPADDED_VOCAB_SIZE, hparams.inkling_unpadded_n_vocab, false);
GGML_ASSERT(hparams.n_shortconv_l_cache > 1);
GGML_ASSERT(hparams.inkling_d_rel > 0);
GGML_ASSERT(hparams.inkling_rel_extent > 0 && hparams.inkling_rel_extent_swa > 0);
// Four streams per layer: K, V, attention output, and MLP output.
const uint32_t d_conv = hparams.n_shortconv_l_cache - 1;
hparams.n_embd_r_impl = d_conv *
(hparams.n_embd_k_gqa_max() + hparams.n_embd_v_gqa_max() + 2*hparams.n_embd);
type = LLM_TYPE_UNKNOWN;
}
void llama_model_inkling::load_arch_tensors(llama_model_loader &) {
LLAMA_LOAD_LOCALS;
const int64_t head_dim = hparams.n_embd_head_k();
const int64_t d_rel = hparams.inkling_d_rel;
const int64_t K = hparams.n_shortconv_l_cache;
const int64_t n_ff_exp = hparams.n_ff_exp;
const int64_t n_shexp = hparams.n_expert_shared;
const int expert_flags = params.load_mode == LLAMA_LOAD_MODE_LONGHAUL ? TENSOR_LONGHAUL : 0;
tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0);
tok_norm = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD_NORM, "weight", 0), {n_embd}, 0);
output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0);
output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, 0);
for (int i = 0; i < n_layer; ++i) {
auto & layer = layers[i];
const int64_t n_head_kv_i = hparams.n_head_kv(i);
const int64_t kvw = n_head_kv_i*head_dim;
const int64_t rel_extent = hparams.is_swa(i)
? hparams.inkling_rel_extent_swa
: hparams.inkling_rel_extent;
layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
layer.wq = create_tensor(tn(LLM_TENSOR_ATTN_Q, "weight", i), {n_embd, n_head*head_dim}, 0);
layer.wk = create_tensor(tn(LLM_TENSOR_ATTN_K, "weight", i), {n_embd, kvw}, 0);
layer.wv = create_tensor(tn(LLM_TENSOR_ATTN_V, "weight", i), {n_embd, kvw}, 0);
layer.wr = create_tensor(tn(LLM_TENSOR_ATTN_R, "weight", i), {n_embd, n_head*d_rel}, 0);
layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), {n_head*head_dim, n_embd}, 0);
layer.attn_q_norm = create_tensor(tn(LLM_TENSOR_ATTN_Q_NORM, "weight", i), {head_dim}, 0);
layer.attn_k_norm = create_tensor(tn(LLM_TENSOR_ATTN_K_NORM, "weight", i), {head_dim}, 0);
layer.attn_rel_proj = create_tensor(
tn(LLM_TENSOR_ATTN_REL_PROJ, "weight", i), {rel_extent, d_rel}, 0);
layer.shortconv_k = create_tensor(tn(LLM_TENSOR_SHORTCONV_K, "weight", i), {K, kvw}, 0);
layer.shortconv_v = create_tensor(tn(LLM_TENSOR_SHORTCONV_V, "weight", i), {K, kvw}, 0);
layer.shortconv_attn = create_tensor(tn(LLM_TENSOR_SHORTCONV_ATTN, "weight", i), {K, n_embd}, 0);
layer.shortconv_mlp = create_tensor(tn(LLM_TENSOR_SHORTCONV_MLP, "weight", i), {K, n_embd}, 0);
layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
layer.ffn_gscale = create_tensor(tn(LLM_TENSOR_FFN_GSCALE, "weight", i), {1}, 0);
if (i < (int) hparams.n_layer_dense_lead) {
const int64_t n_ff_i = hparams.n_ff(i);
layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), {n_embd, n_ff_i}, 0);
layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), {n_embd, n_ff_i}, 0);
layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), {n_ff_i, n_embd}, 0);
} else {
GGML_ASSERT(n_expert > 0 && n_expert_used > 0 && n_shexp > 0);
// The final rows are shared-expert sink logits.
layer.ffn_gate_inp = create_tensor(
tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), {n_embd, n_expert + n_shexp}, 0);
layer.ffn_exp_probs_b = create_tensor(
tn(LLM_TENSOR_FFN_EXP_PROBS_B, "bias", i), {n_expert}, 0);
layer.ffn_gate_exps = create_tensor(
tn(LLM_TENSOR_FFN_GATE_EXPS, "weight", i),
{n_embd, n_ff_exp, n_expert}, expert_flags);
layer.ffn_up_exps = create_tensor(
tn(LLM_TENSOR_FFN_UP_EXPS, "weight", i),
{n_embd, n_ff_exp, n_expert}, expert_flags);
layer.ffn_down_exps = create_tensor(
tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i),
{n_ff_exp, n_embd, n_expert}, expert_flags);
// Shared experts stay resident and are never part of the longhaul cache.
layer.ffn_gate_shexp = create_tensor(
tn(LLM_TENSOR_FFN_GATE_SHEXPS, "weight", i),
{n_embd, n_ff_exp, n_shexp}, 0);
layer.ffn_up_shexp = create_tensor(
tn(LLM_TENSOR_FFN_UP_SHEXPS, "weight", i),
{n_embd, n_ff_exp, n_shexp}, 0);
layer.ffn_down_shexp = create_tensor(
tn(LLM_TENSOR_FFN_DOWN_SHEXPS, "weight", i),
{n_ff_exp, n_embd, n_shexp}, 0);
}
}
}
class llm_graph_input_inkling : public llm_graph_input_i {
public:
llm_graph_input_inkling(
const llama_hparams & hparams,
const llama_memory_hybrid_iswa_context * mctx) :
hparams(hparams), mctx(mctx) {
}
void set_input(const llama_ubatch * ubatch) override {
if (tau) {
GGML_ASSERT(ggml_backend_buffer_is_host(tau->buffer));
float * data = (float *) tau->data;
const float n_floor = (float) hparams.inkling_log_n_floor;
const float alpha = hparams.inkling_log_alpha;
for (int64_t i = 0; i < (int64_t) ubatch->n_tokens; ++i) {
const float eff = (float) (ubatch->pos[i] + 1)/n_floor;
data[i] = 1.0f + alpha*logf(std::max(eff, 1.0f));
}
}
if (rel_idx) {
mctx->get_attn()->get_base()->set_input_pos_rel_flat(
rel_idx, ubatch, hparams.inkling_rel_extent);
}
if (rel_idx_swa) {
mctx->get_attn()->get_swa()->set_input_pos_rel_flat(
rel_idx_swa, ubatch, hparams.inkling_rel_extent_swa);
}
if (vocab_mask) {
GGML_ASSERT(ggml_backend_buffer_is_host(vocab_mask->buffer));
float * data = (float *) vocab_mask->data;
for (int64_t id = 0; id < vocab_mask->ne[0]; ++id) {
data[id] = id < (int64_t) hparams.inkling_unpadded_n_vocab ? 0.0f : -INFINITY;
}
}
if (shexp_idx) {
GGML_ASSERT(ggml_backend_buffer_is_host(shexp_idx->buffer));
int32_t * data = (int32_t *) shexp_idx->data;
for (int64_t j = 0; j < shexp_idx->ne[1]; ++j) {
for (int64_t s = 0; s < shexp_idx->ne[0]; ++s) {
data[j*shexp_idx->ne[0] + s] = (int32_t) s;
}
}
}
}
ggml_tensor * tau = nullptr;
ggml_tensor * rel_idx = nullptr;
ggml_tensor * rel_idx_swa = nullptr;
ggml_tensor * vocab_mask = nullptr;
ggml_tensor * shexp_idx = nullptr;
private:
const llama_hparams hparams;
const llama_memory_hybrid_iswa_context * mctx;
};
std::unique_ptr<llm_graph_context> llama_model_inkling::build_arch_graph(
const llm_graph_params & params) const {
return std::make_unique<graph>(*this, params);
}
llama_model_inkling::graph::graph(
const llama_model & model,
const llm_graph_params & params) :
llm_graph_context(params) {
const int64_t head_dim = hparams.n_embd_head_k();
const int64_t d_rel = hparams.inkling_d_rel;
const int64_t d_conv = hparams.n_shortconv_l_cache - 1;
const int64_t n_embd_r = hparams.n_embd_r();
const int64_t kw_max = hparams.n_embd_k_gqa_max();
const int64_t vw_max = hparams.n_embd_v_gqa_max();
const int64_t off_k = 0;
const int64_t off_v = d_conv*kw_max;
const int64_t off_attn = d_conv*(kw_max + vw_max);
const int64_t off_mlp = d_conv*(kw_max + vw_max + n_embd);
const auto * mctx_hyb = static_cast<const llama_memory_hybrid_iswa_context *>(mctx);
const auto * mctx_recr = mctx_hyb->get_recr();
const auto * mctx_attn = mctx_hyb->get_attn();
const uint32_t kv_head = mctx_recr->get_head();
const int64_t n_seq_tokens = ubatch.n_seq_tokens;
const int64_t n_seqs = ubatch.n_seqs;
GGML_ASSERT(n_seqs != 0);
GGML_ASSERT(ubatch.equal_seqs());
GGML_ASSERT(ubatch.n_tokens == n_seq_tokens*n_seqs);
bool has_global = false;
bool has_local = false;
for (int il = 0; il < n_layer; ++il) {
has_local |= hparams.is_swa(il);
has_global |= !hparams.is_swa(il);
}
auto inp = std::make_unique<llm_graph_input_inkling>(hparams, mctx_hyb);
if (hparams.inkling_log_n_floor > 0 && has_global) {
inp->tau = ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, 1, 1, n_tokens);
ggml_set_input(inp->tau);
ggml_set_name(inp->tau, "inkling_tau");
}
if (has_global) {
inp->rel_idx = ggml_new_tensor_2d(
ctx0, GGML_TYPE_I32, mctx_attn->get_base()->get_n_kv(), n_tokens);
ggml_set_input(inp->rel_idx);
ggml_set_name(inp->rel_idx, "inkling_rel_idx");
}
if (has_local) {
inp->rel_idx_swa = ggml_new_tensor_2d(
ctx0, GGML_TYPE_I32, mctx_attn->get_swa()->get_n_kv(), n_tokens);
ggml_set_input(inp->rel_idx_swa);
ggml_set_name(inp->rel_idx_swa, "inkling_rel_idx_swa");
}
const int64_t n_vocab = model.vocab.n_tokens();
if (!cparams.embeddings &&
hparams.inkling_unpadded_n_vocab > 0 &&
(int64_t) hparams.inkling_unpadded_n_vocab < n_vocab) {
inp->vocab_mask = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, n_vocab);
ggml_set_input(inp->vocab_mask);
ggml_set_name(inp->vocab_mask, "inkling_vocab_mask");
}
if (hparams.n_expert_shared > 0 && (uint32_t) n_layer > hparams.n_layer_dense_lead) {
inp->shexp_idx = ggml_new_tensor_2d(
ctx0, GGML_TYPE_I32, hparams.n_expert_shared, n_tokens);
ggml_set_input(inp->shexp_idx);
ggml_set_name(inp->shexp_idx, "inkling_shexp_idx");
}
ggml_tensor * tau = inp->tau;
ggml_tensor * rel_idx = inp->rel_idx;
ggml_tensor * rel_idx_swa = inp->rel_idx_swa;
ggml_tensor * vocab_mask = inp->vocab_mask;
ggml_tensor * shexp_idx = inp->shexp_idx;
res->add_input(std::move(inp));
auto * inp_hybrid = build_inp_mem_hybrid_iswa();
ggml_tensor * conv_rs_cur = nullptr;
auto build_sconv = [&](ggml_tensor * x2d, ggml_tensor * kernel, int64_t off, int il) {
const int64_t w = x2d->ne[0];
ggml_tensor * x3 = ggml_reshape_3d(ctx0, x2d, w, n_seq_tokens, n_seqs);
ggml_tensor * xt = ggml_transpose(ctx0, x3);
ggml_tensor * conv_state = mctx_recr->get_r_l(il);
GGML_ASSERT(conv_rs_cur != nullptr);
const size_t sz = ggml_element_size(conv_rs_cur);
ggml_tensor * state = ggml_view_3d(
ctx0, conv_rs_cur, d_conv, w, n_seqs,
d_conv*sz, conv_rs_cur->nb[1], off*sz);
ggml_tensor * sx = ggml_concat(ctx0, state, xt, 0);
ggml_tensor * new_state = ggml_view_3d(
ctx0, sx, d_conv, w, n_seqs,
sx->nb[1], sx->nb[2], (sx->ne[0] - d_conv)*sx->nb[0]);
ggml_tensor * state_dst = ggml_view_3d(
ctx0, conv_state, d_conv, w, n_seqs,
d_conv*sz, n_embd_r*sz, (kv_head*n_embd_r + off)*sz);
ggml_build_forward_expand(gf, ggml_cpy(ctx0, new_state, state_dst));
ggml_tensor * conv_out = ggml_ssm_conv(ctx0, sx, kernel);
return ggml_reshape_2d(ctx0, ggml_add(ctx0, x3, conv_out), w, n_seq_tokens*n_seqs);
};
auto build_attn_block = [&](ggml_tensor * cur, int il) {
const auto & layer = model.layers[il];
const bool is_swa = hparams.is_swa(il);
const int64_t n_head_kv = hparams.n_head_kv(il);
const int64_t rel_extent = is_swa
? hparams.inkling_rel_extent_swa
: hparams.inkling_rel_extent;
ggml_tensor * q = build_lora_mm(layer.wq, cur);
ggml_tensor * k = build_lora_mm(layer.wk, cur);
ggml_tensor * v = build_lora_mm(layer.wv, cur);
ggml_tensor * r = build_lora_mm(layer.wr, cur);
k = build_sconv(k, layer.shortconv_k, off_k, il);
v = build_sconv(v, layer.shortconv_v, off_v, il);
q = ggml_reshape_3d(ctx0, q, head_dim, n_head, n_tokens);
k = ggml_reshape_3d(ctx0, k, head_dim, n_head_kv, n_tokens);
v = ggml_reshape_3d(ctx0, v, head_dim, n_head_kv, n_tokens);
q = build_norm(q, layer.attn_q_norm, nullptr, LLM_NORM_RMS, il);
k = build_norm(k, layer.attn_k_norm, nullptr, LLM_NORM_RMS, il);
if (tau && !is_swa) {
q = ggml_mul(ctx0, q, tau);
}
ggml_tensor * r2 = ggml_reshape_2d(ctx0, r, d_rel, n_head*n_tokens);
ggml_tensor * proj = ggml_cont(ctx0, ggml_transpose(ctx0, layer.attn_rel_proj));
ggml_tensor * rel = ggml_mul_mat(ctx0, proj, r2);
ggml_mul_mat_set_prec(rel, GGML_PREC_F32);
rel = ggml_reshape_3d(ctx0, rel, rel_extent, n_head, n_tokens);
if (tau && !is_swa) {
rel = ggml_mul(ctx0, rel, tau);
}
// Scale Q rather than build_attn's joint logits so the relative term
// remains unscaled, matching the reference implementation.
q = ggml_scale(ctx0, q, 1.0f/(float) head_dim);
rel = ggml_pad(ctx0, rel, 1, 0, 0, 0);
rel = ggml_cont(ctx0, ggml_permute(ctx0, rel, 1, 0, 2, 3));
rel = ggml_reshape_2d(ctx0, rel, n_head, (rel_extent + 1)*n_tokens);
ggml_tensor * idx = is_swa ? rel_idx_swa : rel_idx;
GGML_ASSERT(idx != nullptr);
const int64_t n_kv = idx->ne[0];
ggml_tensor * idx1 = ggml_reshape_1d(ctx0, idx, n_kv*n_tokens);
ggml_tensor * kq_b = ggml_get_rows(ctx0, rel, idx1);
kq_b = ggml_reshape_3d(ctx0, kq_b, n_head, n_kv, n_tokens);
kq_b = ggml_cont(ctx0, ggml_permute(ctx0, kq_b, 2, 0, 1, 3));
auto * inp_attn = inp_hybrid->get_attn();
const int64_t n_stream =
(is_swa ? inp_attn->get_kq_mask_swa() : inp_attn->get_kq_mask())->ne[3];
GGML_ASSERT(n_stream > 0 && n_tokens % n_stream == 0);
if (n_stream > 1) {
kq_b = ggml_view_4d(
ctx0, kq_b, n_kv, n_tokens/n_stream, n_head, n_stream,
kq_b->nb[1], kq_b->nb[2], (n_tokens/n_stream)*kq_b->nb[1], 0);
}
return build_attn(
inp_attn, layer.wo, nullptr, nullptr,
q, k, v, kq_b, nullptr, nullptr, 1.0f, il);
};
auto build_dense_ffn = [&](ggml_tensor * cur, int il) {
cur = build_ffn(
cur,
model.layers[il].ffn_up, nullptr, nullptr,
model.layers[il].ffn_gate, nullptr, nullptr,
model.layers[il].ffn_down, nullptr, nullptr,
nullptr, LLM_FFN_SILU, LLM_FFN_PAR, il);
return ggml_mul(ctx0, cur, model.layers[il].ffn_gscale);
};
auto build_moe = [&](ggml_tensor * cur, int il) {
const auto & layer = model.layers[il];
const int64_t n_shexp = hparams.n_expert_shared;
ggml_tensor * logits = build_lora_mm(layer.ffn_gate_inp, cur);
ggml_mul_mat_set_prec(logits, GGML_PREC_F32);
const size_t lsz = ggml_element_size(logits);
ggml_tensor * routed = ggml_cont(ctx0, ggml_view_2d(
ctx0, logits, n_expert, n_tokens, logits->nb[1], 0));
ggml_tensor * shared_logits = ggml_view_2d(
ctx0, logits, n_shexp, n_tokens, logits->nb[1], n_expert*lsz);
ggml_tensor * scores = ggml_add(
ctx0, ggml_sigmoid(ctx0, routed), layer.ffn_exp_probs_b);
ggml_tensor * selected = ggml_argsort_top_k(
ctx0, scores, n_expert_used);
ggml_tensor * routed3 = ggml_reshape_3d(ctx0, routed, 1, n_expert, n_tokens);
ggml_tensor * topk_logits = ggml_reshape_2d(
ctx0, ggml_get_rows(ctx0, routed3, selected), n_expert_used, n_tokens);
ggml_tensor * all_logits = ggml_concat(ctx0, topk_logits, shared_logits, 0);
ggml_tensor * w = ggml_neg(
ctx0, ggml_softplus(ctx0, ggml_neg(ctx0, all_logits)));
w = ggml_soft_max(ctx0, w);
w = ggml_scale(ctx0, w, hparams.expert_weights_scale);
w = ggml_mul(ctx0, w, layer.ffn_gscale);
const size_t wsz = ggml_element_size(w);
ggml_tensor * weights = ggml_cont(ctx0, ggml_view_2d(
ctx0, w, n_expert_used, n_tokens, w->nb[1], 0));
weights = ggml_reshape_3d(ctx0, weights, 1, n_expert_used, n_tokens);
ggml_tensor * moe_out = build_moe_experts(
cur, selected, weights,
layer.ffn_up_exps, layer.ffn_gate_exps, layer.ffn_down_exps,
n_expert_used, LLM_FFN_SILU, il);
GGML_ASSERT(shexp_idx != nullptr);
ggml_tensor * xr = ggml_reshape_3d(ctx0, cur, n_embd, 1, n_tokens);
ggml_tensor * gs = build_lora_mm_id(layer.ffn_gate_shexp, xr, shexp_idx);
ggml_tensor * us = build_lora_mm_id(layer.ffn_up_shexp, xr, shexp_idx);
ggml_tensor * hs = ggml_swiglu_split(ctx0, gs, us);
ggml_tensor * gammas = ggml_cont(ctx0, ggml_view_2d(
ctx0, w, n_shexp, n_tokens, w->nb[1], n_expert_used*wsz));
hs = ggml_mul(ctx0, hs, ggml_reshape_3d(ctx0, gammas, 1, n_shexp, n_tokens));
ggml_tensor * ds = build_lora_mm_id(layer.ffn_down_shexp, hs, shexp_idx);
for (int64_t s = 0; s < n_shexp; ++s) {
ggml_tensor * e = ggml_view_2d(
ctx0, ds, n_embd, n_tokens, ds->nb[2], s*ds->nb[1]);
moe_out = ggml_add(ctx0, moe_out, e);
}
return moe_out;
};
ggml_tensor * cur = build_inp_embd(model.tok_embd);
if (ubatch.token) {
cur = build_norm(cur, model.tok_norm, nullptr, LLM_NORM_RMS, -1);
}
ggml_build_forward_expand(gf, cur);
for (int il = 0; il < n_layer; ++il) {
conv_rs_cur = build_rs(
inp_hybrid->get_recr(), mctx_recr->get_r_l(il), n_embd_r, n_seqs);
ggml_tensor * attn_in = build_norm(
cur, model.layers[il].attn_norm, nullptr, LLM_NORM_RMS, il);
ggml_tensor * attn_out = build_attn_block(attn_in, il);
attn_out = build_sconv(
attn_out, model.layers[il].shortconv_attn, off_attn, il);
cur = ggml_add(ctx0, cur, attn_out);
ggml_tensor * ffn_in = build_norm(
cur, model.layers[il].ffn_norm, nullptr, LLM_NORM_RMS, il);
ggml_tensor * ffn_out = il < (int) hparams.n_layer_dense_lead
? build_dense_ffn(ffn_in, il)
: build_moe(ffn_in, il);
ffn_out = build_sconv(
ffn_out, model.layers[il].shortconv_mlp, off_mlp, il);
cur = ggml_add(ctx0, cur, ffn_out);
cur = build_cvec(cur, il);
cb(cur, "l_out", il);
}
ggml_tensor * inp_out_ids = build_inp_out_ids();
if (inp_out_ids) {
cur = ggml_get_rows(ctx0, cur, inp_out_ids);
}
cur = build_norm(cur, model.output_norm, nullptr, LLM_NORM_RMS, -1);
res->t_embd = cur;
if (!cparams.embeddings) {
cur = ggml_scale(ctx0, cur, hparams.f_logit_scale);
cur = build_lora_mm(model.output, cur);
if (model.output->type == GGML_TYPE_F32) {
ggml_mul_mat_set_prec(cur, GGML_PREC_F32);
}
if (vocab_mask) {
cur = ggml_add(ctx0, cur, vocab_mask);
}
res->t_logits = cur;
}
ggml_build_forward_expand(gf, cur);
}
-27
View File
@@ -1865,18 +1865,6 @@ struct llama_model_lfm2moe : public llama_model_base {
std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
};
struct llama_model_inkling : public llama_model_base {
llama_model_inkling(const struct llama_model_params & params) : llama_model_base(params) {}
void load_arch_hparams(llama_model_loader & ml) override;
void load_arch_tensors(llama_model_loader & ml) override;
struct graph : public llm_graph_context {
graph(const llama_model & model, const llm_graph_params & params);
};
std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
};
struct llama_model_smallthinker : public llama_model_base {
llama_model_smallthinker(const struct llama_model_params & params) : llama_model_base(params) {}
@@ -2139,21 +2127,6 @@ struct llama_model_mimo2 : public llama_model_base {
};
struct llama_model_bailingmoe3 : public llama_model_base {
llama_model_bailingmoe3(const struct llama_model_params & params) : llama_model_base(params) {}
void load_arch_hparams(llama_model_loader & ml) override;
void load_arch_tensors(llama_model_loader & ml) override;
struct graph : public llm_build_delta_net_base {
graph(const llama_model & model, const llm_graph_params & params);
const llama_model & model;
};
std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
};
struct llama_model_kimi_linear : public llama_model_base {
llama_model_kimi_linear(const struct llama_model_params & params) : llama_model_base(params) {}
void load_arch_hparams(llama_model_loader & ml) override;
+2 -74
View File
@@ -259,80 +259,8 @@ set_tests_properties(test-thread-safety PROPERTIES FIXTURES_REQUIRED test-downlo
llama_build_and_test(test-arg-parser.cpp)
llama_build_and_test(test-longhaul.cpp)
llama_test(
test-longhaul
NAME test-longhaul-cpu-qwen35moe
ARGS --cpu-model "${MODEL_DIR}/qwen35moe-moe.gguf" 3145728
)
set_tests_properties(test-longhaul-cpu-qwen35moe PROPERTIES
FIXTURES_REQUIRED generate-models
)
llama_test(
test-longhaul
NAME test-longhaul-cpu-laguna
ARGS --cpu-model "${MODEL_DIR}/laguna-moe.gguf" 2097152
)
set_tests_properties(test-longhaul-cpu-laguna PROPERTIES
FIXTURES_REQUIRED generate-models
)
llama_test(
test-longhaul
NAME test-longhaul-cpu-inkling
ARGS --cpu-model "${MODEL_DIR}/inkling-moe.gguf" 73728 1
)
set_tests_properties(test-longhaul-cpu-inkling PROPERTIES
FIXTURES_REQUIRED generate-models
)
llama_test(
test-longhaul
NAME test-longhaul-cpu-inkling-six-slots
ARGS --cpu-model "${MODEL_DIR}/inkling-moe.gguf" 442368 6
)
set_tests_properties(test-longhaul-cpu-inkling-six-slots PROPERTIES
FIXTURES_REQUIRED generate-models
)
llama_test(
test-longhaul
NAME test-longhaul-cpu-bailingmoe3
ARGS --cpu-model "${MODEL_DIR}/bailingmoe3-moe.gguf" 49152 1
)
set_tests_properties(test-longhaul-cpu-bailingmoe3 PROPERTIES
FIXTURES_REQUIRED generate-models
)
llama_test(
test-longhaul
NAME test-longhaul-cpu-bailingmoe3-two-slots
ARGS --cpu-model "${MODEL_DIR}/bailingmoe3-moe.gguf" 98304 2
)
set_tests_properties(test-longhaul-cpu-bailingmoe3-two-slots PROPERTIES
FIXTURES_REQUIRED generate-models
)
if (APPLE AND GGML_METAL)
llama_test(
test-longhaul
NAME test-longhaul-metal-inkling
ARGS --metal-model "${MODEL_DIR}/inkling-moe.gguf" 73728 1
)
set_tests_properties(test-longhaul-metal-inkling PROPERTIES
FIXTURES_REQUIRED generate-models
)
llama_test(
test-longhaul
NAME test-longhaul-metal-inkling-six-slots
ARGS --metal-model "${MODEL_DIR}/inkling-moe.gguf" 442368 6
)
set_tests_properties(test-longhaul-metal-inkling-six-slots PROPERTIES
FIXTURES_REQUIRED generate-models
)
llama_test(
test-longhaul
NAME test-longhaul-metal-bailingmoe3
ARGS --metal-model "${MODEL_DIR}/bailingmoe3-moe.gguf" 49152 1
)
set_tests_properties(test-longhaul-metal-bailingmoe3 PROPERTIES
FIXTURES_REQUIRED generate-models
)
endif()
llama_build_and_test(test-tokenizer-longhaul.cpp
ARGS ${PROJECT_SOURCE_DIR}/models/ggml-vocab-qwen35.gguf)
llama_build_and_test(test-token-cache.cpp)
target_include_directories(test-token-cache PRIVATE ${PROJECT_SOURCE_DIR}/src)
+15
View File
@@ -160,6 +160,21 @@ static void test(void) {
assert(params.load_mode == LLAMA_LOAD_MODE_LONGHAUL);
assert(params.longhaul_cache_bytes == 2ULL * 1024 * 1024 * 1024);
params.tokenizer_longhaul = false;
params.tokenizer_longhaul_cache_bytes = 0;
argv = {"binary_name", "--tokenizer-longhaul"};
assert(false == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
argv = {"binary_name", "--tokenizer-longhaul", "--tokenizer-longhaul-cache", "64"};
assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
assert(params.tokenizer_longhaul);
assert(params.tokenizer_longhaul_cache_bytes == 64ULL * 1024 * 1024);
params.tokenizer_longhaul = false;
params.tokenizer_longhaul_cache_bytes = 0;
argv = {"binary_name", "--tokenizer-longhaul-cache", "64"};
assert(false == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
argv = {"binary_name", "--token-cache-size", "256", "--token-cache-dir", "/tmp/llama-token-cache-test.sqlite3"};
assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
assert(params.token_cache_size_mib == 256);
+1 -75
View File
@@ -91,7 +91,6 @@ static void test_normalize_quotes_with_embedded_quotes(testing & t);
static void test_tagged_args_with_embedded_quotes(testing & t);
static void test_role_markers_all_templates(testing & t);
static void test_inkling_tml_parser(testing & t);
int main(int argc, char * argv[]) {
testing t(std::cout);
@@ -118,7 +117,6 @@ int main(int argc, char * argv[]) {
t.test("standard_json_tools", test_standard_json_tools_formats);
t.test("normalize_quotes_to_json", test_normalize_quotes_to_json);
t.test("tagged_args_embedded_quotes", test_tagged_args_with_embedded_quotes);
t.test("inkling_tml", test_inkling_tml_parser);
t.test("role_markers_all_templates", test_role_markers_all_templates);
return t.summary();
@@ -2077,79 +2075,6 @@ static void test_role_markers_all_templates(testing & t) {
}
}
static void test_inkling_tml_parser(testing & t) {
const std::string tmpl_src =
"{# <|content_thinking|><|content_text|> #}"
"{%- for message in messages -%}"
"<|message_user|><|content_text|>{{ message.content }}<|end_message|>"
"{%- endfor -%}"
"{%- if add_generation_prompt -%}<|message_model|>{%- endif -%}";
common_chat_templates_ptr tmpls =
common_chat_templates_init(nullptr, tmpl_src);
common_chat_templates_inputs inputs;
common_chat_msg user_message;
user_message.role = "user";
user_message.content = "Hello";
inputs.messages = { user_message };
inputs.add_generation_prompt = true;
inputs.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
inputs.tools = common_chat_tools_parse_oaicompat(build_test_tools());
inputs.parallel_tool_calls = true;
inputs.use_jinja = true;
const common_chat_params params =
common_chat_templates_apply(tmpls.get(), inputs);
t.assert_equal(
"Inkling uses the native PEG parser",
COMMON_CHAT_FORMAT_PEG_NATIVE,
params.format);
t.assert_true("Inkling exposes thinking blocks", params.supports_thinking);
t.assert_equal(
"Inkling generation marker",
"<|message_model|>",
params.generation_prompt);
common_peg_arena arena;
arena.load(params.parser);
common_chat_parser_params parser_params(params);
parser_params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
const common_chat_msg parsed = common_chat_peg_parse(
arena,
"<|content_thinking|>plan<|end_message|>"
"<|message_model|><|content_text|>answer<|end_message|>"
"<|content_model_end_sampling|>",
false,
parser_params);
t.assert_equal("reasoning block", "plan", parsed.reasoning_content);
t.assert_equal("text block", "answer", parsed.content);
const common_chat_msg tool_parsed = common_chat_peg_parse(
arena,
"<|content_thinking|>I should check.<|end_message|>"
"<|message_model|><|content_invoke_tool_json|>"
R"({"name":"get_current_weather","args":{"location":"Chicago","unit":"celsius"}})"
"<|end_message|>"
"<|message_model|><|content_invoke_tool_json|>"
R"({"name":"get_current_weather","args":{"location":"Austin","unit":"fahrenheit"}})"
"<|end_message|><|content_model_end_sampling|>",
false,
parser_params);
t.assert_equal("tool reasoning block", "I should check.", tool_parsed.reasoning_content);
t.assert_equal("parallel tool-call count", 2u, tool_parsed.tool_calls.size());
if (tool_parsed.tool_calls.size() == 2) {
t.assert_equal("first tool name", "get_current_weather", tool_parsed.tool_calls[0].name);
t.assert_equal("second tool name", "get_current_weather", tool_parsed.tool_calls[1].name);
t.assert_true(
"first tool arguments",
tool_parsed.tool_calls[0].arguments.find("Chicago") != std::string::npos);
t.assert_true(
"second tool arguments",
tool_parsed.tool_calls[1].arguments.find("Austin") != std::string::npos);
}
}
// Test that reproduces the Seed-OSS template issue with embedded quotes
static void test_tagged_args_with_embedded_quotes(testing & t) {
json tools = build_edit_tool();
@@ -2267,3 +2192,4 @@ static void test_tagged_args_with_embedded_quotes(testing & t) {
}
}
}
+11 -83
View File
@@ -9,7 +9,6 @@
// TODO: replace with #include "llama-ext.h" in the future
#include "../src/llama-arch.h"
#include "../src/llama-model.h"
#include "../src/llama-model-saver.h"
#include <cinttypes>
@@ -114,16 +113,6 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
n_layer = 3;
} else if (arch == LLM_ARCH_CHAMELEON) {
n_vocab = 10240;
} else if (arch == LLM_ARCH_INKLING) {
n_embd = 64;
n_head = 2;
n_ff = 96;
n_layer = 2;
} else if (arch == LLM_ARCH_BAILINGMOE3) {
n_embd = 64;
n_head = 2;
n_ff = 96;
n_layer = 2;
}
const uint32_t n_embd_head = n_embd / n_head;
@@ -134,7 +123,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
ms.add_kv(LLM_KV_EMBEDDING_LENGTH, n_embd);
ms.add_kv(LLM_KV_FEATURES_LENGTH, n_embd);
ms.add_kv(LLM_KV_BLOCK_COUNT, n_layer);
ms.add_kv(LLM_KV_LEADING_DENSE_BLOCK_COUNT, arch == LLM_ARCH_BAILINGMOE3 ? uint32_t(0) : uint32_t(1));
ms.add_kv(LLM_KV_LEADING_DENSE_BLOCK_COUNT, uint32_t(1));
if (arch == LLM_ARCH_NEMOTRON_H || arch == LLM_ARCH_NEMOTRON_H_MOE) {
std::vector<uint32_t> n_ff_per_layer;
@@ -153,10 +142,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
ms.add_kv(LLM_KV_TIME_DECAY_EXTRA_DIM, uint32_t(128));
ms.add_kv(LLM_KV_FULL_ATTENTION_INTERVAL, uint32_t(2));
if (arch == LLM_ARCH_BAILINGMOE3) {
ms.add_kv(LLM_KV_ATTENTION_HEAD_COUNT, std::vector<uint32_t>(n_layer, n_head));
ms.add_kv(LLM_KV_ATTENTION_HEAD_COUNT_KV, std::vector<uint32_t>({0, 1}));
} else if (arch == LLM_ARCH_PLAMO2 || arch == LLM_ARCH_JAMBA || arch == LLM_ARCH_NEMOTRON_H || arch == LLM_ARCH_NEMOTRON_H_MOE ||
if (arch == LLM_ARCH_PLAMO2 || arch == LLM_ARCH_JAMBA || arch == LLM_ARCH_NEMOTRON_H || arch == LLM_ARCH_NEMOTRON_H_MOE ||
arch == LLM_ARCH_GRANITE_HYBRID || arch == LLM_ARCH_LFM2 || arch == LLM_ARCH_LFM2MOE || arch == LLM_ARCH_KIMI_LINEAR) {
GGML_ASSERT(n_layer >= 2);
std::vector<uint32_t> n_head_per_layer;
@@ -172,13 +158,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
}
ms.add_kv(LLM_KV_ATTENTION_MAX_ALIBI_BIAS, 8.0f);
if (arch == LLM_ARCH_BAILINGMOE3) {
ms.add_kv(LLM_KV_ATTENTION_KEY_LENGTH, uint32_t(16));
ms.add_kv(LLM_KV_ATTENTION_VALUE_LENGTH, uint32_t(8));
ms.add_kv(LLM_KV_ROPE_DIMENSION_COUNT, uint32_t(8));
ms.add_kv(LLM_KV_ATTENTION_KEY_LENGTH_MLA, uint32_t(16));
ms.add_kv(LLM_KV_ATTENTION_VALUE_LENGTH_MLA, uint32_t(8));
} else if (arch == LLM_ARCH_DEEPSEEK2
if (arch == LLM_ARCH_DEEPSEEK2
|| arch == LLM_ARCH_DEEPSEEK32
|| arch == LLM_ARCH_GLM_DSA
|| arch == LLM_ARCH_KIMI_LINEAR
@@ -198,7 +178,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
ms.add_kv(LLM_KV_ATTENTION_GROUPNORM_EPS, 1e-5f);
ms.add_kv(LLM_KV_ATTENTION_GROUPNORM_GROUPS, uint32_t(8));
ms.add_kv(LLM_KV_ATTENTION_Q_LORA_RANK, uint32_t(512));
ms.add_kv(LLM_KV_ATTENTION_KV_LORA_RANK, arch == LLM_ARCH_BAILINGMOE3 ? uint32_t(8) : uint32_t(512));
ms.add_kv(LLM_KV_ATTENTION_KV_LORA_RANK, uint32_t(512));
ms.add_kv(LLM_KV_ATTENTION_RELATIVE_BUCKETS_COUNT, uint32_t(8));
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW, n_ctx/8);
@@ -217,10 +197,6 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
pattern.push_back(il % 2);
}
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, pattern);
} else if (arch == LLM_ARCH_INKLING) {
ms.add_kv(
LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN,
std::vector<uint32_t>({1, 0}));
} else {
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, uint32_t(2));
}
@@ -238,34 +214,14 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
// ms.add_kv(LLM_KV_DENSE_3_FEAT_IN, n_embd);
if (moe) {
ms.add_kv(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, arch == LLM_ARCH_BAILINGMOE3 ? uint32_t(32) : n_ff);
ms.add_kv(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, n_ff);
ms.add_kv(LLM_KV_INTERLEAVE_MOE_LAYER_STEP, uint32_t(2));
ms.add_kv(LLM_KV_EXPERT_COUNT, arch == LLM_ARCH_INKLING || arch == LLM_ARCH_BAILINGMOE3 ? uint32_t(8) : uint32_t(2));
ms.add_kv(LLM_KV_EXPERT_USED_COUNT, arch == LLM_ARCH_INKLING ? uint32_t(6) : arch == LLM_ARCH_BAILINGMOE3 ? uint32_t(2) : uint32_t(1));
ms.add_kv(LLM_KV_EXPERT_SHARED_COUNT, arch == LLM_ARCH_INKLING ? uint32_t(2) : uint32_t(1));
ms.add_kv(LLM_KV_EXPERT_COUNT, uint32_t(2));
ms.add_kv(LLM_KV_EXPERT_USED_COUNT, uint32_t(1));
ms.add_kv(LLM_KV_EXPERT_SHARED_COUNT, uint32_t(1));
ms.add_kv(LLM_KV_EXPERT_GATING_FUNC, uint32_t(2)); // sigmoid
ms.add_kv(LLM_KV_EXPERT_GROUP_SCALE, 1.0f);
ms.add_kv(LLM_KV_EXPERTS_PER_GROUP, uint32_t(1));
if (arch == LLM_ARCH_INKLING) {
ms.add_kv(LLM_KV_EXPERT_WEIGHTS_SCALE, 1.0f);
} else if (arch == LLM_ARCH_BAILINGMOE3) {
ms.add_kv(LLM_KV_EXPERT_WEIGHTS_SCALE, 1.0f);
ms.add_kv(LLM_KV_EXPERT_WEIGHTS_NORM, true);
ms.add_kv(LLM_KV_SWIGLU_CLAMP_EXP, std::vector<float>({4.0f, 4.0f}));
ms.add_kv(LLM_KV_SWIGLU_CLAMP_SHEXP, std::vector<float>({5.0f, 5.0f}));
}
}
if (arch == LLM_ARCH_INKLING) {
ms.add_kv(LLM_KV_INKLING_D_REL, uint32_t(8));
ms.add_kv(LLM_KV_INKLING_REL_EXTENT, uint32_t(32));
ms.add_kv(LLM_KV_INKLING_REL_EXTENT_SWA, uint32_t(16));
ms.add_kv(LLM_KV_INKLING_SHORTCONV_KERNEL, uint32_t(4));
ms.add_kv(LLM_KV_INKLING_DENSE_BLOCK_COUNT, uint32_t(1));
ms.add_kv(LLM_KV_INKLING_LOGIT_SCALE_DENOM, 1.0f);
ms.add_kv(LLM_KV_INKLING_LOG_SCALING_N_FLOOR, uint32_t(16));
ms.add_kv(LLM_KV_INKLING_LOG_SCALING_ALPHA, 0.1f);
ms.add_kv(LLM_KV_INKLING_UNPADDED_VOCAB_SIZE, n_vocab);
}
ms.add_kv(LLM_KV_POSNET_EMBEDDING_LENGTH, n_embd);
@@ -281,10 +237,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
ms.add_kv(LLM_KV_SSM_STATE_SIZE, uint32_t(128));
ms.add_kv(LLM_KV_SSM_TIME_STEP_RANK, n_head);
ms.add_kv(LLM_KV_SSM_GROUP_COUNT, arch == LLM_ARCH_PLAMO2 ? 0 : uint32_t(2));
ms.add_kv(LLM_KV_KDA_HEAD_DIM, arch == LLM_ARCH_BAILINGMOE3 ? uint32_t(16) : uint32_t(128));
if (arch == LLM_ARCH_BAILINGMOE3) {
ms.add_kv(LLM_KV_KDA_GATE_LOWER_BOUND, -5.0f);
}
ms.add_kv(LLM_KV_KDA_HEAD_DIM, uint32_t(128));
ms.add_kv(LLM_KV_WKV_HEAD_SIZE, n_embd/n_head);
ms.add_kv(LLM_KV_SHORTCONV_L_CACHE, uint32_t(3));
@@ -397,7 +350,6 @@ static bool moe_mandatory(const llm_arch arch) {
case LLM_ARCH_EXAONE_MOE:
case LLM_ARCH_BAILINGMOE:
case LLM_ARCH_BAILINGMOE2:
case LLM_ARCH_BAILINGMOE3:
case LLM_ARCH_DOTS1:
case LLM_ARCH_AFMOE:
case LLM_ARCH_ERNIE4_5:
@@ -419,7 +371,6 @@ static bool moe_mandatory(const llm_arch arch) {
case LLM_ARCH_MISTRAL4:
case LLM_ARCH_MELLUM:
case LLM_ARCH_LAGUNA:
case LLM_ARCH_INKLING:
return true;
default:
return false;
@@ -525,11 +476,7 @@ static int save_models(const llm_arch target_arch, const size_t seed, const ggml
if (!moe && moe_mandatory(arch)) {
continue;
}
const bool can_save_fixture =
llama_model_saver_supports_arch(arch) ||
arch == LLM_ARCH_LAGUNA ||
arch == LLM_ARCH_INKLING;
if (!can_save_fixture || !arch_supported(arch)) {
if (!llama_model_saver_supports_arch(arch) || !arch_supported(arch)) {
LOG_INF("%s: %s model (%s) is unsupported, skipping\n", __func__, llm_arch_name(arch), moe ? "MoE" : "dense");
continue;
}
@@ -537,26 +484,7 @@ static int save_models(const llm_arch target_arch, const size_t seed, const ggml
auto model_and_ctx = get_model_and_ctx(gguf_ctx.get(), nullptr, seed, {});
const std::string path = dir + "/" + llm_arch_name(arch) + (moe ? "-moe.gguf" : "-dense.gguf");
LOG_INF("%s: Saving %s model (%s) to %s...\n", __func__, llm_arch_name(arch), moe ? "MoE" : "dense", path.c_str());
const bool private_fixture =
arch == LLM_ARCH_LAGUNA || arch == LLM_ARCH_INKLING;
if (llama_model_saver_supports_arch(arch) && !private_fixture) {
llama_model_save_to_file(model_and_ctx.first.get(), path.c_str());
} else {
// Laguna and Inkling are not supported by the production
// model saver yet.
// Preserve the exact synthetic fixture metadata and attach the
// initialized model tensors so longhaul can be tested from a
// real file without broadening the saver API.
gguf_context_ptr fixture(gguf_init_empty());
// Model initialization normalizes the caller's GGUF context,
// so regenerate the exact private metadata for the file.
gguf_context_ptr fixture_metadata = get_gguf_ctx(arch, moe);
gguf_set_kv(fixture.get(), fixture_metadata.get());
for (const auto & item : llama_internal_get_tensor_map(model_and_ctx.first.get())) {
gguf_add_tensor(fixture.get(), item.second);
}
gguf_write_to_file(fixture.get(), path.c_str(), false);
}
llama_model_save_to_file(model_and_ctx.first.get(), path.c_str());
}
}
llama_log_set(ud.original_logger.callback, ud.original_logger.user_data);
+1 -179
View File
@@ -1,16 +1,11 @@
#include "testing.h"
#include "../src/llama-longhaul.h"
#include "../src/llama-model.h"
#include "ggml-backend.h"
#include "llama-cpp.h"
#include <cmath>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
struct longhaul_fixture {
@@ -133,32 +128,6 @@ static void test_multiple_sources(testing & t) {
t.assert_equal(uint8_t(34), fixture.slot_value(fixture.weights_b, remapped[1]));
}
static void test_concurrent_source_reads(testing & t) {
longhaul_fixture fixture(2, 4, true);
for (int iteration = 0; iteration < 64; ++iteration) {
const int32_t first = iteration % 2 == 0 ? 0 : 2;
const int32_t second = first + 1;
const auto remapped = fixture.remap({first, second});
if (!t.assert_equal(2u, remapped.size())) {
return;
}
t.assert_equal(
uint8_t(1 + first),
fixture.slot_value(fixture.weights_a, remapped[0]));
t.assert_equal(
uint8_t(33 + first),
fixture.slot_value(fixture.weights_b, remapped[0]));
t.assert_equal(
uint8_t(1 + second),
fixture.slot_value(fixture.weights_a, remapped[1]));
t.assert_equal(
uint8_t(33 + second),
fixture.slot_value(fixture.weights_b, remapped[1]));
}
}
static void test_invalid_ids(testing & t) {
longhaul_fixture fixture(2, 4);
@@ -183,137 +152,8 @@ static void test_read_failure_recovery(testing & t) {
t.assert_equal(uint8_t(1), fixture.slot_value(fixture.weights_a, remapped[0]));
}
struct cpu_decode_result {
std::vector<float> logits;
uint32_t capacity = 0;
uint64_t misses = 0;
uint64_t bytes_read = 0;
bool expert_buffer_is_host = false;
};
static cpu_decode_result decode_model(
const std::string & path,
llama_load_mode load_mode,
uint64_t cache_bytes,
int32_t n_gpu_layers,
bool incremental = false) {
llama_model_params model_params = llama_model_default_params();
model_params.n_gpu_layers = n_gpu_layers;
model_params.load_mode = load_mode;
model_params.longhaul_cache_bytes = cache_bytes;
model_params.use_extra_bufts = true;
llama_model_ptr model(llama_model_load_from_file(path.c_str(), model_params));
if (!model) {
throw std::runtime_error("failed to load CPU model: " + path);
}
llama_context_params context_params = llama_context_default_params();
context_params.n_ctx = 32;
context_params.n_batch = 8;
context_params.n_ubatch = 8;
context_params.n_threads = 2;
context_params.n_threads_batch = 2;
llama_context_ptr context(llama_init_from_model(model.get(), context_params));
if (!context) {
throw std::runtime_error("failed to create CPU context: " + path);
}
std::vector<llama_token> tokens = {1, 2, 3, 4, 5, 6, 7, 8};
if (incremental) {
for (size_t i = 0; i < tokens.size(); ++i) {
llama_batch batch = llama_batch_get_one(&tokens[i], 1);
if (llama_decode(context.get(), batch) != 0) {
throw std::runtime_error("failed incremental decode: " + path);
}
}
} else {
llama_batch batch = llama_batch_get_one(tokens.data(), tokens.size());
if (llama_decode(context.get(), batch) != 0) {
throw std::runtime_error("failed batched decode: " + path);
}
}
const int32_t n_vocab = llama_vocab_n_tokens(llama_model_get_vocab(model.get()));
const float * logits = llama_get_logits_ith(context.get(), -1);
if (!logits) {
throw std::runtime_error("CPU model produced no logits: " + path);
}
cpu_decode_result result;
result.logits.assign(logits, logits + n_vocab);
if (load_mode == LLAMA_LOAD_MODE_LONGHAUL) {
llama_longhaul_cache * cache = model->longhaul_cache();
if (!cache) {
throw std::runtime_error("longhaul cache was not created: " + path);
}
result.capacity = cache->capacity();
result.misses = cache->misses();
result.bytes_read = cache->bytes_read_count();
for (const auto & item : llama_internal_get_tensor_map(model.get())) {
const std::string & name = item.first;
if (name.find("ffn_") != std::string::npos &&
name.find("_exps.weight") != std::string::npos &&
item.second->ne[2] == result.capacity) {
result.expert_buffer_is_host = ggml_backend_buffer_is_host(item.second->buffer);
break;
}
}
}
return result;
}
static void test_model(
testing & t,
const std::string & path,
uint64_t cache_bytes,
int32_t n_gpu_layers,
uint32_t expected_capacity) {
const cpu_decode_result regular =
decode_model(path, LLAMA_LOAD_MODE_NONE, 0, n_gpu_layers);
const cpu_decode_result incremental =
decode_model(path, LLAMA_LOAD_MODE_NONE, 0, n_gpu_layers, true);
const cpu_decode_result longhaul =
decode_model(path, LLAMA_LOAD_MODE_LONGHAUL, cache_bytes, n_gpu_layers);
t.assert_equal(expected_capacity, longhaul.capacity);
t.assert_true("longhaul loaded at least one expert", longhaul.misses > 0);
t.assert_true("longhaul read expert bytes", longhaul.bytes_read > 0);
if (n_gpu_layers == 0) {
t.assert_true(
"streamed CPU experts use a directly writable host buffer",
longhaul.expert_buffer_is_host);
}
const auto assert_logits_close = [&](const char * name, const cpu_decode_result & actual) {
if (!t.assert_equal(regular.logits.size(), actual.logits.size())) {
return;
}
double squared_error = 0.0;
double squared_reference = 0.0;
for (size_t i = 0; i < regular.logits.size(); ++i) {
const double delta = double(regular.logits[i]) - double(actual.logits[i]);
squared_error += delta * delta;
squared_reference += double(regular.logits[i]) * double(regular.logits[i]);
}
const double nmse = squared_reference > 0.0
? squared_error / squared_reference
: squared_error;
t.assert_true(
std::string(name) + " logits NMSE = " + std::to_string(nmse),
std::isfinite(nmse) && nmse <= 1e-6);
};
assert_logits_close("incremental", incremental);
assert_logits_close("longhaul", longhaul);
}
int main(int argc, char ** argv) {
testing t;
llama_backend_init();
const char * verbose = getenv("LLAMA_TEST_VERBOSE");
if (verbose) {
@@ -323,32 +163,14 @@ int main(int argc, char ** argv) {
llama_log_set([](ggml_log_level, const char *, void *) {}, nullptr);
}
if ((argc == 4 || argc == 5) &&
(std::string(argv[1]) == "--cpu-model" ||
std::string(argv[1]) == "--metal-model")) {
const std::string path = argv[2];
const uint64_t cache_bytes = std::stoull(argv[3]);
const uint32_t expected_capacity = argc == 5 ? std::stoul(argv[4]) : 1;
const bool metal = std::string(argv[1]) == "--metal-model";
t.test(metal ? "metal_model" : "cpu_model", [&](testing & current) {
test_model(current, path, cache_bytes, metal ? 99 : 0, expected_capacity);
});
const int result = t.summary();
llama_backend_free();
return result;
}
if (argc > 1) {
t.set_filter(argv[1]);
}
t.test("batch_planning", test_batch_planning);
t.test("multiple_sources", test_multiple_sources);
t.test("concurrent_reads", test_concurrent_source_reads);
t.test("invalid_ids", test_invalid_ids);
t.test("read_failure", test_read_failure_recovery);
const int result = t.summary();
llama_backend_free();
return result;
return t.summary();
}
+144
View File
@@ -0,0 +1,144 @@
#include "llama.h"
#include "../src/llama-token-cache.h"
#include "../src/llama-tokenizer-longhaul.h"
#include <chrono>
#include <cstring>
#include <filesystem>
#include <string>
#include <thread>
#include <vector>
#undef NDEBUG
#include <cassert>
static std::vector<llama_token> tokenize(const llama_vocab * vocab, const std::string & text) {
int32_t count = llama_tokenize(vocab, text.data(), int32_t(text.size()), nullptr, 0, false, true);
assert(count < 0);
std::vector<llama_token> result(size_t(-count));
count = llama_tokenize(vocab, text.data(), int32_t(text.size()), result.data(), result.size(), false, true);
assert(count == int32_t(result.size()));
return result;
}
int main(int argc, char ** argv) {
namespace fs = std::filesystem;
const fs::path root = fs::temp_directory_path() /
("llama-tokenizer-longhaul-test-" + std::to_string(
std::chrono::high_resolution_clock::now().time_since_epoch().count()));
const std::string db = (root / "token-cache.sqlite3").string();
const llama_token_cache_params params = {
db.c_str(),
128 * 1024 * 1024,
};
assert(llama_token_cache_configure(params));
assert(llama_token_cache_clear());
std::vector<llama_tokenizer_longhaul_reverse> reverse;
std::vector<llama_tokenizer_longhaul_merge> merges;
for (int i = 0; i < 200; ++i) {
reverse.push_back({"token-" + std::to_string(i), i});
if (i > 0) {
merges.push_back({
"token-" + std::to_string(i - 1),
"token-" + std::to_string(i),
i - 1,
});
}
}
{
llama_tokenizer_longhaul_index index("source:test", 512);
std::string error;
assert(index.prepare(reverse, merges, "tokenizer-id", error));
assert(error.empty());
assert(index.n_merges() == merges.size());
for (int i = 0; i < 200; ++i) {
assert(index.text_to_token("token-" + std::to_string(i)) == i);
}
assert(index.text_to_token("missing") == LLAMA_TOKEN_NULL);
for (int i = 1; i < 200; ++i) {
assert(index.find_bpe_rank(
"token-" + std::to_string(i - 1),
"token-" + std::to_string(i)) == i - 1);
}
assert(index.find_bpe_rank("missing", "pair") == -1);
assert(index.cache_bytes_used() <= index.cache_capacity());
const auto loaded_merges = index.get_bpe_merges();
assert(loaded_merges.size() == merges.size());
assert(loaded_merges.front() == "token-0 token-1");
assert(loaded_merges.back() == "token-198 token-199");
}
// A warm open must use the completed artifact even without rebuild inputs.
{
llama_tokenizer_longhaul_index index("source:test", 128);
std::string error;
assert(index.prepare({}, {}, "tokenizer-id", error));
assert(index.text_to_token("token-42") == 42);
std::vector<std::thread> workers;
for (int thread = 0; thread < 4; ++thread) {
workers.emplace_back([&index, thread] {
for (int i = thread; i < 200; i += 4) {
assert(index.text_to_token("token-" + std::to_string(i)) == i);
}
});
}
for (auto & worker : workers) worker.join();
assert(index.cache_bytes_used() <= index.cache_capacity());
}
if (argc > 1) {
llama_backend_init();
llama_model_params normal_params = llama_model_default_params();
normal_params.vocab_only = true;
llama_model * normal = llama_model_load_from_file(argv[1], normal_params);
assert(normal != nullptr);
llama_model_kv_override overrides[2] = {};
std::strcpy(overrides[0].key, "general.architecture");
overrides[0].tag = LLAMA_KV_OVERRIDE_TYPE_STR;
std::strcpy(overrides[0].val_str, "qwen35moe");
llama_model_params longhaul_params = llama_model_default_params();
longhaul_params.vocab_only = true;
longhaul_params.kv_overrides = overrides;
longhaul_params.tokenizer_longhaul = true;
longhaul_params.tokenizer_longhaul_cache_bytes = 1024;
llama_model * longhaul = llama_model_load_from_file(argv[1], longhaul_params);
assert(longhaul != nullptr);
const llama_vocab * normal_vocab = llama_model_get_vocab(normal);
const llama_vocab * longhaul_vocab = llama_model_get_vocab(longhaul);
for (const std::string & text : {
"Hello, world!",
" tokenizer longhaul",
"Qwen3.5: 12345\n",
"<|im_end|>",
"Unicode: Καλημέρα 世界 🚚",
}) {
const auto expected = tokenize(normal_vocab, text);
const auto actual = tokenize(longhaul_vocab, text);
assert(actual == expected);
for (const auto token : actual) {
assert(std::strcmp(
llama_vocab_get_text(normal_vocab, token),
llama_vocab_get_text(longhaul_vocab, token)) == 0);
}
}
llama_model_free(longhaul);
llama_model_free(normal);
llama_backend_free();
}
assert(llama_token_cache_clear());
std::error_code ec;
fs::remove_all(root, ec);
return 0;
}
+3 -1
View File
@@ -35,6 +35,8 @@
| `--token-cache-size N` | shared tokenizer and tokenized-text cache size in MiB (default: 5120, 0 = disable)<br/>(env: LLAMA_ARG_TOKEN_CACHE_SIZE) |
| `--token-cache-dir PATH` | path to the shared tokenizer and tokenized-text cache database<br/>(env: LLAMA_ARG_TOKEN_CACHE_DIR) |
| `--token-cache-clear` | clear the shared tokenizer and tokenized-text cache before loading the model<br/>(env: LLAMA_ARG_TOKEN_CACHE_CLEAR) |
| `--tokenizer-longhaul` | build and page the Qwen3.5/Laguna tokenizer through a bounded cache<br/>(env: LLAMA_ARG_TOKENIZER_LONGHAUL) |
| `--tokenizer-longhaul-cache N` | tokenizer-longhaul RAM cache size in MiB<br/>(env: LLAMA_ARG_TOKENIZER_LONGHAUL_CACHE) |
| `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) |
| `-p, --prompt PROMPT` | prompt to start generation with; for system message, use -sys |
| `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) |
@@ -61,7 +63,7 @@
| `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) |
| `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) |
| `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/>- longhaul: stream routed MoE experts through a bounded cache<br/><br/>(env: LLAMA_ARG_LOAD_MODE) |
| `--longhaul` | stream routed Qwen3.5 MoE, Laguna, Inkling, or Ling 3.0 experts through a bounded CPU or Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul` | stream routed Qwen3.5 MoE experts through a bounded Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul-cache N` | longhaul expert cache size in GiB<br/>(env: LLAMA_ARG_LONGHAUL_CACHE) |
| `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) |
| `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) |
+3 -1
View File
@@ -118,6 +118,8 @@ llama-completion.exe -m models\gemma-1.1-7b-it.Q4_K_M.gguf --ignore-eos -n -1
| `--token-cache-size N` | shared tokenizer and tokenized-text cache size in MiB (default: 5120, 0 = disable)<br/>(env: LLAMA_ARG_TOKEN_CACHE_SIZE) |
| `--token-cache-dir PATH` | path to the shared tokenizer and tokenized-text cache database<br/>(env: LLAMA_ARG_TOKEN_CACHE_DIR) |
| `--token-cache-clear` | clear the shared tokenizer and tokenized-text cache before loading the model<br/>(env: LLAMA_ARG_TOKEN_CACHE_CLEAR) |
| `--tokenizer-longhaul` | build and page the Qwen3.5/Laguna tokenizer through a bounded cache<br/>(env: LLAMA_ARG_TOKENIZER_LONGHAUL) |
| `--tokenizer-longhaul-cache N` | tokenizer-longhaul RAM cache size in MiB<br/>(env: LLAMA_ARG_TOKENIZER_LONGHAUL_CACHE) |
| `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) |
| `-p, --prompt PROMPT` | prompt to start generation with; for system message, use -sys |
| `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) |
@@ -144,7 +146,7 @@ llama-completion.exe -m models\gemma-1.1-7b-it.Q4_K_M.gguf --ignore-eos -n -1
| `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) |
| `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) |
| `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/>- longhaul: stream routed MoE experts through a bounded cache<br/><br/>(env: LLAMA_ARG_LOAD_MODE) |
| `--longhaul` | stream routed Qwen3.5 MoE, Laguna, Inkling, or Ling 3.0 experts through a bounded CPU or Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul` | stream routed Qwen3.5 MoE experts through a bounded Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul-cache N` | longhaul expert cache size in GiB<br/>(env: LLAMA_ARG_LONGHAUL_CACHE) |
| `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) |
| `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) |
+2
View File
@@ -57,6 +57,8 @@ test parameters:
-ctk, --cache-type-k <t> (default: f16)
-ctv, --cache-type-v <t> (default: f16)
--longhaul-cache <GiB> expert cache size for longhaul mode
--tokenizer-longhaul page tokenizer indexes through a bounded cache
--tokenizer-longhaul-cache <MiB> tokenizer lookup cache size
-t, --threads <n> (default: system dependent)
-C, --cpu-mask <hex,hex> (default: 0x0)
--cpu-strict <0|1> (default: 0)
+53 -3
View File
@@ -342,6 +342,8 @@ struct cmd_params {
std::vector<llama_split_mode> split_mode;
std::vector<llama_load_mode> load_mode;
uint64_t longhaul_cache_bytes;
bool tokenizer_longhaul;
uint64_t tokenizer_longhaul_cache_bytes;
std::vector<int> main_gpu;
std::vector<bool> no_kv_offload;
std::vector<llama_flash_attn_type> flash_attn;
@@ -387,6 +389,8 @@ static const cmd_params cmd_params_defaults = {
/* split_mode */ { LLAMA_SPLIT_MODE_LAYER },
/* load_mode */ { LLAMA_LOAD_MODE_MMAP },
/* longhaul_cache_bytes */ 0,
/* tokenizer_longhaul */ false,
/* tokenizer_longhaul_cache_bytes */ 0,
/* main_gpu */ { 0 },
/* no_kv_offload */ { false },
/* flash_attn */ { LLAMA_FLASH_ATTN_TYPE_AUTO },
@@ -463,6 +467,8 @@ static void print_usage(int /* argc */, char ** argv) {
printf(" -dev, --device <dev0/dev1/...> (default: auto)\n");
printf(" -lm, --load-mode <none|mmap|mlock|mmap+mlock|dio|longhaul> (default: %s)\n", join(transform_to_str(cmd_params_defaults.load_mode, llama_load_mode_name), ",").c_str());
printf(" --longhaul-cache <GiB> expert cache size for longhaul mode\n");
printf(" --tokenizer-longhaul page tokenizer indexes through a bounded cache\n");
printf(" --tokenizer-longhaul-cache <MiB> tokenizer lookup cache size (required with --tokenizer-longhaul)\n");
printf(" -mmp, --mmap <0|1> (DEPRECATED IN FAVOUR OF --load-mode)\n");
printf(" -dio, --direct-io <0|1> (DEPRECATED IN FAVOUR OF --load-mode)\n");
printf(" -embd, --embeddings <0|1> (default: %s)\n", join(cmd_params_defaults.embeddings, ",").c_str());
@@ -525,6 +531,8 @@ static cmd_params parse_cmd_params(int argc, char ** argv) {
params.no_warmup = cmd_params_defaults.no_warmup;
params.offline = cmd_params_defaults.offline;
params.longhaul_cache_bytes = cmd_params_defaults.longhaul_cache_bytes;
params.tokenizer_longhaul = cmd_params_defaults.tokenizer_longhaul;
params.tokenizer_longhaul_cache_bytes = cmd_params_defaults.tokenizer_longhaul_cache_bytes;
if (const char * env = getenv("HF_TOKEN")) {
params.hf_token = env;
@@ -801,6 +809,19 @@ static cmd_params parse_cmd_params(int argc, char ** argv) {
break;
}
params.longhaul_cache_bytes = gib * 1024 * 1024 * 1024;
} else if (arg == "--tokenizer-longhaul") {
params.tokenizer_longhaul = true;
} else if (arg == "--tokenizer-longhaul-cache") {
if (++i >= argc) {
invalid_param = true;
break;
}
const uint64_t mib = std::stoull(argv[i]);
if (mib == 0 || mib > UINT64_MAX / 1024 / 1024) {
invalid_param = true;
break;
}
params.tokenizer_longhaul_cache_bytes = mib * 1024 * 1024;
} else if (arg == "-mg" || arg == "--main-gpu") {
if (++i >= argc) {
invalid_param = true;
@@ -1157,6 +1178,10 @@ static cmd_params parse_cmd_params(int argc, char ** argv) {
fprintf(stderr, "error: longhaul load mode requires --longhaul-cache N\n");
exit(1);
}
if (params.tokenizer_longhaul != (params.tokenizer_longhaul_cache_bytes != 0)) {
fprintf(stderr, "error: --tokenizer-longhaul and --tokenizer-longhaul-cache must be used together\n");
exit(1);
}
if (params.main_gpu.empty()) {
params.main_gpu = cmd_params_defaults.main_gpu;
}
@@ -1224,6 +1249,8 @@ struct cmd_params_instance {
llama_split_mode split_mode;
llama_load_mode load_mode;
uint64_t longhaul_cache_bytes;
bool tokenizer_longhaul;
uint64_t tokenizer_longhaul_cache_bytes;
int main_gpu;
bool no_kv_offload;
llama_flash_attn_type flash_attn;
@@ -1246,6 +1273,8 @@ struct cmd_params_instance {
mparams.split_mode = split_mode;
mparams.load_mode = load_mode;
mparams.longhaul_cache_bytes = longhaul_cache_bytes;
mparams.tokenizer_longhaul = tokenizer_longhaul;
mparams.tokenizer_longhaul_cache_bytes = tokenizer_longhaul_cache_bytes;
mparams.main_gpu = main_gpu;
mparams.tensor_split = tensor_split.data();
mparams.no_host = no_host;
@@ -1294,6 +1323,8 @@ struct cmd_params_instance {
split_mode == other.split_mode &&
main_gpu == other.main_gpu && tensor_split == other.tensor_split &&
load_mode == other.load_mode && longhaul_cache_bytes == other.longhaul_cache_bytes &&
tokenizer_longhaul == other.tokenizer_longhaul &&
tokenizer_longhaul_cache_bytes == other.tokenizer_longhaul_cache_bytes &&
devices == other.devices && no_host == other.no_host &&
vec_tensor_buft_override_equal(tensor_buft_overrides, other.tensor_buft_overrides);
}
@@ -1368,6 +1399,8 @@ static std::vector<cmd_params_instance> get_cmd_params_instances(const cmd_param
/* .split_mode = */ sm,
/* .load_mode = */ lm,
/* .longhaul_cache_bytes = */ params.longhaul_cache_bytes,
/* .tokenizer_longhaul = */ params.tokenizer_longhaul,
/* .tokenizer_longhaul_cache_bytes = */ params.tokenizer_longhaul_cache_bytes,
/* .main_gpu = */ mg,
/* .no_kv_offload = */ nkvo,
/* .flash_attn = */ fa,
@@ -1405,6 +1438,8 @@ static std::vector<cmd_params_instance> get_cmd_params_instances(const cmd_param
/* .split_mode = */ sm,
/* .load_mode = */ lm,
/* .longhaul_cache_bytes = */ params.longhaul_cache_bytes,
/* .tokenizer_longhaul = */ params.tokenizer_longhaul,
/* .tokenizer_longhaul_cache_bytes = */ params.tokenizer_longhaul_cache_bytes,
/* .main_gpu = */ mg,
/* .no_kv_offload = */ nkvo,
/* .flash_attn = */ fa,
@@ -1442,6 +1477,8 @@ static std::vector<cmd_params_instance> get_cmd_params_instances(const cmd_param
/* .split_mode = */ sm,
/* .load_mode = */ lm,
/* .longhaul_cache_bytes = */ params.longhaul_cache_bytes,
/* .tokenizer_longhaul = */ params.tokenizer_longhaul,
/* .tokenizer_longhaul_cache_bytes = */ params.tokenizer_longhaul_cache_bytes,
/* .main_gpu = */ mg,
/* .no_kv_offload = */ nkvo,
/* .flash_attn = */ fa,
@@ -1484,6 +1521,8 @@ struct test {
llama_split_mode split_mode;
llama_load_mode load_mode;
uint64_t longhaul_cache_bytes;
bool tokenizer_longhaul;
uint64_t tokenizer_longhaul_cache_bytes;
int main_gpu;
bool no_kv_offload;
llama_flash_attn_type flash_attn;
@@ -1524,6 +1563,8 @@ struct test {
split_mode = inst.split_mode;
load_mode = inst.load_mode;
longhaul_cache_bytes = inst.longhaul_cache_bytes;
tokenizer_longhaul = inst.tokenizer_longhaul;
tokenizer_longhaul_cache_bytes = inst.tokenizer_longhaul_cache_bytes;
main_gpu = inst.main_gpu;
no_kv_offload = inst.no_kv_offload;
flash_attn = inst.flash_attn;
@@ -1591,7 +1632,8 @@ struct test {
"n_ubatch", "n_threads", "cpu_mask", "cpu_strict", "poll",
"type_k", "type_v", "n_gpu_layers", "n_cpu_moe", "split_mode",
"main_gpu", "no_kv_offload", "flash_attn", "devices", "tensor_split",
"tensor_buft_overrides", "load_mode", "longhaul_cache_bytes", "embeddings",
"tensor_buft_overrides", "load_mode", "longhaul_cache_bytes",
"tokenizer_longhaul", "tokenizer_longhaul_cache_bytes", "embeddings",
"no_op_offload", "no_host", "fit_target", "fit_min_ctx",
"n_prompt", "n_gen", "n_depth",
"test_time", "avg_ns", "stddev_ns", "avg_ts", "stddev_ts"
@@ -1607,11 +1649,11 @@ struct test {
field == "main_gpu" || field == "n_prompt" || field == "n_gen" || field == "n_depth" || field == "avg_ns" ||
field == "stddev_ns" || field == "no_op_offload" || field == "n_cpu_moe" ||
field == "fit_target" || field == "fit_min_ctx" || field == "flash_attn" ||
field == "longhaul_cache_bytes") {
field == "longhaul_cache_bytes" || field == "tokenizer_longhaul_cache_bytes") {
return INT;
}
if (field == "f16_kv" || field == "no_kv_offload" || field == "cpu_strict" ||
field == "embeddings" || field == "no_host") {
field == "embeddings" || field == "no_host" || field == "tokenizer_longhaul") {
return BOOL;
}
if (field == "avg_ts" || field == "stddev_ts") {
@@ -1688,6 +1730,8 @@ struct test {
tensor_buft_overrides_str,
llama_load_mode_name(load_mode),
std::to_string(longhaul_cache_bytes),
std::to_string(tokenizer_longhaul),
std::to_string(tokenizer_longhaul_cache_bytes),
std::to_string(embeddings),
std::to_string(no_op_offload),
std::to_string(no_host),
@@ -2005,6 +2049,12 @@ struct markdown_printer : public printer {
if (params.longhaul_cache_bytes != cmd_params_defaults.longhaul_cache_bytes) {
fields.emplace_back("longhaul_cache_bytes");
}
if (params.tokenizer_longhaul != cmd_params_defaults.tokenizer_longhaul) {
fields.emplace_back("tokenizer_longhaul");
}
if (params.tokenizer_longhaul_cache_bytes != cmd_params_defaults.tokenizer_longhaul_cache_bytes) {
fields.emplace_back("tokenizer_longhaul_cache_bytes");
}
if (params.embeddings.size() > 1 || params.embeddings != cmd_params_defaults.embeddings) {
fields.emplace_back("embeddings");
}
+3 -1
View File
@@ -56,6 +56,8 @@ For the full list of features, please refer to [server's changelog](https://gith
| `--token-cache-size N` | shared tokenizer and tokenized-text cache size in MiB (default: 5120, 0 = disable)<br/>(env: LLAMA_ARG_TOKEN_CACHE_SIZE) |
| `--token-cache-dir PATH` | path to the shared tokenizer and tokenized-text cache database<br/>(env: LLAMA_ARG_TOKEN_CACHE_DIR) |
| `--token-cache-clear` | clear the shared tokenizer and tokenized-text cache before loading the model<br/>(env: LLAMA_ARG_TOKEN_CACHE_CLEAR) |
| `--tokenizer-longhaul` | build and page the Qwen3.5/Laguna tokenizer through a bounded cache<br/>(env: LLAMA_ARG_TOKENIZER_LONGHAUL) |
| `--tokenizer-longhaul-cache N` | tokenizer-longhaul RAM cache size in MiB<br/>(env: LLAMA_ARG_TOKENIZER_LONGHAUL_CACHE) |
| `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) |
| `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) |
| `-e, --escape, --no-escape` | whether to process escapes sequences (\n, \r, \t, \', \", \\) (default: true) |
@@ -78,7 +80,7 @@ For the full list of features, please refer to [server's changelog](https://gith
| `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) |
| `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) |
| `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/>- longhaul: stream routed MoE experts through a bounded cache<br/><br/>(env: LLAMA_ARG_LOAD_MODE) |
| `--longhaul` | stream routed Qwen3.5 MoE, Laguna, Inkling, or Ling 3.0 experts through a bounded CPU or Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul` | stream routed Qwen3.5 MoE experts through a bounded Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul-cache N` | longhaul expert cache size in GiB<br/>(env: LLAMA_ARG_LONGHAUL_CACHE) |
| `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) |
| `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) |