fix(config): invalidate fields with missing env refs
This commit is contained in:
@@ -121,15 +121,18 @@ def resolve_env_refs(value: str) -> str:
|
|||||||
"""Resolve ``${VAR}`` references in a single string, leniently.
|
"""Resolve ``${VAR}`` references in a single string, leniently.
|
||||||
|
|
||||||
Unlike :func:`resolve_config_env_vars` (which walks a whole ``Config`` and
|
Unlike :func:`resolve_config_env_vars` (which walks a whole ``Config`` and
|
||||||
raises on a missing variable), this resolves one value and substitutes an
|
raises on a missing variable), this resolves one value and returns an empty
|
||||||
empty string for any unset reference. It is meant for individual, lazily
|
string if any reference is unset. It is meant for individual, lazily consumed
|
||||||
consumed secret fields — e.g. a transcription provider's ``api_key`` — so a
|
fields — e.g. a transcription provider's ``api_key`` or ``api_base`` — so a
|
||||||
missing variable degrades to "not configured" instead of sending the literal
|
missing variable degrades to "not configured" instead of producing a partial
|
||||||
``${VAR}`` text to the provider. Non-string input is returned unchanged.
|
value. Non-string input is returned unchanged.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
return _ENV_REF_PATTERN.sub(lambda m: os.environ.get(m.group(1), ""), value)
|
names = _ENV_REF_PATTERN.findall(value)
|
||||||
|
if any(name not in os.environ for name in names):
|
||||||
|
return ""
|
||||||
|
return _ENV_REF_PATTERN.sub(lambda m: os.environ[m.group(1)], value)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_in_place(obj: Any) -> Any:
|
def _resolve_in_place(obj: Any) -> Any:
|
||||||
|
|||||||
@@ -185,6 +185,18 @@ def test_resolver_env_ref_missing_var_degrades_to_not_configured() -> None:
|
|||||||
assert resolved.configured is False
|
assert resolved.configured is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolver_missing_embedded_api_key_ref_degrades_to_not_configured() -> None:
|
||||||
|
config = Config()
|
||||||
|
config.transcription.provider = "groq"
|
||||||
|
config.providers.groq.api_key = "Bearer ${MISSING_GROQ_KEY}"
|
||||||
|
|
||||||
|
with patch.dict(os.environ, {}, clear=True):
|
||||||
|
resolved = resolve_transcription_config(config)
|
||||||
|
|
||||||
|
assert not resolved.api_key
|
||||||
|
assert resolved.configured is False
|
||||||
|
|
||||||
|
|
||||||
def test_resolver_interpolates_env_ref_in_api_base() -> None:
|
def test_resolver_interpolates_env_ref_in_api_base() -> None:
|
||||||
config = Config()
|
config = Config()
|
||||||
config.transcription.provider = "groq"
|
config.transcription.provider = "groq"
|
||||||
@@ -197,6 +209,18 @@ def test_resolver_interpolates_env_ref_in_api_base() -> None:
|
|||||||
assert resolved.api_base == "https://groq.example/v1"
|
assert resolved.api_base == "https://groq.example/v1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolver_missing_embedded_api_base_ref_uses_provider_default() -> None:
|
||||||
|
config = Config()
|
||||||
|
config.transcription.provider = "groq"
|
||||||
|
config.providers.groq.api_key = "gsk-test"
|
||||||
|
config.providers.groq.api_base = "https://${MISSING_GROQ_HOST}/openai/v1"
|
||||||
|
|
||||||
|
with patch.dict(os.environ, {}, clear=True):
|
||||||
|
resolved = resolve_transcription_config(config)
|
||||||
|
|
||||||
|
assert resolved.api_base == "https://api.groq.com/openai/v1"
|
||||||
|
|
||||||
|
|
||||||
def test_resolver_supports_xiaomi_mimo_transcription_provider() -> None:
|
def test_resolver_supports_xiaomi_mimo_transcription_provider() -> None:
|
||||||
config = Config()
|
config = Config()
|
||||||
config.transcription.provider = "xiaomi_mimo"
|
config.transcription.provider = "xiaomi_mimo"
|
||||||
|
|||||||
Reference in New Issue
Block a user