AIM Models#
AIMModel and AIMClusterModel are the entry point for getting a model onto the cluster. Each resource describes one model and produces a set of AIMProfile resources that AIMService can deploy.
v1alpha2
This page documents the aim.eai.amd.com/v1alpha2 API. For the v1alpha1 AIMModel shape (with spec.custom, spec.modelSources, spec.discovery, etc.) see Legacy AIMModel.
Three flows#
Every v1alpha2 AIMModel uses one of three flows. The flow is determined by which field you populate on the spec:
Flow |
When to use |
Spec shape |
Source of profiles |
|
|---|---|---|---|---|
Official |
Deploying a published AMD-supported AIM model unmodified |
|
The AIM container image itself (discovery) |
|
Fine-tuned |
Deploying a fine-tune of a published architecture |
|
A previously-applied official AIMModel |
|
Custom |
Deploying a model whose architecture isn’t in the catalog |
|
A previously-applied base-image AIMModel |
|
The onboarding contract is enforced by CRD validation: exactly one of spec.image or spec.profiles must be set. Mixing them — or setting neither — is rejected at admission.
The flow you used is surfaced as status.kind and shown as the KIND column in kubectl get aimmodel. Note that base-image AIMModels classify as Image (they’re a sub-case of the Official flow with a generic image, not a fourth flow). To filter for base-image models specifically, combine status.kind=Image with status.managedProfiles.base > 0 — see Base-image models below.
End-to-end guides for the two derivation flows:
Cluster vs namespace scope#
Resource |
Scope |
Typical owner |
Produces |
|---|---|---|---|
|
Namespace |
Application / ML team |
|
|
Cluster |
Platform team |
|
When an AIMService references a model by name, AIM Engine looks for a namespace-scoped AIMModel first, then falls back to a cluster-scoped AIMClusterModel with the same name. This lets teams override a cluster default without affecting other namespaces.
The two kinds share the same spec and status shape — every spec example below applies to both unless noted.
Flow 1 — Official AIM model#
Use this when AMD publishes an AIM image for the exact model you want to deploy.
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMModel
metadata:
name: llama-3-8b-official
namespace: ml-team
spec:
image: quay.io/amd/aim-llama-3-8b-instruct:0.9.0
imagePullSecrets:
- name: registry-creds
serviceAccountName: aim-runtime
The controller:
Runs an in-cluster discovery Job that inspects the image’s profile YAMLs (
/workspace/aim-runtime/profiles/<aim_id>/*.yaml) and writes a normalised catalog into aConfigMap(status.discoveryCacheRef).Evaluates each discovered profile against the cluster’s nodes — GPU model, accelerator count, resource capacity.
Materialises one
AIMProfileper supported entry. Unsupported entries (no matching nodes) are counted instatus.discoveredProfiles.unsupportedbut not materialised.Watches node events and re-evaluates the catalog when nodes are added or removed.
The resulting profiles are deployable (carry aimId and modelSources) and labelled aim.eai.amd.com/profile-role=deployable, aim.eai.amd.com/profile-origin=discovered.
Status signal#
kubectl get aimmodel llama-3-8b-official -o jsonpath='{.status}' | jq
{
"status": "Ready",
"aimId": "meta-llama/Llama-3-8B-Instruct",
"baseImage": "ghcr.io/silogen/aim-base:0.11",
"discoveryCacheRef": { "name": "llama-3-8b-official-discovery-5e8ad928" },
"discoveredProfiles": { "total": 7, "supported": 5, "unsupported": 2 },
"managedProfiles": { "total": 5, "ready": 5, "deployable": 5, "base": 0, "notAvailable": 0 }
}
Flow 2 — Fine-tuned model#
Use this when you have your own fine-tune of a model whose architecture is in the catalog. The official AIMModel’s profiles already carry the tuned engine arguments and accelerator pairings — you reuse them and override only the model sources.
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMModel
metadata:
name: llama-3-8b-finetune-acme
namespace: ml-team
spec:
profiles:
derivedFrom:
selector:
aimId: meta-llama/Llama-3-8B-Instruct
acceleratorModel: MI300X
versionPolicy: pinned
version: "0.9.0"
overrides:
modelSources:
- modelId: acme/llama-3-8b-finetune
sourceUri: hf://acme/llama-3-8b-finetune
selector.aimId matches source profiles by their existing spec.aimId. The derived profile inherits aimId from the source and only modelSources and modelId change.
See Fine-Tuned Models for the full walkthrough, version-policy semantics, and override merge rules.
Flow 3 — Custom model#
Use this when your model’s architecture isn’t in the catalog. You apply a base-image AIMModel first to produce base profiles (generic runtime configs with no model identity), then a custom-model AIMModel that overlays your weights and identity on top.
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMModel
metadata:
name: aim-base-vllm
namespace: ml-team
spec:
image: amdenterpriseai/aim-base:0.11
---
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMModel
metadata:
name: acme-custom-transformer
namespace: ml-team
spec:
profiles:
derivedFrom:
selector:
role: base
modelRef:
name: aim-base-vllm
scope: Namespace
versionPolicy: all
overrides:
aimId: acme/custom-transformer
modelId: acme/custom-transformer
modelSources:
- modelId: acme/custom-transformer
sourceUri: s3://acme-models/custom-transformer
The shape differs from fine-tunes in two ways:
selector.role: basematches only base profiles;selector.modelRef.namepins to a specific base-image AIMModel.Target identity for the derived profile lives on
overrides.{aimId,modelId}(required by CEL whenrole: base— base profiles ship with no identity to inherit).
See Custom Models for the full walkthrough, the identity-stamping semantics, and base-image requirements.
Base-image models#
A base-image AIMModel is just an official-flow AIMModel whose image happens to be a generic AIM base image — one whose profile YAMLs declare runtime/engine/accelerator settings but leave model_id and model_sources empty. The controller materialises those entries as base AIMProfiles:
status.deployable: falseaim.eai.amd.com/profile-role: baseaim.eai.amd.com/profile-origin: discovered
Base profiles cannot back an AIMService directly. They exist as source material for a custom-model derivation (see Flow 3).
A model is a base-image AIMModel when status.managedProfiles.base > 0 and status.managedProfiles.deployable == 0 (with status.kind: Image). The kind is Image rather than its own value because — mechanically — a base image is just a regular image whose profile YAMLs happen to leave the model identity fields blank; the image-discovery flow is the same. The managedProfiles.base > 0 count is what flags the profiles as base-only.
Discovery cache#
For both official and base-image flows, the discovery Job writes its output into a ConfigMap that the controller treats as the normalised catalog. The cache stores:
Parsed profile YAMLs from the image
Normalised metadata in the same shape that real
AIMProfileresources useThe base image extracted from
AIM_BASE_IMAGE_REFwhen available
status.discoveryCacheRef points at this ConfigMap. status.discovery tracks the job lifecycle:
Field |
Description |
|---|---|
|
Number of discovery Job attempts so far |
|
Timestamp of the most recent attempt |
|
Reason the most recent attempt failed (e.g. |
|
Hash of the inputs that invalidate the cache (image, imagePullSecrets, serviceAccountName, discovery command version) |
When specHash changes — for example, you rotate serviceAccountName or upgrade the image tag — the operator drops the cache and re-runs discovery.
Profile-set delegation#
For derivation flows (fine-tuned and custom), the controller synthesises a child AIMProfileSet (or AIMClusterProfileSet) named <model>-profiles-<hash> and lets it produce the derived profiles. The model summarises the profile-set output back onto its own status; see AIM Profile Sets for the mechanics.
status.profileSetRef points at the child resource:
kubectl get aimmodel acme-custom-transformer \
-o jsonpath='{.status.profileSetRef.name}'
# acme-custom-transformer-profiles-2be25a47
Official-flow models do not create a child profile set — status.profileSetRef is empty.
Spec fields#
The unified spec shape used by all three flows:
Field |
Used by |
Description |
|---|---|---|
|
Official, Base |
Source image to inspect for discovery. Mutually exclusive with |
|
Fine-tuned, Custom |
Derivation spec — selector, version policy, overrides. Mutually exclusive with |
|
All |
Secrets used for image inspection and propagated to derived runtime profiles. |
|
All |
Service account propagated to managed child resources. |
The v1alpha2 CRD explicitly forbids the legacy v1alpha1 fields (spec.aimId, spec.modelSources, spec.custom, spec.customTemplates, spec.discovery, spec.defaultServiceTemplate, spec.runtimeConfigName, spec.env, spec.imageMetadata, spec.profileCopy) — admission rejects them with a targeted error message.
Status fields#
Field |
Description |
|---|---|
|
Overall status ( |
|
Detailed reconciliation conditions (see Conditions Reference) |
|
Discriminator for the onboarding flow that produced this model’s profiles: |
|
Resolved architecture identifier — populated by discovery for |
|
Extracted |
|
Reference to the normalised discovery cache |
|
Counts: |
|
Discovery Job state: |
|
Reference to the synthesised child profile set (derivation flows only) |
|
Counts: |
The managedProfiles counts always serialise zero values explicitly (no omitempty), so kubectl printcolumns and assertions on .status.managedProfiles.{total,ready,...} are stable.
Resolution and precedence#
When an AIMService references a model by name, AIM Engine resolves it in this order:
Namespace-scoped
AIMModelwith that name.Cluster-scoped
AIMClusterModelwith that name.
Namespace wins. The resolved scope is recorded in the service’s status.resolvedModel.scope.
Troubleshooting#
Discovery fails#
Check operator logs for image-inspection errors:
kubectl -n aim-system logs -l app.kubernetes.io/name=aim-engine --tail=100 \
| grep -i "<model-name>"
Inspect the discovery Job directly:
kubectl -n <namespace> get jobs -l aim.eai.amd.com/model=<model-name>
kubectl -n <namespace> logs job/<discovery-job>
Common causes:
Missing or invalid
imagePullSecretsImage reference not found at the registry
LogParseFailed— the container is not an AIM-compatible imageRegistry authentication or connectivity failures
status.discovery.lastFailureReason captures the most recent reason, and status.conditions[?(@.type=='DiscoveryReady')] carries the human-readable message including the next retry interval.
Model is Ready but no profiles materialised#
status.managedProfiles.total == 0 and status.discoveredProfiles.unsupported > 0 means discovery succeeded but none of the discovered profiles are compatible with the cluster’s hardware. Compare the cluster’s accelerator labels:
kubectl get nodes -o jsonpath='{.items[*].metadata.labels}' \
| tr ',' '\n' | grep aim-accelerator
against the accelerator requirements in the discovery cache ConfigMap. Add matching nodes or, if the discovered profiles are wrong, fix the source image.
Derivation produces no profiles#
status.profileSetRef points at the child AIMProfileSet. Inspect it:
kubectl get aimprofileset $(kubectl get aimmodel <name> \
-o jsonpath='{.status.profileSetRef.name}') -o yaml
DerivationReady=False / NoMatchingProfiles means the selector matched zero source candidates. See the troubleshooting sections in Fine-Tuned Models and Custom Models.