diff --git a/langfuse/_client/client.py b/langfuse/_client/client.py index 42d861fd4..a56188ab3 100644 --- a/langfuse/_client/client.py +++ b/langfuse/_client/client.py @@ -268,7 +268,7 @@ def mask_otel_spans( additional_headers (Optional[Dict[str, str]]): Additional headers to include in all API requests and in the default OTLPSpanExporter requests. These headers will be merged with default headers. Note: If httpx_client is provided, additional_headers must be set directly on your custom httpx_client as well. If `span_exporter` is provided, these headers are not wired into that exporter and must be configured on the exporter instance directly. tracer_provider(Optional[TracerProvider]): OpenTelemetry TracerProvider to use for Langfuse. This can be useful to set to have disconnected tracing between Langfuse and other OpenTelemetry-span emitting libraries. Note: To track active spans, the context is still shared between TracerProviders. This may lead to broken trace trees. id_generator (Optional[IdGenerator]): OpenTelemetry ID generator to use when Langfuse creates its own TracerProvider. If omitted, the OpenTelemetry SDK default is used. If `tracer_provider` is provided, or an OpenTelemetry TracerProvider is already registered globally, configure the ID generator on that provider instead. - span_exporter (Optional[SpanExporter]): Custom OpenTelemetry span exporter for the Langfuse span processor. If omitted, Langfuse creates an OTLPSpanExporter pointed at the Langfuse OTLP endpoint. If provided, Langfuse does not wire `base_url`, exporter headers, exporter auth, or exporter timeout into it. Configure endpoint, headers, and timeout on the exporter instance directly. If you are sending spans to Langfuse v4 or using Langfuse Cloud Fast Preview, include `x-langfuse-ingestion-version=4` on the exporter to enable real time processing of exported spans. + span_exporter (Optional[SpanExporter]): Custom OpenTelemetry span exporter for the Langfuse span processor. If omitted, Langfuse creates an OTLPSpanExporter pointed at the Langfuse OTLP endpoint. If provided, Langfuse does not wire `base_url`, exporter headers, exporter auth, exporter timeout, or the `LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES` request size limit into it. Configure endpoint, headers, and timeout on the exporter instance directly. If you are sending spans to Langfuse v4 or using Langfuse Cloud Fast Preview, include `x-langfuse-ingestion-version=4` on the exporter to enable real time processing of exported spans. Example: ```python diff --git a/langfuse/_client/environment_variables.py b/langfuse/_client/environment_variables.py index 0a95d53ba..424ba53fd 100644 --- a/langfuse/_client/environment_variables.py +++ b/langfuse/_client/environment_variables.py @@ -62,6 +62,17 @@ **Default value:** ``/api/public/otel/v1/traces`` """ +LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES = "LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES" +""" +.. envvar:: LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES + +Maximum serialized size in bytes (before compression) of a span batch exported by the +default OTLP exporter. Oversized batches are dropped before sending and logged as a +failed export. Must be a positive integer. Custom span exporters are not affected. + +**Default value:** ``67108864`` (64 MiB) +""" + LANGFUSE_DEBUG = "LANGFUSE_DEBUG" """ .. envvar:: LANGFUSE_DEBUG diff --git a/langfuse/_client/span_exporter.py b/langfuse/_client/span_exporter.py index 7ea56f81c..661a1a26c 100644 --- a/langfuse/_client/span_exporter.py +++ b/langfuse/_client/span_exporter.py @@ -9,7 +9,6 @@ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.sdk.util import BoundedList from opentelemetry.trace import format_span_id, format_trace_id -from opentelemetry.util.types import AttributeValue from langfuse._client.attributes import LangfuseOtelSpanAttributes from langfuse._task_manager.media_manager import MediaManager @@ -17,6 +16,7 @@ from langfuse.logger import langfuse_logger from langfuse.media import LangfuseMedia from langfuse.types import ( + AttributeValue, MaskOtelSpansFunction, MaskOtelSpansParams, MaskOtelSpansResult, diff --git a/langfuse/_client/span_processor.py b/langfuse/_client/span_processor.py index 9107ae7fc..524b2996d 100644 --- a/langfuse/_client/span_processor.py +++ b/langfuse/_client/span_processor.py @@ -28,6 +28,7 @@ from langfuse._client.environment_variables import ( LANGFUSE_FLUSH_AT, LANGFUSE_FLUSH_INTERVAL, + LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, LANGFUSE_OTEL_TRACES_EXPORT_PATH, ) from langfuse._client.propagation import ( @@ -47,6 +48,23 @@ from langfuse.types import MaskOtelSpansFunction +def _resolve_max_batch_size_bytes() -> Optional[int]: + """Return the configured batch byte limit, or None to keep the exporter default.""" + raw_value = os.environ.get(LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, "").strip() + if not raw_value: + return None + + if raw_value.isascii() and raw_value.isdigit() and int(raw_value) > 0: + return int(raw_value) + + langfuse_logger.warning( + "Invalid %s=%r. Expected a positive integer. Using the default limit.", + LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, + raw_value, + ) + return None + + class LangfuseSpanProcessor(BatchSpanProcessor): """OpenTelemetry span processor that exports spans to the Langfuse API. @@ -127,6 +145,7 @@ def __init__( endpoint=endpoint, headers=headers, timeout=timeout, + max_request_size=_resolve_max_batch_size_bytes(), ) if media_manager is not None or mask_otel_spans is not None: diff --git a/langfuse/types.py b/langfuse/types.py index 357438d56..66bddbca6 100644 --- a/langfuse/types.py +++ b/langfuse/types.py @@ -28,10 +28,9 @@ def my_evaluator(*, output: str, **kwargs) -> Evaluation: Protocol, Sequence, TypedDict, + Union, ) -from opentelemetry.util.types import AttributeValue - try: from typing import NotRequired # type: ignore except ImportError: @@ -40,6 +39,20 @@ def my_evaluator(*, output: str, **kwargs) -> Evaluation: from langfuse.api import MediaContentType +# Span attribute values accepted by the OpenTelemetry trace API. OpenTelemetry 1.45 +# widened ``opentelemetry.util.types.AttributeValue`` to the recursive ``AnyValue``, +# which mypy cannot use as a type alias. +AttributeValue = Union[ + str, + bool, + int, + float, + Sequence[str], + Sequence[bool], + Sequence[int], + Sequence[float], +] + SpanLevel = Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"] ScoreDataType = Literal["NUMERIC", "CATEGORICAL", "BOOLEAN", "TEXT", "CORRECTION"] diff --git a/pyproject.toml b/pyproject.toml index ab7c9170c..80ecefd23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,9 +29,9 @@ dependencies = [ "backoff>=1.10.0", "wrapt>=1.14,<3", "packaging>=23.2,<27.0", - "opentelemetry-api>=1.33.1,<2", - "opentelemetry-sdk>=1.33.1,<2", - "opentelemetry-exporter-otlp-proto-http>=1.33.1,<2", + "opentelemetry-api>=1.45.0,<2", + "opentelemetry-sdk>=1.45.0,<2", + "opentelemetry-exporter-otlp-proto-http>=1.45.0,<2", "typing-extensions>=4.12.2,<5", ] @@ -71,6 +71,8 @@ build-backend = "uv_build" [tool.uv] # Basic protection against supply chain attacks exclude-newer = "7 days" +# OpenTelemetry 1.45 ships the OTLP request size cap the SDK relies on +exclude-newer-package = { "opentelemetry-api" = "3 days", "opentelemetry-sdk" = "3 days", "opentelemetry-semantic-conventions" = "3 days", "opentelemetry-proto" = "3 days", "opentelemetry-exporter-otlp-proto-common" = "3 days", "opentelemetry-exporter-otlp-proto-http" = "3 days", "opentelemetry-exporter-otlp-common" = "3 days", "opentelemetry-exporter-http-transport" = "3 days", "opentelemetry-instrumentation" = "3 days", "opentelemetry-instrumentation-threading" = "3 days" } [tool.uv.build-backend] module-root = "" diff --git a/tests/live_provider/test_langchain.py b/tests/live_provider/test_langchain.py index 5d0fcb94e..d04ddb81c 100644 --- a/tests/live_provider/test_langchain.py +++ b/tests/live_provider/test_langchain.py @@ -180,7 +180,7 @@ def test_callback_simple_openai(): # Initialize components handler = CallbackHandler() - llm = OpenAI() + llm = ChatOpenAI(model="gpt-4o-mini") text = ( "What would be a good company name for a company that makes colorful socks?" ) @@ -222,7 +222,7 @@ def test_callback_multiple_invocations_on_different_traces(): test_name_2 = f"Test Multiple Invocations 2 {create_uuid()}" # Setup components - llm = OpenAI() + llm = ChatOpenAI(model="gpt-4o-mini") text = ( "What would be a good company name for a company that makes colorful socks?" ) @@ -257,6 +257,10 @@ def test_callback_multiple_invocations_on_different_traces(): assert generation.output != "" +@pytest.mark.skip( + reason="OpenAI retired gpt-3.5-turbo-instruct, the legacy completions model " + "this test depends on" +) def test_openai_instruct_usage(): langfuse = Langfuse() diff --git a/tests/live_provider/test_openai.py b/tests/live_provider/test_openai.py index b0294fecd..43a07be23 100644 --- a/tests/live_provider/test_openai.py +++ b/tests/live_provider/test_openai.py @@ -10,6 +10,11 @@ langfuse: Langfuse | None = None +requires_legacy_completion_model = pytest.mark.skip( + reason="OpenAI retired gpt-3.5-turbo-instruct, the legacy completions model " + "this test depends on" +) + @pytest.fixture(scope="module") def openai(): @@ -405,6 +410,7 @@ def test_openai_chat_completion_with_seed(openai): assert len(completion.choices) != 0 +@requires_legacy_completion_model def test_openai_completion(openai): generation_name = create_uuid() completion = openai.OpenAI().completions.create( @@ -445,6 +451,7 @@ def test_openai_completion(openai): assert generation.data[0].output == "2\n\n1 + 2 = 3\n\n2 + 3 = " +@requires_legacy_completion_model def test_openai_completion_stream(openai): generation_name = create_uuid() completion = openai.OpenAI().completions.create( @@ -588,6 +595,7 @@ def test_openai_completion_stream_fail(openai): openai.api_key = os.environ["OPENAI_API_KEY"] +@requires_legacy_completion_model def test_openai_completion_with_langfuse_prompt(openai): generation_name = create_uuid() langfuse = Langfuse() diff --git a/tests/unit/test_additional_headers_simple.py b/tests/unit/test_additional_headers_simple.py index dd843b35a..7837c5c3e 100644 --- a/tests/unit/test_additional_headers_simple.py +++ b/tests/unit/test_additional_headers_simple.py @@ -180,16 +180,16 @@ def test_span_processor_has_additional_headers_in_otel_exporter(self): exporter = processor.span_exporter # Verify additional headers are in the exporter's headers - assert exporter._headers["X-Custom-Trace-Header"] == "trace-value" - assert exporter._headers["X-Override-Default"] == "override-value" + assert exporter._client._headers["x-custom-trace-header"] == "trace-value" + assert exporter._client._headers["x-override-default"] == "override-value" # Verify default headers are still present - assert "Authorization" in exporter._headers - assert "x-langfuse-sdk-name" in exporter._headers - assert "x-langfuse-public-key" in exporter._headers + assert "authorization" in exporter._client._headers + assert "x-langfuse-sdk-name" in exporter._client._headers + assert "x-langfuse-public-key" in exporter._client._headers # Check that our override worked - assert exporter._headers["X-Override-Default"] == "override-value" + assert exporter._client._headers["x-override-default"] == "override-value" def test_span_processor_none_additional_headers_works(self): """Test that span processor works with None additional headers.""" @@ -207,9 +207,9 @@ def test_span_processor_none_additional_headers_works(self): exporter = processor.span_exporter # Verify default headers are present - assert "Authorization" in exporter._headers - assert "x-langfuse-sdk-name" in exporter._headers - assert "x-langfuse-public-key" in exporter._headers + assert "authorization" in exporter._client._headers + assert "x-langfuse-sdk-name" in exporter._client._headers + assert "x-langfuse-public-key" in exporter._client._headers def test_span_processor_uses_custom_span_exporter_when_provided(self): """Test that a custom exporter bypasses the default OTLP exporter construction.""" diff --git a/tests/unit/test_span_processor.py b/tests/unit/test_span_processor.py index f16a59926..a123fd108 100644 --- a/tests/unit/test_span_processor.py +++ b/tests/unit/test_span_processor.py @@ -1,15 +1,26 @@ import logging -from typing import Sequence +import threading +from http.server import BaseHTTPRequestHandler, HTTPServer +from typing import List, Sequence from unittest.mock import patch import pytest +from opentelemetry.exporter.otlp.proto.common.trace_encoder import encode_spans from opentelemetry.sdk.trace import ReadableSpan, TracerProvider -from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult +from opentelemetry.sdk.trace.export import ( + SimpleSpanProcessor, + SpanExporter, + SpanExportResult, +) +from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, +) import langfuse._client.span_processor as span_processor_module from langfuse._client.environment_variables import ( LANGFUSE_FLUSH_AT, LANGFUSE_FLUSH_INTERVAL, + LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, ) from langfuse._client.span_processor import LangfuseSpanProcessor @@ -60,6 +71,148 @@ def test_span_processor_uses_env_flush_settings_when_constructor_omits_them( processor.shutdown() +class _RecordingOTLPHandler(BaseHTTPRequestHandler): + received_body_sizes: List[int] + + def do_POST(self): + body = self.rfile.read(int(self.headers.get("Content-Length", 0))) + self.received_body_sizes.append(len(body)) + self.send_response(200) + self.end_headers() + + def log_message(self, *args): + pass + + +@pytest.fixture +def otlp_http_server(): + received_body_sizes: List[int] = [] + handler = type( + "Handler", + (_RecordingOTLPHandler,), + {"received_body_sizes": received_body_sizes}, + ) + server = HTTPServer(("127.0.0.1", 0), handler) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + + yield f"http://127.0.0.1:{server.server_port}", received_body_sizes + + server.shutdown() + server.server_close() + + +def _finished_spans(payload: str) -> List[ReadableSpan]: + exporter = InMemorySpanExporter() + provider = TracerProvider() + provider.add_span_processor(SimpleSpanProcessor(exporter)) + with provider.get_tracer("test").start_as_current_span("span") as span: + span.set_attribute("input", payload) + provider.shutdown() + + return list(exporter.get_finished_spans()) + + +def _serialized_request_size(spans: List[ReadableSpan]) -> int: + return len(encode_spans(spans).SerializePartialToString()) + + +def _default_exporter_processor(base_url: str) -> LangfuseSpanProcessor: + return LangfuseSpanProcessor( + public_key="pk-test", + secret_key="sk-test", + base_url=base_url, + ) + + +@pytest.mark.parametrize( + ("limit_offset", "expected_result"), + [ + (-1, SpanExportResult.FAILURE), + (0, SpanExportResult.SUCCESS), + (1, SpanExportResult.SUCCESS), + ], +) +def test_default_exporter_enforces_max_batch_size_bytes_at_boundary( + monkeypatch, otlp_http_server, limit_offset, expected_result +): + base_url, received_body_sizes = otlp_http_server + spans = _finished_spans("x" * 1_000) + request_size = _serialized_request_size(spans) + monkeypatch.setenv( + LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, str(request_size + limit_offset) + ) + processor = _default_exporter_processor(base_url) + + try: + result = processor._batch_processor._exporter.export(spans) + finally: + processor.shutdown() + + assert result == expected_result + expected_requests = ( + [] if expected_result == SpanExportResult.FAILURE else [request_size] + ) + assert received_body_sizes == expected_requests + + +def test_oversized_batch_is_dropped_on_flush_without_blocking_later_batches( + monkeypatch, caplog, otlp_http_server +): + base_url, received_body_sizes = otlp_http_server + oversized_spans = _finished_spans("secret-payload" * 1_000) + small_spans = _finished_spans("ok") + monkeypatch.setenv( + LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, + str(_serialized_request_size(oversized_spans) - 1), + ) + processor = _default_exporter_processor(base_url) + + try: + with caplog.at_level(logging.WARNING): + super(LangfuseSpanProcessor, processor).on_end(oversized_spans[0]) + assert processor.force_flush() + + assert received_body_sizes == [] + assert "Dropping span batch" in caplog.text + assert "secret-payload" not in caplog.text + + super(LangfuseSpanProcessor, processor).on_end(small_spans[0]) + assert processor.force_flush() + finally: + processor.shutdown() + + assert received_body_sizes == [_serialized_request_size(small_spans)] + + +def test_default_exporter_uses_64_mib_limit_when_env_unset(monkeypatch): + monkeypatch.delenv(LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, raising=False) + processor = _default_exporter_processor("http://localhost:3000") + + try: + exporter = processor._batch_processor._exporter + assert exporter._max_request_size == 64 * 1024 * 1024 + finally: + processor.shutdown() + + +@pytest.mark.parametrize("raw_value", ["0", "-5", "abc", "1.5"]) +def test_invalid_max_batch_size_bytes_falls_back_to_default_limit( + monkeypatch, caplog, raw_value +): + monkeypatch.setenv(LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES, raw_value) + + with caplog.at_level(logging.WARNING, logger="langfuse"): + processor = _default_exporter_processor("http://localhost:3000") + + try: + exporter = processor._batch_processor._exporter + assert exporter._max_request_size == 64 * 1024 * 1024 + assert LANGFUSE_OTEL_MAX_BATCH_SIZE_BYTES in caplog.text + finally: + processor.shutdown() + + @pytest.fixture def tracer_with_processor(): processor = LangfuseSpanProcessor( diff --git a/uv.lock b/uv.lock index 0556ffa83..cd25b16de 100644 --- a/uv.lock +++ b/uv.lock @@ -3,9 +3,21 @@ revision = 3 requires-python = ">=3.10, <4.0" [options] -exclude-newer = "2026-09-17T13:32:25.771088061Z" +exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. exclude-newer-span = "P7D" +[options.exclude-newer-package] +opentelemetry-api = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-exporter-http-transport = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-exporter-otlp-common = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-exporter-otlp-proto-common = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-exporter-otlp-proto-http = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-instrumentation = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-instrumentation-threading = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-proto = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-sdk = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } +opentelemetry-semantic-conventions = { timestamp = "0001-01-01T00:00:00Z", span = "P3D" } + [[package]] name = "annotated-types" version = "0.7.0" @@ -235,7 +247,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -327,18 +339,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, ] -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - [[package]] name = "iniconfig" version = "2.3.0" @@ -594,9 +594,9 @@ docs = [ requires-dist = [ { name = "backoff", specifier = ">=1.10.0" }, { name = "httpx", specifier = ">=0.15.4,<1.0" }, - { name = "opentelemetry-api", specifier = ">=1.33.1,<2" }, - { name = "opentelemetry-exporter-otlp-proto-http", specifier = ">=1.33.1,<2" }, - { name = "opentelemetry-sdk", specifier = ">=1.33.1,<2" }, + { name = "opentelemetry-api", specifier = ">=1.45.0,<2" }, + { name = "opentelemetry-exporter-otlp-proto-http", specifier = ">=1.45.0,<2" }, + { name = "opentelemetry-sdk", specifier = ">=1.45.0,<2" }, { name = "packaging", specifier = ">=23.2,<27.0" }, { name = "pydantic", specifier = ">=2,<3" }, { name = "typing-extensions", specifier = ">=4.12.2,<5" }, @@ -954,50 +954,79 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.40.0" +version = "1.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/1d/4049a9e8698361cc1a1aa03a6c59e4fa4c71e0c0f94a30f988a6876a2ae6/opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f", size = 70851, upload-time = "2026-03-04T14:17:21.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/dc/e12c1fe1ed8a7b7149777127b1a0e12ce5bd5a81d97408bedc2128c260f5/opentelemetry_api-1.45.0.tar.gz", hash = "sha256:711ede81773c8025c2c03dac0450bc89f3d30aea6eabcc815c570d4e35a963f7", size = 72115, upload-time = "2026-09-25T12:32:28.399Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, + { url = "https://files.pythonhosted.org/packages/44/b9/040d1a1c7836922828e6480cd2366bb8fe0ebf75b413d2bb51a9b0e7f78f/opentelemetry_api-1.45.0-py3-none-any.whl", hash = "sha256:80e068aba7cd56c8b58512d6a36f8d25cb1dfaa0c0a4cc1c938ccf9f362d9cb3", size = 60020, upload-time = "2026-09-25T12:32:03.192Z" }, +] + +[[package]] +name = "opentelemetry-exporter-http-transport" +version = "0.66b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/31/cbedb10e08c3c932b80f58edf055a16bb48a500c23c617b758fb3ec18f08/opentelemetry_exporter_http_transport-0.66b0.tar.gz", hash = "sha256:2c229b6593eaa22c86d9b8a15843dc23b406dbda00fb138339189aab07923b4e", size = 11694, upload-time = "2026-09-25T12:32:29.689Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/65/34d7106eb45acda40f016c5b31b313ffbbf4744acd5f099f29eaf2958b4d/opentelemetry_exporter_http_transport-0.66b0-py3-none-any.whl", hash = "sha256:c82689928a11505a2a0a26a04afe0ce06d270d3db984bc16c1a80ffa902db00e", size = 12156, upload-time = "2026-09-25T12:32:05.621Z" }, +] + +[package.optional-dependencies] +urllib3 = [ + { name = "urllib3" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-common" +version = "0.66b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/09/01239cdfe8a414d46ed625b68b6da92666ffd16d93cc6dadf89404b4bd85/opentelemetry_exporter_otlp_common-0.66b0.tar.gz", hash = "sha256:362268ec6aa705e183776ff938539df1e8ce45bc5509d242538b1d40c26fe6a6", size = 14323, upload-time = "2026-09-25T12:32:31.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/a9/b6a7a9d40ba99ed71b18e17310339024b086578f5f32162df1c1c0cf6270/opentelemetry_exporter_otlp_common-0.66b0-py3-none-any.whl", hash = "sha256:35d24c867310f4713a9738b0202b6bade77955418fccb9e2f7eea01b76263916", size = 12387, upload-time = "2026-09-25T12:32:08.438Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.40.0" +version = "1.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/e0/ee3823dbdc10da15b5750becc37b61194dd7c55e3b56764ecbbe446f659a/opentelemetry_exporter_otlp_proto_common-1.45.0.tar.gz", hash = "sha256:36495115a0c6a7aa946cfda9d59b6ed4e917b6ab0f75cdaf66bc1b176ec1be1f", size = 18870, upload-time = "2026-09-25T12:32:34.141Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, + { url = "https://files.pythonhosted.org/packages/f0/bf/3e21917368a81282ce9fc6d085a944dcaea6eed19de7d29cc979375db347/opentelemetry_exporter_otlp_proto_common-1.45.0-py3-none-any.whl", hash = "sha256:7e1410ae6d3ed301f7a74bec96467d519d3f37e52f9d95b309ae125e5a1af863", size = 15394, upload-time = "2026-09-25T12:32:12.663Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.40.0" +version = "1.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-http-transport", extra = ["urllib3"] }, + { name = "opentelemetry-exporter-otlp-common" }, { name = "opentelemetry-exporter-otlp-proto-common" }, { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, - { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/fa/73d50e2c15c56be4d000c98e24221d494674b0cc95524e2a8cb3856d95a4/opentelemetry_exporter_otlp_proto_http-1.40.0.tar.gz", hash = "sha256:db48f5e0f33217588bbc00274a31517ba830da576e59503507c839b38fa0869c", size = 17772, upload-time = "2026-03-04T14:17:25.324Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/78/a503801c1c8f80b1d8aad0e14106a7c57f39344001652a78a683f9a9089e/opentelemetry_exporter_otlp_proto_http-1.45.0.tar.gz", hash = "sha256:2f35496d96809f946f41b8805e6b93aec6c9b71b5fd759b75b6af4c084d992ae", size = 28818, upload-time = "2026-09-25T12:32:35.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/3a/8865d6754e61c9fb170cdd530a124a53769ee5f740236064816eb0ca7301/opentelemetry_exporter_otlp_proto_http-1.40.0-py3-none-any.whl", hash = "sha256:a8d1dab28f504c5d96577d6509f80a8150e44e8f45f82cdbe0e34c99ab040069", size = 19960, upload-time = "2026-03-04T14:17:07.153Z" }, + { url = "https://files.pythonhosted.org/packages/e7/18/875e0ee47e035f324e42eda9aab47d58381a4412181dca251d3504e24680/opentelemetry_exporter_otlp_proto_http-1.45.0-py3-none-any.whl", hash = "sha256:74385f99266dafdefff41f3da4b76f6dba79d237a9d4487e6ff1ee653c74a0a3", size = 22260, upload-time = "2026-09-25T12:32:14.658Z" }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.61b0" +version = "0.66b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -1005,62 +1034,62 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/37/6bf8e66bfcee5d3c6515b79cb2ee9ad05fe573c20f7ceb288d0e7eeec28c/opentelemetry_instrumentation-0.61b0.tar.gz", hash = "sha256:cb21b48db738c9de196eba6b805b4ff9de3b7f187e4bbf9a466fa170514f1fc7", size = 32606, upload-time = "2026-03-04T14:20:16.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/97/e6959310cff3dbbce24ca0a60e5e12f773320d17480c779b038b55defd35/opentelemetry_instrumentation-0.66b0.tar.gz", hash = "sha256:316715ed7b33a12530c0ae3e8d4cdf26d70e1aad358d7ba256e5dea2d4ceed71", size = 43308, upload-time = "2026-09-25T12:35:46.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/3e/f6f10f178b6316de67f0dfdbbb699a24fbe8917cf1743c1595fb9dcdd461/opentelemetry_instrumentation-0.61b0-py3-none-any.whl", hash = "sha256:92a93a280e69788e8f88391247cc530fd81f16f2b011979d4d6398f805cfbc63", size = 33448, upload-time = "2026-03-04T14:19:02.447Z" }, + { url = "https://files.pythonhosted.org/packages/4b/08/bf39ca469f178963715ff6fd7012eec0b438f472703b11a64a40cbb14f6f/opentelemetry_instrumentation-0.66b0-py3-none-any.whl", hash = "sha256:0b32e2fcfec1e4bd48511a3b77ce32bfb1c16dcd5479396b21f9086a4b06c0c9", size = 36900, upload-time = "2026-09-25T12:34:50.029Z" }, ] [[package]] name = "opentelemetry-instrumentation-threading" -version = "0.61b0" +version = "0.66b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/12/8f/8dedba66100cda58af057926449a5e58e6c008bec02bc2746c03c3d85dcd/opentelemetry_instrumentation_threading-0.61b0.tar.gz", hash = "sha256:38e0263c692d15a7a458b3fa0286d29290448fa4ac4c63045edac438c6113433", size = 9163, upload-time = "2026-03-04T14:20:50.546Z" } +sdist = { url = "https://files.pythonhosted.org/packages/03/ee/968d70b3c80c6f3bbdb168a3018cf18c78fbeadd330f6f4851fe029f8410/opentelemetry_instrumentation_threading-0.66b0.tar.gz", hash = "sha256:4c1c9adf42b83b6f44ca4bb99799e066ccfb1869c2029bc73d862c40a8ef932e", size = 9033, upload-time = "2026-09-25T12:36:17.626Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/77/c06d960aede1a014812aa4fafde0ae546d790f46416fbeafa2b32095aae3/opentelemetry_instrumentation_threading-0.61b0-py3-none-any.whl", hash = "sha256:735f4a1dc964202fc8aff475efc12bb64e6566f22dff52d5cb5de864b3fe1a70", size = 9337, upload-time = "2026-03-04T14:19:57.983Z" }, + { url = "https://files.pythonhosted.org/packages/37/24/0667079f02a507952baa6e14df90d3da870650fb029b6e1860a9e2dde3a9/opentelemetry_instrumentation_threading-0.66b0-py3-none-any.whl", hash = "sha256:4aaf38b96b47a04319b8d96bee4f1daa04e0d6c697525595746aeb61618c002d", size = 8491, upload-time = "2026-09-25T12:35:34.555Z" }, ] [[package]] name = "opentelemetry-proto" -version = "1.40.0" +version = "1.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/28/67c38cfb7e2bdfdd0cde7dcdd0424aed0291fbd64aa4ea3e7e913734711a/opentelemetry_proto-1.45.0.tar.gz", hash = "sha256:96ee414f24bc3f61ea8e17dc56b4348d4049d73db3eb17c6b3edf75b5b403300", size = 46489, upload-time = "2026-09-25T12:32:41.409Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, + { url = "https://files.pythonhosted.org/packages/6b/45/814d55714507c7ce9c9cd86a4f83ceb5a74ab0bffcc63ff493943f138de2/opentelemetry_proto-1.45.0-py3-none-any.whl", hash = "sha256:9731566359d7b8e1ee1e149d8e4ad6865bab965e657ad3387ec2784d16927666", size = 72486, upload-time = "2026-09-25T12:32:23.36Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.40.0" +version = "1.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/fd/3c3125b20ba18ce2155ba9ea74acb0ae5d25f8cd39cfd37455601b7955cc/opentelemetry_sdk-1.40.0.tar.gz", hash = "sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2", size = 184252, upload-time = "2026-03-04T14:17:31.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/ed/ad32d76cc86ebce601105d01d13f8a08d6abc1852eaf69d79cf199bc1ee7/opentelemetry_sdk-1.45.0.tar.gz", hash = "sha256:20caa5130505e386c67c3da1c76e446c842698ced54c76c6148679539aa97972", size = 218337, upload-time = "2026-09-25T12:32:43.591Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/c5/6a852903d8bfac758c6dc6e9a68b015d3c33f2f1be5e9591e0f4b69c7e0a/opentelemetry_sdk-1.40.0-py3-none-any.whl", hash = "sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1", size = 141951, upload-time = "2026-03-04T14:17:17.961Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ae/46963201ce58d6b86f9e54087a39720a35ac9f57c3a9e440d275995c2b02/opentelemetry_sdk-1.45.0-py3-none-any.whl", hash = "sha256:5dc634c946546f61b757c5b1781f9fd1a10e96ffaf7357c797e508fe9c57e75e", size = 140132, upload-time = "2026-09-25T12:32:25.399Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.61b0" +version = "0.66b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/c0/4ae7973f3c2cfd2b6e321f1675626f0dab0a97027cc7a297474c9c8f3d04/opentelemetry_semantic_conventions-0.61b0.tar.gz", hash = "sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a", size = 145755, upload-time = "2026-03-04T14:17:32.664Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/21/910f085c0b83b80e45c341c7859baef37c877046b2c44c88d5ff2d5db948/opentelemetry_semantic_conventions-0.66b0.tar.gz", hash = "sha256:97a77dce484c54861e7eeff7651fd8a806dd3c30e501dc316730215ec36890e6", size = 150247, upload-time = "2026-09-25T12:32:44.827Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/37/cc6a55e448deaa9b27377d087da8615a3416d8ad523d5960b78dbeadd02a/opentelemetry_semantic_conventions-0.61b0-py3-none-any.whl", hash = "sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2", size = 231621, upload-time = "2026-03-04T14:17:19.33Z" }, + { url = "https://files.pythonhosted.org/packages/f4/93/2ad416d69c4ca8fd21ebc6894f23f4eda744dc6f43a14d7a9ffd7ed3abbd/opentelemetry_semantic_conventions-0.66b0-py3-none-any.whl", hash = "sha256:175b19dd98c4473f4f43a2b1df59186fd7b4a48cd3f77cbe03438b6d6fda230a", size = 206277, upload-time = "2026-09-25T12:32:26.484Z" }, ] [[package]] @@ -2358,15 +2387,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/d9/8d95e906764a386a3d3b596f3c68bb63687dfca806373509f51ce8eea81f/xxhash-3.6.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:15e0dac10eb9309508bfc41f7f9deaa7755c69e35af835db9cb10751adebc35d", size = 31565, upload-time = "2025-10-02T14:37:06.966Z" }, ] -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] - [[package]] name = "zstandard" version = "0.25.0"