feat: remove BuildConfig and its usage

Signed-off-by: Charlie Doern <cdoern@redhat.com>
This commit is contained in:
Charlie Doern 2025-11-21 12:06:47 -05:00
parent 3916015755
commit 0424afb7ed
6 changed files with 9 additions and 202 deletions

View file

@ -4,36 +4,9 @@
# This source code is licensed under the terms described in the LICENSE file in # This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree. # the root directory of this source tree.
import json
import sys
from enum import Enum from enum import Enum
from functools import lru_cache
from pathlib import Path from pathlib import Path
import yaml
from termcolor import cprint
from llama_stack.core.datatypes import (
BuildConfig,
Provider,
StackConfig,
StorageConfig,
)
from llama_stack.core.distribution import get_provider_registry
from llama_stack.core.resolver import InvalidProviderError
from llama_stack.core.storage.datatypes import (
InferenceStoreReference,
KVStoreReference,
ServerStoresConfig,
SqliteKVStoreConfig,
SqliteSqlStoreConfig,
SqlStoreReference,
)
from llama_stack.core.utils.config_dirs import DISTRIBS_BASE_DIR, EXTERNAL_PROVIDERS_DIR
from llama_stack.core.utils.dynamic import instantiate_class_type
from llama_stack.core.utils.image_types import LlamaStackImageType
from llama_stack_api import Api
TEMPLATES_PATH = Path(__file__).parent.parent.parent / "distributions" TEMPLATES_PATH = Path(__file__).parent.parent.parent / "distributions"
@ -49,103 +22,3 @@ def print_subcommand_description(parser, subparsers):
description = subcommand.description description = subcommand.description
description_text += f" {name:<21} {description}\n" description_text += f" {name:<21} {description}\n"
parser.epilog = description_text parser.epilog = description_text
def generate_run_config(
build_config: BuildConfig,
build_dir: Path,
image_name: str,
) -> Path:
"""
Generate a config.yaml template file for user to edit from a build.yaml file
"""
apis = list(build_config.distribution_spec.providers.keys())
distro_dir = DISTRIBS_BASE_DIR / image_name
run_config = StackConfig(
container_image=(image_name if build_config.image_type == LlamaStackImageType.CONTAINER.value else None),
image_name=image_name,
apis=apis,
providers={},
storage=StorageConfig(
backends={
"kv_default": SqliteKVStoreConfig(db_path=str(distro_dir / "kvstore.db")),
"sql_default": SqliteSqlStoreConfig(db_path=str(distro_dir / "sql_store.db")),
},
stores=ServerStoresConfig(
metadata=KVStoreReference(backend="kv_default", namespace="registry"),
inference=InferenceStoreReference(backend="sql_default", table_name="inference_store"),
conversations=SqlStoreReference(backend="sql_default", table_name="openai_conversations"),
),
),
external_providers_dir=build_config.external_providers_dir
if build_config.external_providers_dir
else EXTERNAL_PROVIDERS_DIR,
)
# build providers dict
provider_registry = get_provider_registry(build_config)
for api in apis:
run_config.providers[api] = []
providers = build_config.distribution_spec.providers[api]
for provider in providers:
pid = provider.provider_type.split("::")[-1]
p = provider_registry[Api(api)][provider.provider_type]
if p.deprecation_error:
raise InvalidProviderError(p.deprecation_error)
try:
config_type = instantiate_class_type(provider_registry[Api(api)][provider.provider_type].config_class)
except (ModuleNotFoundError, ValueError) as exc:
# HACK ALERT:
# This code executes after building is done, the import cannot work since the
# package is either available in the venv or container - not available on the host.
# TODO: use a "is_external" flag in ProviderSpec to check if the provider is
# external
cprint(
f"Failed to import provider {provider.provider_type} for API {api} - assuming it's external, skipping: {exc}",
color="yellow",
file=sys.stderr,
)
# Set config_type to None to avoid UnboundLocalError
config_type = None
if config_type is not None and hasattr(config_type, "sample_run_config"):
config = config_type.sample_run_config(__distro_dir__=f"~/.llama/distributions/{image_name}")
else:
config = {}
p_spec = Provider(
provider_id=pid,
provider_type=provider.provider_type,
config=config,
module=provider.module,
)
run_config.providers[api].append(p_spec)
run_config_file = build_dir / f"{image_name}-config.yaml"
with open(run_config_file, "w") as f:
to_write = json.loads(run_config.model_dump_json())
f.write(yaml.dump(to_write, sort_keys=False))
# Only print this message for non-container builds since it will be displayed before the
# container is built
# For non-container builds, the config.yaml is generated at the very end of the build process so it
# makes sense to display this message
if build_config.image_type != LlamaStackImageType.CONTAINER.value:
cprint(f"You can now run your stack with `llama stack run {run_config_file}`", color="green", file=sys.stderr)
return run_config_file
@lru_cache
def available_templates_specs() -> dict[str, BuildConfig]:
import yaml
template_specs = {}
for p in TEMPLATES_PATH.rglob("*build.yaml"):
template_name = p.parent.name
with open(p) as f:
build_config = BuildConfig(**yaml.safe_load(f))
template_specs[template_name] = build_config
return template_specs

