Skip to content

GGUF_do_not_need_an_external_model_specs_folder - #53

Merged
0xShug0 merged 5 commits into
0xShug0:mainfrom
mirek190:agent/gguf-no-external-model-specs
Jul 15, 2026
Merged

GGUF_do_not_need_an_external_model_specs_folder#53
0xShug0 merged 5 commits into
0xShug0:mainfrom
mirek190:agent/gguf-no-external-model-specs

Conversation

@mirek190

@mirek190 mirek190 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR makes model package specifications portable without forcing every runtime binary to carry every model layout.

  • New GGUF files embed the resolved package spec and family in GGUF metadata.
  • CLI and server support an explicit --model-spec-override file or directory.
  • Runtime resolution is deterministic and preserves compatibility with older packages.
  • audiocpp_gguf always carries the conversion-time package catalog, even when copied away from the source tree.
  • Standalone conversion validates required tensor namespaces and sidecars before writing.
  • --no-sidecars creates an intentional tensor-only GGUF, but still embeds and validates the package spec.
  • AUDIOCPP_DEPLOYMENT_BUILD=ON optionally compiles fallback specs into CLI/server binaries for safetensors and legacy GGUF deployments.

Runtime package-spec resolution

explicit --model-spec-override
              |
              v
   package spec embedded in GGUF
              |
              v
  compiled deployment catalog
  AUDIOCPP_DEPLOYMENT_BUILD=ON
              |
              v
   external model_specs discovery

An explicit override is authoritative. If it is invalid, loading fails instead of silently falling through to another source.

Deployment behavior

Model format Package spec source External model files
Safetensors Override, deployment binary, or discovered model_specs Required
New standalone GGUF Embedded in GGUF None
New tensor-only GGUF made with --no-sidecars Embedded in GGUF Required sidecars
Legacy GGUF without embedded spec Deployment binary or discovered model_specs Depends on sidecars

A normal new standalone GGUF deployment can therefore be:

application/
+-- audiocpp_cli.exe or audiocpp_server.exe
+-- model.gguf
+-- runtime/CUDA libraries when required

No external model_specs folder is needed for that GGUF.

Converter behavior

The converter finds a package spec in this order:

  1. --model-spec or --model-spec-override
  2. Model config.json metadata
  3. model_spec.json or model_specs below the model root
  4. An externally discovered model_specs catalog
  5. The converter's bundled catalog

The selected spec is embedded as versioned audiocpp.model_spec metadata. The converter rejects missing required namespaces and sidecars before producing a deployable model. --allow-missing-model-spec remains an explicit escape hatch for generic tensor archives that are not expected to load as audio.cpp models.

Compatibility

  • Existing safetensors layouts continue to work.
  • Legacy GGUF files continue to use compiled or externally discovered specs.
  • Explicit CLI and server overrides support custom and development layouts.
  • Uppercase .GGUF paths are handled.

Validation

  • Clean MinGW CPU compilation of engine_runtime, audiocpp_gguf, and both targeted tests
  • gguf_tensor_source_test passed
  • asr_standalone_gguf_test passed
  • CPU and CUDA builds were completed during implementation
  • Deployment catalog enabled and disabled configurations tested
  • Portable copied converter tested without a neighboring model_specs directory
  • Standalone Citrinet conversion and CLI loading tested
  • Explicit override precedence and invalid-override failure tested
  • Qwen3 ASR 1.7B and Forced Aligner family inference tested
  • git diff --check passed

The MinGW CLI executable link still encounters the repository's existing WinMain entry-point mismatch; all changed CLI sources compile, and the supported Windows build/check remains covered by CI.

