Plugin Configuration (NemoConfig)
Defining Config
Both plugin_name AND plugin_description are required ClassVar[str] fields. Omitting either raises TypeError at class-definition time.
from typing import ClassVar
from pydantic import Field
from nemo_platform_plugin.config import NemoConfig
class MyPluginConfig(NemoConfig):
plugin_name: ClassVar[str] = "my-plugin" # kebab-case — matches CLI entry-point key
plugin_description: ClassVar[str] = "Configuration for my plugin."
debug: bool = Field(default=False)
max_workers: int = Field(default=4)
log_level: str = Field(default="INFO")
Environment Variable Formula
NMP_<SAFE_NAME>_<FIELD> where SAFE_NAME = plugin_name uppercased with non-word chars replaced by _.
plugin_name |
Env prefix | Field | Env var |
|---|---|---|---|
"my-plugin" |
NMP_MY_PLUGIN_ |
debug |
NMP_MY_PLUGIN_DEBUG |
"agents" |
NMP_AGENTS_ |
runner_backend |
NMP_AGENTS_RUNNER_BACKEND |
"example" |
NMP_EXAMPLE_ |
greeting_style |
NMP_EXAMPLE_GREETING_STYLE |
Nested fields use _ as delimiter:
NMP_AGENTS_CONTROLLER_INTERVAL_SECONDS=10
NMP_AGENTS_CONTROLLER_PORT_RANGE_START=49152
YAML Config File
Config key in /etc/nmp/config.yaml equals plugin_name:
my_plugin:
debug: true
max_workers: 8
# Helm value path: platformConfig.my_plugin.debug
Override config file location: NMP_CONFIG_FILE_PATH=/my/custom/config.yaml
Priority Order
- Environment variables (
NMP_MY_PLUGIN_*) — highest - Config file (
/etc/nmp/config.yaml) - Pydantic field defaults — lowest
Accessing Config
# Option 1: classmethod (preferred in plugin code)
config = MyPluginConfig.get()
# Option 2: standalone function (preferred when importing one name for all config types)
from nemo_platform_plugin.config import get_nemo_config
config = get_nemo_config(MyPluginConfig)
Both return the same cached singleton. Multiple calls in the same process return the same instance.
Example — ExampleConfig from example-plugin (verbatim)
from typing import ClassVar, Literal
from nemo_platform_plugin.config import NemoConfig
from pydantic import Field
class ExampleConfig(NemoConfig):
plugin_name: ClassVar[str] = "example"
plugin_description: ClassVar[str] = "Configuration for the NeMo Platform example plugin."
greeting_style: Literal["formal", "casual"] = Field(
default="formal",
description=(
"Controls the tone of /hello/{name} responses. "
'"formal" → "Hello, {name}." "casual" → "Hey, {name}!"'
" Set NMP_EXAMPLE_GREETING_STYLE to override."
),
)
log_requests: bool = Field(
default=False,
description=(
"When True, each request to the items list endpoint emits a "
"structured INFO log line including the workspace and page number. "
"Set NMP_EXAMPLE_LOG_REQUESTS=true to enable."
),
)
Usage across multiple components (no wiring needed):
# In service.py AND controller.py — both call get() independently
config = ExampleConfig.get()
Nested Config Models
Use a plain BaseModel (NOT NemoConfig) for nested sections:
from pydantic import BaseModel, Field
from nemo_platform_plugin.config import NemoConfig
class ControllerConfig(BaseModel): # plain BaseModel — NOT NemoConfig
interval_seconds: int = Field(default=5)
health_check_timeout_seconds: int = Field(default=120)
class AgentsConfig(NemoConfig):
plugin_name: ClassVar[str] = "agents"
plugin_description: ClassVar[str] = "Configuration for the NeMo Platform agents plugin."
controller: ControllerConfig = Field(default_factory=ControllerConfig)
runner_backend: str = Field(default="in_memory")
Access nested fields: AgentsConfig.get().controller.interval_seconds
Set nested via env: NMP_AGENTS_CONTROLLER_INTERVAL_SECONDS=10
Test Override Pattern
from nemo_platform_plugin.config import (
set_nemo_config_override,
clear_nemo_config_override,
clear_nemo_config_overrides,
)
# In a test:
def test_casual_greeting():
set_nemo_config_override(ExampleConfig(greeting_style="casual"))
try:
config = ExampleConfig.get()
assert config.greeting_style == "casual"
finally:
clear_nemo_config_override(ExampleConfig)
# With autouse pytest fixture (recommended):
import pytest
@pytest.fixture(autouse=True)
def reset_config():
yield
clear_nemo_config_overrides() # clears ALL overrides after each test
Gotchas
plugin_namemust be kebab-case:"my-plugin"not"my_plugin". Use the same value as your CLI entry-point key. The env prefix replaces-with_automatically.- Both
plugin_nameANDplugin_descriptionrequired:TypeErrorat class-definition time if either is missing or empty. - Do NOT call
MyPluginConfig()directly: This bypasses env/file loading. Always useMyPluginConfig.get()orget_nemo_config(MyPluginConfig). - Do NOT set
model_configmanually: The_NemoConfigMetametaclass sets it before Pydantic builds the schema. Setting it manually will be overridden and may leave the schema inconsistent. - Config is cached per process: In tests, always clean up with
clear_nemo_config_override(cls)orclear_nemo_config_overrides(). Failing to do so pollutes subsequent tests. - Abstract intermediates: A class that declares neither
plugin_namenorplugin_descriptionis treated as an abstract intermediate (exempt from validation). Declaringplugin_descriptionwithoutplugin_nameraisesTypeError— you cannot partially fill the requirement.