Base models
BaseModel and ClusterBaseModel tell OME where a model's weights live and which nodes get them; OME parses the model's architecture, size and capabilities.
A BaseModel or ClusterBaseModel lets OME manage a model's weights. You say where the weights live and which nodes keep a copy. OME downloads the weights to those nodes, and runs serving pods only on nodes that hold them. A model on a PersistentVolumeClaim is mounted into the pods instead. OME also reads the model's files to learn its architecture, size and capabilities, and uses them to pick a serving runtime. An InferenceService then serves the model by naming it.
Since v1.3, a base model is optional: an InferenceService can name its runtime and load the weights another way. The model agent that downloads base models is optional too, so turn it on when you install OME.
BaseModel and ClusterBaseModel¶
The two kinds have the same spec and differ in scope:
| Kind | Scope | Used by |
|---|---|---|
| BaseModel | Namespaced | InferenceServices in the same namespace |
| ClusterBaseModel | Cluster | InferenceServices in any namespace |
An InferenceService names its model in spec.model.name, and the model's kind in spec.model.kind, which defaults to ClusterBaseModel. To serve a BaseModel, set kind: BaseModel. With the wrong kind, OME admits the InferenceService but doesn't create or update its serving workload; see An InferenceService that serves a BaseModel gets no pods.
Give BaseModels and ClusterBaseModels different names. When both kinds share a name, InferenceServices in the BaseModel's namespace get the BaseModel, even when they ask for the ClusterBaseModel.
This ClusterBaseModel downloads Qwen/Qwen2.5-7B-Instruct from Hugging Face to /mnt/data/models/qwen/qwen2-5-7b-instruct on the nodes that run the model agent:
apiVersion: ome.io/v1beta1
kind: ClusterBaseModel
metadata:
name: qwen2-5-7b-instruct
spec:
storage:
storageUri: hf://Qwen/Qwen2.5-7B-Instruct
path: /mnt/data/models/qwen/qwen2-5-7b-instructApply the file:
kubectl apply -f model.yamlclusterbasemodel.ome.io/qwen2-5-7b-instruct createdSet storage.storageUri, and for hf://, oci://, modelpack:// and local:// models, storage.path. OME fills in the rest of the metadata from the model's files, and the OME API reference lists every field.
Where the weights come from¶
storage.storageUri says where the weights are, and its scheme decides what OME does:
| Scheme | URI form | What OME does |
|---|---|---|
hf |
hf://{model-id}[@{revision}] |
Downloads the model from Hugging Face to path on the selected nodes. The revision defaults to main. |
oci |
oci://n/{namespace}/b/{bucket}/o/{object_path} |
Downloads the model from OCI Object Storage to path on the selected nodes. |
modelpack |
modelpack://{registry}/{repository}[:{tag}], or @{digest} in place of the tag |
Pulls the model, a CNCF ModelPack artifact, from a container registry to path on the selected nodes, through an llmman serve daemon that you provide. The repository can contain slashes. See ModelPack models. |
pvc |
pvc://{pvc-name}/{sub-path} for a BaseModel, pvc://{namespace}:{pvc-name}/{sub-path} for a ClusterBaseModel |
A metadata Job reads the model from the claim. Whatever the kind, only InferenceServices in the claim's namespace can use it. See Serve models from a PVC. |
local |
local://{path} |
Reads the model from a directory that's already on the selected nodes. See Serve models from node-local storage. |
vendor |
vendor://{vendor-name}/{resource-type}/{resource-path} |
Downloads and reads nothing. The selected nodes report the model ready. |
Credentials¶
hf:// models read their token from a Secret that you name in storage.key. The agent reads the Secret from the model's namespace, or from ome for a ClusterBaseModel. The token comes from the Secret's token key, or from the key in storage.parameters.secretKey.
Create the namespace llama-demo, and a Secret in it with your Hugging Face token:
kubectl create namespace llama-demo
kubectl create secret generic hf-token -n llama-demo --from-literal=token="$HF_TOKEN"namespace/llama-demo created
secret/hf-token createdThis BaseModel downloads the gated model meta-llama/Llama-3.1-8B-Instruct with the token:
apiVersion: ome.io/v1beta1
kind: BaseModel
metadata:
name: llama-3-1-8b-instruct
namespace: llama-demo
spec:
storage:
storageUri: hf://meta-llama/Llama-3.1-8B-Instruct
path: /mnt/data/models/meta/llama-3-1-8b-instruct
key: hf-tokenThe agent uses the first token it finds:
- The Secret in
storage.key. storage.parameters.token, which leaves the token in plain text in the model's spec, so prefer a Secret.- The agent's own
HF_TOKENenvironment variable, if you set one.
Without a token, a gated model fails to download, and the node reports Failed.
oci:// models authenticate with the method in storage.parameters.auth: InstancePrincipal, the default, UserPrincipal, ResourcePrincipal or OkeWorkloadIdentity. storage.parameters.region overrides the region.
modelpack:// models take no credentials from the model: the llmman serve daemon talks to the registry, so storage.key and storage.parameters play no role. Give the daemon the registry's credentials in its own configuration. See ModelPack models.
How weights reach the nodes¶
The model agent puts the weights on the nodes. It runs as the DaemonSet ome-model-agent-daemonset, by default on all nodes whose taints it tolerates, including nodes with the nvidia.com/gpu taint. Helm values lists the settings that choose its nodes.
Since v1.3, the ome-resources chart installs the agent only when you set modelAgent.enabled: true. On v1.2.2, the chart always installs it. Without the agent, models stay In_Transit, except pvc:// models.
storage.path is the directory on the nodes that holds the model. Serving pods mount it read-only at the same path, and OME sets the MODEL_PATH environment variable to it, which the pre-configured runtimes load the model from.
The agent sees the host directory in modelAgent.hostPath, /mnt/data/models by default. Keep path under it, or mount more directories with modelAgent.extraVolumes and modelAgent.extraVolumeMounts.
With storage.downloadPolicy: ReuseIfExists, models that use the same Hugging Face files share one copy on each node. The default, AlwaysDownload, gives each model its own copy. See Share Hugging Face artifacts.
Choose the nodes¶
By default, all nodes that run the agent keep a copy of the model. Two storage fields narrow that down, and a node must match both when you set both:
| Field | A node matches when |
|---|---|
nodeSelector |
It has all the labels in the selector, with the same values. |
nodeAffinity |
It matches one of the requiredDuringSchedulingIgnoredDuringExecution terms. The agent ignores preferred terms. Unlike in pod scheduling, NotIn doesn't match a node that lacks the label. |
This BaseModel keeps a copy on the nodes of two instance types:
apiVersion: ome.io/v1beta1
kind: BaseModel
metadata:
name: llama-3-3-70b-instruct
namespace: llama-demo
spec:
storage:
storageUri: hf://meta-llama/Llama-3.3-70B-Instruct
path: /mnt/data/models/meta/llama-3-3-70b-instruct
key: hf-token
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- BM.GPU.H100.8
- BM.GPU.H200.8When you change the model or a node:
| Change | What happens |
|---|---|
nodeSelector or nodeAffinity |
Nodes that stop matching delete their copy, and nodes that start matching download it. Since v1.3, nodes that still match keep their copy. On v1.2.2, they process the model again. |
The model's labels or annotations, or another storage field, such as storageUri, path or key |
All matching nodes process the model again. A downloadPolicy change does this only for hf:// models. |
displayName, modelCapabilities or other descriptive metadata |
Nodes keep their copy as it is. |
| A node's labels | Nothing, until you restart the agent pod on that node. The new agent downloads models that now select the node, and keeps copies of models that no longer do. |
While a node processes the model, the model's label there is Updating, so new serving pods avoid that node until it's Ready again. Running pods keep running.
Node labels and status¶
The agent labels each selected Node with the model's state there. The label key depends on the kind:
| Kind | Label key | Example |
|---|---|---|
| ClusterBaseModel | models.ome.io/clusterbasemodel.{name} |
models.ome.io/clusterbasemodel.qwen2-5-7b-instruct |
| BaseModel | models.ome.io/{namespace}.basemodel.{name} |
models.ome.io/llama-demo.basemodel.llama-3-1-8b-instruct |
When a ClusterBaseModel's name is longer than 32 characters, or a BaseModel's namespace and name together are longer than 38, OME shortens the key with a hash. Read the key from the Node's labels then.
The label is Updating while the agent downloads or checks the model, then Ready or Failed.
The model's status lists the nodes where it's ready and where it failed:
kubectl get clusterbasemodel qwen2-5-7b-instruct \
-o 'custom-columns=STATE:.status.state,READY:.status.nodesReady[*],FAILED:.status.nodesFailed[*]'STATE READY FAILED
Ready gpu-node-1,gpu-node-2 <none>Or select the nodes by label:
kubectl get nodes -l models.ome.io/clusterbasemodel.qwen2-5-7b-instruct=Ready -o namenode/gpu-node-1
node/gpu-node-2OME runs an InferenceService's serving pods only on nodes where the model's label is Ready. pvc:// models have no copy on the nodes, so their pods have no such constraint.
Model lifecycle¶
status.state is the model's state across the cluster, shown in the READY column of kubectl get basemodel and kubectl get clusterbasemodel:
| State | Meaning |
|---|---|
In_Transit |
No node has the model Ready or Failed yet. Nodes are still downloading it, the model agent is off, or the model selects no node that runs the agent. |
Ready |
At least one node has the model ready. |
Failed |
No node has the model ready, and at least one has failed. |
A model is Ready as soon as one node has it, so some serving pods can stay Pending while other nodes download it. If a model stays In_Transit or becomes Failed, see Troubleshooting in the model agent guide, or in the PVC guide for a pvc:// model.
Create an InferenceService once its model is Ready. OME needs the model's format, which it reads from the model's files, to choose or check a runtime. Without it, the admission webhook rejects the InferenceService with a message that ends in model format name is required.
Disable a model¶
To stop new use of a model, set disabled: true in its spec. The admission webhook then rejects new and changed InferenceServices that use it, with model qwen2-5-7b-instruct is disabled. OME also stops creating or updating their serving workloads, and records a ModelReconcileError event with specified base model qwen2-5-7b-instruct is disabled. Running pods keep running, and the weights stay on the nodes.
Delete a model¶
When you delete a model, a finalizer keeps it until the agent on each node that recorded the model reports its copy deleted. If a node's agent isn't running, the deletion waits until it runs again or the node leaves the cluster.
You don't have to wait for the download. On a node that's still downloading the model, the agent cancels the download, and a queued download never starts, so the nodes don't keep downloading weights you deleted. A canceled download doesn't mark the model Failed on the node. How a delete stops a download in the model agent guide says how quickly a running download stops; on v1.2.2, the agent can't stop an oci:// download that has started.
On each node, the agent removes the model's label and deletes the files of an hf://, oci:// or modelpack:// model from path, keeping every file that another model, in any namespace, still uses. A model with the same path — even written with a trailing slash, or reached through a symlink — keeps the whole directory, and a model whose path leads inside it keeps its own files and their parent directories while the rest goes; When the agent deletes files in the retention guide has the exact rules. The agent also keeps the whole directory when the model has the label models.ome/reserve-model-artifact: "true". It leaves local:// and vendor:// files in place. Files shared through ReuseIfExists follow the rules in Share Hugging Face artifacts. For pvc:// models, see Clean up in the PVC guide.
Set the reserve label when you create the model: adding a label later makes all matching nodes process the model again. See Keep downloaded model files.
What OME learns from the model¶
Once a node has the weights, the agent reads the model's config.json, or model_index.json for a diffusion pipeline, and hf_quant_config.json if there is one. OME fills in these fields:
| Field | What OME records |
|---|---|
modelType |
The model type, such as llama. |
modelArchitecture |
The architecture, such as LlamaForCausalLM. |
modelFormat |
safetensors with version 1.0.0, or diffusers for a diffusion pipeline. |
modelFramework |
transformers with the config's transformers_version, or diffusers. |
modelParameterSize |
The number of parameters, such as 3.21B. |
maxTokens |
The context length. |
modelCapabilities |
What the model does, such as TEXT_TO_TEXT, IMAGE_TEXT_TO_TEXT, EMBEDDING or TEXT_TO_IMAGE. Some models get several. |
quantization |
fp8, fbgemm_fp8, int4, nvfp4, mxfp4 or compressed-tensors. 4-bit GPTQ and AWQ models get int4. Other methods and unquantized models leave it empty. |
modelConfiguration |
A summary of the config, with keys such as context_length and torch_dtype. |
diffusionPipeline |
The pipeline's components, from model_index.json. |
OME fills in only empty fields, so a value that you set always wins. For pvc:// models, a metadata Job fills in the same fields, except modelConfiguration and diffusionPipeline.
Set the metadata yourself¶
Set these fields yourself:
modelFormatandmodelFramework, forvendor://models and models with unreadable files, such as a directory withoutconfig.json. These models still becomeReady, but OME needs both fields to choose a runtime.modelCapabilities, when OME doesn't recognize the architecture and leaves an empty entry.displayName,version,vendorandapiCapabilities, which OME never fills in.
To skip reading a model's files, add the annotation ome.oracle.com/skip-config-parsing: "true". OME then keeps only the metadata you set. For a pvc:// model, the metadata Job ignores the annotation and fills in the fields you left empty. This BaseModel sets the metadata that the runtime srt-llama-3-2-3b-instruct supports:
apiVersion: ome.io/v1beta1
kind: BaseModel
metadata:
name: llama-3-2-3b-instruct
namespace: llama-demo
annotations:
ome.oracle.com/skip-config-parsing: "true"
spec:
modelType: llama
modelArchitecture: LlamaForCausalLM
modelFormat:
name: safetensors
version: "1.0.0"
modelFramework:
name: transformers
version: "4.45.0.dev0"
modelParameterSize: "3.21B"
modelCapabilities:
- TEXT_TO_TEXT
storage:
storageUri: hf://meta-llama/Llama-3.2-3B-Instruct
path: /mnt/data/models/meta/llama-3-2-3b-instruct
key: hf-tokenHow runtimes match the model¶
A serving runtime lists the models it supports in supportedModelFormats, by format, framework, architecture and quantization, and can limit their size with modelSizeRange. Model version matching and Diffusion pipeline runtime matching explain when a model matches.
When an InferenceService doesn't name a runtime, OME chooses among the matching runtimes that have an entry with autoSelect: true. A ServingRuntime in the InferenceService's namespace wins over any ClusterServingRuntime, and among runtimes of the same kind, the highest score wins.
When an InferenceService names a runtime, OME uses it even if the runtime doesn't declare support for the model, and records a RuntimeCompatibilityAdvisory event. Sharded models are the exception; see Distribution.
DistributionUnreleased: coming in v1.3¶
spec.distribution decides how the weights are laid out. PerNode, the default, puts a full copy on each selected node; leave the field out to get it.
Sharded is for loading the model in chunks from a model cache provider, which this release can't configure yet. The admission webhook rejects InferenceServices that use a Sharded model, even when they name a runtime. The message includes no model cache provider is configured for sharded model loading.
Fine-tuned weights¶
A FineTunedWeight holds weights fine-tuned from the base model in its baseModelRef. To serve it, name the base model in spec.model.name and the FineTunedWeight in spec.model.fineTunedWeights. OME serves one fine-tuned weight per InferenceService.
Next steps¶
- Serve models from a PVC: serve weights from a PersistentVolumeClaim.
- Serve models from node-local storage: serve weights already on the nodes' disks.
- Run the model agent: install, tune and troubleshoot the agent.
- Share Hugging Face artifacts: keep one copy of shared Hugging Face files per node.
- Serving runtimes: how a runtime declares the models it supports.