diff --git a/src/langbot/pkg/telemetry/execution.py b/src/langbot/pkg/telemetry/execution.py index f4af0defa..da0111f47 100644 --- a/src/langbot/pkg/telemetry/execution.py +++ b/src/langbot/pkg/telemetry/execution.py @@ -107,6 +107,12 @@ class ExecutionCounters: # ------------------------------------------------------------------ config def trace_mode(self) -> str: + # Cloud activity counts must not be computed from sampled successes. + # Telemetry opt-out is still enforced before recording observations. + from ..utils import constants + + if constants.edition == 'cloud': + return 'all' mode = str(self.manager.telemetry_config.get('execution_trace', DEFAULT_TRACE_MODE) or '').strip().lower() return mode if mode in TRACE_MODES else DEFAULT_TRACE_MODE diff --git a/tests/unit_tests/telemetry/test_cloud_sampling.py b/tests/unit_tests/telemetry/test_cloud_sampling.py new file mode 100644 index 000000000..b1a604489 --- /dev/null +++ b/tests/unit_tests/telemetry/test_cloud_sampling.py @@ -0,0 +1,34 @@ +from types import SimpleNamespace + +from langbot.pkg.telemetry.execution import ExecutionCounters +from langbot.pkg.utils import constants + + +def test_cloud_counts_are_not_sampled(monkeypatch): + monkeypatch.setattr(constants, 'edition', 'cloud') + counters = ExecutionCounters(SimpleNamespace(telemetry_config={'execution_trace': 'sampled'})) + assert counters.trace_mode() == 'all' + state = SimpleNamespace(failure_reason='', stages=[], synthetic=False, trace_id='00000001') + assert counters._trace_emitted(state) + + +def test_community_retains_sampling(monkeypatch): + monkeypatch.setattr(constants, 'edition', 'community') + counters = ExecutionCounters(SimpleNamespace(telemetry_config={'execution_trace': 'sampled'})) + assert counters.trace_mode() == 'sampled' + + +def test_dns_survives_saturated_executor(): + import socket + import threading + from langbot.pkg.utils.bounded_executor import BoundedThreadPoolExecutor + + gate = threading.Event() + executor = BoundedThreadPoolExecutor(max_workers=1, max_pending=0) + try: + blocked = executor.submit(gate.wait) + assert executor.submit(socket.getaddrinfo, '127.0.0.1', 443).result(timeout=2) + finally: + gate.set() + blocked.result(timeout=2) + executor.shutdown()