Your architecture joins the catalog
Part 1 rode the built-in gemma-embedding family. Your real architecture has its own name, and the catalog does not know it; this part makes the failure visible and then fixes it the way every built-in family fixes it, by self-registration.
Break the match first
In part 1's kConfigJson, rename the identity keys the way a Hugging Face checkpoint of your own architecture would name them:
"model_type": "my_model",
"architectures": ["MyModelModel"],
Rebuild and run, and the registry refuses precisely; the raised ClikaRT::Error carries:
wrote /tmp/my_first_model
no model template registered for model_type='my_model' / architecture='MyModelModel'
Nothing else changed. The files are fine; the catalog has no entry whose identity keys match them.
Register the family
A family is one translation unit that pushes a ModelRegistration into the shared registry at static-initialization time, and a TU compiled into your own program registers exactly like a built-in one. Add this file to the project and add it to add_executable:
#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"; }
};
constexpr std::string_view kModelTypes[] = {"my_model"};
constexpr std::string_view kArchitectures[] = {"MyModelModel"};
// Constructing the registrar is the whole hookup; there is no list to edit.
const ModelFamilyRegistrar kRegistrar{
ModelRegistration{
.metadata = ModelMetaData{
.family = "my-model",
.vendor = "my-org",
.description = "the tutorial's own model family",
.input_combos = kTextOnlyCombos,
.output_combos = kTextOnlyCombos,
},
.hf_model_types = kModelTypes,
.hf_architectures = kArchitectures,
.factory = make_model<MyModel>(),
},
};
} // namespace
To watch it resolve, have main ask for identity instead of embeddings for a moment:
const std::unique_ptr<mv::Model> matched = mv::ModelRegistry::builtin().open(dir.string());
std::printf("%s resolved: family=%s model_type=%s\n", dir.string().c_str(),
std::string(matched->family()).c_str(),
std::string(matched->model_type()).c_str());
wrote /tmp/my_first_model
/tmp/my_first_model resolved: family=my-model model_type=my_model
What you registered, and what you did not
The registration so far is identity-only: metadata (the family's name, its publisher, its declared modalities), the match keys, and the factory that builds the identity object. That is enough for the directory to resolve, for the family to appear in a catalog listing, and for every runnable command to refuse with a precise message naming what the family provides (nothing yet). An incomplete declaration (no family name, no modalities, no identity factory) is refused at process start, not papered over downstream.
Running is a separate concern on the same registration, and that split is deliberate: identity must stay cheap (no weights) and total (every checkpoint of yours resolves), while running is opt-in per capability. Wiring it is part 3; the field-by-field reference for everything a registration can carry is Register your own model family.