From 0f76b053878797bbd92bfe6522f9a5084b3d53ca Mon Sep 17 00:00:00 2001 From: Jake LoRocco Date: Thu, 9 Apr 2026 14:40:46 -0400 Subject: [PATCH 1/6] fix: reload module for telemetry testing so all tests can run --- test/telemetry/test_tracing.py | 16 +++++++++++++++- test/telemetry/test_tracing_backend.py | 8 +++++++- 2 files changed, 22 insertions(+), 2 deletions(-) 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..781ae1e62 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 From 90796461ef506407fb941691a7679779e9ebf76b Mon Sep 17 00:00:00 2001 From: Jake LoRocco Date: Fri, 10 Apr 2026 09:01:28 -0400 Subject: [PATCH 2/6] fix: remove unecessary ci skip checks --- test/telemetry/test_tracing_backend.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/telemetry/test_tracing_backend.py b/test/telemetry/test_tracing_backend.py index 781ae1e62..e8d764bbf 100644 --- a/test/telemetry/test_tracing_backend.py +++ b/test/telemetry/test_tracing_backend.py @@ -75,8 +75,6 @@ def span_exporter(): @pytest.mark.asyncio async def test_span_duration_captures_async_operation(span_exporter, gh_run): """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() @@ -121,8 +119,6 @@ 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): """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() @@ -166,8 +162,6 @@ 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): """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() @@ -217,8 +211,6 @@ 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): """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() @@ -253,8 +245,6 @@ 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): """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() @@ -287,8 +277,6 @@ async def test_multiple_generations_separate_spans(span_exporter, gh_run): @pytest.mark.asyncio async def test_streaming_span_duration(span_exporter, gh_run): """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 From 28894e7251e7550ed9c11cb4065e5bab53f45a2f Mon Sep 17 00:00:00 2001 From: Jake LoRocco Date: Fri, 10 Apr 2026 09:09:47 -0400 Subject: [PATCH 3/6] fix: get tracers from tracing provider --- mellea/telemetry/tracing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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: From fdbd57817ab73090bf2f01fc816e80eb5467a873 Mon Sep 17 00:00:00 2001 From: Jake LoRocco Date: Fri, 10 Apr 2026 09:20:59 -0400 Subject: [PATCH 4/6] fix: tracing test and env var setting for tests --- test/telemetry/test_metrics.py | 11 +++++++++++ test/telemetry/test_tracing_backend.py | 8 ++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/test/telemetry/test_metrics.py b/test/telemetry/test_metrics.py index b9a1a4c01..feccb1157 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("OTL_METRIC_EXPORT_INTERVAL", raising=False) + monkeypatch.delenv("OTL_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_backend.py b/test/telemetry/test_tracing_backend.py index e8d764bbf..126db95c0 100644 --- a/test/telemetry/test_tracing_backend.py +++ b/test/telemetry/test_tracing_backend.py @@ -124,8 +124,12 @@ async def test_context_propagation_parent_child(span_exporter, gh_run): 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 From 8c8b1761f6d837e69c59e0d60e14e5a07f5f2687 Mon Sep 17 00:00:00 2001 From: Jake LoRocco Date: Fri, 10 Apr 2026 13:58:53 -0400 Subject: [PATCH 5/6] fix: issues with telemetry test changes --- test/telemetry/test_metrics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/telemetry/test_metrics.py b/test/telemetry/test_metrics.py index feccb1157..8c9e19e4d 100644 --- a/test/telemetry/test_metrics.py +++ b/test/telemetry/test_metrics.py @@ -52,8 +52,8 @@ def enable_metrics(monkeypatch): 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("OTL_METRIC_EXPORT_INTERVAL", raising=False) - monkeypatch.delenv("OTL_SERVICE_NAME", 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 From 6841ce5a51fb959d750c10ed09f495ee23b8ca86 Mon Sep 17 00:00:00 2001 From: Jake LoRocco Date: Fri, 10 Apr 2026 16:12:28 -0400 Subject: [PATCH 6/6] fix: remove unused fixture from telemetry tests --- test/telemetry/test_tracing_backend.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/telemetry/test_tracing_backend.py b/test/telemetry/test_tracing_backend.py index 126db95c0..4cbe5937b 100644 --- a/test/telemetry/test_tracing_backend.py +++ b/test/telemetry/test_tracing_backend.py @@ -73,7 +73,7 @@ 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.""" backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore @@ -117,7 +117,7 @@ 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.""" backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore @@ -164,7 +164,7 @@ 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.""" backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore @@ -213,7 +213,7 @@ 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.""" backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore @@ -247,7 +247,7 @@ 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.""" backend = OllamaModelBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO.ollama_name) # type: ignore @@ -279,7 +279,7 @@ 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.""" from mellea.backends.model_options import ModelOption