View file

@ -46,7 +46,7 @@ def get_provider_dependencies(
deps = [] deps = []
external_provider_deps = [] external_provider_deps = []
registry = get_provider_registry(config) registry = get_provider_registry(config, True)
for api_str, provider_or_providers in providers.items(): for api_str, provider_or_providers in providers.items():
providers_for_api = registry[Api(api_str)] providers_for_api = registry[Api(api_str)]

View file

@ -607,35 +607,3 @@ can be instantiated multiple times (with different configs) if necessary.
_ensure_backend(stores.responses, sql_backends, "storage.stores.responses") _ensure_backend(stores.responses, sql_backends, "storage.stores.responses")
_ensure_backend(stores.prompts, kv_backends, "storage.stores.prompts") _ensure_backend(stores.prompts, kv_backends, "storage.stores.prompts")
return self return self
class BuildConfig(BaseModel):
version: int = LLAMA_STACK_BUILD_CONFIG_VERSION
distribution_spec: DistributionSpec = Field(description="The distribution spec to build including API providers. ")
image_type: str = Field(
default="venv",
description="Type of package to build (container | venv)",
)
image_name: str | None = Field(
default=None,
description="Name of the distribution to build",
)
external_providers_dir: Path | None = Field(
default=None,
description="Path to directory containing external provider implementations. The providers packages will be resolved from this directory. "
"pip_packages MUST contain the provider package name.",
)
external_apis_dir: Path | None = Field(
default=None,
description="Path to directory containing external API implementations. The APIs code and dependencies must be installed on the system.",
)
@field_validator("external_providers_dir")
@classmethod
def validate_external_providers_dir(cls, v):
if v is None:
return None
if isinstance(v, str):
return Path(v)
return v

View file

@ -12,7 +12,7 @@ from typing import Any
import yaml import yaml
from pydantic import BaseModel from pydantic import BaseModel
from llama_stack.core.datatypes import BuildConfig, DistributionSpec from llama_stack.core.datatypes import StackConfig
from llama_stack.core.external import load_external_apis from llama_stack.core.external import load_external_apis
from llama_stack.log import get_logger from llama_stack.log import get_logger
from llama_stack_api import ( from llama_stack_api import (
@ -85,7 +85,9 @@ def _load_inline_provider_spec(spec_data: dict[str, Any], api: Api, provider_nam
return spec return spec
def get_provider_registry(config=None) -> dict[Api, dict[str, ProviderSpec]]: def get_provider_registry(
config: StackConfig | None = None, building: bool = False
) -> dict[Api, dict[str, ProviderSpec]]:
"""Get the provider registry, optionally including external providers. """Get the provider registry, optionally including external providers.
This function loads both built-in providers and external providers from YAML files or from their provided modules. This function loads both built-in providers and external providers from YAML files or from their provided modules.
@ -161,7 +163,7 @@ def get_provider_registry(config=None) -> dict[Api, dict[str, ProviderSpec]]:
registry = get_external_providers_from_module( registry = get_external_providers_from_module(
registry=registry, registry=registry,
config=config, config=config,
building=(isinstance(config, BuildConfig) or isinstance(config, DistributionSpec)), building=building,
) )
return registry return registry
@ -223,9 +225,6 @@ def get_external_providers_from_module(
registry: dict[Api, dict[str, ProviderSpec]], config, building: bool registry: dict[Api, dict[str, ProviderSpec]], config, building: bool
) -> dict[Api, dict[str, ProviderSpec]]: ) -> dict[Api, dict[str, ProviderSpec]]:
provider_list = None provider_list = None
if isinstance(config, BuildConfig):
provider_list = config.distribution_spec.providers.items()
else:
provider_list = config.providers.items() provider_list = config.providers.items()
if provider_list is None: if provider_list is None:
logger.warning("Could not get list of providers from config") logger.warning("Could not get list of providers from config")

View file

@ -7,14 +7,14 @@
import yaml import yaml
from llama_stack.core.datatypes import BuildConfig, StackConfig from llama_stack.core.datatypes import StackConfig
from llama_stack.log import get_logger from llama_stack.log import get_logger
from llama_stack_api import Api, ExternalApiSpec from llama_stack_api import Api, ExternalApiSpec
logger = get_logger(name=__name__, category="core") logger = get_logger(name=__name__, category="core")
def load_external_apis(config: StackConfig | BuildConfig | None) -> dict[Api, ExternalApiSpec]: def load_external_apis(config: StackConfig | None) -> dict[Api, ExternalApiSpec]:
"""Load external API specifications from the configured directory. """Load external API specifications from the configured directory.
Args: Args:

View file

@ -16,10 +16,8 @@ from llama_stack.core.datatypes import (
LLAMA_STACK_RUN_CONFIG_VERSION, LLAMA_STACK_RUN_CONFIG_VERSION,
Api, Api,
BenchmarkInput, BenchmarkInput,
BuildConfig,
BuildProvider, BuildProvider,
DatasetInput, DatasetInput,
DistributionSpec,
ModelInput, ModelInput,
Provider, Provider,
SafetyConfig, SafetyConfig,
@ -38,7 +36,6 @@ from llama_stack.core.storage.datatypes import (
from llama_stack.core.storage.kvstore.config import SqliteKVStoreConfig from llama_stack.core.storage.kvstore.config import SqliteKVStoreConfig
from llama_stack.core.storage.sqlstore.sqlstore import SqliteSqlStoreConfig from llama_stack.core.storage.sqlstore.sqlstore import SqliteSqlStoreConfig
from llama_stack.core.utils.dynamic import instantiate_class_type from llama_stack.core.utils.dynamic import instantiate_class_type
from llama_stack.core.utils.image_types import LlamaStackImageType
from llama_stack.providers.utils.inference.model_registry import ProviderModelEntry from llama_stack.providers.utils.inference.model_registry import ProviderModelEntry
from llama_stack_api import DatasetPurpose, ModelType from llama_stack_api import DatasetPurpose, ModelType
@ -320,28 +317,6 @@ class DistributionTemplate(BaseModel):
available_models_by_provider: dict[str, list[ProviderModelEntry]] | None = None available_models_by_provider: dict[str, list[ProviderModelEntry]] | None = None
def build_config(self) -> BuildConfig:
# Create minimal providers for build config (without runtime configs)
build_providers = {}
for api, providers in self.providers.items():
build_providers[api] = []
for provider in providers:
# Create a minimal build provider object with only essential build information
build_provider = BuildProvider(
provider_type=provider.provider_type,
module=provider.module,
)
build_providers[api].append(build_provider)
return BuildConfig(
distribution_spec=DistributionSpec(
description=self.description,
container_image=self.container_image,
providers=build_providers,
),
image_type=LlamaStackImageType.VENV.value, # default to venv
)
def generate_markdown_docs(self) -> str: def generate_markdown_docs(self) -> str:
providers_table = "| API | Provider(s) |\n" providers_table = "| API | Provider(s) |\n"
providers_table += "|-----|-------------|\n" providers_table += "|-----|-------------|\n"
@ -413,14 +388,6 @@ class DistributionTemplate(BaseModel):
for output_dir in [yaml_output_dir, doc_output_dir]: for output_dir in [yaml_output_dir, doc_output_dir]:
output_dir.mkdir(parents=True, exist_ok=True) output_dir.mkdir(parents=True, exist_ok=True)
build_config = self.build_config()
with open(yaml_output_dir / "build.yaml", "w") as f:
yaml.safe_dump(
filter_empty_values(build_config.model_dump(exclude_none=True)),
f,
sort_keys=False,
)
for yaml_pth, settings in self.run_configs.items(): for yaml_pth, settings in self.run_configs.items():
run_config = settings.run_config(self.name, self.providers, self.container_image) run_config = settings.run_config(self.name, self.providers, self.container_image)
with open(yaml_output_dir / yaml_pth, "w") as f: with open(yaml_output_dir / yaml_pth, "w") as f: