Source code for qbiocode.apps.quvine.api.config

# Copyright 2026, IBM Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Config loading/merging for the QuVINE embedding API.

The API reuses the same Hydra/OmegaConf YAML schema the pipeline uses
(``configs/config.yaml``). Unlike the CLI, the API does not need the Hydra
runtime -- it simply loads a base ``DictConfig`` and deep-merges overrides.
"""

from __future__ import annotations

import os
from pathlib import Path
from typing import Optional, Union

from omegaconf import DictConfig, OmegaConf


def _find_default_config() -> Path:
    """
    Locate the packaged default ``config.yaml``.

    Search order:
    1. ``$QUVINE_DEFAULT_CONFIG`` if set.
    2. ``configs/config.yaml`` walking up from this file (repo / installed layout).
    """
    env_path = os.environ.get("QUVINE_DEFAULT_CONFIG")
    if env_path:
        return Path(env_path)

    here = Path(__file__).resolve()
    for parent in here.parents:
        candidate = parent / "configs" / "config.yaml"
        if candidate.is_file():
            return candidate

    raise FileNotFoundError(
        "Could not locate a default QuVINE config.yaml. Pass config=<path> "
        "explicitly or set the QUVINE_DEFAULT_CONFIG environment variable."
    )


[docs] def load_config( config: Optional[Union[DictConfig, dict, str]] = None, overrides: Optional[Union[dict, list, DictConfig]] = None, default_path: Optional[str] = None, ) -> DictConfig: """ Build the OmegaConf config the API operates on. Args: config: One of * ``None`` -- load the packaged default config. * a ``str``/path -- load that YAML file. * a ``dict`` -- treat as a full config. * a ``DictConfig`` -- used as-is (a deep copy is made). overrides: Deep-merged on top of ``config``. Accepts a dict, a ``DictConfig``, or a list of Hydra-style ``"a.b=c"`` dotlist strings. default_path: Explicit path to use instead of auto-discovery when ``config is None``. Returns: A resolved-on-access ``DictConfig``. """ if config is None: base = OmegaConf.load(default_path or _find_default_config()) elif isinstance(config, str): base = OmegaConf.load(config) elif isinstance(config, DictConfig): base = OmegaConf.create(OmegaConf.to_container(config, resolve=False)) elif isinstance(config, dict): base = OmegaConf.create(config) else: raise TypeError( f"config must be None, str, dict, or DictConfig; got {type(config)!r}" ) if overrides: if isinstance(overrides, list): override_cfg = OmegaConf.from_dotlist(list(overrides)) else: override_cfg = OmegaConf.create(overrides) base = OmegaConf.merge(base, override_cfg) return base