refactor(config): use process-global + ContextVar override for AppConfig lifecycle

AppConfig.init() now sets a process-global variable visible to all async
tasks, fixing the issue where ContextVar-only storage didn't propagate
config reloads across request boundaries. ContextVar is retained as
set_override() for per-invocation overrides.
This commit is contained in:
greatmengqi 2026-04-15 23:48:05 +08:00
parent 6c0c2ecf62
commit 7a11e9258c

View File

@ -222,26 +222,34 @@ class AppConfig(BaseModel):
"""
return next((group for group in self.tool_groups if group.name == name), None)
# -- Lifecycle (class-level singleton via ContextVar) --
# -- Lifecycle (process-global + per-context override) --
_current: ClassVar[ContextVar[AppConfig]] = ContextVar("deerflow_app_config")
_global: ClassVar[AppConfig | None] = None
_override: ClassVar[ContextVar[AppConfig]] = ContextVar("deerflow_app_config_override")
@classmethod
def init(cls, config: AppConfig) -> None:
"""Set the AppConfig for the current context. Call once at process startup."""
cls._current.set(config)
"""Set the process-global AppConfig. Visible to all subsequent requests."""
cls._global = config
@classmethod
def set_override(cls, config: AppConfig) -> None:
"""Set a per-context override (e.g., for a single invocation with custom config)."""
cls._override.set(config)
@classmethod
def current(cls) -> AppConfig:
"""Get the current AppConfig.
Auto-initializes from config file on first access for backward compatibility.
Prefer calling AppConfig.init() explicitly at process startup.
Priority: per-context override > process-global > auto-load from file.
"""
try:
return cls._current.get()
return cls._override.get()
except LookupError:
logger.debug("AppConfig not initialized, auto-loading from file")
config = cls.from_file()
cls._current.set(config)
return config
pass
if cls._global is not None:
return cls._global
logger.debug("AppConfig not initialized, auto-loading from file")
config = cls.from_file()
cls._global = config
return config