Here is brief instruction about how to add a new server in this framework. Since the registry-driven refactor, adding a served model is one CvModelEntry row in the family catalog() (two TOML section names, a worker factory and a response filler). ProductIndex projects that row onto both unified CLIs. The generic implementation lives in jinq::server::CvModelServer<MODEL_OUTPUT> on top of jinq::server::BaseAiServerImpl<WORKER, MODEL_OUTPUT>, which keeps providing auth, rate limiting, request validation, per-request timeout, the worker pool, Prometheus metrics and the /openapi.json endpoint. The model input uses base64 encoded images uniformly. The example below adds a densenet image classification server; the model itself comes from how_to_add_new_model.md.
This step is the same as adding a new model. Default model output types for each vision task live in model_io_define.h; the types named std_*_output are the default outputs. For classification:
namespace classification {
struct cls_output {
int class_id;
std::vector<float> scores;
};
using std_classification_output = cls_output;
}
class_id equals the index of the max score in scores. If your task needs a new
output shape, define it here first — the server spec and the response serializer both
refer to it.
Open the task header of the model’s family (src/factory/classification_task.h for the example) and add a CvModelEntry to catalog(). That single row is the product identity: model_section is the --model id, server_section names the TOML table, make_worker and fill_response make it HTTP-servable, and family-default vis/default-image make it benchmarkable.
inline const std::vector<Entry> &catalog() {
static const std::vector<Entry> entries = {
// ...existing models...
Entry{"DENSENET", "densenet classification", "DENSENET_CLASSIFICATION_SERVER",
&create_densenet_classifier<ImageInput, Output>,
&jinq::server::response::fill_classification,
classification_param_specs()},
};
return entries;
}
ProductIndex iterates every family catalog() at startup. There is no second product table and no create_densenet_cls_server wrapper. CvModelServer<Output>::init reads the server section (worker pool, timeouts, auth, rate limit — see
about_model_server_configuration.md),
loads the model config referenced by the model section, creates worker_nums workers and
assembles the HTTP server.
If your output is a brand-new shape, add the matching serializer in
src/server/response_serializers.h and point
fill_response at it; field names and JSON types must follow
docs/openapi.json components.schemas (regenerate with python scripts/gen_openapi.py).
Do not add a per-model src/apps/server/*.cpp or add_mortred_app line.
ProductIndex projects every CvModelEntry in the family catalog, so a new
catalog row is enough for:
mortred-model-server.out --model DENSENET /path/to/densenet_server_config.toml
mortred-model-benchmark.out --model DENSENET /path/to/densenet_config.toml [image]
Then:
conf/server/<task>/<model>/ with model = "DENSENET" and
server_exe = "mortred-model-server.out" (copy a sibling and adjust
server_uri, port, worker_nums);python3 scripts/check_consistency.py must stay green (catalog id ↔ conf model=,
a catalog_tiers entry in conf/ci_hosted_golden.json: hosted, gpu-smoke,
or nightly, and a committed test/golden/<case>.json or .png if the
tier is hosted);python3 scripts/gen_openapi.py and commit docs/openapi.json plus
src/server/openapi_doc.h. The generator reads the [*_SERVER] section
name and server_uri, so adding a server or renaming either field
requires a regen (stale Request_* schemas otherwise linger). port and
worker_nums do not appear in OpenAPI. If you also changed the catalog
server_section or ParamSpec, refresh docs/contract_dump.json first
(contract_dump binary), then run gen_openapi.py.You usually do NOT need to touch request serving. BaseAiServerImpl provides
serve_process / do_work / do_work_cb: JSON request parsing (with 400/413/415/405
contract errors), bearer auth, per-IP rate limiting, worker checkout from the blocking
queue (timeout budgeted), model inference, response serialization through
fill_response, Prometheus metrics and structured request logs.