Files
ollama/llama/compat/llama-ollama-compat.h
jmorganca 25223160d8 llama/compat: add in-memory shim so llama-server can load Ollama-format GGUFs
Older Ollama builds ship GGUFs that diverge slightly from upstream llama.cpp
in arch names, KV keys, tensor names, and (for vision models) file layout
(text+vision in one monolithic file). This adds a self-contained compat
layer that translates those files in memory at load time, so
~/.ollama/models/blobs/* can be served by upstream llama-server with no
re-conversion and no re-download.

Structure:
  llama/compat/
    llama-ollama-compat.{h,cpp}   — the shim (Ollama-owned, ~500 LOC)
    upstream-edits.patch          — ~48 lines of call-site hooks in 6 upstream files
    compat.cmake                  — include()-able CMake fragment
    README.md                     — what/why/how-to-regen

Integration: llama/server/CMakeLists.txt includes compat.cmake and passes
OLLAMA_LLAMA_CPP_COMPAT_PATCH_COMMAND to FetchContent_Declare via
PATCH_COMMAND. When OLLAMA_LLAMA_CPP_SOURCE is set (dev mode), the patch is
skipped so the developer's tree stays untouched.

Currently handles gemma3 (text + vision). Pattern is data-driven — adding
other archs is a new handle_<arch>() + one dispatch line. See README for
the per-arch checklist.

Verified end-to-end: `llama-server --model BLOB --mmproj BLOB` with an
Ollama gemma3:latest blob answers both text prompts ("Paris") and vision
prompts (correct image descriptions).
2026-04-20 09:29:34 -07:00

79 lines
3.4 KiB
C++
Vendored

#pragma once
// Ollama-format GGUF compatibility shim.
//
// Older Ollama builds ship GGUFs that differ from upstream in a handful of
// ways per-architecture. This shim detects those files during load and
// translates them in-memory so the rest of llama.cpp can load them
// unmodified. Single entry point per hook; all logic is data-driven from
// per-architecture rules.
//
// Two hooks:
// 1. translate_metadata() — runs after gguf_init_from_file, mutates KVs
// and (optionally) tensor names on the gguf_context / ggml_context.
// 2. apply_tensor_transforms() — runs after load_all_data, rewrites
// tensor data that differs numerically (e.g. gemma3 RMSNorm +1).
#include <cstdint>
#include <string>
#include <vector>
struct gguf_context;
struct ggml_context;
struct ggml_tensor;
struct llama_model_loader;
namespace llama_ollama_compat {
// Inspect and mutate the just-loaded gguf_context. May update arch_name if
// the file uses an Ollama-specific architecture name. Safe to call for any
// model — no-op when no Ollama markers are present.
//
// If compat was applied, registers any tensor transforms against `ml` for
// apply_tensor_transforms() to consume later.
void translate_metadata(const llama_model_loader * ml,
gguf_context * meta,
ggml_context * ctx,
std::string & arch_name);
// Returns true if the loader should skip this tensor entirely (not add to
// weights_map, not count toward n_tensors). Used to drop embedded vision
// tensors from the text model without physically removing them.
bool should_skip_tensor(const llama_model_loader * ml, const char * tensor_name);
// Called after load_all_data returns for a model context. Applies any
// registered transforms (read tensor data from the backend buffer, modify,
// write back) to tensors in `ctx`. Call once per model context.
void apply_tensor_transforms(const llama_model_loader * ml, ggml_context * ctx);
// Called from the clip loader (tools/mtmd/clip.cpp). If the file is an
// Ollama-format monolithic GGUF (text + embedded vision), rewrites the
// clip-facing view of the metadata so the clip loader sees it as a normal
// mmproj file. Safe to call unconditionally — no-op when not an Ollama file.
//
// Operations:
// - sets general.architecture = "clip"
// - sets clip.has_vision_encoder, clip.projector_type, clip.use_gelu
// - copies gemma3.vision.* KVs into clip.vision.*
// - renames vision tensors (v.patch_embedding -> v.patch_embd, etc.)
// - promotes specific F16 tensors to F32 in the ggml_context so clip
// allocates the correct buffer size
//
// Non-vision text tensors remain in the gguf but are never looked up by
// clip, so they cost nothing.
void translate_clip_metadata(gguf_context * meta, ggml_context * ctx);
// Called from clip.cpp's tensor-loading loop, before reading bytes from the
// file. If this tensor was marked for type promotion by translate_clip_metadata,
// fills `out` with the promoted data (e.g. F16→F32) and returns true. The
// caller should then use `out` instead of reading from the file.
//
// `file_offset` is the absolute file offset of the original (pre-promotion)
// tensor data in the source GGUF.
bool supply_promoted_tensor_data(const ggml_tensor * cur,
const char * source_file,
size_t file_offset,
std::vector<uint8_t> & out);
} // namespace llama_ollama_compat