How to add a new model depends on how 'new' the model is. First, let's review how aiconfigurator does latency estimation.
In aiconfigurator, the end-to-end latency estimation depends on operation-level latency estimation. There are 3 steps to achieve this:
The model is broken down into operations, as shown in the source file models.py. A model is composed of operations such as GEMM and MoE defined in the operations package (see its README for the single-oracle contract).
Since #1357 PR-5 (single-oracle), per-op latency/energy/SOL values are
computed only by the compiled Rust engine
(aic-core/rust/aiconfigurator-core). The Python Operation classes are
typed parameter bags: constructor + fields (the wire parameters),
get_weights() (the memory model), and — for table-backed ops — the parquet
loader that feeds enumeration and charts. They contain no interpolation or
lookup math.
Evaluation flows through the engine surfaces in
engine.py: each op
converts to a wire OpSpec (_to_opspec), and per-op values come back from
EngineHandle.evaluate_ops_json / evaluate_ops_sol_json (op-list FFI) or
the compiled per-phase/whole-run entry points (run_static,
InferenceSession). The legacy per-call surface
(Operation.query() / PerfDatabase.query_*) still exists for one release
as a DEPRECATION SHIM routed through the same engine
(sdk/engine.py::_evaluate_single_op) — do not build new code on it, and do
not extend it to new ops.
Taking MoE for TensorRT-LLM as an example, the current collector lives in collector/trtllm/collect_moe.py, while shared model/op case values live under collector/cases/.
If the MoE operation you want is not covered by the current inherited database, you need to add the test case in the relevant YAML case file and collect your own data.
For example, if you want to cover a new model with num_experts=1024, topk=16, you should extend the model's *_cases.yaml under collector/cases/models/ or the shared MoE cases under collector/cases/base_ops/ when the case is common across models.
Finalize the collector's moe_perf.txt staging output as
moe_perf.parquet, place it in the canonical moe family directory, and
rebuild and reinstall aiconfigurator.
Now let's revisit how to add a new model in aiconfigurator. There are 3 situations:
If the model is a simple variant of an existing architecture (for example, it's similar to Qwen3 32B and only has slight differences, such as different positional embedding, different q/k/v heads of GQA, different number of layers, different hidden size), these are treated as simple variants.
In this case, you just need to ensure the architecture is supported in ARCHITECTURE_TO_MODEL_FAMILY in common.py:
"YourModelForCausalLM": "LLAMA", # or "MOE", "DEEPSEEK", etc.AIConfigurator will automatically download the model's config.json from HuggingFace when you run with --model-path your-org/Your-New-Model. The model config is parsed to extract layer count, hidden size, attention heads, etc.
Note: If the architecture already exists in ARCHITECTURE_TO_MODEL_FAMILY (e.g., LlamaForCausalLM, Qwen3ForCausalLM, MixtralForCausalLM), no changes are needed - just use the model directly.
Here 'LLAMA', 'MOE', 'DEEPSEEK' are the model families defined in ModelFamily in common.py
This typically refers to a MoE model, as the MoE operation of a new model usually has different num_experts and topk values, etc. This difference is captured by different data points in aiconfigurator.
You need to follow several steps:
-
Define a new MoE operation test case in the relevant collector YAML case file and follow the collector README to collect the MoE data points for your model.
-
Finalize the collected staging file as parquet and update the inherited database, for example
aic-core/src/aiconfigurator_core/systems/data/h200_sxm/moe/trtllm/1.3.0rc10/moe_perf.parquet. -
Ensure the architecture mapping exists in ARCHITECTURE_TO_MODEL_FAMILY (see Situation 1).
Models with different MLA operations also follow a similar process. For example, if it's a variant to model family 'DEEPSEEK' and has different definition of MLA, you need to collect new MLA data points.
Today, we don't support the Mamba model yet. By looking at the Mamba model, it relies on the support of convolution operations. Convolution is not yet supported, so you need to add a new operation Conv.
Steps required (per-op performance math lives ONLY in the compiled Rust
engine — see .claude/rules/rust-core/parity.md Rule 2 and
aic-core/src/aiconfigurator_core/sdk/operations/README.md for the full
single-oracle flow):
- Model
Convin the Rust engine: an operator inaic-core/rust/aiconfigurator-core/src/operators/(query + SOL roofline + energy) and a parquet loader inaic-core/rust/aiconfigurator-core/src/perf_database/, anchored by a Rust#[cfg(test)]oracle test. - Define the Python
Convop class inaic-core/src/aiconfigurator_core/sdk/operations/— constructor, fields,get_weights, and the parquet loader /load_data(the raw data plane for charts and the support matrix). No Pythonquery()body or interpolation: the single-oracle contract test rejects those. - Wire the spec conversion: a
_to_opspecbranch inaic-core/src/aiconfigurator_core/sdk/engine.pyand anOpvariant appended at the tail ofaic-core/rust/aiconfigurator-core/src/operators/op.rs(mid-enum insertion requires anENGINE_SPEC_SCHEMA_VERSIONbump on both sides), plus theaic-core/rust/aiconfigurator-core/src/engine/spec.rsround-trip fixture.tests/unit/sdk/test_opspec_coverage.pyenforces this. - Define the data collection process in collector by referring to existing operations' collection code, such as
collect_gemm.py - Collect data for conv, register its family in
collector/op_backend_catalog.yaml, and add the finalized parquet file underaic-core/src/aiconfigurator_core/systems/data/<system>/<family>/<backend>/<version>/(the engine reads parquet only). - Pin the behavior: once a shipped model reaches the op, add a parity
case via
aic-core/rust/aiconfigurator-core/parity_tests/pin_goldens.py(append-only); later modeling changes carry their golden diff. - Add new model definition in
models.pyto build your model with new operation. A new model class is mapping to a new model family.
update your model in ModelFamily dict defined incommon.py
Attention-FFN Disaggregated (AFD) estimate mode has one additional maintenance contract beyond the normal aggregated and P/D-disaggregated paths. sdk/afd_partition.py splits a model's context_ops / generation_ops into A-worker and F-worker pools by operation name. When adding a new model family or new operation, make sure the generated operation names can be classified by the AFD partitioner.
The current AFD partitioning contract is:
- A-worker ops: operations that belong to the embedding / attention side, such as
embedding,add_norm_1, attention norms,qkv, MLA, BMM, RoPE, attention kernels, and projection GEMMs. - F-worker ops: operations that belong to the FFN / MoE side, such as router GEMMs, dense
ffn/mlpops, routed expert ops, shared expert ops, and activation / gate / up / down GEMMs. - Boundary ops: operations that naturally sit at the A/F boundary, such as
add_norm_2, FFN/MoE/MLP norms,logits_gemm,reduce_add, and*_combine. These default to the A-worker and can be moved to the F-worker with the AFD boundary placement option. - Skipped model-internal communication ops: communication or dispatch operations already represented by the AFD communication model, such as
CustomAllReduce,P2P,NCCL, TP all-gather / reduce-scatter, and MoE dispatch ops. These should not be counted again in either compute pool because AFD adds its own cross-pool and intra-pool communication throughAFDTransfer,AFDFAllGather,AFDFReduceScatter, andAFDCombine. - Overlap ops: an
OverlapOpcan stay atomic only when every non-skipped inner op belongs to the same side. If an overlap group spans the A/F boundary, the partitioner must fail or be extended to split / rebuild that overlap explicitly. - Layer families that need explicit rules: Mamba and GDN layers are not covered by the current attention/FFN partition rules. Until a dedicated partitioning rule, operation model, and communication / memory accounting are added, the partitioner raises an explicit
AFDPartitionErrorfor these ops instead of falling back to an unknown-op side assignment.
If a new operation cannot be classified, do not rely on an unknown-op fallback for production use. Update sdk/afd_partition.py with an explicit classification rule and add a focused unit test in tests/unit/sdk/test_afd_partition.py.
Rebuild & reinstall aiconfigurator to add this model's support.
Need Help? If you still have difficulty adding the model you want, please create an issue in github.
flowchart TD
A[Does the model belong to an existing model_family?]
A --> |YES| B([Simple dense, moe variants like <i><b>Qwen/Qwen3-32B</b></i> can directly use the existing <i><b>LLAMA</b></i> or <i><b>MOE</b></i> model_family])
B --> C[Ensure architecture exists in <i><b>ARCHITECTURE_TO_MODEL_FAMILY</b></i>, then use directly with <i><b>--model-path</b></i>]
A --> |NO| D([Each layer in <i><b>Nemotron</b></i> can have a different <i><b>inter_size</b></i>, so we defined a new class for this model])
D --> E[Does the model need new operations?]
E --> |YES| F([for instance, new model might have covolution, which isn't defined in sdk/operations.py])
F --> G[Define your operations in <b><i>sdk/operations.py</b></i>]
G --> H[Define the model as a new model class in <i><b>sdk/models.py</b></i> using the op classes from <i><b>sdk/operations/</b></i>]
E --> |NO|H
H --> i[Add architecture mapping to <i><b>ARCHITECTURE_TO_MODEL_FAMILY</b></i>]
i --> j[Do you need to collect performance data for the new model?]
C --> j
j --> |YES|K([Some common cases in which you will need to collect new data])
K --> L[/• You haved defined new operations<br/> • <i><b>MoE</b></i> with different <i><b>num_experts</b></i> or <i><b>topk</b></i> from existing ones<br/>• New <i><b>attention</b></i> variant, such as <b><i>attention</b></i> with <b><i>head_size</b></i> other than 64 or 128/]
L --> M[Add new test cases to the relevant collector files under aiconfigurator/collector/]
M --> N[Collect staging data with <i><b>collect.py</b></i>, then finalize <i><b>XX_XX_perf.parquet</b></i>]
N --> Z
j --> |NO|Z[<i><b>Good news, you are now all set</b></i>]