diff --git a/mellea/telemetry/tracing.py b/mellea/telemetry/tracing.py index f5cc03c7e..12b7b486f 100644 --- a/mellea/telemetry/tracing.py +++ b/mellea/telemetry/tracing.py @@ -87,8 +87,10 @@ def _setup_tracer_provider() -> Any: _tracer_provider = _setup_tracer_provider() # Create separate tracers for application and backend _mellea_version = version("mellea") - _application_tracer = trace.get_tracer("mellea.application", _mellea_version) # type: ignore - _backend_tracer = trace.get_tracer("mellea.backend", _mellea_version) # type: ignore + _application_tracer = _tracer_provider.get_tracer( + "mellea.application", _mellea_version + ) + _backend_tracer = _tracer_provider.get_tracer("mellea.backend", _mellea_version) def is_application_tracing_enabled() -> bool: diff --git a/test/telemetry/test_metrics.py b/test/telemetry/test_metrics.py index b9a1a4c01..8c9e19e4d 100644 --- a/test/telemetry/test_metrics.py +++ b/test/telemetry/test_metrics.py @@ -46,6 +46,14 @@ def clean_metrics_env(monkeypatch): def enable_metrics(monkeypatch): """Enable metrics for tests.""" monkeypatch.setenv("MELLEA_METRICS_ENABLED", "true") + # Clear other env vars to prevent user-set values from leaking into reload + monkeypatch.delenv("MELLEA_METRICS_CONSOLE", raising=False) + monkeypatch.delenv("MELLEA_METRICS_OTLP", raising=False) + monkeypatch.delenv("MELLEA_METRICS_PROMETHEUS", raising=False) + monkeypatch.delenv("OTEL_EXPORTER_OTLP_ENDPOINT", raising=False) + monkeypatch.delenv("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", raising=False) + monkeypatch.delenv("OTEL_METRIC_EXPORT_INTERVAL", raising=False) + monkeypatch.delenv("OTEL_SERVICE_NAME", raising=False) # Force reload of metrics module to pick up env vars import importlib @@ -454,6 +462,9 @@ def test_otlp_enabled_without_endpoint_warning(monkeypatch): """Test that enabling OTLP without endpoint produces helpful warning.""" monkeypatch.setenv("MELLEA_METRICS_ENABLED", "true") monkeypatch.setenv("MELLEA_METRICS_OTLP", "true") + # Ensure no endpoint env vars are set (user env could have these) + monkeypatch.delenv("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", raising=False) + monkeypatch.delenv("OTEL_EXPORTER_OTLP_ENDPOINT", raising=False) import importlib diff --git a/test/telemetry/test_tracing.py b/test/telemetry/test_tracing.py index 6566e8e70..af83de5ad 100644 --- a/test/telemetry/test_tracing.py +++ b/test/telemetry/test_tracing.py @@ -43,7 +43,21 @@ def enable_backend_tracing(monkeypatch): importlib.reload(mellea.telemetry.tracing) -def test_telemetry_disabled_by_default(): +@pytest.fixture +def disable_tracing(monkeypatch): + """Disable all tracing for tests.""" + monkeypatch.delenv("MELLEA_TRACE_APPLICATION", raising=False) + monkeypatch.delenv("MELLEA_TRACE_BACKEND", raising=False) + import importlib + + import mellea.telemetry.tracing + + importlib.reload(mellea.telemetry.tracing) + yield + importlib.reload(mellea.telemetry.tracing) + + +def test_telemetry_disabled_by_default(disable_tracing): """Test that telemetry is disabled by default.""" from mellea.telemetry import ( is_application_tracing_enabled, diff --git a/test/telemetry/test_tracing_backend.py b/test/telemetry/test_tracing_backend.py index 044b336a8..4cbe5937b 100644 --- a/test/telemetry/test_tracing_backend.py +++ b/test/telemetry/test_tracing_backend.py @@ -37,12 +37,18 @@ @pytest.fixture(scope="module", autouse=True) def setup_telemetry(): """Set up telemetry for all tests in this module.""" + import importlib + + import mellea.telemetry.tracing + mp = pytest.MonkeyPatch() mp.setenv("MELLEA_TRACE_BACKEND", "true") + importlib.reload(mellea.telemetry.tracing) yield - mp.undo() + mp.setenv("MELLEA_TRACE_BACKEND", "false") + importlib.reload(mellea.telemetry.tracing) @pytest.fixture @@ -67,10 +73,8 @@ def span_exporter(): @pytest.mark.asyncio -async def test_span_duration_captures_async_operation(span_exporter, gh_run): +async def test_span_duration_captures_async_operation(span_exporter): """Test that span duration includes the full async operation time.""" - if gh_run: - pytest.skip("Skipping in CI - requires Ollama") backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore ctx = SimpleContext() @@ -113,17 +117,19 @@ async def test_span_duration_captures_async_operation(span_exporter, gh_run): @pytest.mark.asyncio -async def test_context_propagation_parent_child(span_exporter, gh_run): +async def test_context_propagation_parent_child(span_exporter): """Test that parent-child span relationships are maintained.""" - if gh_run: - pytest.skip("Skipping in CI - requires Ollama") backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore ctx = SimpleContext() ctx = ctx.add(Message(role="user", content="Say 'test' and nothing else")) - # Create a parent span - tracer = trace.get_tracer(__name__) + # Create a parent span using the module's own tracer provider + # (not the global one, which may be pinned to a different provider + # due to OTel's set-once semantics for set_tracer_provider) + from mellea.telemetry import tracing + + tracer = tracing._tracer_provider.get_tracer(__name__) with tracer.start_as_current_span("parent_operation"): mot, _ = await backend.generate_from_context( Message(role="assistant", content=""), ctx @@ -158,10 +164,8 @@ async def test_context_propagation_parent_child(span_exporter, gh_run): @pytest.mark.asyncio -async def test_token_usage_recorded_after_completion(span_exporter, gh_run): +async def test_token_usage_recorded_after_completion(span_exporter): """Test that token usage metrics are recorded after async completion.""" - if gh_run: - pytest.skip("Skipping in CI - requires Ollama") backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore ctx = SimpleContext() @@ -209,10 +213,8 @@ async def test_token_usage_recorded_after_completion(span_exporter, gh_run): @pytest.mark.asyncio -async def test_span_not_closed_prematurely(span_exporter, gh_run): +async def test_span_not_closed_prematurely(span_exporter): """Test that spans are not closed before async operations complete.""" - if gh_run: - pytest.skip("Skipping in CI - requires Ollama") backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore ctx = SimpleContext() @@ -245,10 +247,8 @@ async def test_span_not_closed_prematurely(span_exporter, gh_run): @pytest.mark.asyncio -async def test_multiple_generations_separate_spans(span_exporter, gh_run): +async def test_multiple_generations_separate_spans(span_exporter): """Test that multiple generations create separate spans.""" - if gh_run: - pytest.skip("Skipping in CI - requires Ollama") backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore ctx = SimpleContext() @@ -279,10 +279,8 @@ async def test_multiple_generations_separate_spans(span_exporter, gh_run): @pytest.mark.asyncio -async def test_streaming_span_duration(span_exporter, gh_run): +async def test_streaming_span_duration(span_exporter): """Test that streaming operations have accurate span durations.""" - if gh_run: - pytest.skip("Skipping in CI - requires Ollama") from mellea.backends.model_options import ModelOption