mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-02 22:02:13 +08:00
fix(tracing): support LANGCHAIN_* env fallback for LangSmith config (#1065)
* fix(tracing): support LANGCHAIN_* env fallback for LangSmith config - add backward-compatible env parsing in tracing_config.py - support fallback keys: LANGCHAIN_TRACING_V2 / LANGCHAIN_TRACING LANGCHAIN_API_KEY LANGCHAIN_PROJECT LANGCHAIN_ENDPOINT - keep LANGSMITH_* as preferred source when both are present - add regression tests in test_tracing_config.py * fix(tracing): correct LANGSMITH_* precedence over LANGCHAIN_* for enabled flag (#1067) * Initial plan * fix(tracing): use first-present-wins logic for enabled flag, add precedence docs and test Co-authored-by: WillemJiang <219644+WillemJiang@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: WillemJiang <219644+WillemJiang@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -25,8 +25,48 @@ class TracingConfig(BaseModel):
|
||||
_tracing_config: TracingConfig | None = None
|
||||
|
||||
|
||||
_TRUTHY_VALUES = {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
def _env_flag_preferred(*names: str) -> bool:
|
||||
"""Return the boolean value of the first env var that is present and non-empty.
|
||||
|
||||
Accepted truthy values (case-insensitive): ``1``, ``true``, ``yes``, ``on``.
|
||||
Any other non-empty value is treated as falsy. If none of the named
|
||||
variables is set, returns ``False``.
|
||||
"""
|
||||
for name in names:
|
||||
value = os.environ.get(name)
|
||||
if value is not None and value.strip():
|
||||
return value.strip().lower() in _TRUTHY_VALUES
|
||||
return False
|
||||
|
||||
|
||||
def _first_env_value(*names: str) -> str | None:
|
||||
"""Return the first non-empty environment value from candidate names."""
|
||||
for name in names:
|
||||
value = os.environ.get(name)
|
||||
if value and value.strip():
|
||||
return value.strip()
|
||||
return None
|
||||
|
||||
|
||||
def get_tracing_config() -> TracingConfig:
|
||||
"""Get the current tracing configuration from environment variables.
|
||||
|
||||
``LANGSMITH_*`` variables take precedence over their legacy ``LANGCHAIN_*``
|
||||
counterparts. For boolean flags (``enabled``), the *first* variable that is
|
||||
present and non-empty in the priority list is the sole authority – its value
|
||||
is parsed and returned without consulting the remaining candidates. Accepted
|
||||
truthy values are ``1``, ``true``, ``yes``, and ``on`` (case-insensitive);
|
||||
any other non-empty value is treated as falsy.
|
||||
|
||||
Priority order:
|
||||
enabled : LANGSMITH_TRACING > LANGCHAIN_TRACING_V2 > LANGCHAIN_TRACING
|
||||
api_key : LANGSMITH_API_KEY > LANGCHAIN_API_KEY
|
||||
project : LANGSMITH_PROJECT > LANGCHAIN_PROJECT (default: "deer-flow")
|
||||
endpoint : LANGSMITH_ENDPOINT > LANGCHAIN_ENDPOINT (default: https://api.smith.langchain.com)
|
||||
|
||||
Returns:
|
||||
TracingConfig with current settings.
|
||||
"""
|
||||
@@ -37,10 +77,11 @@ def get_tracing_config() -> TracingConfig:
|
||||
if _tracing_config is not None: # Double-check after acquiring lock
|
||||
return _tracing_config
|
||||
_tracing_config = TracingConfig(
|
||||
enabled=os.environ.get("LANGSMITH_TRACING", "").lower() == "true",
|
||||
api_key=os.environ.get("LANGSMITH_API_KEY"),
|
||||
project=os.environ.get("LANGSMITH_PROJECT", "deer-flow"),
|
||||
endpoint=os.environ.get("LANGSMITH_ENDPOINT", "https://api.smith.langchain.com"),
|
||||
# Keep compatibility with both legacy LANGCHAIN_* and newer LANGSMITH_* variables.
|
||||
enabled=_env_flag_preferred("LANGSMITH_TRACING", "LANGCHAIN_TRACING_V2", "LANGCHAIN_TRACING"),
|
||||
api_key=_first_env_value("LANGSMITH_API_KEY", "LANGCHAIN_API_KEY"),
|
||||
project=_first_env_value("LANGSMITH_PROJECT", "LANGCHAIN_PROJECT") or "deer-flow",
|
||||
endpoint=_first_env_value("LANGSMITH_ENDPOINT", "LANGCHAIN_ENDPOINT") or "https://api.smith.langchain.com",
|
||||
)
|
||||
return _tracing_config
|
||||
|
||||
|
||||
71
backend/tests/test_tracing_config.py
Normal file
71
backend/tests/test_tracing_config.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""Tests for src.config.tracing_config."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from src.config import tracing_config as tracing_module
|
||||
|
||||
|
||||
def _reset_tracing_cache() -> None:
|
||||
tracing_module._tracing_config = None
|
||||
|
||||
|
||||
def test_prefers_langsmith_env_names(monkeypatch):
|
||||
monkeypatch.setenv("LANGSMITH_TRACING", "true")
|
||||
monkeypatch.setenv("LANGSMITH_API_KEY", "lsv2_key")
|
||||
monkeypatch.setenv("LANGSMITH_PROJECT", "smith-project")
|
||||
monkeypatch.setenv("LANGSMITH_ENDPOINT", "https://smith.example.com")
|
||||
|
||||
_reset_tracing_cache()
|
||||
cfg = tracing_module.get_tracing_config()
|
||||
|
||||
assert cfg.enabled is True
|
||||
assert cfg.api_key == "lsv2_key"
|
||||
assert cfg.project == "smith-project"
|
||||
assert cfg.endpoint == "https://smith.example.com"
|
||||
assert tracing_module.is_tracing_enabled() is True
|
||||
|
||||
|
||||
def test_falls_back_to_langchain_env_names(monkeypatch):
|
||||
monkeypatch.delenv("LANGSMITH_TRACING", raising=False)
|
||||
monkeypatch.delenv("LANGSMITH_API_KEY", raising=False)
|
||||
monkeypatch.delenv("LANGSMITH_PROJECT", raising=False)
|
||||
monkeypatch.delenv("LANGSMITH_ENDPOINT", raising=False)
|
||||
|
||||
monkeypatch.setenv("LANGCHAIN_TRACING_V2", "true")
|
||||
monkeypatch.setenv("LANGCHAIN_API_KEY", "legacy-key")
|
||||
monkeypatch.setenv("LANGCHAIN_PROJECT", "legacy-project")
|
||||
monkeypatch.setenv("LANGCHAIN_ENDPOINT", "https://legacy.example.com")
|
||||
|
||||
_reset_tracing_cache()
|
||||
cfg = tracing_module.get_tracing_config()
|
||||
|
||||
assert cfg.enabled is True
|
||||
assert cfg.api_key == "legacy-key"
|
||||
assert cfg.project == "legacy-project"
|
||||
assert cfg.endpoint == "https://legacy.example.com"
|
||||
assert tracing_module.is_tracing_enabled() is True
|
||||
|
||||
|
||||
def test_langsmith_tracing_false_overrides_langchain_tracing_v2_true(monkeypatch):
|
||||
"""LANGSMITH_TRACING=false must win over LANGCHAIN_TRACING_V2=true."""
|
||||
monkeypatch.setenv("LANGSMITH_TRACING", "false")
|
||||
monkeypatch.setenv("LANGCHAIN_TRACING_V2", "true")
|
||||
monkeypatch.setenv("LANGSMITH_API_KEY", "some-key")
|
||||
|
||||
_reset_tracing_cache()
|
||||
cfg = tracing_module.get_tracing_config()
|
||||
|
||||
assert cfg.enabled is False
|
||||
assert tracing_module.is_tracing_enabled() is False
|
||||
|
||||
|
||||
def test_defaults_when_project_not_set(monkeypatch):
|
||||
monkeypatch.setenv("LANGSMITH_TRACING", "yes")
|
||||
monkeypatch.setenv("LANGSMITH_API_KEY", "key")
|
||||
monkeypatch.delenv("LANGSMITH_PROJECT", raising=False)
|
||||
monkeypatch.delenv("LANGCHAIN_PROJECT", raising=False)
|
||||
|
||||
_reset_tracing_cache()
|
||||
cfg = tracing_module.get_tracing_config()
|
||||
|
||||
assert cfg.project == "deer-flow"
|
||||
Reference in New Issue
Block a user