Register your own model family
The catalog covers the common architectures; your in-house model is not in it. Modelverse's answer is self-registration: a family is a translation unit that pushes a ModelRegistration into the shared registry at static-initialization time, and every built-in family registers exactly this way. There is no central list to edit. You compile one registration TU into your program against the public headers, and list, info and the loaders treat your family like any other. This page is the contract at full depth; for the guided walk from a hand-written checkpoint to a running family of your own, start with the Adding your own model tutorial series; this page's worked example is a text-generation family, the detail-heavy case the tutorial deliberately avoids.
What a snapshot must contain
A model is a directory of files, and identity resolution reads only its configuration:
config.jsonwithmodel_typeandarchitectures; these two values are the identity keys a registration matches on.- The processor files the model needs (
tokenizer.jsonand friends for text; preprocessor configs for image and audio). - The weights (
*.safetensors, ONNX, or GGUF); untouched at identity time.
A repo that ships no config.json can still be matched: a registration may carry a probe_snapshot function that recognizes the family from the file set alone and answers the model_type to route as.
The registration, identity first
One complete TU. With only this much, list shows the family (identity-only), info resolves your checkpoints to it, and every runnable command refuses precisely, naming what the family provides:
#include "clika_modelverse/models/registration.h"
namespace {
using namespace clika_modelverse;
// The identity template: what a resolved checkpoint of this family IS.
// The registry fills architecture, model_type and variant from config.json.
class MyModel final : public Model {
public:
std::string_view family() const noexcept override { return "my-model"; }
};
// The identity keys, matched against config.json.
constexpr std::string_view kModelTypes[] = {"my_model"};
constexpr std::string_view kArchitectures[] = {"MyModelForCausalLM"};
// Static-init self-registration: constructing the registrar is the whole hookup.
const ModelFamilyRegistrar kRegistrar{
ModelRegistration{
.metadata = ModelMetaData{
.family = "my-model",
.vendor = "my-org",
.description = "the in-house my_model language models",
.input_combos = kTextOnlyCombos,
.output_combos = kTextOnlyCombos,
},
.hf_model_types = kModelTypes,
.hf_architectures = kArchitectures,
.factory = make_model<MyModel>(),
},
};
} // namespace
Compile that TU into any program that links Modelverse::modelverse (the command-dispatch functions in the library serve programs that embed them; the shipped clika-modelverse binary knows only the built-in families). An incomplete declaration (no family name, no modalities, no identity factory) is refused at process start, not papered over downstream.
my-model identity-only
* Input Modalities: text
* Output Modalities: text
* Commands: none (identity-only)
* Vendor: my-org
Making it runnable
Two more fields turn identity into execution:
build_generativematerializes the runnable model from a resolved snapshot: read the config, adapt the checkpoint's tensor names where they differ from what your forward expects (ClikaRT::io::TensorsAdapter, rename-at-lookup, no payload copies), assemble the forward from theclika_modelverse/modules/building blocks (attention, decoder stack, projections, rope cache) or your ownnn::Module, and return aGenerativeModelwrapping it with the snapshot's tokenizer and generation defaults. The GGUF twin,build_generative_gguf, receives the already-loaded file so identity read and weight bind share one mapping..clideclares the family's commands. The battery builders compose the standard text surface in one line, and with it your family'sprompt,serveandbenchbehave exactly as the tutorial documents them:
.cli = CliSurface{batteries::cli::llm_cli, batteries::cli::llm_cli_verbs()},
.factory = make_model<MyModel>(),
.build_generative = build_my_model,
A family wanting a different surface composes its own FamilyApp from the same public builders (batteries/cli/prompt.h, serve.h, bench.h), one verb per builder; the advertised verb list and the App are pinned to agree by the registration contract.
Custom behavior around an EXISTING family needs none of this: Add your own node to a model pipeline composes your logic with any catalog model. The complete field-by-field contract, including the speech, embedding and reranking factories, is clika_modelverse/models/registration.h in the installed headers.