Pydantic Models
Smarter API LLMHost Manifest Constants.
- smarter.apps.llmhost.manifest.models.llmhost.const.DEFAULT_SHM_SIZE = '8Gi'
Default size of /dev/shm, which PyTorch-based engines use for inter-process communication.
- smarter.apps.llmhost.manifest.models.llmhost.const.DEFAULT_STARTUP_TIMEOUT_SECONDS = 3600
Default time that the inference server has to download and load the weights before it is restarted.
- smarter.apps.llmhost.manifest.models.llmhost.const.DEFAULT_STORAGE_SIZE = '50Gi'
Default size of the model volume, which caches the weights across restarts.
- smarter.apps.llmhost.manifest.models.llmhost.const.K8S_QUANTITY_PATTERN = '^[0-9]+(\\.[0-9]+)?(m|k|Ki|M|Mi|G|Gi|T|Ti|P|Pi|E|Ei)?$'
A Kubernetes resource quantity, e.g.
500m,4or24Gi.
Smarter API Manifest - LLMHost.metadata.
- class smarter.apps.llmhost.manifest.models.llmhost.metadata.SAMLLMHostMetadata(*, name: str, description: str | None, version: str | None, tags: List[str] | None = <factory>, annotations: List[dict[str, str | int | float | bool | ~datetime.date | ~datetime.datetime | ~decimal.Decimal | ~uuid.UUID | bytes | list | dict]] | None=<factory>)[source]
Bases:
AbstractSAMMetadataBaseSmarter API LLMHost Manifest - Metadata class.
- model_config = {'arbitrary_types_allowed': True, 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
Smarter API Manifest - LLMHost.spec.
An LLMHost is a large language model that Smarter hosts on Kubernetes. Applying the
manifest stores it; smarter deploy launches it, creating a Deployment, Service,
PersistentVolumeClaim, and optionally an Ingress and a Secret, in the environment’s
namespace; smarter undeploy destroys them.
The spec separates what is served (model) from how it is served (engine),
and from where it runs (compute, resources, storage, network), so that a
model from any source can be served by any engine that supports that source.
compute names an LLMHostCompute: a kind of node, e.g. g6.2xlarge with one NVIDIA L4, and the
node group of those nodes, which Smarter scales as LLMHosts are launched and destroyed.
resources is what one replica’s pod requests of that node; it must fit the node.
spec:
model:
source: huggingface # huggingface, ollama, s3, url or pvc
repository: meta-llama/Llama-3.1-8B-Instruct
revision: main
servedName: llama-3.1-8b-instruct # the model name of the OpenAI API
tokenSecret: huggingface_token # a Smarter Secret, for gated models
task: text-generation # text-generation or embedding
parameterCount: 8030000000
contextWindow: 131072
quantization: bf16
capabilities:
functionCalling: true
engine:
name: vllm # vllm, tgi, sglang, llama_cpp, ollama, tei or custom
contextLength: 16384
args:
- --enable-auto-tool-choice
- --tool-call-parser=llama3_json
compute: gpu_a10g_1x # an LLMHostCompute: the node, and its node group
resources: # what one replica's pod requests of the node
gpuCount: 1
cpu: "4"
memory: 24Gi
storage:
size: 50Gi
network:
ingress: true
- smarter.apps.llmhost.manifest.models.llmhost.spec.ENGINE_RULES: dict[str, dict[str, Any]] = {'custom': {'apiKey': False, 'sources': frozenset({'huggingface', 'ollama', 'pvc', 's3', 'url'}), 'tasks': None}, 'llama_cpp': {'apiKey': True, 'requiresFile': True, 'sources': frozenset({'huggingface', 'pvc', 's3', 'url'}), 'tasks': None}, 'ollama': {'apiKey': False, 'sources': frozenset({'huggingface', 'ollama'}), 'tasks': None}, 'sglang': {'apiKey': True, 'requiresGpu': True, 'sources': frozenset({'huggingface', 'pvc', 's3', 'url'}), 'tasks': None}, 'tei': {'apiKey': True, 'sources': frozenset({'huggingface', 'pvc', 's3', 'url'}), 'tasks': {'embedding'}}, 'tgi': {'apiKey': False, 'sources': frozenset({'huggingface', 'pvc', 's3', 'url'}), 'tasks': {'text-generation'}}, 'vllm': {'apiKey': True, 'requiresGpu': True, 'sources': frozenset({'huggingface', 'pvc', 's3', 'url'}), 'tasks': None}}
What each engine supports, for validation.
tasks: Nonemeans any task.{ 'custom': { 'apiKey': False, 'sources': frozenset(['huggingface', 'ollama', 'pvc', 's3', 'url']), 'tasks': None, }, 'llama_cpp': { 'apiKey': True, 'requiresFile': True, 'sources': frozenset(['huggingface', 'pvc', 's3', 'url']), 'tasks': None, }, 'ollama': {'apiKey': False, 'sources': frozenset(['huggingface', 'ollama']), 'tasks': None}, 'sglang': { 'apiKey': True, 'requiresGpu': True, 'sources': frozenset(['huggingface', 'pvc', 's3', 'url']), 'tasks': None, }, 'tei': { 'apiKey': True, 'sources': frozenset(['huggingface', 'pvc', 's3', 'url']), 'tasks': set(['embedding']), }, 'tgi': { 'apiKey': False, 'sources': frozenset(['huggingface', 'pvc', 's3', 'url']), 'tasks': set(['text-generation']), }, 'vllm': { 'apiKey': True, 'requiresGpu': True, 'sources': frozenset(['huggingface', 'pvc', 's3', 'url']), 'tasks': None, }, }
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostEngineConfig(*, name: ~typing.Literal['vllm', 'tgi', 'sglang', 'llama_cpp', 'ollama', 'tei', 'custom'], image: str | None = None, apiFormat: ~typing.Literal['openai_compatible', 'huggingface', 'ollama_native', 'custom'] = 'openai_compatible', port: ~typing.Annotated[int | None, ~annotated_types.Ge(ge=1), ~annotated_types.Le(le=65535)] = None, command: list[str] | None = None, contextLength: ~typing.Annotated[int | None, ~annotated_types.Ge(ge=1)] = None, tensorParallelSize: ~typing.Annotated[int | None, ~annotated_types.Ge(ge=1), ~annotated_types.Le(le=16)] = None, gpuMemoryUtilization: ~typing.Annotated[float | None, ~annotated_types.Gt(gt=0.0), ~annotated_types.Le(le=1.0)] = None, dtype: str | None = None, maxConcurrentRequests: ~typing.Annotated[int | None, ~annotated_types.Ge(ge=1)] = None, trustRemoteCode: bool = False, args: list[str] = <factory>, env: dict[str, str] = <factory>)[source]
Bases:
LLMHostBaseModelSpec.engine: the inference server.
The common settings, e.g.
contextLength, are translated into each engine’s own arguments, e.g. vLLM’s--max-model-lenand llama.cpp’s--ctx-size.argsandenvare passed to the engine as they are, for everything else.- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostHealthCheck(*, path: str | None = None, startupTimeoutSeconds: Annotated[int, Ge(ge=60), Le(le=21600)] = 3600, periodSeconds: Annotated[int, Ge(ge=1), Le(le=300)] = 10)[source]
Bases:
LLMHostBaseModelSpec.healthCheck: the probes that decide when a replica is ready.
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostModel(*, source: Literal['huggingface', 'ollama', 's3', 'url', 'pvc']='huggingface', repository: str, revision: str | None = None, file: str | None = None, servedName: str | None = None, task: Literal['text-generation', 'embedding']='text-generation', tokenSecret: str | None = None, license: str | None = None, architecture: str | None = None, parameterCount: Annotated[int | None, ~annotated_types.Ge(ge=0)] = None, contextWindow: Annotated[int | None, ~annotated_types.Ge(ge=0)] = None, embeddingDimensions: Annotated[int | None, ~annotated_types.Ge(ge=0)] = None, quantization: Literal['none', 'fp32', 'fp16', 'bf16', 'fp8', 'mxfp4', 'int8', 'int4', 'gguf', 'awq', 'gptq']='none', capabilities: SAMLLMHostModelCapabilities = <factory>)[source]
Bases:
LLMHostBaseModelSpec.model: what is served, and where its weights come from.
- capabilities: SAMLLMHostModelCapabilities
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- quantization: Literal['none', 'fp32', 'fp16', 'bf16', 'fp8', 'mxfp4', 'int8', 'int4', 'gguf', 'awq', 'gptq']
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostModelCapabilities(*, streaming: bool = True, functionCalling: bool = False, vision: bool = False, reasoning: bool = False)[source]
Bases:
LLMHostBaseModelWhat the model can do.
Informational, for discovery and for the LLMClients that use it.
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostNetwork(*, ingress: bool = False, hostname: str | None = None, apiKeySecret: str | None = None, allowUnauthenticated: bool = False)[source]
Bases:
LLMHostBaseModelSpec.network: how the LLMHost is reached.
It is always reachable inside the cluster, at its Service. With
ingress, it is also reachable athttps://<hostname>, with a TLS certificate from cert-manager. A public endpoint requires an API key, unlessallowUnauthenticated.- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostResources(*, gpuCount: Annotated[int, ~annotated_types.Ge(ge=0), ~annotated_types.Le(le=16)] = 0, gpuResource: str = 'nvidia.com/gpu', vramRequiredGb: Annotated[int | None, ~annotated_types.Ge(ge=0)] = None, cpu: str | None = None, memory: str | None = None, shmSize: str = '8Gi', nodeSelector: dict[str, str]=<factory>, tolerations: list[dict[str, ~typing.Any]]=<factory>, serviceAccountName: str | None = None)[source]
Bases:
LLMHostBaseModelSpec.resources: the compute that each replica, one pod, requests.
These are the pod’s resource requests, not a node’s capacity. They must fit a node of the LLMHost’s compute, which also determines the GPU model.
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostScaling(*, replicas: Annotated[int, Ge(ge=1), Le(le=16)] = 1)[source]
Bases:
LLMHostBaseModelSpec.scaling: the replicas.
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostSpec(*, model: SAMLLMHostModel, engine: SAMLLMHostEngineConfig, compute: str | None = None, resources: SAMLLMHostResources = <factory>, storage: SAMLLMHostStorage = <factory>, network: SAMLLMHostNetwork = <factory>, scaling: SAMLLMHostScaling = <factory>, healthCheck: SAMLLMHostHealthCheck = <factory>)[source]
Bases:
LLMHostBaseModelSmarter API LLMHost Manifest LLMHost.spec.
- engine: SAMLLMHostEngineConfig
- healthCheck: SAMLLMHostHealthCheck
- model: SAMLLMHostModel
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- network: SAMLLMHostNetwork
- resources: SAMLLMHostResources
- scaling: SAMLLMHostScaling
- storage: SAMLLMHostStorage
- class smarter.apps.llmhost.manifest.models.llmhost.spec.SAMLLMHostStorage(*, size: str = '50Gi', storageClass: str | None = None, accessMode: Literal['ReadWriteOnce', 'ReadWriteMany', 'ReadOnlyMany'] = 'ReadWriteOnce', existingClaim: str | None = None, retain: bool = True)[source]
Bases:
LLMHostBaseModelSpec.storage: the model volume, a PersistentVolumeClaim mounted at /models.
It caches the weights, so that a restarted server does not download them again. The cluster’s storage class provisions its PersistentVolume.
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- smarter.apps.llmhost.manifest.models.llmhost.spec.engine_supports_api_key(engine)[source]
Return whether an engine authenticates requests with an API key.
- Return type:
- smarter.apps.llmhost.manifest.models.llmhost.spec.engine_supports_task(engine, task)[source]
Return whether an engine can serve a task.
- Return type:
Smarter API Manifest - LLMHost.status.
- class smarter.apps.llmhost.manifest.models.llmhost.status.SAMLLMHostStatus(*, recordLocator: str, created: datetime, modified: datetime, dependencies: List[SAMDependency] | None = None, accountNumber: str, username: str, hostStatus: str | None = None, message: str | None = None, replicas: int | None = None, readyReplicas: int | None = None, healthy: bool | None = None, endpoint: str | None = None, publicUrl: str | None = None, apiKeySecret: str | None = None, deployedAt: datetime | None = None, lastCheckedAt: datetime | None = None, uptimeHours: float | None = None, estimatedCost: Decimal | None = None)[source]
Bases:
AbstractSAMStatusBaseSmarter API LLMHost Manifest - Status class.
Read only. Besides ownership, it reports the LLMHost’s lifecycle state, where it can be reached, and its accumulated cost, as of its most recent status check.
- model_config = {'arbitrary_types_allowed': True, 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
Smarter API LLMHost Manifest.
- class smarter.apps.llmhost.manifest.models.llmhost.model.SAMLLMHost(*, apiVersion: str, kind: str, metadata: SAMLLMHostMetadata, spec: SAMLLMHostSpec, status: SAMLLMHostStatus | None = None)[source]
Bases:
AbstractSAMBaseSmarter API Manifest - LLMHost.
- metadata: SAMLLMHostMetadata
- model_config = {'arbitrary_types_allowed': True, 'from_attributes': True, 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- spec: SAMLLMHostSpec
- status: SAMLLMHostStatus | None