From c25fb9b5532b17acbba467ef797b2c670f4853cf Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 18:48:10 +0200 Subject: [PATCH 01/11] fix(otel): stamp team attributes before ending proxy span --- litellm/integrations/opentelemetry.py | 8 +-- .../test_otel_proxy_span_lifecycle.py | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 tests/unit/integrations/test_otel_proxy_span_lifecycle.py diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 90383ddb60d..f256dc219de 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1427,6 +1427,10 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): if log_span is not None: self._emit_semantic_logs(kwargs, response_obj, log_span) + # Stamp team attributes onto the SERVER (root) span before it is + # closed, so the trace root carries them like every child span. + self._set_team_attributes_on_proxy_span_from_kwargs(kwargs) + # 6. Do NOT end parent span - it should be managed by its creator # External spans (from Langfuse, user code, HTTP headers, global context) must not be closed by LiteLLM # However, proxy-created spans should be closed here. @@ -1439,10 +1443,6 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): ): self._close_proxy_span_ok(parent_span, end_time) - # Stamp team attributes onto the SERVER (root) span before it is - # closed, so the trace root carries them like every child span. - self._set_team_attributes_on_proxy_span_from_kwargs(kwargs) - # close the proxy span explicitly from kwargs metadata # after all child spans (litellm_request, guardrail, raw_request) # have been fully recorded and exported. diff --git a/tests/unit/integrations/test_otel_proxy_span_lifecycle.py b/tests/unit/integrations/test_otel_proxy_span_lifecycle.py new file mode 100644 index 00000000000..025f57b3f4e --- /dev/null +++ b/tests/unit/integrations/test_otel_proxy_span_lifecycle.py @@ -0,0 +1,61 @@ +import logging +from datetime import datetime, timezone + +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, +) + +from litellm.integrations.opentelemetry import ( + LITELLM_PROXY_REQUEST_SPAN_NAME, + OpenTelemetry, +) + + +def test_success_stamps_team_attributes_before_proxy_span_end(caplog): + exporter = InMemorySpanExporter() + provider = TracerProvider() + provider.add_span_processor(SimpleSpanProcessor(exporter)) + otel = OpenTelemetry() + otel.tracer = provider.get_tracer("otel-proxy-span-lifecycle-test") + otel.message_logging = False + + server_span = otel.create_litellm_proxy_request_started_span( + start_time=datetime.now(timezone.utc), headers={} + ) + kwargs = { + "model": "test-model", + "litellm_call_id": "test-call", + "call_type": "completion", + "litellm_params": { + "metadata": { + "litellm_parent_otel_span": server_span, + "user_api_key_team_id": "test-team-id", + "user_api_key_team_alias": "test-team", + } + }, + "standard_logging_object": { + "metadata": { + "user_api_key_team_id": "test-team-id", + "user_api_key_team_alias": "test-team", + }, + "call_type": "completion", + }, + } + + with caplog.at_level(logging.WARNING, logger="opentelemetry.sdk.trace"): + now = datetime.now(timezone.utc) + otel.log_success_event(kwargs, {"id": "test-response"}, now, now) + + warnings = [ + record + for record in caplog.records + if record.getMessage() == "Setting attribute on ended span." + ] + assert warnings == [] + + spans = {span.name: span for span in exporter.get_finished_spans()} + root = spans[LITELLM_PROXY_REQUEST_SPAN_NAME] + assert root.attributes["metadata.user_api_key_team_id"] == "test-team-id" + assert root.attributes["metadata.user_api_key_team_alias"] == "test-team" From 15ab4f2e43130e0fe2d1ddf6efab259de7cfc45c Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 19:06:41 +0200 Subject: [PATCH 02/11] fix(otel): cover alternate proxy metadata --- litellm/integrations/opentelemetry.py | 5 +- tests/unit/integrations/test_opentelemetry.py | 55 +++++++++++++++++ .../test_otel_proxy_span_lifecycle.py | 61 ------------------- 3 files changed, 59 insertions(+), 62 deletions(-) delete mode 100644 tests/unit/integrations/test_otel_proxy_span_lifecycle.py diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index f256dc219de..bf2063642da 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1554,7 +1554,10 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): """ litellm_params: Final = kwargs.get("litellm_params") or {} metadata: Final = litellm_params.get("metadata") or {} - proxy_span: Final = metadata.get("litellm_parent_otel_span") + litellm_metadata: Final = litellm_params.get("litellm_metadata") or {} + proxy_span: Final = metadata.get("litellm_parent_otel_span") or litellm_metadata.get( + "litellm_parent_otel_span" + ) if ( proxy_span is not None and getattr(proxy_span, "name", None) == LITELLM_PROXY_REQUEST_SPAN_NAME diff --git a/tests/unit/integrations/test_opentelemetry.py b/tests/unit/integrations/test_opentelemetry.py index 56049c53736..797e9db8bf8 100644 --- a/tests/unit/integrations/test_opentelemetry.py +++ b/tests/unit/integrations/test_opentelemetry.py @@ -3,6 +3,7 @@ import concurrent.futures import contextlib import gc import json +import logging import os import sys import threading @@ -19,6 +20,7 @@ import pytest # Adds the grandparent directory to sys.path to allow importing project modules from opentelemetry import trace +from opentelemetry.trace import Span from opentelemetry._logs.severity import SeverityNumber from opentelemetry.sdk._logs import LogData, LogRecord from opentelemetry.sdk._logs import LoggerProvider as OTLoggerProvider @@ -38,6 +40,7 @@ import litellm from litellm.integrations import opentelemetry as otel_module from litellm.integrations.arize.arize_phoenix import ArizePhoenixLogger from litellm.integrations.opentelemetry import ( + LITELLM_PROXY_REQUEST_SPAN_NAME, LITELLM_REQUEST_SPAN_NAME, OpenTelemetry, OpenTelemetryConfig, @@ -7328,3 +7331,55 @@ class TestOpentelemetryUnitTests(BaseLoggingCallbackTest): # Assert: error.message should be set from error_str using ErrorAttributes constant mock_span.set_attribute.assert_called_with(ErrorAttributes.ERROR_MESSAGE, "Fallback error message") + + +@pytest.mark.parametrize("parent_container", ("metadata", "litellm_metadata")) +def test_success_stamps_team_attributes_before_proxy_span_end( + caplog: pytest.LogCaptureFixture, parent_container: str +) -> None: + exporter: Final[InMemorySpanExporter] = InMemorySpanExporter() + provider: Final[TracerProvider] = TracerProvider() + provider.add_span_processor(SimpleSpanProcessor(exporter)) + otel: Final[OpenTelemetry] = OpenTelemetry( + tracer_provider=provider, message_logging=False + ) + server_span: Final[Span] = otel.create_litellm_proxy_request_started_span( + start_time=datetime(2026, 1, 1, tzinfo=timezone.utc), headers={} + ) + parent_metadata: Final[dict[str, object]] = { + "litellm_parent_otel_span": server_span, + "user_api_key_team_id": "test-team-id", + "user_api_key_team_alias": "test-team", + } + litellm_params: Final[dict[str, object]] = {parent_container: parent_metadata} + kwargs: Final[dict[str, object]] = { + "model": "test-model", + "litellm_call_id": "test-call", + "call_type": "completion", + "litellm_params": litellm_params, + "standard_logging_object": { + "metadata": { + "user_api_key_team_id": "test-team-id", + "user_api_key_team_alias": "test-team", + }, + "call_type": "completion", + }, + } + + with caplog.at_level(logging.WARNING, logger="opentelemetry.sdk.trace"): + end_time: Final[datetime] = datetime(2026, 1, 1, 0, 1, tzinfo=timezone.utc) + otel.log_success_event(kwargs, {"id": "test-response"}, end_time, end_time) + + warnings: Final[list[logging.LogRecord]] = [ + record + for record in caplog.records + if record.getMessage() == "Setting attribute on ended span." + ] + assert warnings == [] + + spans: Final[dict[str, ReadableSpan]] = { + span.name: span for span in exporter.get_finished_spans() + } + root: Final[ReadableSpan] = spans[LITELLM_PROXY_REQUEST_SPAN_NAME] + assert root.attributes["metadata.user_api_key_team_id"] == "test-team-id" + assert root.attributes["metadata.user_api_key_team_alias"] == "test-team" diff --git a/tests/unit/integrations/test_otel_proxy_span_lifecycle.py b/tests/unit/integrations/test_otel_proxy_span_lifecycle.py deleted file mode 100644 index 025f57b3f4e..00000000000 --- a/tests/unit/integrations/test_otel_proxy_span_lifecycle.py +++ /dev/null @@ -1,61 +0,0 @@ -import logging -from datetime import datetime, timezone - -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import SimpleSpanProcessor -from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( - InMemorySpanExporter, -) - -from litellm.integrations.opentelemetry import ( - LITELLM_PROXY_REQUEST_SPAN_NAME, - OpenTelemetry, -) - - -def test_success_stamps_team_attributes_before_proxy_span_end(caplog): - exporter = InMemorySpanExporter() - provider = TracerProvider() - provider.add_span_processor(SimpleSpanProcessor(exporter)) - otel = OpenTelemetry() - otel.tracer = provider.get_tracer("otel-proxy-span-lifecycle-test") - otel.message_logging = False - - server_span = otel.create_litellm_proxy_request_started_span( - start_time=datetime.now(timezone.utc), headers={} - ) - kwargs = { - "model": "test-model", - "litellm_call_id": "test-call", - "call_type": "completion", - "litellm_params": { - "metadata": { - "litellm_parent_otel_span": server_span, - "user_api_key_team_id": "test-team-id", - "user_api_key_team_alias": "test-team", - } - }, - "standard_logging_object": { - "metadata": { - "user_api_key_team_id": "test-team-id", - "user_api_key_team_alias": "test-team", - }, - "call_type": "completion", - }, - } - - with caplog.at_level(logging.WARNING, logger="opentelemetry.sdk.trace"): - now = datetime.now(timezone.utc) - otel.log_success_event(kwargs, {"id": "test-response"}, now, now) - - warnings = [ - record - for record in caplog.records - if record.getMessage() == "Setting attribute on ended span." - ] - assert warnings == [] - - spans = {span.name: span for span in exporter.get_finished_spans()} - root = spans[LITELLM_PROXY_REQUEST_SPAN_NAME] - assert root.attributes["metadata.user_api_key_team_id"] == "test-team-id" - assert root.attributes["metadata.user_api_key_team_alias"] == "test-team" From 721705d3c0173ae3d07b7112070c798a8771f930 Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 19:17:24 +0200 Subject: [PATCH 03/11] style(otel): apply ruff formatting --- litellm/integrations/opentelemetry.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index bf2063642da..2a85cf8331c 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1555,9 +1555,7 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): litellm_params: Final = kwargs.get("litellm_params") or {} metadata: Final = litellm_params.get("metadata") or {} litellm_metadata: Final = litellm_params.get("litellm_metadata") or {} - proxy_span: Final = metadata.get("litellm_parent_otel_span") or litellm_metadata.get( - "litellm_parent_otel_span" - ) + proxy_span: Final = metadata.get("litellm_parent_otel_span") or litellm_metadata.get("litellm_parent_otel_span") if ( proxy_span is not None and getattr(proxy_span, "name", None) == LITELLM_PROXY_REQUEST_SPAN_NAME From b1dbd9e29fc19e227b7e94bc4b37268b91374e0b Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 20:39:55 +0200 Subject: [PATCH 04/11] fix(otel): type alternate proxy metadata lookup --- litellm/integrations/opentelemetry.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 2a85cf8331c..91d479b6081 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1552,10 +1552,12 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): Guarded to the LiteLLM-created proxy span (by name + recording) so externally provided parent spans are never mutated. """ - litellm_params: Final = kwargs.get("litellm_params") or {} - metadata: Final = litellm_params.get("metadata") or {} - litellm_metadata: Final = litellm_params.get("litellm_metadata") or {} - proxy_span: Final = metadata.get("litellm_parent_otel_span") or litellm_metadata.get("litellm_parent_otel_span") + litellm_params: Final[dict[str, Any]] = cast(dict[str, Any], kwargs.get("litellm_params") or {}) + metadata: Final[dict[str, Any]] = cast(dict[str, Any], litellm_params.get("metadata") or {}) + litellm_metadata: Final[dict[str, Any]] = cast(dict[str, Any], litellm_params.get("litellm_metadata") or {}) + proxy_span: Final[Any] = metadata.get("litellm_parent_otel_span") or litellm_metadata.get( + "litellm_parent_otel_span" + ) if ( proxy_span is not None and getattr(proxy_span, "name", None) == LITELLM_PROXY_REQUEST_SPAN_NAME From 40f2eb835a8886f6f03b9c4f93c195fe4e77e13c Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 22:15:53 +0200 Subject: [PATCH 05/11] fix(otel): avoid unchecked metadata casts --- litellm/integrations/opentelemetry.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 91d479b6081..c58dc63c407 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1552,9 +1552,16 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): Guarded to the LiteLLM-created proxy span (by name + recording) so externally provided parent spans are never mutated. """ - litellm_params: Final[dict[str, Any]] = cast(dict[str, Any], kwargs.get("litellm_params") or {}) - metadata: Final[dict[str, Any]] = cast(dict[str, Any], litellm_params.get("metadata") or {}) - litellm_metadata: Final[dict[str, Any]] = cast(dict[str, Any], litellm_params.get("litellm_metadata") or {}) + litellm_params_value = kwargs.get("litellm_params") + litellm_params: Final[Mapping[str, Any]] = ( + litellm_params_value if isinstance(litellm_params_value, Mapping) else {} + ) + metadata_value = litellm_params.get("metadata") + metadata: Final[Mapping[str, Any]] = metadata_value if isinstance(metadata_value, Mapping) else {} + litellm_metadata_value = litellm_params.get("litellm_metadata") + litellm_metadata: Final[Mapping[str, Any]] = ( + litellm_metadata_value if isinstance(litellm_metadata_value, Mapping) else {} + ) proxy_span: Final[Any] = metadata.get("litellm_parent_otel_span") or litellm_metadata.get( "litellm_parent_otel_span" ) From 311a3504600e525c40a2a396338ed26fe56e5bee Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 22:24:49 +0200 Subject: [PATCH 06/11] fix(otel): mark validated metadata inputs final --- litellm/integrations/opentelemetry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index c58dc63c407..26032daf36c 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1552,13 +1552,13 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): Guarded to the LiteLLM-created proxy span (by name + recording) so externally provided parent spans are never mutated. """ - litellm_params_value = kwargs.get("litellm_params") + litellm_params_value: Final[Any] = kwargs.get("litellm_params") litellm_params: Final[Mapping[str, Any]] = ( litellm_params_value if isinstance(litellm_params_value, Mapping) else {} ) - metadata_value = litellm_params.get("metadata") + metadata_value: Final[Any] = litellm_params.get("metadata") metadata: Final[Mapping[str, Any]] = metadata_value if isinstance(metadata_value, Mapping) else {} - litellm_metadata_value = litellm_params.get("litellm_metadata") + litellm_metadata_value: Final[Any] = litellm_params.get("litellm_metadata") litellm_metadata: Final[Mapping[str, Any]] = ( litellm_metadata_value if isinstance(litellm_metadata_value, Mapping) else {} ) From ccd0fae0e5d25942de027e8fcc702fbf24a8c89f Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 22:34:27 +0200 Subject: [PATCH 07/11] fix(otel): type dynamic metadata boundary --- litellm/integrations/opentelemetry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 26032daf36c..e62e84035b4 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1552,13 +1552,13 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): Guarded to the LiteLLM-created proxy span (by name + recording) so externally provided parent spans are never mutated. """ - litellm_params_value: Final[Any] = kwargs.get("litellm_params") + litellm_params_value: Final[Any] = cast(Any, kwargs.get("litellm_params")) # cast-ok: dynamic logging kwargs litellm_params: Final[Mapping[str, Any]] = ( litellm_params_value if isinstance(litellm_params_value, Mapping) else {} ) - metadata_value: Final[Any] = litellm_params.get("metadata") + metadata_value: Final[Any] = cast(Any, litellm_params.get("metadata")) # cast-ok: dynamic metadata payload metadata: Final[Mapping[str, Any]] = metadata_value if isinstance(metadata_value, Mapping) else {} - litellm_metadata_value: Final[Any] = litellm_params.get("litellm_metadata") + litellm_metadata_value: Final[Any] = cast(Any, litellm_params.get("litellm_metadata")) # cast-ok: dynamic metadata payload litellm_metadata: Final[Mapping[str, Any]] = ( litellm_metadata_value if isinstance(litellm_metadata_value, Mapping) else {} ) From 5a501184b9c4801dc095aec6e7c784b9dd10ae01 Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 22:39:02 +0200 Subject: [PATCH 08/11] style(otel): format dynamic metadata cast --- litellm/integrations/opentelemetry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index e62e84035b4..8e9998bff92 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1558,7 +1558,9 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): ) metadata_value: Final[Any] = cast(Any, litellm_params.get("metadata")) # cast-ok: dynamic metadata payload metadata: Final[Mapping[str, Any]] = metadata_value if isinstance(metadata_value, Mapping) else {} - litellm_metadata_value: Final[Any] = cast(Any, litellm_params.get("litellm_metadata")) # cast-ok: dynamic metadata payload + litellm_metadata_value: Final[Any] = cast( + Any, litellm_params.get("litellm_metadata") + ) # cast-ok: dynamic metadata payload litellm_metadata: Final[Mapping[str, Any]] = ( litellm_metadata_value if isinstance(litellm_metadata_value, Mapping) else {} ) From faa168d979b23328a395104fb8670d502b01299c Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 22:45:13 +0200 Subject: [PATCH 09/11] fix(otel): keep cast suppression on diagnostic line --- litellm/integrations/opentelemetry.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 8e9998bff92..1204ad51016 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1558,9 +1558,7 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): ) metadata_value: Final[Any] = cast(Any, litellm_params.get("metadata")) # cast-ok: dynamic metadata payload metadata: Final[Mapping[str, Any]] = metadata_value if isinstance(metadata_value, Mapping) else {} - litellm_metadata_value: Final[Any] = cast( - Any, litellm_params.get("litellm_metadata") - ) # cast-ok: dynamic metadata payload + litellm_metadata_value: Final[Any] = cast(Any, litellm_params.get("litellm_metadata")) # cast-ok: dynamic field litellm_metadata: Final[Mapping[str, Any]] = ( litellm_metadata_value if isinstance(litellm_metadata_value, Mapping) else {} ) From 9873ad9eebaf467c1ed622f6d3edb4a6b9129c60 Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Fri, 9 Oct 2026 23:56:27 +0200 Subject: [PATCH 10/11] ci: add manual GHCR publish for pinned OTEL compatibility image --- .github/workflows/publish-homelab-otel.yml | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/publish-homelab-otel.yml diff --git a/.github/workflows/publish-homelab-otel.yml b/.github/workflows/publish-homelab-otel.yml new file mode 100644 index 00000000000..a33de9ac060 --- /dev/null +++ b/.github/workflows/publish-homelab-otel.yml @@ -0,0 +1,47 @@ +name: Publish homelab OTEL compatibility image +on: + workflow_dispatch: + inputs: + publish: + description: Publish the digest-pinned image to GHCR + required: true + default: 'true' + type: boolean +permissions: + contents: read + packages: write +jobs: + publish: + if: ${{ inputs.publish == true }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Write compatibility Dockerfile + run: | + cat > homelab-otel.Dockerfile <<'EOF_DOCKERFILE' + FROM ghcr.io/berriai/litellm@sha256:3d88bd757134a00e8c00237b85a021ab1f6d54abb9c1df750f411dbe7256a500 + LABEL org.opencontainers.image.source="https://github.com/BerriAI/litellm/pull/45612" \ + org.opencontainers.image.revision="faa168d979b23328a395104fb8670d502b01299c" \ + org.opencontainers.image.base.name="ghcr.io/berriai/litellm" \ + org.opencontainers.image.base.digest="sha256:3d88bd757134a00e8c00237b85a021ab1f6d54abb9c1df750f411dbe7256a500" \ + org.opencontainers.image.licenses="MIT" \ + com.maxrink.patch.opentelemetry.sha256="b139f02779ada57ad72e798ccfd3748276956dd88549fbb83ba63261b6f3ce32" + COPY litellm/integrations/opentelemetry.py /app/.venv/lib/python3.13/site-packages/litellm/integrations/opentelemetry.py + EOF_DOCKERFILE + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/build-push-action@v6 + with: + context: . + file: homelab-otel.Dockerfile + push: true + tags: ghcr.io/maxrink/litellm-otel-fix:faa168-pinned-1.104.2 + labels: | + org.opencontainers.image.source=https://github.com/BerriAI/litellm/pull/45612 + org.opencontainers.image.revision=faa168d979b23328a395104fb8670d502b01299c + org.opencontainers.image.base.digest=sha256:3d88bd757134a00e8c00237b85a021ab1f6d54abb9c1df750f411dbe7256a500 + org.opencontainers.image.licenses=MIT + com.maxrink.patch.opentelemetry.sha256=b139f02779ada57ad72e798ccfd3748276956dd88549fbb83ba63261b6f3ce32 From a954c963c7a2ea4cdf52f61246b15cabf4081333 Mon Sep 17 00:00:00 2001 From: Maximilian Rink Date: Sat, 10 Oct 2026 00:18:03 +0200 Subject: [PATCH 11/11] ci: remove temporary GHCR publisher from upstream patch branch --- .github/workflows/publish-homelab-otel.yml | 47 ---------------------- 1 file changed, 47 deletions(-) delete mode 100644 .github/workflows/publish-homelab-otel.yml diff --git a/.github/workflows/publish-homelab-otel.yml b/.github/workflows/publish-homelab-otel.yml deleted file mode 100644 index a33de9ac060..00000000000 --- a/.github/workflows/publish-homelab-otel.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Publish homelab OTEL compatibility image -on: - workflow_dispatch: - inputs: - publish: - description: Publish the digest-pinned image to GHCR - required: true - default: 'true' - type: boolean -permissions: - contents: read - packages: write -jobs: - publish: - if: ${{ inputs.publish == true }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Write compatibility Dockerfile - run: | - cat > homelab-otel.Dockerfile <<'EOF_DOCKERFILE' - FROM ghcr.io/berriai/litellm@sha256:3d88bd757134a00e8c00237b85a021ab1f6d54abb9c1df750f411dbe7256a500 - LABEL org.opencontainers.image.source="https://github.com/BerriAI/litellm/pull/45612" \ - org.opencontainers.image.revision="faa168d979b23328a395104fb8670d502b01299c" \ - org.opencontainers.image.base.name="ghcr.io/berriai/litellm" \ - org.opencontainers.image.base.digest="sha256:3d88bd757134a00e8c00237b85a021ab1f6d54abb9c1df750f411dbe7256a500" \ - org.opencontainers.image.licenses="MIT" \ - com.maxrink.patch.opentelemetry.sha256="b139f02779ada57ad72e798ccfd3748276956dd88549fbb83ba63261b6f3ce32" - COPY litellm/integrations/opentelemetry.py /app/.venv/lib/python3.13/site-packages/litellm/integrations/opentelemetry.py - EOF_DOCKERFILE - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/build-push-action@v6 - with: - context: . - file: homelab-otel.Dockerfile - push: true - tags: ghcr.io/maxrink/litellm-otel-fix:faa168-pinned-1.104.2 - labels: | - org.opencontainers.image.source=https://github.com/BerriAI/litellm/pull/45612 - org.opencontainers.image.revision=faa168d979b23328a395104fb8670d502b01299c - org.opencontainers.image.base.digest=sha256:3d88bd757134a00e8c00237b85a021ab1f6d54abb9c1df750f411dbe7256a500 - org.opencontainers.image.licenses=MIT - com.maxrink.patch.opentelemetry.sha256=b139f02779ada57ad72e798ccfd3748276956dd88549fbb83ba63261b6f3ce32