44definitions (LLM, embeddings, etc.) in a provider-agnostic way.
55
66Now using Pydantic for robust validation and type safety.
7+ Environment variable resolution is handled by OmegaConf in the config module.
78"""
89
910from __future__ import annotations
1011
11- import os
12- import re
1312import yaml
1413from pathlib import Path
1514from typing import Any , Dict , List , Optional
1817from pydantic import BaseModel , Field , field_validator , model_validator , ConfigDict , ValidationError
1918
2019# Import config merging for defaults.yml + config.yml integration
20+ # OmegaConf handles environment variable resolution (${VAR:-default} syntax)
2121from advanced_omi_backend .config import get_config
2222
23- def _resolve_env (value : Any ) -> Any :
24- """Resolve ``${VAR:-default}`` patterns inside a single value.
25-
26- This helper is intentionally minimal: it only operates on strings and leaves
27- all other types unchanged. Patterns of the form ``${VAR}`` or
28- ``${VAR:-default}`` are expanded using ``os.getenv``:
29-
30- - If the environment variable **VAR** is set, its value is used.
31- - Otherwise the optional ``default`` is used (or ``\" \" `` if omitted).
32-
33- Examples:
34- >>> os.environ.get("OLLAMA_MODEL")
35- >>> _resolve_env("${OLLAMA_MODEL:-llama3.1:latest}")
36- 'llama3.1:latest'
37-
38- >>> os.environ["OLLAMA_MODEL"] = "llama3.2:latest"
39- >>> _resolve_env("${OLLAMA_MODEL:-llama3.1:latest}")
40- 'llama3.2:latest'
41-
42- >>> _resolve_env("Bearer ${OPENAI_API_KEY:-}")
43- 'Bearer ' # when OPENAI_API_KEY is not set
44-
45- Note:
46- Use :func:`_deep_resolve_env` to apply this logic to an entire
47- nested config structure (dicts/lists) loaded from YAML.
48- """
49- if not isinstance (value , str ):
50- return value
51-
52- pattern = re .compile (r"\$\{([^}:]+)(?::-(.*?))?\}" )
53-
54- def repl (match : re .Match [str ]) -> str :
55- var , default = match .group (1 ), match .group (2 )
56- return os .getenv (var , default or "" )
57-
58- return pattern .sub (repl , value )
59-
60-
61- def _deep_resolve_env (data : Any ) -> Any :
62- """Recursively resolve environment variables in nested structures.
63-
64- This walks arbitrary Python structures produced by ``yaml.safe_load`` and
65- applies :func:`_resolve_env` to every string it finds. Dictionaries and
66- lists are traversed deeply; scalars are passed through unchanged.
67-
68- Examples:
69- >>> os.environ["OPENAI_MODEL"] = "gpt-4o-mini"
70- >>> cfg = {
71- ... "models": [
72- ... {"model_name": "${OPENAI_MODEL:-gpt-4o-mini}"},
73- ... {"model_url": "${OPENAI_BASE_URL:-https://api.openai.com/v1}"}
74- ... ]
75- ... }
76- >>> resolved = _deep_resolve_env(cfg)
77- >>> resolved["models"][0]["model_name"]
78- 'gpt-4o-mini'
79- >>> resolved["models"][1]["model_url"]
80- 'https://api.openai.com/v1'
81-
82- This is what :func:`load_models_config` uses immediately after loading
83- ``config.yml`` so that all ``${VAR:-default}`` placeholders are resolved
84- before Pydantic validation and model registry construction.
85- """
86- if isinstance (data , dict ):
87- return {k : _deep_resolve_env (v ) for k , v in data .items ()}
88- if isinstance (data , list ):
89- return [_deep_resolve_env (v ) for v in data ]
90- return _resolve_env (data )
91-
9223
9324class ModelDef (BaseModel ):
9425 """Model definition with validation.
@@ -270,7 +201,8 @@ def load_models_config(force_reload: bool = False) -> Optional[AppModels]:
270201 """Load model configuration from merged defaults.yml + config.yml.
271202
272203 This function loads defaults.yml and config.yml, merges them with user overrides,
273- resolves environment variables, validates model definitions using Pydantic, and caches the result.
204+ validates model definitions using Pydantic, and caches the result.
205+ Environment variables are resolved by OmegaConf during config loading.
274206
275207 Args:
276208 force_reload: If True, reload from disk even if already cached
@@ -280,24 +212,18 @@ def load_models_config(force_reload: bool = False) -> Optional[AppModels]:
280212
281213 Raises:
282214 ValidationError: If config.yml has invalid model definitions
283- yaml.YAMLError: If config.yml has invalid YAML syntax
284215 """
285216 global _REGISTRY
286217 if _REGISTRY is not None and not force_reload :
287218 return _REGISTRY
288219
289- # Try to get merged configuration (defaults + user config)
220+ # Get merged configuration (defaults + user config)
221+ # OmegaConf resolves environment variables automatically
290222 try :
291223 raw = get_config (force_reload = force_reload )
292224 except Exception as e :
293225 logging .error (f"Failed to load merged configuration: { e } " )
294- # Fallback to direct config.yml loading
295- cfg_path = _find_config_path ()
296- if not cfg_path .exists ():
297- return None
298- with cfg_path .open ("r" ) as f :
299- raw = yaml .safe_load (f ) or {}
300- raw = _deep_resolve_env (raw )
226+ return None
301227
302228 # Extract sections
303229 defaults = raw .get ("defaults" , {}) or {}
0 commit comments