Skip to content

fix(config): pin explicit app config path across get_app_config - #7092

Merged
WillemJiang merged 2 commits into
bytedance:mainfrom
Hugo-DDT:fix/app-config-explicit-path-pin
Oct 24, 2026
Merged

WillemJiang merged 2 commits into
bytedance:mainfrom
Hugo-DDT:fix/app-config-explicit-path-pin

Conversation

@Hugo-DDT

Copy link
Copy Markdown
Contributor

Summary

Fixes #7086. DeerFlowClient(config_path=...) / reload_app_config(explicit_path) did not remember the explicit path, so a later get_app_config() re-resolved through ambient search and either silently preferred a sibling default config.yaml or raised FileNotFoundError when none existed.

This mirrors the existing _extensions_config_source pin:

  • Successful reload_app_config(config_path) records _app_config_source
  • get_app_config() resolves through that pin (edits to the pinned file still revalidate)
  • Pin clears on reset_app_config(), set_app_config(), and argument-less reload_app_config()
  • A failed explicit reload keeps the prior pin
  • Ambient refreshes via get_app_config() do not pin the resolved path (so DEER_FLOW_CONFIG_PATH changes still take effect)

DeerFlowClient needs no change.

Test plan

  • Red/green: tests/test_app_config_explicit_path.py (4 tests) failed on base, pass with the fix
  • pytest tests/test_app_config_explicit_path.py tests/test_app_config_reload.py — 51 passed
  • Neighboring filtered client/config suites — 76 passed
  • ruff check on touched files — clean

Copilot AI balanced review requested due to automatic review settings October 19, 2026 02:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@github-actions github-actions Bot added area:backend Gateway / runtime / core backend under backend/ risk:medium Medium risk: regular code changes size/M PR changes 100-300 lines labels Oct 19, 2026

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at head abbf7d7 (fix(config): pin explicit app config path across get_app_config).

The fix works as intended and I verified it against the head SHA:

  • get_app_config() now resolves _app_config_source first, so every consumer of the singleton — including DeerFlowClient(config_path=...) (client.py calls reload_app_config(config_path) then get_app_config()) — keeps following the explicitly loaded file instead of silently falling back to sibling/ambient default resolution, and no longer raises when no ambient config exists.
  • The pin is not re-set by ambient refreshes (get_app_config -> _load_and_cache_app_config leaves it alone), is cleared by reset_app_config() / set_app_config() / argument-less reload_app_config(), and a failed explicit reload keeps the prior pin. A missing explicit path still fails fast via AppConfig.resolve_config_path.
  • Tests: ran backend/tests/test_app_config_explicit_path.py against head source (PYTHONPATH pinned to the PR tree): 4 passed. No regressions: test_app_config_reload.py 47 passed, test_client.py -k config 37 passed. No prior human reviews to duplicate.

Two non-blocking observations below, both about consistency with the _extensions_config_source analogue this change explicitly mirrors.

# prior pin (mirrors ``_extensions_config_source``). Ambient refreshes go
# through ``get_app_config`` -> ``_load_and_cache_app_config`` and must not
# pin the resolved path.
_app_config_source = config_path or None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: the analogue this mirrors (_extensions_config_source) mutates its pin under _extensions_config_lock (see _load_and_cache_extensions_config: "The caller holds _extensions_config_lock"), but the app-config cache layer has no lock, and here the pin is written after _load_and_cache_app_config has already swapped _app_config / _app_config_path / _app_config_signature. A concurrent get_app_config() can therefore observe the freshly loaded explicit-path config under the stale pin (e.g. pin=A, cache=B), see _app_config_path != resolved_path, and reload ambient/A over it; interleaving an argument-less reload_app_config() (clears the pin first) with an explicit one can likewise leave _app_config_source pointing at a file the cache no longer holds. This is consistent with the layer's pre-existing lock-free design, so not a regression — but since DeerFlowClient(config_path=...) (which triggers this) can share a process with hot-path get_app_config() reads, consider a module-level lock, or setting the pin inside _load_and_cache_app_config behind a flag, to actually match the extensions contract.

# ``set_app_config()``, or an argument-less reload; probing default resolution
# instead would either flip the cache back to a sibling default or raise when
# no ambient config exists (discarding the explicit load).
_app_config_source: str | None = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the pin stores the caller's raw config_path string, and get_app_config() re-resolves it with Path(_app_config_source) against the then-current working directory — so a relative path pinned once (e.g. reload_app_config("custom/config.yaml")) silently resolves elsewhere, or raises, if the process chdir's before the next get_app_config(). _load_and_cache_app_config already computes the resolved Path; recording str(resolved_path) would make the pin CWD-stable. _extensions_config_source shares this behavior, so this is a consistency question rather than a regression.

@github-actions github-actions Bot added area:docs Documentation and Markdown only size/L PR changes 300-700 lines and removed size/M PR changes 100-300 lines labels Oct 19, 2026
@WillemJiang WillemJiang added this to the 2.2.0 milestone Oct 19, 2026
@Hugo-DDT
Hugo-DDT force-pushed the fix/app-config-explicit-path-pin branch from 66f32f8 to 2034159 Compare October 23, 2026 09:04
Hugo-DDT and others added 2 commits October 24, 2026 13:57
Fixes bytedance#7086. reload_app_config(config_path) now records an
_app_config_source pin (mirroring _extensions_config_source) so
get_app_config and DeerFlowClient(config_path=...) keep following the
explicit file instead of ambient default resolution.
Serialize singleton reads, reloads, resets and injections so a concurrent call cannot publish a config under another file's pin. Keep explicit paths absolute across working-directory changes while preserving ambient discovery and failed-reload behavior.

Add deterministic concurrency and relative-path regressions. Validation: 33,875 offline tests and 262 blocking-I/O tests passed; frontend check, backend formatting and agent guidance passed.
@Hugo-DDT
Hugo-DDT force-pushed the fix/app-config-explicit-path-pin branch from 2034159 to 6f618be Compare October 24, 2026 05:58
@WillemJiang
WillemJiang merged commit afe244e into bytedance:main Oct 24, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:backend Gateway / runtime / core backend under backend/ area:docs Documentation and Markdown only risk:medium Medium risk: regular code changes size/L PR changes 300-700 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] DeerFlowClient(config_path=...) silently uses the default config, and explicit reloads can be discarded

4 participants