AIM Profile Sets#
AIMProfileSet and AIMClusterProfileSet are the derivation engine of v1alpha2. They select source profiles (by label and spec criteria), apply overrides, and produce derived AIMProfile / AIMClusterProfile resources.
You’ll encounter profile sets in two ways:
Implicitly, as children of
AIMModel.spec.profiles— every fine-tuned or custom-modelAIMModelsynthesises anAIMProfileSetnamed<model>-profiles-<hash>and summarises its output.Directly, as standalone resources you author yourself when you want to derive profiles without creating an owning
AIMModel.
The same internal primitive handles both — AIMModel.spec.profiles is translated into an AIMProfileSetSpec for the synthesised child set.
v1alpha2
AIMProfileSet/AIMClusterProfileSet are new CRDs in aim.eai.amd.com/v1alpha2 — v1alpha1 had no first-class derivation resource. The closest legacy primitive is AIMModel.spec.profileCopy, which performed an inline copy step against existing AIMServiceTemplates; v1alpha2 reshapes that into AIMModel.spec.profiles (which synthesises a child profile set), and spec.profileCopy is forbidden on v1alpha2. See Migrating to v1alpha2 → AIMModel mapping for the full field-by-field translation.
Cluster vs namespace scope#
Resource |
Scope |
Produces |
|---|---|---|
|
Namespace |
|
|
Cluster |
|
For sourcing:
AIMProfileSetselects from namespace-scopedAIMProfileobjects in its own namespace.AIMClusterProfileSetselects from cluster-scopedAIMClusterProfileobjects.Cache-backed sources (
spec.sourceRef) are read from the same namespace forAIMProfileSetand from the operator namespace forAIMClusterProfileSet.
Two source modes#
A profile set selects sources in one of two modes — set by whether spec.sourceRef is populated.
Mode |
|
Source pool |
|---|---|---|
Real-profile-backed |
omitted |
Existing |
Cache-backed |
set |
Entries from a discovery cache |
The two modes are mutually exclusive — the controller doesn’t mix them in a single reconcile.
Cache-backed mode is mainly used by AIMModel.spec.profiles.derivedFrom.sourceRef when the source is an image’s discovery output rather than already-materialised profiles. For standalone use, you’ll almost always omit sourceRef.
Selector#
spec.selector chooses candidates from the source pool. At least one matching field must be set (validated by CRD CEL).
Field |
Description |
|---|---|
|
Match by source |
|
Match by source |
|
Match by source |
|
Match by inference engine ( |
|
Match |
|
Match by numeric precision ( |
|
Match optimization level exactly ( |
|
Match optimization level as a floor — that tier or better ( |
|
Match by accelerator identifier ( |
|
Match |
|
Match by accelerator unit count. |
|
Partial match on engine args — every provided top-level key must exist on the source with an equal value. |
|
Narrow to profiles produced by a specific AIM(Cluster)Model (matched via the |
|
|
|
|
|
|
Identity stamping#
Identity for the derived profile is set via overrides.{aimId,modelId,profileId}. The selector never writes identity onto the derived profile — its identity fields are always source-side filters (strict equality, empty = wildcard).
When selector.role: base, CEL on AIMProfileSetSpec and AIMModelProfilesSpec enforces this split:
Rule |
Effect |
|---|---|
|
Base profiles have no source identity to filter on. The fields would silently do nothing useful. |
|
Base profiles ship with empty |
|
Same reasoning for the model-level identifier. ( |
|
Most callers don’t pin a profileId; the AIMProfile reconciler synthesises one if unset. |
For role: deployable derivations (the AIMProfileSet remix case where you’re re-deriving an already-deployable profile into accelerator/engine variants), all three identity overrides are optional — the source profile’s existing identity carries through unchanged unless you explicitly override it.
The same shape applies to AIMModel.spec.profiles.derivedFrom: selector filters source candidates, overrides stamp the target identity onto the derived profile. CEL enforces the split on both surfaces identically.
Version policy#
spec.versionPolicy controls how multiple versions of matching source profiles are filtered.
Policy |
Behavior |
|---|---|
|
Only match profiles whose version (extracted from |
|
Within each (accelerator, precision, metric) group, only match the highest-version profile. |
|
Match every profile that satisfies the selector, regardless of version. |
pinned is the typical choice for reproducibility. latest auto-tracks upstream improvements. all is common for custom-model derivation (where each version represents a different accelerator/precision combo rather than a temporal sequence).
Overrides#
spec.overrides mutates selected sources before writing them out as derived profiles.
Field |
Merge semantics |
|---|---|
|
Replaces source |
|
Replaces source |
|
Replaces source |
|
Merges by env-var name; matching names override, new names append. |
|
Merges by key; matching keys override, new keys append. |
|
Shallow merge on top of source |
|
Replaces the derived profile’s container image entirely. When unset, the image is resolved from the source profile and whether the override replaces the weights — see Image resolution below. |
Image resolution#
When overrides.image is unset, the derived profile’s image depends on the source profile’s role and whether the override replaces the model weights (overrides.modelSources):
Source profile |
Override replaces weights? |
Resulting image |
|---|---|---|
Deployable ( |
Yes ( |
Resolves back to the source’s |
Deployable |
No (partitioning-only / env-only / engineArgs-only override) |
Keeps the optimized source image. The model optimizations and tuning still apply, so a re-partition or env tweak stays on the model-optimized image instead of silently downgrading to the base runtime. |
Base ( |
— |
Keeps the source image unchanged. A base profile already is the runtime image; weights come from |
An explicit overrides.image always wins outright, regardless of the above.
Derived profiles inherit the source profile’s status.baseImage so subsequent derivations or overlays can rebase consistently.
Spec shape#
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMProfileSet
metadata:
name: mi308x-variants
namespace: ml-team
spec:
selector:
role: deployable
modelRef:
name: llama-3-8b-official
scope: Auto
origin: discovered
acceleratorModel: MI300X
engineArgs:
tensor-parallel-size: 2
versionPolicy: latest
overrides:
image: quay.io/acme/aim-base-vllm:0.9.0
acceleratorModel: MI308X
acceleratorCount: 4
engineArgs:
tensor-parallel-size: 4
max-model-len: 32768
Field |
Description |
|---|---|
|
Optional reference to a discovery-cache |
|
Match criteria — see Selector. |
|
|
|
Required when |
|
Optional image override applied to derived profiles. |
|
Mutations applied to selected sources — see Overrides. |
|
Propagated to derived profiles. |
|
Propagated to derived profiles. |
See the CRD API Reference for the full schema.
Examples#
Real-profile-backed derivation#
Standalone profile set that derives MI308X variants from existing MI300X profiles owned by llama-3-8b-official:
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMProfileSet
metadata:
name: mi308x-variants
namespace: ml-team
spec:
selector:
modelRef:
name: llama-3-8b-official
acceleratorModel: MI300X
versionPolicy: latest
overrides:
acceleratorModel: MI308X
acceleratorCount: 4
Cache-backed derivation#
Profile set that derives directly from an AIMModel’s discovery cache without going through materialised profiles:
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMProfileSet
metadata:
name: qwen3-cache-derived
namespace: ml-team
spec:
sourceRef:
name: qwen3-32b-custom-discovery
selector:
aimId: qwen/Qwen3-32B
acceleratorModel: MI300X
versionPolicy: pinned
version: "0.9.0"
overrides:
image: quay.io/acme/aim-base-vllm:0.9.0
modelSources:
- modelId: acme/qwen3-32b-custom
sourceUri: s3://models/qwen3-32b-custom/
Custom-model derivation (role: base)#
The derivation half of the custom-model flow. When authored by an AIMModel.spec.profiles it’s synthesised automatically; here it’s shown standalone for reference:
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMProfileSet
metadata:
name: custom-derivatives
namespace: ml-team
spec:
selector:
role: base
aimId: acme/custom-transformer
modelRef:
name: aim-base-vllm
scope: Namespace
versionPolicy: all
overrides:
modelSources:
- modelId: acme/custom-transformer
sourceUri: s3://acme-models/custom-transformer
Status#
Field |
Description |
|---|---|
|
|
|
Detailed conditions — see Conditions Reference |
|
Number of derived profiles the set intends to manage |
|
Number of managed profiles whose own status is |
|
Number of managed profiles whose own status is |
|
Number of managed profiles that carry |
|
Number of managed profiles that don’t carry both fields. Non-zero typically means a base-image AIMModel was the source. |
Zero values serialise explicitly (no omitempty), so kubectl printcolumns and assertions on count fields are always present.
Conditions#
Condition |
Reasons |
|---|---|
|
|
|
|
DerivationReady=False / NoMatchingProfiles means the selector matched zero source candidates. This is by far the most common failure mode; see troubleshooting below.
Derived-profile labelling#
Profiles produced by an AIMProfileSet carry these labels:
Label |
Value |
|---|---|
|
|
|
|
|
|
|
Name of the AIM(Cluster)Model that owns the profile set (when applicable) |
|
|
By default, derived profiles are not marked profile-copyable, which prevents accidental cascading copy chains. If you want a derived profile to itself be a valid derivation source, set the label explicitly.
Source eligibility#
For real-profile-backed mode, a source profile must carry aim.eai.amd.com/profile-copyable="true" to be considered. This separates provenance (“where did this come from”) from source eligibility (“can this be copied”):
Image-discovery-produced profiles owned by AIM(Cluster)Models are marked copyable by default.
Derived profiles are not marked copyable by default — opt in explicitly if you want chained derivation.
Hand-authored profiles need the label added explicitly to participate.
Cache-backed mode bypasses this — the entries are taken directly from the discovery catalog.
Troubleshooting#
DerivationReady=False / NoMatchingProfiles#
The selector matched zero source candidates. Diagnose by listing candidates and comparing:
# What did the selector look for?
kubectl get aimprofileset <name> -o jsonpath='{.spec.selector}'
# What profiles are in the pool?
kubectl get aimprofile -n <namespace> -o custom-columns=\
NAME:.metadata.name,\
AIMID:.spec.aimId,\
PRECISION:.spec.precision,\
ACCEL:.spec.acceleratorModel,\
ROLE:.metadata.labels.aim\\.eai\\.amd\\.com/profile-role,\
COPYABLE:.metadata.labels.aim\\.eai\\.amd\\.com/profile-copyable
Common causes:
A selector field is too restrictive — relax
precision,acceleratorModel, etc.modelRef.nametypo or wrong scope.Source profiles lack the
profile-copyable: "true"label.For cache-backed mode:
spec.sourceRef.namedoesn’t reference an existing ConfigMap.
managedProfiles.total == 0 after applying#
Either the set hasn’t been reconciled yet (look at status.conditions[?(@.type=='DerivationReady')]) or every candidate was filtered out before the derived-profile build step. The selector is the most common culprit.
Model-managed derivation is stuck#
When an AIMModel.spec.profiles synthesises a profile set, the model’s status.profileSetRef points at it. Inspect directly:
kubectl get aimmodel <name> -o jsonpath='{.status.profileSetRef.name}'
kubectl get aimprofileset <profileset-name> -o yaml
Conditions on the profile set carry the actionable detail; the model just summarises.