Compile model_specs/*.json into engine_runtime at CMake generation time so CLI and server deployments do not need an external model_specs directory. This applies to both safetensors packages and supported GGUF models: GGUF weights and sidecars remain model data, while the matching package layout is carried by the binary.

Add an explicit --model-spec-override escape hatch for CLI workflows and server deployments, including server-wide and per-model JSON configuration. Built-in specs remain the default and per-model server overrides take precedence.
@mirek190
mirek190 marked this pull request as ready for review July 14, 2026 18:21
@0xShug0

0xShug0 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

@mirek190 Good catch. I will take a look.

@mirek190

Copy link
Copy Markdown
Contributor Author

Even a safetensor models had that problem.

Currently that config can be straight in the folder with safetensor model.

@0xShug0

0xShug0 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

@mirek190 Thanks, the motivation makes sense. I'd like to split this into two smaller PRs though.
The model_spec_override support is clearly useful. Could you make that the first PR by itself?

I’m less sure about embedding all model_specs into the audio.cpp binary. One workflow I care about is contributor-driven model support. Suppose a contributor adds a new GGUF package under a different GGUF layout. In the current design, the package layout is runtime data: they can contribute or ship an updated model_specs/.json, and the existing binary can use that new layout without recompilation. Of course, the current spec-based mechanism is still preliminary. Longer term, I’d like this to become more robust: audio.cpp could automatically discover/match package specs instead of relying only on a fixed built-in spec for a family. For example, it could search a spec directory and match by family/layout/version metadata.

Maybe a build flag would be a good compromise here. What about making embedded specs optional, e.g. AUDIOCPP_DEPLOYMENT_BUILD=ON/OFF? With it off, audio.cpp keeps today’s behavior and loads model_specs/.json from disk. With it on, CMake embeds the specs into the binary for portable deployments.

@mirek190

mirek190 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

For GGUF models, you can load a separate configuration file from a specified path, such as model_specs/*.json, by using the --model-spec-override option.

Without this option, the configuration is loaded in the following order:

From the GGUF model metadata.
If it is not present in the GGUF file, from the binary.

By default, the GGUF build tool updated in this commit will not build a GGUF model when a model_specs/*.json configuration is required but cannot be found. The tool searches for this configuration in the model_specs directory and in the SafeTensors model configuration.

Also AUDIOCPP_DEPLOYMENT_BUILD=ON/OF is added. Read bellow how it works.


Changes - continuation for gguf workground:

1. Package specification embedded in GGUF - example

Newly converted GGUF files store:

audiocpp.model_spec.version = 1
audiocpp.model_spec.family = qwen3_asr
audiocpp.model_spec.json = { complete package-spec JSON }

The embedded JSON describes:

  • Tensor sources and namespaces
  • Configuration files
  • Tokenizers
  • Required sidecars
  • GGUF and safetensors layouts
  • Model family

The GGUF reader validates:

  • All three metadata fields are present together
  • Metadata types are correct
  • Version is supported
  • Family and JSON are not empty
  • The family inside the JSON matches the GGUF metadata

Therefore, a new standalone GGUF normally needs only:

application/
├── audiocpp_cli.exe or audiocpp_server.exe
├── model.gguf
└── CUDA/runtime DLLs

No external model_specs directory is required.

2. Runtime package-spec resolution

CLI and server now resolve the package specification in this order:

--model-spec-override


spec embedded in model.gguf


spec compiled into deployment binary


external model_specs discovery


error

Important details:

  • An explicit override is authoritative. If it is invalid, loading fails instead of silently selecting another spec.
  • If a GGUF embeds a spec for the wrong family, loading fails with a family-mismatch error.
  • Both direct model.gguf paths and directories containing model.gguf work.
  • Uppercase extensions such as MODEL.GGUF are recognized.
  • External discovery checks locations near the model and model_specs directories from the working directory upward.

The registry now passes the actual model path into package-spec resolution, allowing it to inspect GGUF metadata before choosing a
specification.

3. Converter and runtime catalogs are separated

Previously, PR #53 compiled all model_specs/*.json files into every runtime binary.

The update separates this into two cases.

audiocpp_gguf converter

The converter always carries a compiled package-spec catalog.

This means you can copy:

audiocpp_gguf.exe
runtime DLLs
model directory

and convert a model without keeping the audio.cpp source-tree model_specs directory beside the converter.

The converter uses that catalog to select and embed the correct specification into the generated GGUF.

CLI and server

CLI/server compile the fallback catalog only when built with:

cmake -S . -B build -DAUDIOCPP_DEPLOYMENT_BUILD=ON

The option is off by default because new GGUF models already contain their own spec.

The deployment catalog remains useful for:

  • Safetensors packages
  • Legacy GGUF files
  • Portable binaries that must support models without embedded specs

4. New converter options

The converter now supports:

--family
--model-spec
--model-spec-override
--allow-missing-model-spec
--no-sidecars

--model-spec and --model-spec-override are aliases in the converter.

Example:

audiocpp_gguf.exe --input model.safetensors
--output model.gguf --type q8_0
--family qwen3_asr `
--root path\to\model

5. Automatic specification discovery during conversion

The converter searches in this order:

  1. Explicit --model-spec or --model-spec-override
  2. Specification stored or referenced in config.json
  3. model_spec.json or model_specs/*.json inside the model directory
  4. External model_specs directories discovered from the working directory upward
  5. The catalog compiled into audiocpp_gguf

config.json can provide:

  • A package-spec JSON object
  • A JSON string
  • A relative path to a package-spec file
  • An explicit audio.cpp family

Recognized keys include:

audiocpp_model_spec
model_spec
audiocpp.model_spec
audiocpp.package_spec
audiocpp_family
audiocpp.family

6. Improved automatic family detection

The converter can infer known audio.cpp families from upstream model_type values, including:

  • Qwen3 ASR
  • Qwen3 Forced Aligner
  • Nemotron ASR
  • VibeVoice ASR
  • Higgs Audio STT
  • Hviske ASR
  • Qwen3 TTS

Qwen3 Forced Aligner detection specifically checks:

thinker_config.model_type = qwen3_forced_aligner

This prevents Qwen3-ASR 1.7B from being incorrectly detected as the forced aligner.

7. Conversion validation

Before writing the GGUF, the converter now validates:

  • Every expected tensor namespace exists
  • No unexpected tensor namespace was supplied
  • Required sidecars exist
  • Explicit sidecar sources exist
  • Sidecar destinations are safe relative paths
  • Duplicate destinations are rejected
  • Multiple matching package specs are treated as ambiguous
  • The selected spec contains a GGUF source

This prevents successfully producing a GGUF that audio.cpp cannot subsequently load.

8. Default sidecar behavior

Standalone GGUF creation remains the default.

If sidecar embedding is enabled but the converter cannot find any sidecars, conversion now fails instead of silently creating a
tensor-only GGUF.

The error tells you to provide:

--root
--sidecar =

or explicitly choose:

--no-sidecars

--sidecar and --no-sidecars cannot be combined.

9. Tensor-only GGUF behavior

A GGUF created with --no-sidecars still contains:

  • Tensors
  • Package-spec family
  • Complete package-spec JSON

It does not contain required tokenizer/configuration/processor files.

Deployment therefore looks like:

model-directory/
├── model.gguf
├── config.json
├── tokenizer files
└── other required sidecars

The loader was updated so a tensor-only GGUF can use external sidecars from its surrounding directory. Previously, GGUF package
loading could reject a GGUF simply because it had no embedded sidecars.

10. Generic tensor archive escape hatch

The converter normally refuses to create a GGUF without a valid package spec.

For a generic tensor archive that is not intended to run as an audio.cpp model, you can use:

--allow-missing-model-spec

The converter tries to find a spec, prints a warning if it cannot, and creates the archive without embedded model-spec metadata.

This option is not recommended for deployable models.

11. Improved inspection output

Running:

audiocpp_gguf.exe --inspect model.gguf

now additionally reports:

embedded_sidecars=true
embedded_model_spec=true
model_spec_family=qwen3_asr

It continues reporting tensor count, scalar count, and namespaces.

12. Documentation updates

The following were updated:

  • Main README
  • docs/gguf.md
  • Server README
  • CLI help

They now document:

  • Runtime resolution order
  • Converter discovery order
  • Standalone versus tensor-only GGUF
  • Legacy GGUF behavior
  • Deployment builds
  • Override options
  • Model configuration keys
  • Required external files by format

What this update does not do

  • It does not make arbitrary safetensors architectures automatically supported.
  • Legacy GGUF files are not modified; they still need a deployment catalog or external model_specs.
  • --no-sidecars does not create a fully standalone model.

I think I have too much free time today...

Store the resolved package spec and family in GGUF metadata so new standalone and tensor-only GGUF files do not depend on an external model_specs directory. Resolve runtime specs in explicit-override, embedded-GGUF, deployment-catalog, then external-discovery order. Keep the converter catalog portable, validate required namespaces and sidecars before writing, and document deployment and legacy GGUF behavior.
@0xShug0

0xShug0 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

@mirek190 Sorry, I somehow missed your long update earlier :( Thanks a lot for taking the time to write everything up. I’ll go through it and test the changes tomorrow.

@0xShug0

0xShug0 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

@mirek190 Merged! Kudos to you for putting this together!

A few changes:

  • Clarified package-spec loading errors so users see which spec source was selected and what file/resource is missing.
  • Added a regression/unit test test_package_spec_errors_name_selected_spec for GGUF package-spec behavior.
  • Updated docs/gguf.md with the validated compatibility behavior for standalone GGUF, legacy GGUF, normal builds, and deployment builds.
  • Added --deployment-build support to Linux/macOS helper scripts and -DeploymentBuild to the Windows script.
  • Updated README build examples and build-options table for the deployment-build option.

@0xShug0
0xShug0 merged commit bf1ac67 into 0xShug0:main Jul 15, 2026
4 checks passed
@mirek190

Copy link
Copy Markdown
Contributor Author

no problem

dleiferives pushed a commit to dleiferives/audio.cpp that referenced this pull request Jul 25, 2026
…del-specs

GGUF_do_not_need_an_external_model_specs_folder
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants