mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-19 11:41:04 +02:00
feat: stamp API key into model override at save time to survive global provider change (#362)
* fix: stamp API key into model override at save time to survive global provider change When a workflow overrides the TTS/LLM/STT provider to match the current global config, the override dict only stores model/voice fields, not the API key. If the global config later switches to a different provider, the override can no longer inherit the API key and calls fail. Fix: enrich_overrides_with_api_keys() copies the global provider's API key (and other secret fields) into the override dict at workflow-save time, making the override self-contained regardless of future global config changes. * feat: add test coverage and masking logic --------- Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
This commit is contained in:
parent
8a58b0992d
commit
5b61ad645f
6 changed files with 451 additions and 39 deletions
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
|
||||
from api.schemas.user_configuration import UserConfiguration
|
||||
from api.services.configuration.registry import (
|
||||
REGISTRY,
|
||||
|
|
@ -29,6 +31,48 @@ def _build_section_from_override(service_type: ServiceType, override: dict):
|
|||
return config_cls(**override)
|
||||
|
||||
|
||||
_SECRET_FIELDS = ("api_key", "credentials", "aws_access_key", "aws_secret_key")
|
||||
|
||||
|
||||
def enrich_overrides_with_api_keys(
|
||||
model_overrides: dict,
|
||||
user_config: UserConfiguration,
|
||||
) -> dict:
|
||||
"""Copy API keys from the global config into model_overrides where missing.
|
||||
|
||||
When a workflow override selects the same provider as the current global
|
||||
config but omits the API key, the override becomes broken if the global
|
||||
config later switches to a different provider. This function stamps the
|
||||
global provider's API key (and other secret fields) into the override at
|
||||
save time so the override is self-contained.
|
||||
"""
|
||||
result = copy.deepcopy(model_overrides)
|
||||
for section_key in _SECTION_MAP:
|
||||
if section_key not in result:
|
||||
continue
|
||||
override = result[section_key]
|
||||
override_provider = override.get("provider")
|
||||
if not override_provider:
|
||||
continue
|
||||
global_section = getattr(user_config, section_key, None)
|
||||
if global_section is None:
|
||||
continue
|
||||
if getattr(global_section, "provider", None) != override_provider:
|
||||
continue
|
||||
for field in _SECRET_FIELDS:
|
||||
if override.get(field):
|
||||
continue
|
||||
if field == "api_key" and hasattr(global_section, "get_all_api_keys"):
|
||||
all_keys = global_section.get_all_api_keys()
|
||||
if all_keys:
|
||||
override[field] = all_keys[0] if len(all_keys) == 1 else all_keys
|
||||
else:
|
||||
global_value = getattr(global_section, field, None)
|
||||
if global_value is not None:
|
||||
override[field] = global_value
|
||||
return result
|
||||
|
||||
|
||||
def resolve_effective_config(
|
||||
user_config: UserConfiguration,
|
||||
model_overrides: dict | None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue