From 490e1baad15b2043abc2cc376a2db0f6fdd5e7a1 Mon Sep 17 00:00:00 2001 From: J_Sinclair <56301946+E-R-Butch@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:00:35 +0800 Subject: [PATCH] revert: separate dependency fixes from Chinese localization Revert merge 3a3bc917fbe90bf376ad57e100c48a3d6d432b0a relative to its first parent. Restore the exact premerge Chinese localization tree from 9baf20e312f2584ec98dedc3a342e6149b57edda. Preserve the reviewed security and dependency fixes on their separate branch for an independent PR. --- litellm/integrations/langfuse/langfuse_sdk.py | 3 - litellm/integrations/opentelemetry.py | 21 +- litellm/integrations/otel/mount.py | 3 - .../integrations/otel/plumbing/otlp_json.py | 79 +++++- .../integrations/otel/plumbing/otlp_tls.py | 70 +---- .../otel/plumbing/otlp_transport.py | 95 ------- .../integrations/otel/plumbing/providers.py | 47 ++-- litellm/integrations/otel/presets/agentops.py | 78 ++--- .../enterprise_billing/billing_metrics.py | 12 +- pyproject.toml | 31 +- .../langfuse/test_langfuse_sdk.py | 24 -- .../otel/test_otel_v2_components.py | 76 +---- .../otel/test_otel_v2_destinations.py | 266 ------------------ .../integrations/otel/test_otel_v2_mount.py | 15 +- .../integrations/otel/test_otel_v2_presets.py | 89 +++--- tests/unit/integrations/test_langfuse_otel.py | 26 +- tests/unit/integrations/test_opentelemetry.py | 52 ++-- .../test_litellm_logging.py | 18 +- .../test_billing_metrics.py | 39 +-- ui/litellm-dashboard/package-lock.json | 210 +++++++------- ui/litellm-dashboard/package.json | 13 +- uv.lock | 246 ++++++---------- vscode-extension/package-lock.json | 90 +++--- vscode-extension/package.json | 2 +- 24 files changed, 511 insertions(+), 1094 deletions(-) delete mode 100644 litellm/integrations/otel/plumbing/otlp_transport.py diff --git a/litellm/integrations/langfuse/langfuse_sdk.py b/litellm/integrations/langfuse/langfuse_sdk.py index 1bbc18d5dd4..51b08cb72df 100644 --- a/litellm/integrations/langfuse/langfuse_sdk.py +++ b/litellm/integrations/langfuse/langfuse_sdk.py @@ -317,9 +317,6 @@ class _RequestedIdGenerator(RandomIdGenerator): the tracer's id generator, so the request rides on a context variable instead. """ - def is_trace_id_random(self) -> bool: - return _requested_trace_id.get() is None - def generate_trace_id(self) -> int: requested: Final = _requested_trace_id.get() return super().generate_trace_id() if requested is None else requested diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 0f1212721d9..0d0e309034a 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -3161,14 +3161,13 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): otel_exporter, ) normalized_endpoint = self._normalize_otel_endpoint(otel_endpoint, "traces") - from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport - - tls: Final = resolve_otlp_http_tls("TRACES", _split_otel_headers) + tls: Final = resolve_otlp_http_tls("TRACES") return BatchSpanProcessor( OTLPSpanExporterHTTP( endpoint=normalized_endpoint, headers=_split_otel_headers, - _transport=build_otlp_http_transport("TRACES", tls), + certificate_file=tls.certificate_file, + session=tls.session, ), ) elif otel_exporter == "otlp_grpc" or otel_exporter == "grpc": @@ -3250,13 +3249,12 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): self.OTEL_EXPORTER, normalized_endpoint, ) - from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport - - tls: Final = resolve_otlp_http_tls("LOGS", _split_otel_headers) + tls: Final = resolve_otlp_http_tls("LOGS") return OTLPLogExporter( endpoint=normalized_endpoint, headers=_split_otel_headers, - _transport=build_otlp_http_transport("LOGS", tls), + certificate_file=tls.certificate_file, + session=tls.session, ) elif self.OTEL_EXPORTER == "otlp_grpc" or self.OTEL_EXPORTER == "grpc": try: @@ -3320,13 +3318,12 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): OTLPMetricExporter, ) - from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport - - tls: Final = resolve_otlp_http_tls("METRICS", _split_otel_headers) + tls: Final = resolve_otlp_http_tls("METRICS") exporter = OTLPMetricExporter( endpoint=normalized_endpoint, headers=_split_otel_headers, - _transport=build_otlp_http_transport("METRICS", tls), + certificate_file=tls.certificate_file, + session=tls.session, ) return PeriodicExportingMetricReader(exporter, export_interval_millis=5000) diff --git a/litellm/integrations/otel/mount.py b/litellm/integrations/otel/mount.py index c033bc1935b..cb52ad24c8e 100644 --- a/litellm/integrations/otel/mount.py +++ b/litellm/integrations/otel/mount.py @@ -126,9 +126,6 @@ def instrument_fastapi_app(app: "FastAPI", tracer_provider: "TracerProvider | No try: if not is_otel_v2_enabled(): return - if app.middleware_stack is not None: - verbose_logger.debug("Skipping OTel V2 FastAPI instrumentation after the middleware stack was built") - return # Lazy: only the V2-enabled path needs the optional # ``opentelemetry-instrumentation-fastapi`` package, which is not part of the diff --git a/litellm/integrations/otel/plumbing/otlp_json.py b/litellm/integrations/otel/plumbing/otlp_json.py index f3f4bb897b7..bc6d7d435b0 100644 --- a/litellm/integrations/otel/plumbing/otlp_json.py +++ b/litellm/integrations/otel/plumbing/otlp_json.py @@ -1,26 +1,77 @@ -"""OTLP/JSON export with the same requests and TLS configuration as OTLP/protobuf.""" +"""OTLP/HTTP span exporter that sends the OTLP/JSON encoding instead of protobuf. -from collections.abc import Mapping -from typing import Final +The SDK only ships a protobuf OTLP/HTTP exporter; this reuses its transport and +retry loop and swaps the payload for OTLP/JSON (enums as integers, ids as hex). +""" + +import base64 +import json +from collections.abc import Mapping, Sequence +from types import MappingProxyType +from typing import Final, TypeAlias import requests -from opentelemetry.exporter.otlp.json.http.trace_exporter import OTLPSpanExporter +from google.protobuf.json_format import MessageToDict +from opentelemetry.exporter.otlp.proto.common.trace_encoder import encode_spans +from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk.trace import ReadableSpan -from litellm.integrations.otel.plumbing.otlp_tls import OtlpHttpTls -from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport +JSON_CONTENT_TYPE: Final = "application/json" +_HEX_ID_KEYS: Final = frozenset({"traceId", "spanId", "parentSpanId"}) + +_JsonValue: TypeAlias = "Mapping[str, _JsonValue] | Sequence[_JsonValue] | str | int | float | bool | None" +_JsonObject: TypeAlias = Mapping[str, "_JsonValue"] + + +def _objects(node: _JsonObject, key: str) -> tuple[_JsonObject, ...]: + items: Final = node.get(key) + if isinstance(items, str) or not isinstance(items, Sequence): + return () + return tuple(item for item in items if isinstance(item, Mapping)) + + +def _hex_ids(node: _JsonObject) -> _JsonObject: + return MappingProxyType( + { + key: base64.b64decode(item).hex() if key in _HEX_ID_KEYS and isinstance(item, str) else item + for key, item in node.items() + } + ) + + +def _hex_span(span: _JsonObject) -> _JsonObject: + links: Final = _objects(span, "links") + if not links: + return _hex_ids(span) + return MappingProxyType({**_hex_ids(span), "links": tuple(_hex_ids(link) for link in links)}) + + +def _hex_scope_spans(scope: _JsonObject) -> _JsonObject: + return MappingProxyType({**scope, "spans": tuple(_hex_span(span) for span in _objects(scope, "spans"))}) + + +def _hex_resource_spans(resource: _JsonObject) -> _JsonObject: + scope_spans: Final = tuple(_hex_scope_spans(scope) for scope in _objects(resource, "scopeSpans")) + return MappingProxyType({**resource, "scopeSpans": scope_spans}) + + +def encode_spans_json(spans: Sequence[ReadableSpan]) -> bytes: + payload: Final[_JsonObject] = MessageToDict(encode_spans(spans), use_integers_for_enums=True) + resource_spans: Final = tuple(_hex_resource_spans(resource) for resource in _objects(payload, "resourceSpans")) + hexed: Final[_JsonObject] = MappingProxyType({**payload, "resourceSpans": resource_spans}) + return json.dumps(hexed, default=dict, separators=(",", ":")).encode() class OTLPJsonSpanExporter(OTLPSpanExporter): def __init__( self, endpoint: str | None, - headers: Mapping[str, str], + headers: dict[str, str], # mutable-ok: SDK __init__ takes Dict certificate_file: str | None = None, - session: requests.Session | None = None, + session: "requests.Session | None" = None, ) -> None: - transport: Final = build_otlp_http_transport( - "TRACES", OtlpHttpTls(certificate_file=certificate_file, session=session), use_credential_provider=False - ) - super().__init__( - endpoint=endpoint, headers={**headers, "content-type": "application/json"}, _transport=transport - ) + super().__init__(endpoint=endpoint, headers=headers, certificate_file=certificate_file, session=session) + self._session.headers["Content-Type"] = JSON_CONTENT_TYPE + + def _serialize_spans(self, spans: Sequence[ReadableSpan]) -> bytes: + return encode_spans_json(spans) diff --git a/litellm/integrations/otel/plumbing/otlp_tls.py b/litellm/integrations/otel/plumbing/otlp_tls.py index 959e45e9bd7..9de97e0c77a 100644 --- a/litellm/integrations/otel/plumbing/otlp_tls.py +++ b/litellm/integrations/otel/plumbing/otlp_tls.py @@ -1,19 +1,11 @@ import os -from collections.abc import Mapping -from copy import copy from dataclasses import dataclass from typing import Final, Literal import requests -from pydantic import BaseModel, ConfigDict from requests.adapters import HTTPAdapter -class _RequestHeaders(BaseModel): - model_config = ConfigDict(frozen=True, from_attributes=True) - headers: Mapping[str, str] | None = None - - @dataclass(frozen=True, slots=True) class OtlpHttpTls: certificate_file: str | None @@ -33,65 +25,17 @@ class _NoVerifyAdapter(HTTPAdapter): ) -class _IsolatedHeadersSession(requests.Session): - def __init__(self, signal: str, headers: Mapping[str, str], content_type: str) -> None: - from opentelemetry.util.re import parse_env_headers - - super().__init__() - environment: Final = parse_env_headers( - os.getenv(f"OTEL_EXPORTER_OTLP_{signal}_HEADERS") or os.getenv("OTEL_EXPORTER_OTLP_HEADERS", ""), - liberal=True, - ) - explicit: Final = frozenset(key.lower() for key in headers) - self._excluded_headers: Final = frozenset(key.lower() for key in environment) - explicit - self._content_type: Final = content_type - self._compression: Final = ( - ( - os.getenv(f"OTEL_EXPORTER_OTLP_{signal}_COMPRESSION") - or os.getenv("OTEL_EXPORTER_OTLP_COMPRESSION", "none") - ) - .strip() - .lower() - ) - - def prepare_request(self, request: requests.Request) -> requests.PreparedRequest: - isolated: Final = copy(request) - source: Final[Mapping[str, str]] = _RequestHeaders.model_validate(request).headers or {} - kept: Final = {key: value for key, value in source.items() if key.lower() not in self._excluded_headers} - content_type: Final = {"Content-Type": self._content_type} if "content-type" in self._excluded_headers else {} - compression: Final = ( - {"Content-Encoding": self._compression} - if "content-encoding" in self._excluded_headers and self._compression in ("gzip", "deflate") - else {} - ) - isolated.headers = {**kept, **content_type, **compression} - session: Final = copy(self) - session.headers = { - key: value for key, value in self.headers.items() if key.lower() not in self._excluded_headers - } - return requests.Session.prepare_request(session, isolated) - - -def resolve_otlp_http_tls( - signal: Literal["TRACES", "METRICS", "LOGS"], - headers: Mapping[str, str] | None = None, - *, - content_type: str = "application/x-protobuf", - isolate_headers: bool = False, -) -> OtlpHttpTls: - session: Final = ( - _IsolatedHeadersSession(signal, headers or {}, content_type) if headers or isolate_headers else None - ) +def resolve_otlp_http_tls(signal: Literal["TRACES", "METRICS", "LOGS"]) -> OtlpHttpTls: if os.getenv(f"OTEL_EXPORTER_OTLP_{signal}_CERTIFICATE") or os.getenv("OTEL_EXPORTER_OTLP_CERTIFICATE"): - return OtlpHttpTls(certificate_file=None, session=session) + return OtlpHttpTls(certificate_file=None, session=None) from litellm.llms.custom_httpx.http_handler import get_ssl_verify verify: Final = get_ssl_verify() if verify is False: - unverified: Final = session or requests.Session() - unverified.mount("https://", _NoVerifyAdapter()) - return OtlpHttpTls(certificate_file=None, session=unverified) + session: Final = requests.Session() + session.mount("https://", _NoVerifyAdapter()) + return OtlpHttpTls(certificate_file=None, session=session) if isinstance(verify, str): - return OtlpHttpTls(certificate_file=verify, session=session) - return OtlpHttpTls(certificate_file=None, session=session) + return OtlpHttpTls(certificate_file=verify, session=None) + return OtlpHttpTls(certificate_file=None, session=None) diff --git a/litellm/integrations/otel/plumbing/otlp_transport.py b/litellm/integrations/otel/plumbing/otlp_transport.py deleted file mode 100644 index 31ff3a85c17..00000000000 --- a/litellm/integrations/otel/plumbing/otlp_transport.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Keep explicit collector TLS settings authoritative at request time.""" - -import os -from collections.abc import Mapping -from typing import Final, Literal - -import requests -from opentelemetry.exporter.http.transport._requests import RequestsHTTPResult, RequestsHTTPTransport -from opentelemetry.exporter.otlp.proto.http._common import ( - _load_session_from_envvar, # pyright: ignore[reportPrivateUsage] # pinned SDK loader preserves operator auth -) - -from litellm.integrations.otel.plumbing.otlp_tls import OtlpHttpTls - - -class _OtlpHTTPTransport(RequestsHTTPTransport): - def __init__( - self, - verify: bool | str, - cert: str | tuple[str, str] | None, - session: requests.Session | None, - ) -> None: - self._request_session: Final = session if session is not None else requests.Session() - self._request_verify: Final = verify - super().__init__(verify=verify, cert=cert, session=self._request_session) - - def request( - self, - method: str, - url: str, - *, - headers: Mapping[str, str] | None = None, - timeout: float | None = None, - data: bytes | None = None, - ) -> RequestsHTTPResult: - try: - response: Final = self._request_session.request( - method=method, - url=url, - headers=headers, - data=data, - timeout=timeout, - verify=self._request_verify, - allow_redirects=False, - ) - except Exception as error: # noqa: BLE001 # SDK transport returns all session and auth failures as values - return RequestsHTTPResult(error=error) - return RequestsHTTPResult(status_code=response.status_code, reason=response.reason, response=response) - - -def build_otlp_http_transport( - signal: Literal["TRACES", "LOGS", "METRICS"], - tls: OtlpHttpTls, - *, - use_credential_provider: bool = True, - client_key_file: str | None = None, - client_certificate_file: str | None = None, -) -> RequestsHTTPTransport: - verify: Final = ( - tls.certificate_file - or os.getenv(f"OTEL_EXPORTER_OTLP_{signal}_CERTIFICATE") - or os.getenv("OTEL_EXPORTER_OTLP_CERTIFICATE") - or True - ) - client_key: Final = ( - client_key_file - or os.getenv(f"OTEL_EXPORTER_OTLP_{signal}_CLIENT_KEY") - or os.getenv("OTEL_EXPORTER_OTLP_CLIENT_KEY") - ) - client_certificate: Final = ( - client_certificate_file - or os.getenv(f"OTEL_EXPORTER_OTLP_{signal}_CLIENT_CERTIFICATE") - or os.getenv("OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE") - ) - credential_variable: Final[ - Literal[ - "OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER", - "OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER", - "OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER", - ] - ] = ( - "OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER" - if signal == "TRACES" - else "OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER" - if signal == "LOGS" - else "OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER" - ) - session: Final = tls.session or ( - _load_session_from_envvar(credential_variable) if use_credential_provider else None - ) - return _OtlpHTTPTransport( - verify=verify, - cert=(client_certificate, client_key) if client_certificate and client_key else client_certificate, - session=session, - ) diff --git a/litellm/integrations/otel/plumbing/providers.py b/litellm/integrations/otel/plumbing/providers.py index f5c1637ee63..898eab5715b 100644 --- a/litellm/integrations/otel/plumbing/providers.py +++ b/litellm/integrations/otel/plumbing/providers.py @@ -16,9 +16,9 @@ from opentelemetry.metrics import MeterProvider, NoOpMeterProvider from opentelemetry.sdk._logs import LoggerProvider as SDKLoggerProvider from opentelemetry.sdk._logs.export import ( BatchLogRecordProcessor, - ConsoleLogRecordExporter, - InMemoryLogRecordExporter, - LogRecordExporter, + ConsoleLogExporter, + InMemoryLogExporter, + LogExporter, SimpleLogRecordProcessor, ) from opentelemetry.sdk.metrics import MeterProvider as SDKMeterProvider @@ -194,7 +194,7 @@ def exporter_transport(kind: str) -> Literal["http", "grpc", "headerless"]: return "headerless" -def _exporter_from_spec(spec: ExporterSpec, *, isolate_headers: bool = False) -> SpanExporter: +def _exporter_from_spec(spec: ExporterSpec) -> SpanExporter: kind: Final = (spec.kind or "console").lower() factory: Final = _EXPORTER_FACTORIES.get(kind) if factory is not None: @@ -204,9 +204,7 @@ def _exporter_from_spec(spec: ExporterSpec, *, isolate_headers: bool = False) -> if kind in _OTLP_HTTP_JSON_KINDS: from litellm.integrations.otel.plumbing.otlp_json import OTLPJsonSpanExporter - tls: Final = resolve_otlp_http_tls( - "TRACES", parse_headers(spec.headers), content_type="application/json", isolate_headers=isolate_headers - ) + tls: Final = resolve_otlp_http_tls("TRACES") return OTLPJsonSpanExporter( endpoint=spec.traces_endpoint or _otlp_traces_endpoint(spec.endpoint), headers=parse_headers(spec.headers), @@ -218,13 +216,12 @@ def _exporter_from_spec(spec: ExporterSpec, *, isolate_headers: bool = False) -> OTLPSpanExporter as HTTPExporter, ) - from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport - - http_tls: Final = resolve_otlp_http_tls("TRACES", parse_headers(spec.headers), isolate_headers=isolate_headers) + http_tls: Final = resolve_otlp_http_tls("TRACES") return HTTPExporter( endpoint=spec.traces_endpoint or _otlp_traces_endpoint(spec.endpoint), headers=parse_headers(spec.headers), - _transport=build_otlp_http_transport("TRACES", http_tls), + certificate_file=http_tls.certificate_file, + session=http_tls.session, ) if kind in _OTLP_GRPC_KINDS: from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( @@ -1016,7 +1013,7 @@ def _destination_processor(destination: "OtelDestination") -> SpanProcessor | No headers=destination.header_string(), owner=None, ) - return _processor_for(_exporter_from_spec(spec, isolate_headers=True), use_simple=False) + return _processor_for(_exporter_from_spec(spec), use_simple=False) except Exception as exc: # noqa: BLE001 # a malformed destination must not break the request or the other destinations verbose_logger.debug("OTel V2 fan-out: no processor for %s: %s", destination.endpoint, exc) return None @@ -1189,13 +1186,12 @@ def build_metric_reader(config: OpenTelemetryV2Config) -> "MetricReader": OTLPMetricExporter as HTTPMetricExporter, ) - from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport - - tls: Final = resolve_otlp_http_tls("METRICS", parse_headers(config.headers)) + tls: Final = resolve_otlp_http_tls("METRICS") exporter: object = HTTPMetricExporter( endpoint=_otlp_metrics_endpoint(config.endpoint), headers=parse_headers(config.headers), - _transport=build_otlp_http_transport("METRICS", tls), + certificate_file=tls.certificate_file, + session=tls.session, ) elif kind in ("otlp_grpc", "grpc"): try: @@ -1237,7 +1233,7 @@ def _otlp_logs_endpoint(endpoint: str | None) -> str | None: return endpoint + "/v1/logs" -def build_log_exporter(config: OpenTelemetryV2Config) -> LogRecordExporter: +def build_log_exporter(config: OpenTelemetryV2Config) -> LogExporter: """Build a log exporter mirroring the exporter selection of the other signals. ``console`` (and any unrecognized kind) exports to the console; ``otlp_http`` @@ -1247,19 +1243,18 @@ def build_log_exporter(config: OpenTelemetryV2Config) -> LogRecordExporter: """ kind: Final = (config.exporter or "console").lower() if kind in ("in_memory", "inmemory", "memory"): - return InMemoryLogRecordExporter() + return InMemoryLogExporter() if kind in ("otlp_http", "http", "http/protobuf", "http/json"): from opentelemetry.exporter.otlp.proto.http._log_exporter import ( OTLPLogExporter as HTTPLogExporter, ) - from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport - - tls: Final = resolve_otlp_http_tls("LOGS", parse_headers(config.headers)) + tls: Final = resolve_otlp_http_tls("LOGS") return HTTPLogExporter( endpoint=_otlp_logs_endpoint(config.endpoint), headers=parse_headers(config.headers), - _transport=build_otlp_http_transport("LOGS", tls), + certificate_file=tls.certificate_file, + session=tls.session, ) if kind in ("otlp_grpc", "grpc"): try: @@ -1273,24 +1268,24 @@ def build_log_exporter(config: OpenTelemetryV2Config) -> LogRecordExporter: ) from exc return GRPCLogExporter(endpoint=config.endpoint, headers=parse_headers(config.headers)) - return ConsoleLogRecordExporter() + return ConsoleLogExporter() def build_logger_provider( config: OpenTelemetryV2Config, - log_exporter: LogRecordExporter | None = None, + log_exporter: LogExporter | None = None, ) -> SDKLoggerProvider: """Build the :class:`LoggerProvider` GenAI events export through. ``log_exporter`` is an explicit override (tests inject an - ``InMemoryLogRecordExporter``); otherwise the exporter is selected from the config's + ``InMemoryLogExporter``); otherwise the exporter is selected from the config's exporter kind via :func:`build_log_exporter`. Console and in-memory exporters get a Simple processor (synchronous export, which tests rely on), everything else a Batch processor — the same split as span processing. """ exporter: Final = log_exporter if log_exporter is not None else build_log_exporter(config) provider: Final = SDKLoggerProvider(resource=build_resource(config)) - use_simple: Final = isinstance(exporter, (ConsoleLogRecordExporter, InMemoryLogRecordExporter)) + use_simple: Final = isinstance(exporter, (ConsoleLogExporter, InMemoryLogExporter)) provider.add_log_record_processor( SimpleLogRecordProcessor(exporter) if use_simple else BatchLogRecordProcessor(exporter) ) diff --git a/litellm/integrations/otel/presets/agentops.py b/litellm/integrations/otel/presets/agentops.py index 3408adbfb5f..3befb73b0cc 100644 --- a/litellm/integrations/otel/presets/agentops.py +++ b/litellm/integrations/otel/presets/agentops.py @@ -9,14 +9,14 @@ this preset registers a custom exporter (``kind="agentops"``) that mints the JWT worker thread, off any event loop — and caches it for the process lifetime. """ +from collections.abc import Sequence from typing import Final import httpx -import requests -from opentelemetry.sdk.trace.export import SpanExporter +from opentelemetry.sdk.trace import ReadableSpan +from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict -from requests.auth import AuthBase from litellm._logging import verbose_logger from litellm.integrations.otel.model.config import ( @@ -24,7 +24,6 @@ from litellm.integrations.otel.model.config import ( ExporterSpec, OpenTelemetryV2Config, ) -from litellm.integrations.otel.plumbing.otlp_tls import OtlpHttpTls, resolve_otlp_http_tls from litellm.integrations.otel.plumbing.providers import register_exporter_factory _AGENTOPS_ENDPOINT: Final = "https://otlp.agentops.ai/v1/traces" @@ -75,44 +74,47 @@ def agentops_preset( ) -class _AgentOpsAuth(AuthBase): - def __init__(self, api_key: str | None) -> None: - self._api_key = api_key - self._resolved = False - self._token: str | None = None - - def __call__(self, request: requests.PreparedRequest) -> requests.PreparedRequest: - if not self._resolved: - self._resolved = True - if self._api_key: - try: - token: Final = _fetch_agentops_jwt(self._api_key).get("token") - if isinstance(token, str) and token: - self._token = token - except Exception as error: - verbose_logger.debug("AgentOps JWT fetch failed: %s", error) - authenticated: Final = request.copy() - if self._token: - authenticated.headers["Authorization"] = f"Bearer {self._token}" - return authenticated - - def _build_agentops_exporter(spec: ExporterSpec) -> SpanExporter: - """Authenticate lazily through requests' public session authentication API.""" - from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter + """Factory for the ``agentops`` exporter kind: a lazy-auth OTLP/HTTP exporter.""" + from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( + OTLPSpanExporter, + ) - from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport + class _LazyAuthAgentOpsExporter(OTLPSpanExporter): + """OTLP/HTTP exporter that mints the AgentOps JWT on its first export. + + ``export`` runs in the ``BatchSpanProcessor`` worker thread, so the + blocking token fetch never touches an event loop. The result is cached + after the first attempt (success or failure) so it runs at most once. + """ + + def __init__(self, *, endpoint: str | None, api_key: str | None) -> None: + super().__init__(endpoint=endpoint) + self._agentops_api_key = api_key + self._auth_resolved = False + + def _ensure_authenticated(self) -> None: + if self._auth_resolved: + return + self._auth_resolved = True + if not self._agentops_api_key: + return + try: + token: Final = _fetch_agentops_jwt(self._agentops_api_key).get("token") + if token: + # ``_session`` is the requests.Session the base exporter + # POSTs through; updating its Authorization header is how the + # minted JWT reaches every subsequent export. + self._session.headers["Authorization"] = f"Bearer {token}" + except Exception as e: + verbose_logger.debug("AgentOps JWT fetch failed: %s", e) + + def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: + self._ensure_authenticated() + return super().export(spans) options: Final = spec.options or {} - tls: Final = resolve_otlp_http_tls("TRACES", isolate_headers=True) - session: Final = tls.session or requests.Session() - session.auth = _AgentOpsAuth(options.get("api_key")) - return OTLPSpanExporter( - endpoint=spec.endpoint, - _transport=build_otlp_http_transport( - "TRACES", OtlpHttpTls(certificate_file=tls.certificate_file, session=session) - ), - ) + return _LazyAuthAgentOpsExporter(endpoint=spec.endpoint, api_key=options.get("api_key")) def _fetch_agentops_jwt(api_key: str) -> dict[str, object]: diff --git a/litellm/proxy/enterprise_billing/billing_metrics.py b/litellm/proxy/enterprise_billing/billing_metrics.py index 90616e8acb1..166ece28b83 100644 --- a/litellm/proxy/enterprise_billing/billing_metrics.py +++ b/litellm/proxy/enterprise_billing/billing_metrics.py @@ -25,8 +25,6 @@ from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.sdk.resources import Resource from litellm._logging import verbose_proxy_logger -from litellm.integrations.otel.plumbing.otlp_tls import OtlpHttpTls -from litellm.integrations.otel.plumbing.otlp_transport import build_otlp_http_transport from litellm.proxy.middleware.billable_request_metrics_middleware import ( BillableCategory, ) @@ -106,12 +104,10 @@ def build_mtls_meter_provider(config: BillingMetricsConfig) -> MeterProvider: """ exporter: Final = OTLPMetricExporter( endpoint=_metrics_endpoint(config.endpoint), - _transport=build_otlp_http_transport( - "METRICS", - OtlpHttpTls(certificate_file=config.ca_cert_path, session=None), - client_certificate_file=config.client_cert_path, - client_key_file=config.client_key_path, - ), + # None -> exporter falls back to the system trust store. + certificate_file=config.ca_cert_path, + client_certificate_file=config.client_cert_path, + client_key_file=config.client_key_path, ) reader: Final = PeriodicExportingMetricReader(exporter, export_interval_millis=config.export_interval_ms) return MeterProvider(metric_readers=[reader], resource=Resource.create(_resource_attributes(config))) diff --git a/pyproject.toml b/pyproject.toml index 1789d5503c0..3aefd911937 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -177,17 +177,16 @@ proxy-runtime = [ "grpcio==1.78.0", "prometheus-client>=0.20.0,<1.0", "langfuse>=4.7,<5.0", - "opentelemetry-api==1.45.1", - "opentelemetry-sdk==1.45.1", - "opentelemetry-exporter-otlp==1.45.1", - "opentelemetry-exporter-otlp-json-http==0.66b1", - "opentelemetry-instrumentation-fastapi==0.66b1", + "opentelemetry-api==1.33.1", + "opentelemetry-sdk==1.33.1", + "opentelemetry-exporter-otlp==1.33.1", + "opentelemetry-instrumentation-fastapi==0.54b1", "ddtrace>=4.8.2,<5.0", "sentry-sdk>=2.21.0,<3.0", "mangum>=0.17.0,<1.0", "azure-ai-contentsafety>=1.0.0,<2.0", "azure-storage-file-datalake>=12.20.0,<13.0", - "pypdf>=6.21.1,<7.0", + "pypdf>=6.16.1,<7.0", "llm-sandbox>=0.3.39,<1.0", "detect-secrets>=1.5.0,<2.0", ] @@ -224,11 +223,10 @@ dev = [ "types-PyYAML==6.0.12.20250915", "botocore-stubs==1.43.14", "types-boto3[bedrock,bedrock-agent,bedrock-runtime,kms,s3,sagemaker-runtime,sts]==1.43.30", - "opentelemetry-api==1.45.1", - "opentelemetry-sdk==1.45.1", - "opentelemetry-exporter-otlp==1.45.1", - "opentelemetry-exporter-otlp-json-http==0.66b1", - "opentelemetry-instrumentation-fastapi==0.66b1", + "opentelemetry-api==1.33.1", + "opentelemetry-sdk==1.33.1", + "opentelemetry-exporter-otlp==1.33.1", + "opentelemetry-instrumentation-fastapi==0.54b1", "langfuse>=4.7,<5.0", "fastapi-offline==1.7.6", "fakeredis==2.34.1", @@ -257,11 +255,10 @@ proxy-dev = [ "hypercorn==0.17.3", "prometheus-client==0.20.0", "sentry-sdk==2.21.0", - "opentelemetry-api==1.45.1", - "opentelemetry-sdk==1.45.1", - "opentelemetry-exporter-otlp==1.45.1", - "opentelemetry-exporter-otlp-json-http==0.66b1", - "opentelemetry-instrumentation-fastapi==0.66b1", + "opentelemetry-api==1.33.1", + "opentelemetry-sdk==1.33.1", + "opentelemetry-exporter-otlp==1.33.1", + "opentelemetry-instrumentation-fastapi==0.54b1", "azure-identity==1.25.2", "a2a-sdk==1.1.0", "backports-zstd==1.8.0; python_version < '3.14'", @@ -283,7 +280,7 @@ ci = [ "langchain==1.3.9", "lunary==1.4.36; python_version == '3.10'", "lunary==1.4.37; python_version >= '3.11'", - "logfire==5.2.0", + "logfire==4.6.0", "traceloop-sdk==0.34.0", "detect-secrets==1.5.0", "PyGithub==2.8.1", diff --git a/tests/unit/integrations/langfuse/test_langfuse_sdk.py b/tests/unit/integrations/langfuse/test_langfuse_sdk.py index 8f5a8fc603b..cef2b13f0fd 100644 --- a/tests/unit/integrations/langfuse/test_langfuse_sdk.py +++ b/tests/unit/integrations/langfuse/test_langfuse_sdk.py @@ -165,30 +165,6 @@ def test_requested_trace_id_is_the_exported_trace_id_and_the_generation_is_its_r assert span.parent is None - -@pytest.mark.parametrize("trace_id", ["0" * 31 + "1", None]) -def test_trace_randomness_flag_matches_the_id_source(channel, trace_id): - from opentelemetry.context import Context - from litellm.integrations.langfuse.langfuse_sdk import _start_span - - tracing, exporter = channel - span = _start_span( - tracing.tracer, - name="id-source", - context=Context(), - start_time=CALL_START, - trace_id=trace_id, - observation_id=None, - attributes={}, - ) - span.end(end_time=to_unix_nanos(CALL_END)) - tracing.flush() - exported = _only_span(exporter, "id-source") - assert exported.context.trace_flags.random_trace_id is (trace_id is None) - assert exported.context.trace_flags.sampled - if trace_id is not None: - assert exported.context.trace_id == int(trace_id, 16) - def test_parent_observation_id_nests_the_generation_under_the_callers_observation(channel): tracing, exporter = channel _generation(tracing, name="child-gen", parent=PARENT_C).end(CALL_END) diff --git a/tests/unit/integrations/otel/test_otel_v2_components.py b/tests/unit/integrations/otel/test_otel_v2_components.py index 37392e3c77f..3f4389765d3 100644 --- a/tests/unit/integrations/otel/test_otel_v2_components.py +++ b/tests/unit/integrations/otel/test_otel_v2_components.py @@ -23,9 +23,8 @@ from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( # noqa: from opentelemetry import baggage # noqa: E402 from opentelemetry.context import attach, detach # noqa: E402 from opentelemetry._logs.severity import SeverityNumber # noqa: E402 -from opentelemetry._logs import LogRecord -from opentelemetry.sdk._logs import ReadableLogRecord # noqa: E402 -from opentelemetry.sdk._logs.export import LogRecordExportResult # noqa: E402 +from opentelemetry.sdk._logs import LogData, LogRecord # noqa: E402 +from opentelemetry.sdk._logs.export import LogExportResult # noqa: E402 from opentelemetry.sdk.metrics import MeterProvider # noqa: E402 from opentelemetry.sdk.metrics.export import InMemoryMetricReader # noqa: E402 from opentelemetry.sdk.trace import TracerProvider # noqa: E402 @@ -677,9 +676,7 @@ def test_parse_headers_percent_decodes_values(): documents ``Authorization=Basic%20``. Forwarding the literal ``%20`` makes the backend reject the export as a malformed credential. """ - from base64 import b64encode - - token: Final = b64encode(b"TEST_ONLY_USER:TEST_ONLY_PASSWORD").decode() + token = "MTMzNzc4MzpnbGNfZXlKdklqb2lNVEl6TkNJPQ==" assert providers.parse_headers(f"Authorization=Basic%20{token}") == {"authorization": f"Basic {token}"} assert providers.parse_headers("x-scope-orgid=team%20a") == {"x-scope-orgid": "team a"} @@ -736,7 +733,7 @@ def _export_one_trace_to_local_collector(exporter_kind: str) -> tuple[list[dict] class Collector(BaseHTTPRequestHandler): def do_POST(self): body = self.rfile.read(int(self.headers["Content-Length"])) - received.append({"path": self.path, "headers": requests.structures.CaseInsensitiveDict(self.headers), "body": body}) + received.append({"path": self.path, "headers": dict(self.headers), "body": body}) self.send_response(200) self.end_headers() @@ -789,60 +786,13 @@ def test_http_json_exporter_posts_otlp_json_to_traces_endpoint(): assert parent["kind"] == 2 assert parent["traceId"] == format(trace_id, "032x") assert parent["spanId"] == format(parent_id, "016x") - assert parent.get("parentSpanId", "") == "" + assert "parentSpanId" not in parent child = _only_span(child_request) assert child["traceId"] == format(trace_id, "032x") assert child["spanId"] == format(child_id, "016x") assert child["parentSpanId"] == format(parent_id, "016x") - -@pytest.mark.parametrize("compression", ["none", "gzip", "deflate"]) -def test_http_json_exporter_preserves_session_tls_auth_and_compression(monkeypatch, requests_mock, compression): - import gzip - import zlib - - from opentelemetry.sdk.trace.export import SpanExportResult - from litellm.integrations.otel.plumbing.otlp_json import OTLPJsonSpanExporter - - monkeypatch.setenv("REQUESTS_CA_BUNDLE", "/corporate/global-ca.pem") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE", "/collector/environment-ca.pem") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_COMPRESSION", compression) - monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE", "/collector/client.crt") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY", "/collector/client.key") - monkeypatch.setenv("HTTPS_PROXY", "http://proxy.example:8080") - collector = requests_mock.post("https://collector.example/v1/traces", status_code=200) - session = requests.Session() - session.headers["x-session-marker"] = "retained" - session.auth = requests.auth.HTTPBasicAuth("session-user", "synthetic-password") - exporter = OTLPJsonSpanExporter( - endpoint="https://collector.example/v1/traces", - headers={"x-exporter-marker": "configured"}, - certificate_file="/collector/ca.crt", - session=session, - ) - provider = TracerProvider() - span = provider.get_tracer(__name__).start_span("session-export") - span.end() - try: - assert exporter.export([span]) is SpanExportResult.SUCCESS - request = collector.last_request - assert request.headers["Content-Type"] == "application/json" - assert request.headers["x-session-marker"] == "retained" - assert request.headers["x-exporter-marker"] == "configured" - assert request.headers["Authorization"] == "Basic c2Vzc2lvbi11c2VyOnN5bnRoZXRpYy1wYXNzd29yZA==" - assert request.verify == "/collector/ca.crt" - assert request.cert == ("/collector/client.crt", "/collector/client.key") - assert request.proxies["https"] == "http://proxy.example:8080" - decompress = {"none": lambda body: body, "gzip": gzip.decompress, "deflate": zlib.decompress}[compression] - payload = json.loads(decompress(request.body)) - exported = payload["resourceSpans"][0]["scopeSpans"][0]["spans"][0] - assert exported["name"] == "session-export" - assert exported["traceId"] == f"{span.get_span_context().trace_id:032x}" - finally: - exporter.shutdown() - provider.shutdown() - def test_http_protobuf_exporter_still_posts_protobuf(): (_child_request, parent_request), (trace_id, _parent_id, _child_id) = _export_one_trace_to_local_collector( "http/protobuf" @@ -974,20 +924,20 @@ def test_otlp_logs_endpoint_normalization(): def test_build_log_exporter_variants(): - from opentelemetry.sdk._logs.export import ConsoleLogRecordExporter, InMemoryLogRecordExporter + from opentelemetry.sdk._logs.export import ConsoleLogExporter, InMemoryLogExporter assert isinstance( providers.build_log_exporter(OpenTelemetryV2Config(exporter="console")), - ConsoleLogRecordExporter, + ConsoleLogExporter, ) assert isinstance( providers.build_log_exporter(OpenTelemetryV2Config(exporter="in_memory")), - InMemoryLogRecordExporter, + InMemoryLogExporter, ) # An unrecognized kind falls back to console rather than dropping events. assert isinstance( providers.build_log_exporter(OpenTelemetryV2Config(exporter="unknown")), - ConsoleLogRecordExporter, + ConsoleLogExporter, ) http_exporter = providers.build_log_exporter(OpenTelemetryV2Config(exporter="otlp_http", endpoint="http://h:4318")) assert "OTLPLogExporter" in type(http_exporter).__name__ @@ -1680,13 +1630,9 @@ def test_v2_otlp_http_log_export_trusts_ssl_cert_file(monkeypatch: pytest.Monkey severity_number=SeverityNumber.INFO, body="v2-tls-test", ) - log_data = ReadableLogRecord( - log_record=record, - resource=providers.build_resource(cfg), - instrumentation_scope=InstrumentationScope("v2-tls-test"), - ) + log_data = LogData(log_record=record, instrumentation_scope=InstrumentationScope("v2-tls-test")) result = exporter.export([log_data]) - assert result is LogRecordExportResult.SUCCESS, f"log export failed: {result}" + assert result is LogExportResult.SUCCESS, f"log export failed: {result}" assert tls_sink.received.get(timeout=5) == "/v1/logs" finally: exporter.shutdown() diff --git a/tests/unit/integrations/otel/test_otel_v2_destinations.py b/tests/unit/integrations/otel/test_otel_v2_destinations.py index a8d8da9b426..c7a057cca5c 100644 --- a/tests/unit/integrations/otel/test_otel_v2_destinations.py +++ b/tests/unit/integrations/otel/test_otel_v2_destinations.py @@ -2,8 +2,6 @@ import asyncio import contextvars -import gzip -import os import threading import time from base64 import b64encode @@ -5069,267 +5067,3 @@ class TestArizeProjectRouting: monkeypatch.setenv("ARIZE_PROJECT_NAME", "gateway-prod") assert self._forwarded_projects() == {"gateway-prod"} - - -def _export_header_probe(api: str, signal: str, endpoint: str, headers: str | None) -> None: - from opentelemetry.sdk.metrics import MeterProvider - - from litellm.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig - - config: Final = OpenTelemetryV2Config( - exporter="http/json" if api == "v2-json" else "otlp_http", endpoint=endpoint, headers=headers - ) - tracing: Final = TracerProvider() - legacy: Final = ( - OpenTelemetry( - config=OpenTelemetryConfig(exporter="otlp_http", endpoint=endpoint, headers=headers), - tracer_provider=tracing, - ) - if api == "v1" - else None - ) - try: - if signal == "metrics": - reader: Final = legacy._get_metric_reader() if legacy else otel_providers.build_metric_reader(config) - metrics: Final = MeterProvider(metric_readers=(reader,)) - try: - metrics.get_meter("header-isolation").create_counter("header_probe").add(1) - assert metrics.force_flush() - finally: - metrics.shutdown() - elif signal == "logs": - logs: Final = legacy._get_log_exporter() if legacy else otel_providers.build_log_exporter(config) - try: - assert logs.export(()).name == "SUCCESS" - finally: - logs.shutdown() - elif legacy: - processor: Final = legacy._get_span_processor() - try: - assert processor.span_exporter.export(()).name == "SUCCESS" - finally: - processor.shutdown() - else: - spans: Final = otel_providers.build_span_exporter(config) - try: - assert spans.export(()).name == "SUCCESS" - finally: - spans.shutdown() - finally: - tracing.shutdown() - - -@pytest.mark.parametrize( - ("api", "signal"), - ( - ("v1", "traces"), - ("v1", "metrics"), - ("v1", "logs"), - ("v2", "traces"), - ("v2", "metrics"), - ("v2", "logs"), - ("v2-json", "traces"), - ), -) -@pytest.mark.parametrize("signal_headers", (False, True)) -def test_http_destinations_do_not_inherit_other_accounts_headers( - monkeypatch, requests_mock, api: str, signal: str, signal_headers: bool -) -> None: - variable: Final = f"OTEL_EXPORTER_OTLP_{signal.upper()}_HEADERS" if signal_headers else "OTEL_EXPORTER_OTLP_HEADERS" - operator_headers: Final = "Authorization=Bearer%20operator-key,x-operator-key=operator-only" - monkeypatch.setenv(variable, operator_headers) - for account, configured in (("tenant-a", "a-key"), ("tenant-b", "b-key"), ("operator", None)): - endpoint: Final = f"https://{account}.example.test" - captured: Final = requests_mock.post(f"{endpoint}/v1/{signal}", content=b"") - - _export_header_probe(api, signal, endpoint, f"x-tenant-key={configured}" if configured else None) - - assert captured.request_history, f"{account} never exported {signal}" - for request in captured.request_history: - credentials: Final = { - name.lower(): value - for name, value in request.headers.items() - if name.lower() in {"authorization", "x-operator-key", "x-tenant-key"} - } - assert credentials == ( - {"x-tenant-key": configured} - if configured - else {"authorization": "Bearer operator-key", "x-operator-key": "operator-only"} - ) - assert os.environ[variable] == operator_headers - - -@pytest.mark.parametrize("protocol", ("otlp_http", "http/json")) -def test_headerless_tenant_destination_does_not_receive_operator_credentials(monkeypatch, requests_mock, protocol): - monkeypatch.setenv("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer%20operator-key") - captured: Final = requests_mock.post("https://tenant.example.test/v1/traces", content=b"") - processor: Final = otel_providers._destination_processor( - OtelDestination(endpoint="https://tenant.example.test", headers={}, protocol=protocol) - ) - assert processor is not None - provider: Final = TracerProvider() - provider.add_span_processor(processor) - try: - emit(provider, "headerless-tenant") - assert provider.force_flush() - assert captured.request_history - assert all("Authorization" not in request.headers for request in captured.request_history) - finally: - provider.shutdown() - - -@pytest.mark.parametrize("api", ("v2", "v2-json")) -def test_isolated_headers_keep_the_exporters_content_type_and_compression(monkeypatch, requests_mock, api): - monkeypatch.setenv("OTEL_EXPORTER_OTLP_HEADERS", "Content-Type=operator-only,Content-Encoding=operator-only") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_COMPRESSION", "gzip") - captured: Final = requests_mock.post("https://tenant.example.test/v1/traces", content=b"") - - _export_header_probe(api, "traces", "https://tenant.example.test", "x-tenant-key=tenant-key") - - assert len(captured.request_history) == 1 - request: Final = captured.last_request - assert request.headers["Content-Type"] == ("application/json" if api == "v2-json" else "application/x-protobuf") - assert request.headers["Content-Encoding"] == "gzip" - monkeypatch.delenv("OTEL_EXPORTER_OTLP_TRACES_COMPRESSION") - - _export_header_probe(api, "traces", "https://tenant.example.test", "x-tenant-key=tenant-key") - - assert gzip.decompress(request.body) == (captured.last_request.body or b"") - - -@pytest.mark.parametrize("api", ("v2", "v2-json")) -@pytest.mark.parametrize("header", ("Authorization", "AUTHORIZATION", "authorization")) -def test_tenant_authorization_replaces_operator_authorization(monkeypatch, requests_mock, api, header): - monkeypatch.setenv("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer%20operator-key") - captured: Final = requests_mock.post("https://tenant.example.test/v1/traces", content=b"") - - _export_header_probe(api, "traces", "https://tenant.example.test", f"{header}=Bearer tenant-key") - - assert len(captured.request_history) == 1 - assert captured.last_request.headers["Authorization"] == "Bearer tenant-key" - assert "Bearer operator-key" not in captured.last_request.headers.values() - - -@pytest.mark.parametrize("protocol", ("otlp_http", "http/json")) -def test_session_defaults_cannot_restore_excluded_operator_headers(monkeypatch, requests_mock, protocol): - from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter - - from litellm.integrations.otel.plumbing.otlp_json import OTLPJsonSpanExporter - from litellm.integrations.otel.plumbing.otlp_tls import resolve_otlp_http_tls - - monkeypatch.setenv("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer%20operator-key,x-operator-key=operator-only") - captured: Final = requests_mock.post("https://tenant.example.test/v1/traces", content=b"") - headers: Final = {"x-tenant-key": "tenant-key"} - tls: Final = resolve_otlp_http_tls("TRACES", headers) - assert tls.session is not None - tls.session.headers.update({"AUTHORIZATION": "Bearer operator-key", "X-Operator-Key": "operator-only"}) - exporter_type: Final = OTLPJsonSpanExporter if protocol == "http/json" else OTLPSpanExporter - exporter: Final = exporter_type( - endpoint="https://tenant.example.test/v1/traces", headers=headers, session=tls.session - ) - try: - assert exporter.export(()).name == "SUCCESS" - assert len(captured.request_history) == 1 - assert captured.last_request.headers["x-tenant-key"] == "tenant-key" - assert "Authorization" not in captured.last_request.headers - assert "x-operator-key" not in captured.last_request.headers - finally: - exporter.shutdown() - - -@pytest.mark.parametrize( - ("api", "signal"), - (("v1", "traces"), ("v1", "logs"), ("v1", "metrics"), - ("v2", "traces"), ("v2", "logs"), ("v2", "metrics"), ("v2-json", "traces")), -) -@pytest.mark.parametrize("requests_ca", ("REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE")) -@pytest.mark.parametrize("collector_ca", ("signal", "global", "SSL_CERT_FILE", "ssl_verify")) -@pytest.mark.parametrize("tenant", (False, True)) -def test_explicit_collector_ca_precedes_requests_environment( - monkeypatch, requests_mock, tmp_path, api: str, signal: str, requests_ca: str, collector_ca: str, tenant: bool -) -> None: - certificate_path: Final = tmp_path / "collector-ca.pem" - certificate_path.write_text("TEST_ONLY_CA") - certificate: Final = str(certificate_path) - for variable in ("REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "SSL_CERT_FILE", "SSL_VERIFY", - "OTEL_EXPORTER_OTLP_CERTIFICATE", f"OTEL_EXPORTER_OTLP_{signal.upper()}_CERTIFICATE"): - monkeypatch.delenv(variable, raising=False) - monkeypatch.setattr(litellm, "ssl_verify", True) - monkeypatch.setenv(requests_ca, "/corporate/global-ca.pem") - if collector_ca == "ssl_verify": - monkeypatch.setattr(litellm, "ssl_verify", certificate) - else: - variable: Final = ( - f"OTEL_EXPORTER_OTLP_{signal.upper()}_CERTIFICATE" if collector_ca == "signal" - else "OTEL_EXPORTER_OTLP_CERTIFICATE" if collector_ca == "global" else collector_ca - ) - monkeypatch.setenv(variable, certificate) - if collector_ca == "signal": - monkeypatch.setenv("OTEL_EXPORTER_OTLP_CERTIFICATE", "/collector/generic-ca.pem") - monkeypatch.setenv(f"OTEL_EXPORTER_OTLP_{signal.upper()}_CLIENT_CERTIFICATE", "/collector/client.pem") - monkeypatch.setenv(f"OTEL_EXPORTER_OTLP_{signal.upper()}_CLIENT_KEY", "/collector/client.key") - monkeypatch.setenv("HTTPS_PROXY", "http://proxy.example.invalid:8080") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer%20TEST_ONLY_OPERATOR") - captured: Final = requests_mock.post(f"https://collector.example.invalid/v1/{signal}", content=b"") - - _export_header_probe(api, signal, "https://collector.example.invalid", "x-tenant-key=TEST_ONLY" if tenant else None) - - assert captured.request_history - for request in captured.request_history: - assert request.verify == certificate - assert request.cert == ("/collector/client.pem", "/collector/client.key") - assert request.proxies["https"] == "http://proxy.example.invalid:8080" - assert request.headers.get("Authorization") == (None if tenant else "Bearer TEST_ONLY_OPERATOR") - assert request.headers.get("x-tenant-key") == ("TEST_ONLY" if tenant else None) - - -@pytest.mark.parametrize("api", ("v1", "v2")) -@pytest.mark.parametrize("signal", ("traces", "logs", "metrics")) -@pytest.mark.parametrize("global_provider", (False, True)) -@pytest.mark.parametrize("tenant", (False, True)) -def test_collector_tls_preserves_operator_credential_provider_without_tenant_leakage( - monkeypatch, requests_mock, api: str, signal: str, global_provider: bool, tenant: bool -) -> None: - from types import SimpleNamespace - - import requests - from opentelemetry.exporter.otlp.proto.http import _common - - session: Final = requests.Session() - session.auth = requests.auth.HTTPBasicAuth("TEST_ONLY_USER", "TEST_ONLY_PASSWORD") - session.headers["x-session-marker"] = "TEST_ONLY_SESSION" - provider_variable: Final = ( - "OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER" if global_provider - else f"OTEL_PYTHON_EXPORTER_OTLP_HTTP_{signal.upper()}_CREDENTIAL_PROVIDER" - ) - monkeypatch.setenv(provider_variable, "TEST_ONLY_PROVIDER") - monkeypatch.setenv(f"OTEL_EXPORTER_OTLP_{signal.upper()}_CERTIFICATE", "/collector/collector-ca.pem") - monkeypatch.setenv("REQUESTS_CA_BUNDLE", "/corporate/global-ca.pem") - monkeypatch.setattr(_common, "entry_points", lambda **kwargs: (SimpleNamespace(load=lambda: lambda: session),)) - captured: Final = requests_mock.post(f"https://collector.example.invalid/v1/{signal}", content=b"") - - _export_header_probe(api, signal, "https://collector.example.invalid", "x-tenant-key=TEST_ONLY" if tenant else None) - - assert captured.request_history - for request in captured.request_history: - assert request.verify == "/collector/collector-ca.pem" - assert request.headers.get("x-session-marker") == (None if tenant else "TEST_ONLY_SESSION") - assert request.headers.get("Authorization") == ( - None if tenant else "Basic " + b64encode(b"TEST_ONLY_USER:TEST_ONLY_PASSWORD").decode() - ) - - -@pytest.mark.parametrize("api", ("v1", "v2", "v2-json")) -@pytest.mark.parametrize("requests_ca", ("REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE")) -def test_requests_environment_ca_remains_fallback(monkeypatch, requests_mock, api: str, requests_ca: str) -> None: - for variable in ("REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "SSL_CERT_FILE", "SSL_VERIFY", - "OTEL_EXPORTER_OTLP_CERTIFICATE", "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE"): - monkeypatch.delenv(variable, raising=False) - monkeypatch.setattr(litellm, "ssl_verify", True) - monkeypatch.setenv(requests_ca, "/corporate/global-ca.pem") - captured: Final = requests_mock.post("https://collector.example.invalid/v1/traces", content=b"") - - _export_header_probe(api, "traces", "https://collector.example.invalid", None) - - assert captured.request_history - assert captured.last_request.verify == "/corporate/global-ca.pem" diff --git a/tests/unit/integrations/otel/test_otel_v2_mount.py b/tests/unit/integrations/otel/test_otel_v2_mount.py index 22d7e4cd317..3a27c3ec4e4 100644 --- a/tests/unit/integrations/otel/test_otel_v2_mount.py +++ b/tests/unit/integrations/otel/test_otel_v2_mount.py @@ -382,7 +382,7 @@ def test_every_traced_response_names_its_own_server_span(monkeypatch): assert responses["/stream"].text == "data: first\n\ndata: [DONE]\n\n" for path, response in responses.items(): context = server_spans[path].context - assert response.headers.get_list("traceresponse") == [f"00-{context.trace_id:032x}-{context.span_id:016x}-{context.trace_flags:02x}"] + assert response.headers.get_list("traceresponse") == [f"00-{context.trace_id:032x}-{context.span_id:016x}-01"] @pytest.mark.parametrize("path", ["/relayed", "/relayed/health"]) @@ -472,20 +472,13 @@ def test_gate_off_responses_carry_no_traceresponse(monkeypatch): assert "traceresponse" not in TestClient(app).get("/ping").headers -def test_started_app_keeps_serving_without_registering_a_response_propagator(monkeypatch): +def test_failed_instrumentation_registers_no_response_propagator(monkeypatch): monkeypatch.setenv("LITELLM_OTEL_V2", "1") app = fastapi.FastAPI() - @app.get("/running") - def running(): - return {"ok": True} - - with TestClient(app) as client: + with TestClient(app): instrument_fastapi_app(app) - response = client.get("/running") - assert response.json() == {"ok": True} - assert "traceresponse" not in response.headers assert get_global_response_propagator() is None @@ -552,4 +545,4 @@ def test_the_unhandled_error_helper_names_the_span_it_is_given_in_any_context(mo headers = contextvars.Context().run(trace_response_headers, span) - assert headers["traceresponse"] == f"00-{context.trace_id:032x}-{context.span_id:016x}-{context.trace_flags:02x}" + assert headers["traceresponse"] == f"00-{context.trace_id:032x}-{context.span_id:016x}-01" diff --git a/tests/unit/integrations/otel/test_otel_v2_presets.py b/tests/unit/integrations/otel/test_otel_v2_presets.py index 60207a2b5bc..64366626784 100644 --- a/tests/unit/integrations/otel/test_otel_v2_presets.py +++ b/tests/unit/integrations/otel/test_otel_v2_presets.py @@ -2,8 +2,6 @@ event loop: the preset does no network I/O, and a custom exporter mints the JWT lazily on its first export (in the BatchSpanProcessor worker thread).""" -import json - import httpx import pytest @@ -77,53 +75,50 @@ def test_dynamic_cred_presets_tag_exporter_with_matching_owner(monkeypatch): ) -def test_agentops_exporter_mints_jwt_lazily(monkeypatch, requests_mock, respx_mock): - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.sdk.trace.export import SpanExportResult - - monkeypatch.setenv("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer%20operator,x-operator-key=operator-secret") - auth = respx_mock.post(agentops_mod._AGENTOPS_AUTH_ENDPOINT).respond(200, json={"token": "jwt-xyz"}) - collector = requests_mock.post(_AGENTOPS_ENDPOINT, status_code=200) - exporter = _build_agentops_exporter( - ExporterSpec(kind=_AGENTOPS_EXPORTER_KIND, endpoint=_AGENTOPS_ENDPOINT, options={"api_key": "ak"}) +def test_agentops_exporter_mints_jwt_lazily(monkeypatch): + pytest.importorskip("opentelemetry.exporter.otlp.proto.http.trace_exporter") + monkeypatch.setattr( + agentops_mod, "_fetch_agentops_jwt", lambda _k: {"token": "jwt-xyz"} ) - provider = TracerProvider() - span = provider.get_tracer(__name__).start_span("authenticated") - span.end() - try: - assert auth.call_count == 0 - assert exporter.export([span]) is SpanExportResult.SUCCESS - assert exporter.export([]) is SpanExportResult.SUCCESS - assert auth.call_count == 1 - assert json.loads(auth.calls[0].request.content) == {"api_key": "ak"} - assert [request.headers["Authorization"] for request in collector.request_history] == [ - "Bearer jwt-xyz", - "Bearer jwt-xyz", - ] - assert all("x-operator-key" not in request.headers for request in collector.request_history) - finally: - exporter.shutdown() - provider.shutdown() - - -def test_agentops_exporter_tolerates_fetch_failure(monkeypatch, requests_mock, respx_mock): - from opentelemetry.sdk.trace.export import SpanExportResult - - monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "Authorization=Bearer%20operator,x-operator-key=operator-secret") - auth = respx_mock.post(agentops_mod._AGENTOPS_AUTH_ENDPOINT).respond(503, text="auth down") - collector = requests_mock.post(_AGENTOPS_ENDPOINT, status_code=200) - exporter = _build_agentops_exporter( - ExporterSpec(kind=_AGENTOPS_EXPORTER_KIND, endpoint=_AGENTOPS_ENDPOINT, options={"api_key": "ak"}) + spec = ExporterSpec( + kind=_AGENTOPS_EXPORTER_KIND, + endpoint=_AGENTOPS_ENDPOINT, + options={"api_key": "ak"}, ) - try: - assert exporter.export([]) is SpanExportResult.SUCCESS - assert exporter.export([]) is SpanExportResult.SUCCESS - assert auth.call_count == 1 - assert collector.call_count == 2 - assert all("Authorization" not in request.headers for request in collector.request_history) - assert all("x-operator-key" not in request.headers for request in collector.request_history) - finally: - exporter.shutdown() + exporter = _build_agentops_exporter(spec) + + # No auth header until the first export triggers the (off-loop) fetch. + assert "Authorization" not in exporter._session.headers + exporter._ensure_authenticated() + assert exporter._session.headers["Authorization"] == "Bearer jwt-xyz" + + # Cached: a second resolution does not re-fetch. + calls = [] + monkeypatch.setattr( + agentops_mod, + "_fetch_agentops_jwt", + lambda k: calls.append(k) or {"token": "again"}, + ) + exporter._ensure_authenticated() + assert calls == [] + + +def test_agentops_exporter_tolerates_fetch_failure(monkeypatch): + pytest.importorskip("opentelemetry.exporter.otlp.proto.http.trace_exporter") + + def _raise(_k): + raise RuntimeError("auth down") + + monkeypatch.setattr(agentops_mod, "_fetch_agentops_jwt", _raise) + exporter = _build_agentops_exporter( + ExporterSpec( + kind=_AGENTOPS_EXPORTER_KIND, + endpoint=_AGENTOPS_ENDPOINT, + options={"api_key": "ak"}, + ) + ) + exporter._ensure_authenticated() # must not raise + assert "Authorization" not in exporter._session.headers def test_fetch_jwt_uses_owned_client_not_shared_pool(monkeypatch): diff --git a/tests/unit/integrations/test_langfuse_otel.py b/tests/unit/integrations/test_langfuse_otel.py index cc31e2788da..9f536ab0dac 100644 --- a/tests/unit/integrations/test_langfuse_otel.py +++ b/tests/unit/integrations/test_langfuse_otel.py @@ -591,7 +591,7 @@ class TestLangfuseOtelKeyDynamicConfig: assert logger.construct_dynamic_otel_config(self._dynamic_params(langfuse_secret_key=None)) is None assert logger.construct_dynamic_otel_config(self._dynamic_params(langfuse_public_key=None)) is None - def test_key_dynamic_params_create_otlp_exporter_without_global_env(self, requests_mock): + def test_key_dynamic_params_create_otlp_exporter_without_global_env(self): """Without global LANGFUSE_* env vars, a request carrying key-scoped Langfuse credentials must get a tracer exporting via OTLP HTTP to that key's host.""" from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( @@ -617,17 +617,15 @@ class TestLangfuseOtelKeyDynamicConfig: exporter = span_processors[0].span_exporter assert isinstance(exporter, OTLPSpanExporter) + assert exporter._endpoint == "https://langfuse.example.com/api/public/otel/v1/traces" import base64 expected_auth = base64.b64encode(b"key_public:key_secret").decode() - collector = requests_mock.post("https://langfuse.example.com/api/public/otel/v1/traces", status_code=200) - with tracer.start_as_current_span("dynamic-credentials"): - pass - assert provider.force_flush() - assert collector.call_count == 1 - assert collector.last_request.headers["Authorization"] == f"Basic {expected_auth}" - assert collector.last_request.headers["x-langfuse-ingestion-version"] == "4" + assert exporter._headers == { + "Authorization": f"Basic {expected_auth}", + "x-langfuse-ingestion-version": "4", + } def test_key_dynamic_params_reuse_cached_provider(self): with self._clean_env(): @@ -646,7 +644,7 @@ class TestLangfuseOtelKeyDynamicConfig: assert tracer is logger.tracer assert logger._tracer_provider_cache == {} - def test_key_credentials_never_passed_to_debug_logger(self, requests_mock): + def test_key_credentials_never_passed_to_debug_logger(self): """The span-processor debug logs must receive a redacted header value, so the key-scoped Langfuse secret never enters a log record regardless of downstream handler configuration, while the exporter still gets the real header.""" @@ -680,12 +678,10 @@ class TestLangfuseOtelKeyDynamicConfig: provider = next(iter(logger._tracer_provider_cache.values())).provider exporter = provider._active_span_processor._span_processors[0].span_exporter assert isinstance(exporter, OTLPSpanExporter) - from opentelemetry.sdk.trace.export import SpanExportResult - - collector = requests_mock.post("https://langfuse.example.com/api/public/otel/v1/traces", status_code=200) - assert exporter.export([]) is SpanExportResult.SUCCESS - assert collector.last_request.headers["Authorization"] == f"Basic {secret}" - assert collector.last_request.headers["x-langfuse-ingestion-version"] == "4" + assert exporter._headers == { + "Authorization": f"Basic {secret}", + "x-langfuse-ingestion-version": "4", + } class TestLangfuseOtelResponsesAPI: diff --git a/tests/unit/integrations/test_opentelemetry.py b/tests/unit/integrations/test_opentelemetry.py index 8b14fbddaf8..d14703e48a1 100644 --- a/tests/unit/integrations/test_opentelemetry.py +++ b/tests/unit/integrations/test_opentelemetry.py @@ -12,23 +12,21 @@ import weakref from datetime import datetime, timedelta, timezone from pathlib import Path from types import MappingProxyType -from typing import ClassVar, Final +from typing import Final from unittest.mock import MagicMock, patch import pytest # Adds the grandparent directory to sys.path to allow importing project modules from opentelemetry import trace -from opentelemetry._logs import LogRecord from opentelemetry._logs.severity import SeverityNumber -from opentelemetry.sdk._logs import ReadableLogRecord +from opentelemetry.sdk._logs import LogData, LogRecord from opentelemetry.sdk._logs import LoggerProvider as OTLoggerProvider -from opentelemetry.sdk._logs.export import InMemoryLogExporter, LogRecordExportResult, SimpleLogRecordProcessor +from opentelemetry.sdk._logs.export import InMemoryLogExporter, LogExportResult, SimpleLogRecordProcessor from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import InMemoryMetricReader, MetricsData -from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import ReadableSpan, SpanLimits, TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor, SpanExportResult +from opentelemetry.sdk.trace.export import SimpleSpanProcessor, SpanExportResult from opentelemetry.sdk.util.instrumentation import InstrumentationScope from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter from parameterized import parameterized @@ -2323,13 +2321,9 @@ def test_otlp_http_log_export_trusts_ssl_cert_file(monkeypatch: pytest.MonkeyPat severity_number=SeverityNumber.INFO, body="tls-export-test", ) - log_data: Final = ReadableLogRecord( - log_record=record, - resource=Resource.create({}), - instrumentation_scope=InstrumentationScope("tls-export-test"), - ) + log_data: Final = LogData(log_record=record, instrumentation_scope=InstrumentationScope("tls-export-test")) result: Final = exporter.export([log_data]) - assert result is LogRecordExportResult.SUCCESS, f"log export failed: {result}" + assert result is LogExportResult.SUCCESS, f"log export failed: {result}" assert tls_sink.received.get(timeout=5) == "/v1/logs" finally: exporter.shutdown() @@ -6645,7 +6639,7 @@ class TestOpenTelemetryProviderlessCallAttributes(unittest.TestCase): time.sleep(self.POLL_INTERVAL) return None - def _emitted_log_records(self, semconv_opt_in: str) -> tuple[ReadableLogRecord, ...]: + def _emitted_log_records(self, semconv_opt_in: str) -> tuple[LogData, ...]: log_exporter = InMemoryLogExporter() logger_provider = OTLoggerProvider() logger_provider.add_log_record_processor(SimpleLogRecordProcessor(log_exporter)) @@ -6719,19 +6713,7 @@ class TestDynamicTracerProviderCache(unittest.TestCase): must be bounded and must shut down whatever it drops (LIT-5437: threads accumulated until pods were OOMKilled).""" - BSP_THREAD_NAME: ClassVar[str] - - @classmethod - def setUpClass(cls) -> None: - before: Final = frozenset(threading.enumerate()) - processor: Final = BatchSpanProcessor(InMemorySpanExporter()) - try: - workers: Final = tuple(thread for thread in threading.enumerate() if thread not in before) - assert len(workers) == 1 - assert workers[0].is_alive() - cls.BSP_THREAD_NAME = workers[0].name - finally: - processor.shutdown() + BSP_THREAD_NAME = "OtelBatchSpanProcessor" def _logger(self, cap=3, exporter="console"): logger = OpenTelemetry( @@ -6769,6 +6751,8 @@ class TestDynamicTracerProviderCache(unittest.TestCase): logger._get_tracer_with_dynamic_headers({"authorization": f"Basic tenant-{i}"}) self.assertEqual(len(logger._tracer_provider_cache), 3) + # Guards the thread-name constant: a rename upstream would make this read 0 and the + # bound assertion below would pass while measuring nothing. self.assertGreaterEqual(len(self._live_exporter_threads()), 1) self.assertLessEqual(self._wait_for_exporter_threads(before + 3) - before, 3) @@ -6939,8 +6923,8 @@ class TestOpenTelemetryDatabaseSemconvAttributes(unittest.TestCase): database's own metrics. """ - DSN = "postgresql://TEST_ONLY_USER:TEST_ONLY_PASSWORD@primary-db.example.invalid:6432/TEST_ONLY_DATABASE?schema=reporting" - REPLICA_DSN = "postgresql://TEST_ONLY_REPLICA_USER:TEST_ONLY_REPLICA_PASSWORD@replica-db.example.invalid/TEST_ONLY_DATABASE" + DSN = "postgresql://llmproxy:dbpassword9090@litellm-prod.abc123.us-east-1.rds.amazonaws.com:6432/litellm?schema=reporting" + REPLICA_DSN = "postgresql://reader:r3ad0nly@litellm-prod-ro.abc123.us-east-1.rds.amazonaws.com/litellm" def _service_span(self, service, call_type, dsn, error=None, replica_dsn=None): exporter = InMemorySpanExporter() @@ -6981,10 +6965,10 @@ class TestOpenTelemetryDatabaseSemconvAttributes(unittest.TestCase): self.assertEqual(span.attributes["db.operation.name"], "get_data") self.assertEqual( span.attributes["server.address"], - "primary-db.example.invalid", + "litellm-prod.abc123.us-east-1.rds.amazonaws.com", ) self.assertEqual(span.attributes["server.port"], 6432) - self.assertEqual(span.attributes["db.namespace"], "TEST_ONLY_DATABASE|reporting") + self.assertEqual(span.attributes["db.namespace"], "litellm|reporting") def test_datastore_span_is_a_client_span_carrying_the_legacy_db_system(self): """Datadog types a span as a database call from CLIENT kind plus @@ -7010,16 +6994,16 @@ class TestOpenTelemetryDatabaseSemconvAttributes(unittest.TestCase): self.assertEqual(span.kind, trace.SpanKind.CLIENT) self.assertEqual( span.attributes["server.address"], - "primary-db.example.invalid", + "litellm-prod.abc123.us-east-1.rds.amazonaws.com", ) self.assertEqual(span.attributes["error"], "connection refused") def test_no_credential_from_the_dsn_lands_on_the_span(self): span = self._service_span(ServiceTypes.DB, "get_data", self.DSN) exported = " ".join(str(value) for value in span.attributes.values()) - self.assertIn("primary-db.example.invalid", exported) - self.assertNotIn("TEST_ONLY_PASSWORD", exported) - self.assertNotIn("TEST_ONLY_USER", exported) + self.assertIn("litellm-prod.abc123.us-east-1.rds.amazonaws.com", exported) + self.assertNotIn("dbpassword9090", exported) + self.assertNotIn("llmproxy", exported) def test_redis_span_does_not_borrow_the_postgres_endpoint(self): span = self._service_span(ServiceTypes.REDIS, "async_set_cache", self.DSN) diff --git a/tests/unit/litellm_core_utils/test_litellm_logging.py b/tests/unit/litellm_core_utils/test_litellm_logging.py index 5b67250ad00..bbfae2229ce 100644 --- a/tests/unit/litellm_core_utils/test_litellm_logging.py +++ b/tests/unit/litellm_core_utils/test_litellm_logging.py @@ -2043,7 +2043,7 @@ async def test_logfire_logger_accepts_env_vars_for_base_url(monkeypatch): ], ) def test_langtrace_callback_exports_to_api_trace_with_x_api_key( - monkeypatch: pytest.MonkeyPatch, api_host: str | None, expected_endpoint: str, requests_mock + monkeypatch: pytest.MonkeyPatch, api_host: str | None, expected_endpoint: str ) -> None: """The exporter must post to Langtrace's complete /api/trace path with the key in x-api-key, without leaking it into the process-wide OTEL_EXPORTER_OTLP_TRACES_HEADERS.""" @@ -2069,12 +2069,8 @@ def test_langtrace_callback_exports_to_api_trace_with_x_api_key( assert type(logger) is OpenTelemetry and logger.callback_name == "langtrace" exporter: Final = logger._get_span_processor().span_exporter assert isinstance(exporter, OTLPSpanExporter) - from opentelemetry.sdk.trace.export import SpanExportResult - - collector: Final = requests_mock.post(expected_endpoint, status_code=200) - assert exporter.export([]) is SpanExportResult.SUCCESS - assert collector.call_count == 1 - assert collector.last_request.headers["x-api-key"] == api_key + assert exporter._endpoint == expected_endpoint + assert exporter._headers == {"x-api-key": api_key} assert "OTEL_EXPORTER_OTLP_TRACES_HEADERS" not in os.environ finally: logging_module._in_memory_loggers.clear() @@ -8023,14 +8019,14 @@ def test_payload_without_guardrail_cost_is_unchanged(logging_obj): assert payload["cost_breakdown"] is None -_AWS_SECRET = "TEST_ONLY_AWS_SECRET_NOT_A_CREDENTIAL" -_GEMINI_KEY = "TEST_ONLY_GEMINI_KEY_NOT_A_CREDENTIAL" +_AWS_SECRET = "wJalrXUtnFEMIK7MDENGbPxRfiCYEXAMPLEKEY" +_GEMINI_KEY = "AIzaSyC0000000000000000000000000000000" def test_empty_api_base_does_not_dump_call_state(logging_obj): """Direct (non-HTTP) providers pass api_base='', which used to echo model_call_details.""" logging_obj.model_call_details["litellm_params"] = { - "api_key": "sk-TEST_ONLY_API_KEY_NOT_A_CREDENTIAL", + "api_key": "sk-proj-hunter2hunter2hunter2hunter2", "aws_secret_access_key": _AWS_SECRET, } @@ -8043,7 +8039,7 @@ def test_empty_api_base_does_not_dump_call_state(logging_obj): assert "litellm_call_id" not in curl_command assert _AWS_SECRET not in curl_command - assert "sk-TEST_ONLY_API_KEY_NOT_A_CREDENTIAL" not in curl_command + assert "hunter2" not in curl_command def test_pre_call_redacts_and_masks_raw_request(logging_obj): diff --git a/tests/unit/proxy/enterprise_billing/test_billing_metrics.py b/tests/unit/proxy/enterprise_billing/test_billing_metrics.py index 06f4e09697c..0446cfeeab0 100644 --- a/tests/unit/proxy/enterprise_billing/test_billing_metrics.py +++ b/tests/unit/proxy/enterprise_billing/test_billing_metrics.py @@ -12,7 +12,7 @@ import os import socket import stat from pathlib import Path -from typing import Dict, Final, List, Optional +from typing import Dict, List, Optional import pytest from opentelemetry.sdk.metrics import MeterProvider @@ -350,30 +350,19 @@ def _fake_exporter_class(captured: Dict[str, object]) -> type: return _FakeExporter -@pytest.mark.parametrize("requests_ca", ("REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE")) -def test_meter_provider_preserves_explicit_tls_settings(tmp_path, monkeypatch, requests_mock, requests_ca: str) -> None: - monkeypatch.delenv("REQUESTS_CA_BUNDLE", raising=False) - monkeypatch.delenv("CURL_CA_BUNDLE", raising=False) - monkeypatch.setenv(requests_ca, "/corporate/global-ca.pem") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE", "/operator/collector-ca.pem") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE", "/operator/client.pem") - monkeypatch.setenv("OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY", "/operator/client.key") - monkeypatch.setenv("HTTPS_PROXY", "http://proxy.example.invalid:8080") - captured: Final = requests_mock.post("https://collector.example:4317/v1/metrics", content=b"") - config: Final = _config(tmp_path) - provider: Final = bm.build_mtls_meter_provider(config) - try: - bm.BillingMetricsRecorder(provider).record( - category=BillableCategory.LLM, route="/chat/completions", status_code=200, model_id=None - ) - assert provider.force_flush() - assert captured.request_history - for request in captured.request_history: - assert request.verify == config.ca_cert_path - assert request.cert == (config.client_cert_path, config.client_key_path) - assert request.proxies["https"] == "http://proxy.example.invalid:8080" - finally: - provider.shutdown() +def test_meter_provider_wires_client_cert_into_http_exporter(tmp_path, monkeypatch): + """Client cert+key authenticate us at the collector's mTLS front end; CA override rides certificate_file.""" + captured: Dict[str, object] = {} + + monkeypatch.setattr(bm, "OTLPMetricExporter", _fake_exporter_class(captured)) + config = _config(tmp_path) + provider = bm.build_mtls_meter_provider(config) + provider.shutdown() + + assert captured["endpoint"] == "https://collector.example:4317/v1/metrics" + assert captured["client_certificate_file"] == config.client_cert_path + assert captured["client_key_file"] == config.client_key_path + assert captured["certificate_file"] == config.ca_cert_path def test_load_config_without_ca_is_valid(monkeypatch, tmp_path): diff --git a/ui/litellm-dashboard/package-lock.json b/ui/litellm-dashboard/package-lock.json index c6f45fd19f5..d6b98d9be80 100644 --- a/ui/litellm-dashboard/package-lock.json +++ b/ui/litellm-dashboard/package-lock.json @@ -29,7 +29,7 @@ "jwt-decode": "4.0.0", "lucide-react": "0.513.0", "moment": "2.31.0", - "next": "16.4.1", + "next": "16.3.8", "next-themes": "^0.4.6", "nuqs": "^2.9.4", "openai": "6.49.0", @@ -71,8 +71,8 @@ "@types/react-copy-to-clipboard": "5.0.7", "@types/react-dom": "19.2.4", "@types/react-syntax-highlighter": "15.5.13", - "@vitest/coverage-v8": "4.1.12", - "@vitest/ui": "4.1.12", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "eslint": "9.39.2", "eslint-config-next": "16.3.3", "eslint-config-prettier": "10.1.8", @@ -89,7 +89,7 @@ "typescript": "5.9.3", "typescript-eslint": "8.60.1", "vite": "7.3.7", - "vitest": "4.1.12" + "vitest": "4.1.11" }, "engines": { "node": ">=24.14.1", @@ -2155,9 +2155,9 @@ } }, "node_modules/@next/env": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.4.1.tgz", - "integrity": "sha512-5sUUrrSvs/2/2LnLoesybMGx12EtPB46fdZ6RhAw0XDGSPxU2LFyQBB7yuEiOFFJPKmBY6+q2jTr1/AMbnp0/g==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.8.tgz", + "integrity": "sha512-Al9zqHVV7TJv0eFuOU4U7Lvv74PTih4Ch63sk2xCIpSTkE3udFnaOcnzP2lQVymiL7yS9Cj2iClUXlR3EQ5sEw==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -2172,9 +2172,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.4.1.tgz", - "integrity": "sha512-U+hQ5Gj3fpzf4fveTIF3dpUFYhaSW8vHhdAnbiIomzXzg7boreYFgBqprepXFtW4sw+aTttiNmg0gew/iFNhbg==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.8.tgz", + "integrity": "sha512-2JPRMh2nmQG5CiL7cXGL9AGwnPWJQ//cTtAUCT+w511QHk79SYz3LGv/pc5X643B/WEO0rvu3Yww0hqwt3kgeA==", "cpu": [ "arm64" ], @@ -2188,9 +2188,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.4.1.tgz", - "integrity": "sha512-/hqC3V1XLUjhMASf/WridKdO9FXoEgN3HBWmtKbj00MPgnltutfraWJ3bYTpCRIgEIpbjJyOVLpYgAhh/BkdcQ==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.8.tgz", + "integrity": "sha512-GZtCCOBKJ4leVIT/Th0llWKhD1ca92lzbQiS5R5ON9QkoiFnilFsebDae1JU2a3HWoKMEmEZWGs1AGLavVM72Q==", "cpu": [ "x64" ], @@ -2204,12 +2204,15 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.4.1.tgz", - "integrity": "sha512-GHzncHbcaS9g95kcvTNtKQW3aCLoR+xc3Jm7qrkpk1R+D2BjMHIPyvQGRPKCdMX95BsH/421jBUK06q9tEwzjw==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.8.tgz", + "integrity": "sha512-O659ygeQYqneJ1fBKMpFxIFqYkYswu8IAS1OCKK/4f3ZgJJm1dRz4fVJZRi/kLLWjnBKnebOePA4WNv+sV1pVA==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2220,12 +2223,15 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.4.1.tgz", - "integrity": "sha512-1SuJyDxW/PR9dHJat/ZRgcYLhuSZ+1psP4DlKb2tKIuXbjygqpLrJWRSq+onZQBv8LcmU67EIrmb6c3XTaJLjw==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.8.tgz", + "integrity": "sha512-dSjKSyWpzxoO1d3DIZZcP4XJcNaKeLmxQMFOiYl5vuBRMmweIqnAhty8tAmRsvTss779cK1FtYnDMj40e4TQlg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2236,12 +2242,15 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.4.1.tgz", - "integrity": "sha512-Istng1hMGp9TGO4lUqeKLd70MPN/OOkucVTP2Nmj+gLkwBgmPqUw8CJYBCC7nbl/B/23bBCcjCI5Hsh6hazC1A==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.8.tgz", + "integrity": "sha512-lbqOuz3RPRcv+o9msNsJw5x4+Y1ZwPTs6vmL6DCf7i0fZfvng/F59wyeDwqHIvV0mK//RBy/jJkZ+nCKsSMXjQ==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2252,12 +2261,15 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.4.1.tgz", - "integrity": "sha512-aSC9wMNiZRRFVpPkPWBzRwLQxIGm/CiQcXyFF1XNcgodb+uMxdk7aSSGGpDXCbnvZdf4itjHxpOd5h7i7zaiYw==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.8.tgz", + "integrity": "sha512-+316WswI8ScVgZeUd+1KGaXkHhaYQzCjvH/05TZSpJ8zBizb1a4G7DtO7F12jcBIqMOtsz9ji1t48fmKtzqsGA==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2268,9 +2280,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.4.1.tgz", - "integrity": "sha512-t/myvwFDlypMUjRvbBf8qLPyuaB0iXYYdBtwMt6DM6f+60wtGr4bSYholdPqNeV5VUXZhrpMwfza829kmhS3iA==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.8.tgz", + "integrity": "sha512-ji0gd4kMYUxO+1fJBIbiBVRCjzG/lloiyCccnlebvb1ZJ5qXCPZqYg4Jl1DrrixnWNMKylzgpmMWx0yNDYXlzw==", "cpu": [ "arm64" ], @@ -2284,9 +2296,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.4.1.tgz", - "integrity": "sha512-6ICqPo9awCkrtYNPhwkx1uU7rlFWrZRrIboDHS2LU+jv088UBYQtmJ25h6pCp27/+FZjF6BQ4Te4ErugyMeuBg==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.8.tgz", + "integrity": "sha512-WcTlaKt/TWkh5kUjdJcUmB1XgZ+1c6fz4Y9fDHL73YNSdGaUWjceeWrrlwF0nv19iABYWC4iAq1oX1w4Bn0vfg==", "cpu": [ "x64" ], @@ -4480,14 +4492,14 @@ ] }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.12.tgz", - "integrity": "sha512-6S7/iVknKglLAxMFxQGNppBI0iqJS2NgM6FBxavitj7KaDq/vywOWGf6dGu7F2L1Qy0G6S3uTp7aGshHrzK8JA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz", + "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.12", + "@vitest/utils": "4.1.11", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -4501,8 +4513,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.12", - "vitest": "4.1.12" + "@vitest/browser": "4.1.11", + "vitest": "4.1.11" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -4511,16 +4523,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.12.tgz", - "integrity": "sha512-vFlTt+YEu9WBpNjdGayNmlMVzrWPi1+sv853/7t2FU7hpO5gAFBKHzQmkCq5Xjpr+evegF2994LQT3hF3mRlqQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.12", - "@vitest/utils": "4.1.12", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -4529,13 +4541,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.12.tgz", - "integrity": "sha512-fKShNpKZzupWZuY0z7egydvL0sqW4INEZBEExMBMAa6izlENEVHwoXgdaTCaBZ5fwlr4xf2o1wXC83n9RpO+NA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.12", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -4556,9 +4568,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.12.tgz", - "integrity": "sha512-XW9O8+Mxx4VpCtkYdMJLZQQwsVryP9wk7cPKDxtXFfwSak6yRh5+2fep/Y0XF/ElBvJHq8bcYdi6aeuai0opZA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -4569,13 +4581,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.12.tgz", - "integrity": "sha512-NKlVKCr5QnCe8jijdMnmkpHHU+WTqiLFR6G0pHji9uRjXK9xJl1k1nTw6F3pOOj2PDcsDt2GKQMI7wIsaT491w==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.12", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -4583,14 +4595,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.12.tgz", - "integrity": "sha512-ko1y1Qgb/slzQ9mr+b89Cuqud8WG1TKtsPD8r7ZUBdvVOGt85Af2HwCqHujrhwJq95hs/9RM7fcAPg57o1vlzw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.12", - "@vitest/utils": "4.1.12", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -4599,9 +4611,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.12.tgz", - "integrity": "sha512-RGq9x4JNGNTZHEJxvN0oSiJjeSCExm0n2gZKKJZHY9cX5AVA6M7VJILBT3ZJ/tJ/0J9uWT7fETV9/btoi//vSg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -4609,13 +4621,13 @@ } }, "node_modules/@vitest/ui": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.12.tgz", - "integrity": "sha512-eYUTHtsp52Fu6YGUw9T9173bba7YUr9j/Xj9CdhUaOb8AppLKcknHUaiZQeqJ8MiJ8C2VyKiZuUGSKdvPIKQKA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.11.tgz", + "integrity": "sha512-r/rwyKoev21mWdRGSEkZOqkQ2BYy68mwjihg9M90nNRbf4NGrgzZ4cj6JNCEwlOGJkbKeMgsjlykvwKUbRr7gw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.12", + "@vitest/utils": "4.1.11", "fflate": "^0.8.2", "flatted": "^3.4.2", "pathe": "^2.0.3", @@ -4627,17 +4639,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.12" + "vitest": "4.1.11" } }, "node_modules/@vitest/utils": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.12.tgz", - "integrity": "sha512-Q3awXrwOfEV+qc3WmO5ONyE0yO7Q1gQ9/oyHxGLZ/pl7N3NO/ZztqQwKLuwMkV+CA1JYSIeO3gkHnRDIo4AVaA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.12", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -9737,12 +9749,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.4.1.tgz", - "integrity": "sha512-uc657V62E34fOI6W5NtG9IzwsPcms0Z60grz2MJGtglgD+pTFnNt4Yw2PzaU622tfX4vhrDgLZzicE2YlhI20Q==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.8.tgz", + "integrity": "sha512-U7QEZaTini6wKrb8A8hqLLqYQyCetegKjCpJOyxk642vWoMoU1x5PyZCJFvgYgiptA8xc5j/9xYlZFO7w9Sjmw==", "license": "MIT", "dependencies": { - "@next/env": "16.4.1", + "@next/env": "16.3.8", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -9756,15 +9768,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.4.1", - "@next/swc-darwin-x64": "16.4.1", - "@next/swc-linux-arm64-gnu": "16.4.1", - "@next/swc-linux-arm64-musl": "16.4.1", - "@next/swc-linux-x64-gnu": "16.4.1", - "@next/swc-linux-x64-musl": "16.4.1", - "@next/swc-win32-arm64-msvc": "16.4.1", - "@next/swc-win32-x64-msvc": "16.4.1", - "sharp": "^0.35.5" + "@next/swc-darwin-arm64": "16.3.8", + "@next/swc-darwin-x64": "16.3.8", + "@next/swc-linux-arm64-gnu": "16.3.8", + "@next/swc-linux-arm64-musl": "16.3.8", + "@next/swc-linux-x64-gnu": "16.3.8", + "@next/swc-linux-x64-musl": "16.3.8", + "@next/swc-win32-arm64-msvc": "16.3.8", + "@next/swc-win32-x64-msvc": "16.3.8", + "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -12566,19 +12578,19 @@ } }, "node_modules/vitest": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.12.tgz", - "integrity": "sha512-5eWdz2yb3ajxWF3C8C4TjkXl7FGm3n2E/VlazALrQHzK7nYMSGFnCZto0bbPNl77AwkOZVLk0YnBAejUu0NiWA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.12", - "@vitest/mocker": "4.1.12", - "@vitest/pretty-format": "4.1.12", - "@vitest/runner": "4.1.12", - "@vitest/snapshot": "4.1.12", - "@vitest/spy": "4.1.12", - "@vitest/utils": "4.1.12", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -12606,12 +12618,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.12", - "@vitest/browser-preview": "4.1.12", - "@vitest/browser-webdriverio": "^4.1.11", - "@vitest/coverage-istanbul": "4.1.12", - "@vitest/coverage-v8": "4.1.12", - "@vitest/ui": "4.1.12", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/ui/litellm-dashboard/package.json b/ui/litellm-dashboard/package.json index 432e327ec5a..8d1b7752b25 100644 --- a/ui/litellm-dashboard/package.json +++ b/ui/litellm-dashboard/package.json @@ -46,7 +46,7 @@ "jwt-decode": "4.0.0", "lucide-react": "0.513.0", "moment": "2.31.0", - "next": "16.4.1", + "next": "16.3.8", "next-themes": "^0.4.6", "nuqs": "^2.9.4", "openai": "6.49.0", @@ -88,8 +88,8 @@ "@types/react-copy-to-clipboard": "5.0.7", "@types/react-dom": "19.2.4", "@types/react-syntax-highlighter": "15.5.13", - "@vitest/coverage-v8": "4.1.12", - "@vitest/ui": "4.1.12", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "eslint": "9.39.2", "eslint-config-next": "16.3.3", "eslint-config-prettier": "10.1.8", @@ -106,7 +106,7 @@ "typescript": "5.9.3", "typescript-eslint": "8.60.1", "vite": "7.3.7", - "vitest": "4.1.12" + "vitest": "4.1.11" }, "overrides": { "prismjs": "1.30.0", @@ -119,10 +119,7 @@ "axios": "1.13.6", "postcss": "8.5.23", "esbuild": "0.28.1", - "sharp": "^0.35.5", - "@litellm/lens-ui": { - "next": "$next" - } + "sharp": "^0.35.5" }, "engines": { "node": ">=24.14.1", diff --git a/uv.lock b/uv.lock index 17247935ebc..8d2a38c1806 100644 --- a/uv.lock +++ b/uv.lock @@ -2482,14 +2482,14 @@ wheels = [ [[package]] name = "gitpython" -version = "3.2.1" +version = "3.1.62" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/ff/fee293f1ec3e1b64db6eb270e00964f29c086d769201474cedb8d1608930/gitpython-3.2.1.tar.gz", hash = "sha256:0f1229ec5c057bdabdeeda8bda4f17bda679ab33dbccc00f24e6a480553c8613", size = 241688, upload-time = "2026-10-14T12:20:20.589Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/db/3ca813cbacb23ab6fe46ff38a9b5ef8e73e970c8051f2ce903aacafe0446/gitpython-3.1.62.tar.gz", hash = "sha256:1791de66309bc0c7cfca40bf8d2e3de7ca091cbf94e6051be1ad0722c61062af", size = 231728, upload-time = "2026-09-07T02:57:21.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/50/2a923c4dc02522ff9567f07c67ed1deca6d5fa9474f4708168b8088dfa37/gitpython-3.2.1-py3-none-any.whl", hash = "sha256:d66b0832aa8755c1a84892937282df0befb16c97e5a64aeebd68be9c5e323f53", size = 231548, upload-time = "2026-10-14T12:20:18.844Z" }, + { url = "https://files.pythonhosted.org/packages/d6/0b/29d7965215f8ef830a7ca1f42997fe13e5693d85e9edb18f938d063ef5f2/gitpython-3.1.62-py3-none-any.whl", hash = "sha256:7002251225e10e29d2e1f49e6532613fe5d5d9f0b6f1f02997a52b38fe56899e", size = 222753, upload-time = "2026-09-07T02:57:19.762Z" }, ] [[package]] @@ -2987,11 +2987,11 @@ wheels = [ [[package]] name = "graphql-core" -version = "3.2.13" +version = "3.2.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/5e/aa0d4e701b50db0bab71b125dd19ddb0f98c638d008d6f7e3d8ce9cbc92e/graphql_core-3.2.13.tar.gz", hash = "sha256:bb81dd266d4ab7b591bd976f1b23639d97776cb9ac1a896b4a93c271e11ed618", size = 577471, upload-time = "2026-09-27T12:22:57.136Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/c5/36aa96205c3ecbb3d34c7c24189e4553c7ca2ebc7e1dd07432339b980272/graphql_core-3.2.8.tar.gz", hash = "sha256:015457da5d996c924ddf57a43f4e959b0b94fb695b85ed4c29446e508ed65cf3", size = 513181, upload-time = "2026-03-05T19:55:37.332Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b4/b52a324b0b297ddc49b8ea31a51d39fae63132f694a267b9381d94e2e0af/graphql_core-3.2.13-py3-none-any.whl", hash = "sha256:b0eb04f2c31556b2310a77c8fb53c74e8b56570f8fea594d89c6ef7827dbb497", size = 268191, upload-time = "2026-09-27T12:22:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/cb887d9afc5dabd78feefe6ccbaf83ff423c206a7a1b7aeeac05120b2125/graphql_core-3.2.8-py3-none-any.whl", hash = "sha256:cbee07bee1b3ed5e531723685369039f32ff815ef60166686e0162f540f1520c", size = 207349, upload-time = "2026-03-05T19:55:35.911Z" }, ] [[package]] @@ -4727,7 +4727,6 @@ proxy-runtime = [ { name = "mangum" }, { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp" }, - { name = "opentelemetry-exporter-otlp-json-http" }, { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-sdk" }, { name = "prometheus-client" }, @@ -4811,7 +4810,6 @@ dev = [ { name = "openapi-core" }, { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp" }, - { name = "opentelemetry-exporter-otlp-json-http" }, { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-sdk" }, { name = "parameterized" }, @@ -4859,7 +4857,6 @@ proxy-dev = [ { name = "litellm-admin-mcp", marker = "python_full_version >= '3.12'" }, { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp" }, - { name = "opentelemetry-exporter-otlp-json-http" }, { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-sdk" }, { name = "prisma" }, @@ -4933,11 +4930,10 @@ requires-dist = [ { name = "numpydoc", marker = "extra == 'utils'", specifier = ">=1.8.0,<2.0" }, { name = "nvidia-riva-client", marker = "extra == 'stt-nvidia-riva'", specifier = ">=2.15.0" }, { name = "openai", specifier = ">=2.20.0,<4.0.0" }, - { name = "opentelemetry-api", marker = "extra == 'proxy-runtime'", specifier = "==1.45.1" }, - { name = "opentelemetry-exporter-otlp", marker = "extra == 'proxy-runtime'", specifier = "==1.45.1" }, - { name = "opentelemetry-exporter-otlp-json-http", marker = "extra == 'proxy-runtime'", specifier = "==0.66b1" }, - { name = "opentelemetry-instrumentation-fastapi", marker = "extra == 'proxy-runtime'", specifier = "==0.66b1" }, - { name = "opentelemetry-sdk", marker = "extra == 'proxy-runtime'", specifier = "==1.45.1" }, + { name = "opentelemetry-api", marker = "extra == 'proxy-runtime'", specifier = "==1.33.1" }, + { name = "opentelemetry-exporter-otlp", marker = "extra == 'proxy-runtime'", specifier = "==1.33.1" }, + { name = "opentelemetry-instrumentation-fastapi", marker = "extra == 'proxy-runtime'", specifier = "==0.54b1" }, + { name = "opentelemetry-sdk", marker = "extra == 'proxy-runtime'", specifier = "==1.33.1" }, { name = "orjson", marker = "extra == 'proxy'", specifier = ">=3.11.6,<4.0" }, { name = "packaging", specifier = ">=24.0" }, { name = "polars", marker = "extra == 'proxy'", specifier = ">=1.38.1,<2.0" }, @@ -4952,7 +4948,7 @@ requires-dist = [ { name = "pydantic-settings", specifier = ">=2.14.1,<3.0" }, { name = "pyjwt", marker = "extra == 'proxy'", specifier = ">=2.13.0,<3.0" }, { name = "pynacl", marker = "extra == 'proxy'", specifier = ">=1.6.2,<2.0" }, - { name = "pypdf", marker = "extra == 'proxy-runtime'", specifier = ">=6.21.1,<7.0" }, + { name = "pypdf", marker = "extra == 'proxy-runtime'", specifier = ">=6.16.1,<7.0" }, { name = "pyroscope-io", marker = "sys_platform != 'win32' and extra == 'proxy'", specifier = ">=0.8.16,<1.0" }, { name = "python-dotenv", specifier = ">=1.0.0,<2.0" }, { name = "python-multipart", marker = "extra == 'proxy'", specifier = ">=0.0.27,<1.0" }, @@ -5008,7 +5004,7 @@ ci = [ { name = "langchain-openai", specifier = "==1.1.14" }, { name = "langgraph", specifier = ">=1.2.4,<1.3.0" }, { name = "langgraph-prebuilt", specifier = ">=1.1.0,<1.3.0" }, - { name = "logfire", specifier = "==5.2.0" }, + { name = "logfire", specifier = "==4.6.0" }, { name = "lunary", marker = "python_full_version == '3.10.*'", specifier = "==1.4.36" }, { name = "lunary", marker = "python_full_version >= '3.11'", specifier = "==1.4.37" }, { name = "pillow", marker = "python_full_version == '3.10.*'", specifier = "==12.3.0" }, @@ -5035,11 +5031,10 @@ dev = [ { name = "mypy", specifier = "==1.20.1" }, { name = "numpy", specifier = ">=1.26.0,<3.0" }, { name = "openapi-core", specifier = "==0.22.0" }, - { name = "opentelemetry-api", specifier = "==1.45.1" }, - { name = "opentelemetry-exporter-otlp", specifier = "==1.45.1" }, - { name = "opentelemetry-exporter-otlp-json-http", specifier = "==0.66b1" }, - { name = "opentelemetry-instrumentation-fastapi", specifier = "==0.66b1" }, - { name = "opentelemetry-sdk", specifier = "==1.45.1" }, + { name = "opentelemetry-api", specifier = "==1.33.1" }, + { name = "opentelemetry-exporter-otlp", specifier = "==1.33.1" }, + { name = "opentelemetry-instrumentation-fastapi", specifier = "==0.54b1" }, + { name = "opentelemetry-sdk", specifier = "==1.33.1" }, { name = "parameterized", specifier = "==0.9.0" }, { name = "psycopg", specifier = "==3.3.3" }, { name = "psycopg-binary", specifier = "==3.3.3" }, @@ -5083,11 +5078,10 @@ proxy-dev = [ { name = "backports-zstd", marker = "python_full_version < '3.14'", specifier = "==1.8.0" }, { name = "hypercorn", specifier = "==0.17.3" }, { name = "litellm-admin-mcp", marker = "python_full_version >= '3.12'", url = "https://github.com/BerriAI/liteadmin-mcp/archive/d35ec9c19c117d4c50cc1eccf6ce1296aac25a1a.tar.gz" }, - { name = "opentelemetry-api", specifier = "==1.45.1" }, - { name = "opentelemetry-exporter-otlp", specifier = "==1.45.1" }, - { name = "opentelemetry-exporter-otlp-json-http", specifier = "==0.66b1" }, - { name = "opentelemetry-instrumentation-fastapi", specifier = "==0.66b1" }, - { name = "opentelemetry-sdk", specifier = "==1.45.1" }, + { name = "opentelemetry-api", specifier = "==1.33.1" }, + { name = "opentelemetry-exporter-otlp", specifier = "==1.33.1" }, + { name = "opentelemetry-instrumentation-fastapi", specifier = "==0.54b1" }, + { name = "opentelemetry-sdk", specifier = "==1.33.1" }, { name = "prisma", specifier = "==0.11.0" }, { name = "prometheus-client", specifier = "==0.20.0" }, { name = "sentry-sdk", specifier = "==2.21.0" }, @@ -5179,24 +5173,21 @@ wheels = [ [[package]] name = "logfire" -version = "5.2.0" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "executing" }, - { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, - { name = "opentelemetry-semantic-conventions" }, { name = "protobuf" }, - { name = "requests" }, { name = "rich" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/5a/e6cccc98d30358b8beee47c61c2a1ca66ac7a76a07de9ba333aa5219b9a6/logfire-5.2.0.tar.gz", hash = "sha256:5fb9ddda303759766e86f5704a1ae96f3a0072906d52e21fbaad2e3c3b03f823", size = 1404990, upload-time = "2026-10-13T17:36:30.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/fa/14cbd976e8ff77290c0c87f93c45029785537ffb84e2f5215416786cda11/logfire-4.6.0.tar.gz", hash = "sha256:974bc8f4efd3aa9df2d0c7b1d53b35dc4612ff5cee2be6c40dd65e5c26eab694", size = 533174, upload-time = "2025-09-10T14:56:42.168Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/cb/bfcd484c16eeac8cdeb2defe1a2ee39bb1b4b6e69324b85515e7818eb678/logfire-5.2.0-py3-none-any.whl", hash = "sha256:c73dd02cfe048f2254e803962aa992d7f53217d121f3488e797926c30442342c", size = 495128, upload-time = "2026-10-13T17:36:27.305Z" }, + { url = "https://files.pythonhosted.org/packages/04/9c/6384dc69601bfba19c1f5909a472b00cc4b5863f411ecf85be308beacf8f/logfire-4.6.0-py3-none-any.whl", hash = "sha256:d322844db7a2f1935976f2852a07e3753489ecc4e83fa288173b05a453bb1cbf", size = 219556, upload-time = "2025-09-10T14:56:37.278Z" }, ] [[package]] @@ -6315,144 +6306,81 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.45.1" +version = "1.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "deprecated" }, + { name = "importlib-metadata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/02/6e0ae9cc61bd3169d401077b507b3ebc344745171e1051ab430be012dcd9/opentelemetry_api-1.45.1.tar.gz", hash = "sha256:aa38ed19bcc084ba42782a73255b3582283eced7ad6dddbd6695189e69adfb75", size = 72804, upload-time = "2026-10-06T17:32:58.133Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8d/1f5a45fbcb9a7d87809d460f09dc3399e3fbd31d7f3e14888345e9d29951/opentelemetry_api-1.33.1.tar.gz", hash = "sha256:1c6055fc0a2d3f23a50c7e17e16ef75ad489345fd3df1f8b8af7c0bbf8a109e8", size = 65002, upload-time = "2025-05-16T18:52:41.146Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/41/f7dcf80b81ee8e71c1a2b59f14208bc723edbd89ed027a73b175abf6348e/opentelemetry_api-1.45.1-py3-none-any.whl", hash = "sha256:b31553efa588ae44bc306f863c785c5333a9ecc091248c6ee68b4b6c87fdedfb", size = 60256, upload-time = "2026-10-06T17:32:33.506Z" }, -] - -[[package]] -name = "opentelemetry-exporter-http-transport" -version = "0.66b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/62/0c/e3ebdb4b507f66afcc905e6885a4946969bd75b45988492643356fbbdc63/opentelemetry_exporter_http_transport-0.66b1.tar.gz", hash = "sha256:443080203bf52586ce0b2ad901e8951c61833eab1aa539ae6f1f16fe9e8e7952", size = 11693, upload-time = "2026-10-06T17:32:59.65Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/69/6af86ff66492b481c6a4c05dcfd68beb47ed8ba046440a26a2aac76b95c7/opentelemetry_exporter_http_transport-0.66b1-py3-none-any.whl", hash = "sha256:2f95404bdee7f9d2d529c7de56c7bd86d014d774d8fbf137810e0167f8a492bf", size = 12155, upload-time = "2026-10-06T17:32:35.454Z" }, -] - -[package.optional-dependencies] -requests = [ - { name = "requests" }, -] -urllib3 = [ - { name = "urllib3" }, + { url = "https://files.pythonhosted.org/packages/05/44/4c45a34def3506122ae61ad684139f0bbc4e00c39555d4f7e20e0e001c8a/opentelemetry_api-1.33.1-py3-none-any.whl", hash = "sha256:4db83ebcf7ea93e64637ec6ee6fabee45c5cbe4abd9cf3da95c43828ddb50b83", size = 65771, upload-time = "2025-05-16T18:52:17.419Z" }, ] [[package]] name = "opentelemetry-exporter-otlp" -version = "1.45.1" +version = "1.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-exporter-otlp-proto-grpc" }, { name = "opentelemetry-exporter-otlp-proto-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e3/6f/5a561048ea372894f22f58f70e8478e4cd97d9a4e2d6f014559584a29bc5/opentelemetry_exporter_otlp-1.45.1.tar.gz", hash = "sha256:d0ac35592e77663a9fabf2740b4818c57196b1b764e0c8449d0d7bb2c7b2bc67", size = 6094, upload-time = "2026-10-06T17:33:00.966Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/3f/c8ad4f1c3aaadcea2b0f1b4d7970e7b7898c145699769a789f3435143f69/opentelemetry_exporter_otlp-1.33.1.tar.gz", hash = "sha256:4d050311ea9486e3994575aa237e32932aad58330a31fba24fdba5c0d531cf04", size = 6189, upload-time = "2025-05-16T18:52:43.176Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/e1/68c28d7da4482ce882a640b2c61405a78284c0a3a17a4db125647189b0b7/opentelemetry_exporter_otlp-1.45.1-py3-none-any.whl", hash = "sha256:ef3910d32b36ccbaf62390189759bd43a8109885e2d50b738ed9c9b255bf5cb5", size = 6730, upload-time = "2026-10-06T17:32:37.236Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-common" -version = "0.66b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/19/41de712173f43057e4532d42ece7d0c6d4210d353e5752433cb14987643f/opentelemetry_exporter_otlp_common-0.66b1.tar.gz", hash = "sha256:6b1403487a2185ac1feb45fd5546fdf8630ce71c36bcefaadf51e2130e9e23f9", size = 14325, upload-time = "2026-10-06T17:33:01.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/39/8c23d67665c762aa51840fa06f86e902e8f6f1693bc8d7e3d98cd6e2f753/opentelemetry_exporter_otlp_common-0.66b1-py3-none-any.whl", hash = "sha256:00ff8592c3a7cb729ff3fdc7ffa12372c243bdf2163e80c180994d0c7bd83ee9", size = 12385, upload-time = "2026-10-06T17:32:38.177Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-json-common" -version = "0.66b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-proto-json" }, - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/27/fd51023a4916ad00ef0c970577e148fba36e3682716d15a1495f74f830fb/opentelemetry_exporter_otlp_json_common-0.66b1.tar.gz", hash = "sha256:d8bd656ee03be5d2f54b533e03e297792c5017501417d132c3c67eb2ac95b3de", size = 24519, upload-time = "2026-10-06T17:33:02.52Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/34/6e7a4794ff9995042c8372af5c7e4e5127aea10dfeec64402fbec93a6d43/opentelemetry_exporter_otlp_json_common-0.66b1-py3-none-any.whl", hash = "sha256:1ae23eb3dfcd756607367c1ff5df2b4120fe481a523e7868b26b61720c558875", size = 14826, upload-time = "2026-10-06T17:32:39.052Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-json-http" -version = "0.66b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-exporter-http-transport", extra = ["urllib3"] }, - { name = "opentelemetry-exporter-otlp-common" }, - { name = "opentelemetry-exporter-otlp-json-common" }, - { name = "opentelemetry-proto-json" }, - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/44/30/47c00f9329242b71724df87536613cb21596b41f294cdc37d8b216c5afaa/opentelemetry_exporter_otlp_json_http-0.66b1.tar.gz", hash = "sha256:940ca96fff3509ca06b3ce8733d4818ee022620aa9329cb7025c63e43c2d224d", size = 17583, upload-time = "2026-10-06T17:33:03.868Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/2d/469689a34b17ed83d4ddee480db8322246dcffeb95ef5fc5050bb8a16c8c/opentelemetry_exporter_otlp_json_http-0.66b1-py3-none-any.whl", hash = "sha256:4bd7607120fb7cc77aae88d5c7f629c40515d225b3ca5db293d20d091a248156", size = 14523, upload-time = "2026-10-06T17:32:40.837Z" }, + { url = "https://files.pythonhosted.org/packages/4d/32/b9add70dd4e845654fc9fcd1401a705477743880be6c3e62acb1ad0d8662/opentelemetry_exporter_otlp-1.33.1-py3-none-any.whl", hash = "sha256:9bcf1def35b880b55a49e31ebd63910edac14b294fd2ab884953c4deaff5b300", size = 7045, upload-time = "2025-05-16T18:52:21.022Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.45.1" +version = "1.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c1/8e/65e85e5137991a3c493b11682151d198638a5bc1dd4b4c5f67e013c57d7c/opentelemetry_exporter_otlp_proto_common-1.45.1.tar.gz", hash = "sha256:2e4adcc3a67bcf57804fc49514f0ef64974ca7590aa3491da389852b4a0628f6", size = 18873, upload-time = "2026-10-06T17:33:04.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/18/a1ec9dcb6713a48b4bdd10f1c1e4d5d2489d3912b80d2bcc059a9a842836/opentelemetry_exporter_otlp_proto_common-1.33.1.tar.gz", hash = "sha256:c57b3fa2d0595a21c4ed586f74f948d259d9949b58258f11edb398f246bec131", size = 20828, upload-time = "2025-05-16T18:52:43.795Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/aa/92f225d353904e7f70b8b3e3c1b02db0cf56f744c2e83c581dc372e78873/opentelemetry_exporter_otlp_proto_common-1.45.1-py3-none-any.whl", hash = "sha256:2f446183ae7047b036226f1d846c41a834b0e8755ad13b51a51dd38952eb466c", size = 15393, upload-time = "2026-10-06T17:32:41.911Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/9bcb17e2c29c1194a28e521b9d3f2ced09028934c3c52a8205884c94b2df/opentelemetry_exporter_otlp_proto_common-1.33.1-py3-none-any.whl", hash = "sha256:b81c1de1ad349785e601d02715b2d29d6818aed2c809c20219f3d1f20b038c36", size = 18839, upload-time = "2025-05-16T18:52:22.447Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.45.1" +version = "1.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "deprecated" }, { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-common" }, { name = "opentelemetry-exporter-otlp-proto-common" }, { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/00/a82af0be959dc58495740b169c6669a86e0811f6cd353a01eda34d255db3/opentelemetry_exporter_otlp_proto_grpc-1.45.1.tar.gz", hash = "sha256:3b3dcfbfdcb4e35149fcf309972282054b45228f5c10547d0095d6578510a9a0", size = 25997, upload-time = "2026-10-06T17:33:05.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/5f/75ef5a2a917bd0e6e7b83d3fb04c99236ee958f6352ba3019ea9109ae1a6/opentelemetry_exporter_otlp_proto_grpc-1.33.1.tar.gz", hash = "sha256:345696af8dc19785fac268c8063f3dc3d5e274c774b308c634f39d9c21955728", size = 22556, upload-time = "2025-05-16T18:52:44.76Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/46/2d1da202f1e17c81aae7efcf702898d524b46709e4d3e2bf1f7f8ca8fbc6/opentelemetry_exporter_otlp_proto_grpc-1.45.1-py3-none-any.whl", hash = "sha256:e42ecb789d2fc5d8145e3dadc3e2991c9f18cd166d7c7514e234702540274b76", size = 19492, upload-time = "2026-10-06T17:32:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ec/6047e230bb6d092c304511315b13893b1c9d9260044dd1228c9d48b6ae0e/opentelemetry_exporter_otlp_proto_grpc-1.33.1-py3-none-any.whl", hash = "sha256:7e8da32c7552b756e75b4f9e9c768a61eb47dee60b6550b37af541858d669ce1", size = 18591, upload-time = "2025-05-16T18:52:23.772Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.45.1" +version = "1.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "deprecated" }, { name = "googleapis-common-protos" }, { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-http-transport", extra = ["requests"] }, - { 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/1b/17/26487707ea4caa97b17e6e4b5fa72133a53512ffa2f5cf7a49ef284b29cb/opentelemetry_exporter_otlp_proto_http-1.45.1.tar.gz", hash = "sha256:45c218405ce3fd879596924b1874bf9a8f6880206d61065c5a912c8e5c297fb7", size = 28839, upload-time = "2026-10-06T17:33:05.713Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/48/e4314ac0ed2ad043c07693d08c9c4bf5633857f5b72f2fefc64fd2b114f6/opentelemetry_exporter_otlp_proto_http-1.33.1.tar.gz", hash = "sha256:46622d964a441acb46f463ebdc26929d9dec9efb2e54ef06acdc7305e8593c38", size = 15353, upload-time = "2025-05-16T18:52:45.522Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/1f/517eaa0187ba106a9da97160ce2add3a371812681dc440930b267f714e42/opentelemetry_exporter_otlp_proto_http-1.45.1-py3-none-any.whl", hash = "sha256:24a97cf3753c7fb52fad44a696e452ff371686339e2acf3309e2eda3d0230700", size = 22180, upload-time = "2026-10-06T17:32:43.946Z" }, + { url = "https://files.pythonhosted.org/packages/63/ba/5a4ad007588016fe37f8d36bf08f325fe684494cc1e88ca8fa064a4c8f57/opentelemetry_exporter_otlp_proto_http-1.33.1-py3-none-any.whl", hash = "sha256:ebd6c523b89a2ecba0549adb92537cc2bf647b4ee61afbbd5a4c6535aa3da7cf", size = 17733, upload-time = "2025-05-16T18:52:25.137Z" }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -6460,9 +6388,9 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/03/89e47ff8d52a4f83b343e6eb9ef1698ff45357216e5b6b2b21e0da5c5c7d/opentelemetry_instrumentation-0.66b1.tar.gz", hash = "sha256:e79a510f7d87c72d95e964ddb42193a0d9a75668c027d980eab032ea1322a5ce", size = 43308, upload-time = "2026-10-06T17:36:10.703Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fd/5756aea3fdc5651b572d8aef7d94d22a0a36e49c8b12fcb78cb905ba8896/opentelemetry_instrumentation-0.54b1.tar.gz", hash = "sha256:7658bf2ff914b02f246ec14779b66671508125c0e4227361e56b5ebf6cef0aec", size = 28436, upload-time = "2025-05-16T19:03:22.223Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/b2/d1413681ff43e13ac9860df27e1226d3199ab0b97b352ceea41abcc660a5/opentelemetry_instrumentation-0.66b1-py3-none-any.whl", hash = "sha256:4c4aa14dc9a24a02325a9d4c42c4d0208dbb1374c2b1b8fe6c9392d59f3e1008", size = 36904, upload-time = "2026-10-06T17:35:11.663Z" }, + { url = "https://files.pythonhosted.org/packages/f4/89/0790abc5d9c4fc74bd3e03cb87afe2c820b1d1a112a723c1163ef32453ee/opentelemetry_instrumentation-0.54b1-py3-none-any.whl", hash = "sha256:a4ae45f4a90c78d7006c51524f57cd5aa1231aef031eae905ee34d5423f5b198", size = 31019, upload-time = "2025-05-16T19:02:15.611Z" }, ] [[package]] @@ -6497,7 +6425,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-asgi" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, @@ -6506,9 +6434,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/d9/ff522f5c3e340e9007554923b1a4d2ac451676f8757bafb3d0057f68b5c3/opentelemetry_instrumentation_asgi-0.66b1.tar.gz", hash = "sha256:78cdc5e45e897e16a8dac9d282e8d5bdf9af2d58e1313fa0bdd4a134c6f9dafc", size = 25992, upload-time = "2026-10-06T17:36:14.593Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/f7/a3377f9771947f4d3d59c96841d3909274f446c030dbe8e4af871695ddee/opentelemetry_instrumentation_asgi-0.54b1.tar.gz", hash = "sha256:ab4df9776b5f6d56a78413c2e8bbe44c90694c67c844a1297865dc1bd926ed3c", size = 24230, upload-time = "2025-05-16T19:03:30.234Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/ea/10ba99110bf3c9fb736af39c96ca8f3668b988cabb6b59309e058c44461c/opentelemetry_instrumentation_asgi-0.66b1-py3-none-any.whl", hash = "sha256:78b3f9bdf0fa38c65935a2ab46d59e0f9de873a51e0c95b0329f106e2ccb5274", size = 15812, upload-time = "2026-10-06T17:35:17.638Z" }, + { url = "https://files.pythonhosted.org/packages/20/24/7a6f0ae79cae49927f528ecee2db55a5bddd87b550e310ce03451eae7491/opentelemetry_instrumentation_asgi-0.54b1-py3-none-any.whl", hash = "sha256:84674e822b89af563b283a5283c2ebb9ed585d1b80a1c27fb3ac20b562e9f9fc", size = 16338, upload-time = "2025-05-16T19:02:22.808Z" }, ] [[package]] @@ -6559,7 +6487,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-fastapi" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -6568,9 +6496,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5d/2a/cd4125b7acbea2ed17f1d31b58c184cb0a79fcb5541ceb4de90ffc6d8c01/opentelemetry_instrumentation_fastapi-0.66b1.tar.gz", hash = "sha256:584cf9d2c4417ff8b2d6ff2bc606bfe13c8b3456018bf94f50f2cf658492505b", size = 26634, upload-time = "2026-10-06T17:36:25.157Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/3b/9a262cdc1a4defef0e52afebdde3e8add658cc6f922e39e9dcee0da98349/opentelemetry_instrumentation_fastapi-0.54b1.tar.gz", hash = "sha256:1fcad19cef0db7092339b571a59e6f3045c9b58b7fd4670183f7addc459d78df", size = 19325, upload-time = "2025-05-16T19:03:45.359Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/70/676928d537978acc7bff2ac8657bd0836ba608ffc8f65455238f1fa2bd0f/opentelemetry_instrumentation_fastapi-0.66b1-py3-none-any.whl", hash = "sha256:97f8ac8fd7537517f9e6988bd0aca04bfa5aad564bcd46c245530739e2be72d1", size = 14216, upload-time = "2026-10-06T17:35:32.827Z" }, + { url = "https://files.pythonhosted.org/packages/df/9c/6b2b0f9d6c5dea7528ae0bf4e461dd765b0ae35f13919cd452970bb0d0b3/opentelemetry_instrumentation_fastapi-0.54b1-py3-none-any.whl", hash = "sha256:fb247781cfa75fd09d3d8713c65e4a02bd1e869b00e2c322cc516d4b5429860c", size = 12125, upload-time = "2025-05-16T19:02:41.172Z" }, ] [[package]] @@ -6666,16 +6594,15 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-logging" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, - { name = "opentelemetry-semantic-conventions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/ca/0fcf525f238f4ab9df3a6d0423fb057085ea39a3e79ac912fef3d5fa16b8/opentelemetry_instrumentation_logging-0.66b1.tar.gz", hash = "sha256:2250240ecb1fce36939f64eafd988832338684564fb4c7b401c9b9af0ee11d46", size = 20353, upload-time = "2026-10-06T17:36:29.546Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/5b/88ed39f22e8c6eb4f6192ab9a62adaa115579fcbcadb3f0241ee645eea56/opentelemetry_instrumentation_logging-0.54b1.tar.gz", hash = "sha256:893a3cbfda893b64ff71b81991894e2fd6a9267ba85bb6c251f51c0419fbe8fa", size = 9976, upload-time = "2025-05-16T19:03:49.976Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/7b/76bea1e80787848a9ac9882a598cbf1e761f16cebdb399d0e675221e34a0/opentelemetry_instrumentation_logging-0.66b1-py3-none-any.whl", hash = "sha256:f13761110c52eecae9d7debedb0f88b46acc248c20437e97c2a5b3a2b4f96a02", size = 15928, upload-time = "2026-10-06T17:35:39.915Z" }, + { url = "https://files.pythonhosted.org/packages/96/0c/b441fb30d860f25040eaed61e89d68f4d9ee31873159ed18cbc1b92eba56/opentelemetry_instrumentation_logging-0.54b1-py3-none-any.whl", hash = "sha256:01a4cec54348f13941707d857b850b0febf9d49f45d0fcf0673866e079d7357b", size = 12579, upload-time = "2025-05-16T19:02:49.039Z" }, ] [[package]] @@ -6801,7 +6728,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-requests" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -6809,9 +6736,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/62/dcca0b7a2008675056040c61b70d37b5a24439613c7822068908ad3255ae/opentelemetry_instrumentation_requests-0.66b1.tar.gz", hash = "sha256:28578f72e68e3a5be3226c618ac9570ed360ef1dd22d5d6e0721c6905a4ccc67", size = 17985, upload-time = "2026-10-06T17:36:37.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/45/116da84930d3dc2f5cdd876283ca96e9b96547bccee7eaa0bd01ce6bf046/opentelemetry_instrumentation_requests-0.54b1.tar.gz", hash = "sha256:3eca5d697c5564af04c6a1dd23b6a3ffbaf11e64887c6051655cee03998f4654", size = 15148, upload-time = "2025-05-16T19:04:00.488Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/01/98e9bb7fef12784cd19811e454cf25bce4d774fd3e46b3f5fd2bce237075/opentelemetry_instrumentation_requests-0.66b1-py3-none-any.whl", hash = "sha256:7ba17d984a2bd876b88bf0aafcca27b5e3ff4c1821e0fe07641e380a3cf3a3a2", size = 13320, upload-time = "2026-10-06T17:35:52.477Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b1/6e33d2c3d3cc9e3ae20a9a77625ec81a509a0e5d7fa87e09e7f879468990/opentelemetry_instrumentation_requests-0.54b1-py3-none-any.whl", hash = "sha256:a0c4cd5d946224f336d6bd73cdabdecc6f80d5c39208f84eb96eb15f16cd41a0", size = 12968, upload-time = "2025-05-16T19:03:03.131Z" }, ] [[package]] @@ -6831,7 +6758,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-sqlalchemy" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -6840,23 +6767,23 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/3e/d69fb08dacc4c248daedf7357732ddca7a0aaff7072a55e311d3da3ea51c/opentelemetry_instrumentation_sqlalchemy-0.66b1.tar.gz", hash = "sha256:a10043953fcba71911bf29a024f8cc337260c1ef0b4fc844b96cae0de0947baa", size = 18181, upload-time = "2026-10-06T17:36:38.111Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/33/78a25ae4233d42058bb0b363ba4fea7d7210e53c24e5e31f16d5cf6cf957/opentelemetry_instrumentation_sqlalchemy-0.54b1.tar.gz", hash = "sha256:97839acf1c9b96ded857fca57a09b86a56cf8d9eb6d706b7ceaee9352a460e03", size = 14620, upload-time = "2025-05-16T19:04:01.215Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/05/f8cff0c68a7f9ab8fab01904be5a49c5214435109c2a6b6d173a974c10d9/opentelemetry_instrumentation_sqlalchemy-0.66b1-py3-none-any.whl", hash = "sha256:aa30b10d880d7e91cf94b23a92ac85cec09ffddd8f0d40256d7c510e3dd33971", size = 14425, upload-time = "2026-10-06T17:35:53.436Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2b/1c954885815614ef5c1e8c7bbf57a5275e64cd6fb5946b65e17162a34037/opentelemetry_instrumentation_sqlalchemy-0.54b1-py3-none-any.whl", hash = "sha256:d2ca5edb4c7ecef120d51aad6793b7da1cc80207ccfd31c437ee18f098e7c4c4", size = 14169, upload-time = "2025-05-16T19:03:04.119Z" }, ] [[package]] name = "opentelemetry-instrumentation-threading" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/4f/5fa05266cf24cae93fc32d557d0d2d2f605f81cca1df90614231bbad3cd1/opentelemetry_instrumentation_threading-0.66b1.tar.gz", hash = "sha256:2c217359439d09ca4a4be524db368c05a3cc7f218b85e8086f22664db0a03538", size = 9031, upload-time = "2026-10-06T17:36:41.147Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/bd/561245292e7cc78ac7a0a75537873aea87440cb9493d41371421b3308c2b/opentelemetry_instrumentation_threading-0.54b1.tar.gz", hash = "sha256:3a081085b59675baf7bd93126a681903e6304a5f283df5eaecdd44bcb66df578", size = 8774, upload-time = "2025-05-16T19:04:04.482Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/da/1cd52b8be7a521958ccdb9a1c4e5ae743aa2be295ba21ec0b620dc87842a/opentelemetry_instrumentation_threading-0.66b1-py3-none-any.whl", hash = "sha256:94039c60603060d26524e31aed1181a5d153e43f58462d110dbeb4830687d7d7", size = 8493, upload-time = "2026-10-06T17:35:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/d87ec07d69546adaad525ba5d40d27324a45cba29097d9854a53d9af5047/opentelemetry_instrumentation_threading-0.54b1-py3-none-any.whl", hash = "sha256:bc229e6cd3f2b29fafe0a8dd3141f452e16fcb4906bca4fbf52609f99fb1eb42", size = 9314, upload-time = "2025-05-16T19:03:09.527Z" }, ] [[package]] @@ -6891,7 +6818,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-urllib3" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -6900,9 +6827,9 @@ dependencies = [ { name = "opentelemetry-util-http" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ab/2dd6b3a69df31ae12714bf24fcc2772956d7cb8f9082432c9e63a37e6930/opentelemetry_instrumentation_urllib3-0.66b1.tar.gz", hash = "sha256:31e84a2ccfa86ca1d4f1209ea66c1f709c335348d5c487477d3843172efcc408", size = 18826, upload-time = "2026-10-06T17:36:43.547Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/6f/76a46806cd21002cac1bfd087f5e4674b195ab31ab44c773ca534b6bb546/opentelemetry_instrumentation_urllib3-0.54b1.tar.gz", hash = "sha256:0d30ba3b230e4100cfadaad29174bf7bceac70e812e4f5204e681e4b55a74cd9", size = 15697, upload-time = "2025-05-16T19:04:07.709Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/7f/16f82bf359c28fb71ad4dc5ad386cce3483abbf1ce0ade087fe78c18daf3/opentelemetry_instrumentation_urllib3-0.66b1-py3-none-any.whl", hash = "sha256:e48b448fed4a127e54c584c37a443c91d80d34b9f9de324af9a00cead8b338b8", size = 13462, upload-time = "2026-10-06T17:36:02.242Z" }, + { url = "https://files.pythonhosted.org/packages/ff/7a/d75bec41edb6deaf1d2859bab66a84c8ba03e822e7eafdb245da205e53f6/opentelemetry_instrumentation_urllib3-0.54b1-py3-none-any.whl", hash = "sha256:e87958c297ddd36d30e1c9069f34a9690e845e4ccc2662dd80e99ed976d4c03e", size = 13123, upload-time = "2025-05-16T19:03:14.053Z" }, ] [[package]] @@ -6952,50 +6879,41 @@ wheels = [ [[package]] name = "opentelemetry-proto" -version = "1.45.1" +version = "1.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/7f/15f014fb195da6c2dbb6c71399b8e76824878718e94de6454038488eed28/opentelemetry_proto-1.45.1.tar.gz", hash = "sha256:79e0fb95e4616691a469439238aa9224d75779b3e108e895d1aa125ab29ca77c", size = 46488, upload-time = "2026-10-06T17:33:11.49Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/dc/791f3d60a1ad8235930de23eea735ae1084be1c6f96fdadf38710662a7e5/opentelemetry_proto-1.33.1.tar.gz", hash = "sha256:9627b0a5c90753bf3920c398908307063e4458b287bb890e5c1d6fa11ad50b68", size = 34363, upload-time = "2025-05-16T18:52:52.141Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/9a/42ec8180a769516ae757e893b69736826efceac7332553915b4528a91c6d/opentelemetry_proto-1.45.1-py3-none-any.whl", hash = "sha256:f38e2a8413053c180cd3d2637fbb279673ec2f6a6e09c995aafa2f452c52b46e", size = 72488, upload-time = "2026-10-06T17:32:53.057Z" }, -] - -[[package]] -name = "opentelemetry-proto-json" -version = "0.66b1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/25/a845cc4644123c6b29b2c21822341a55e7f1808703d4f557d24cbd841290/opentelemetry_proto_json-0.66b1.tar.gz", hash = "sha256:92bb70ff0f05815821e922ea628bfdf96f034b981747b0ba8e09db61e1a9cddd", size = 18708, upload-time = "2026-10-06T17:33:12.169Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/7d/bb6acc006c6c4042ef9f734f4901630a440992033f48b66a58fc8fd5e3f4/opentelemetry_proto_json-0.66b1-py3-none-any.whl", hash = "sha256:0d07f1922451cf3a8705bb46fa807f478a1164fbe9287bf7bd50e1ff43f75da3", size = 32061, upload-time = "2026-10-06T17:32:54.097Z" }, + { url = "https://files.pythonhosted.org/packages/c4/29/48609f4c875c2b6c80930073c82dd1cafd36b6782244c01394007b528960/opentelemetry_proto-1.33.1-py3-none-any.whl", hash = "sha256:243d285d9f29663fc7ea91a7171fcc1ccbbfff43b48df0774fd64a37d98eda70", size = 55854, upload-time = "2025-05-16T18:52:36.269Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.45.1" +version = "1.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/79/7392e21a1c8f0c61d90b223e31c7e48cb9d452e91a6b820ad24cca5f23c4/opentelemetry_sdk-1.45.1.tar.gz", hash = "sha256:63d24a6ca645019a631e6a51999c73e93adcac1196ca640b8ae78a7cc4762bf3", size = 218324, upload-time = "2026-10-06T17:33:13.26Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/12/909b98a7d9b110cce4b28d49b2e311797cffdce180371f35eba13a72dd00/opentelemetry_sdk-1.33.1.tar.gz", hash = "sha256:85b9fcf7c3d23506fbc9692fd210b8b025a1920535feec50bd54ce203d57a531", size = 161885, upload-time = "2025-05-16T18:52:52.832Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/87c42b4bd6dd297536f04cd9383d212ac557ecd49f2cbdcd46da1c9ef5c8/opentelemetry_sdk-1.45.1-py3-none-any.whl", hash = "sha256:c604c11dc429810812348989115fa44bd558772a3d7442afc43d024f2c250ca4", size = 140063, upload-time = "2026-10-06T17:32:55.04Z" }, + { url = "https://files.pythonhosted.org/packages/df/8e/ae2d0742041e0bd7fe0d2dcc5e7cce51dcf7d3961a26072d5b43cc8fa2a7/opentelemetry_sdk-1.33.1-py3-none-any.whl", hash = "sha256:19ea73d9a01be29cacaa5d6c8ce0adc0b7f7b4d58cc52f923e4413609f670112", size = 118950, upload-time = "2025-05-16T18:52:37.297Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "deprecated" }, { name = "opentelemetry-api" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/e4/dbbfb2a010c4db2224a5114638acede6fe563d33cc20fb1752cebcbe6298/opentelemetry_semantic_conventions-0.66b1.tar.gz", hash = "sha256:497ca63bf383723411e8eaf60c8779e9877633c936bb641080adab59d0eb6ec8", size = 150250, upload-time = "2026-10-06T17:33:14.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/2c/d7990fc1ffc82889d466e7cd680788ace44a26789809924813b164344393/opentelemetry_semantic_conventions-0.54b1.tar.gz", hash = "sha256:d1cecedae15d19bdaafca1e56b29a66aa286f50b5d08f036a145c7f3e9ef9cee", size = 118642, upload-time = "2025-05-16T18:52:53.962Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/14/67f8aa798857f8cf686f515bf93d9bb877ce952ddc8efae0fa25b45ce0d6/opentelemetry_semantic_conventions-0.66b1-py3-none-any.whl", hash = "sha256:d4cddeb4315490b35213f55e2bdc9ac54bb1e4d318927475bed62b35545e581b", size = 206279, upload-time = "2026-10-06T17:32:56.103Z" }, + { url = "https://files.pythonhosted.org/packages/0a/80/08b1698c52ff76d96ba440bf15edc2f4bc0a279868778928e947c1004bdd/opentelemetry_semantic_conventions-0.54b1-py3-none-any.whl", hash = "sha256:29dab644a7e435b58d3a3918b58c333c92686236b30f7891d5e51f02933ca60d", size = 194938, upload-time = "2025-05-16T18:52:38.796Z" }, ] [[package]] @@ -7009,11 +6927,11 @@ wheels = [ [[package]] name = "opentelemetry-util-http" -version = "0.66b1" +version = "0.54b1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/b5/df4b61da899f6ebdffdbdf0c8b0f3189ee57151694ccd5b7d50ee2906241/opentelemetry_util_http-0.66b1.tar.gz", hash = "sha256:047dea1a628031f857a5a32261dc0e955bc162d39993ed1cffb8f2cff5ba8a62", size = 11586, upload-time = "2026-10-06T17:36:46.572Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/9f/1d8a1d1f34b9f62f2b940b388bf07b8167a8067e70870055bd05db354e5c/opentelemetry_util_http-0.54b1.tar.gz", hash = "sha256:f0b66868c19fbaf9c9d4e11f4a7599fa15d5ea50b884967a26ccd9d72c7c9d15", size = 8044, upload-time = "2025-05-16T19:04:10.79Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/9b/c77ecaea79ba0de1a11e7f06a7f5eea7043ec23f1860dcf5f03536698e4c/opentelemetry_util_http-0.66b1-py3-none-any.whl", hash = "sha256:8f443d7abcaf29c4a07b373bbd31b5b39132c0ed3c27d015a59dc0323d5b1c58", size = 8387, upload-time = "2026-10-06T17:36:06.984Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ef/c5aa08abca6894792beed4c0405e85205b35b8e73d653571c9ff13a8e34e/opentelemetry_util_http-0.54b1-py3-none-any.whl", hash = "sha256:b1c91883f980344a1c3c486cffd47ae5c9c1dd7323f9cbe9fdb7cadb401c87c9", size = 7301, upload-time = "2025-05-16T19:03:18.18Z" }, ] [[package]] @@ -8239,14 +8157,14 @@ wheels = [ [[package]] name = "pypdf" -version = "6.21.1" +version = "6.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/e1/32a9c4dbd94618145f8bab5c623bdb0a528f23fc8719afd77ba0278fd377/pypdf-6.21.1.tar.gz", hash = "sha256:2d62c383acd6e0dff0a96322ba8b1135229343b084d73c6f6cfd947fd291afcb", size = 7088597, upload-time = "2026-10-17T10:29:52.801Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ac/63d71aaedb59acbcdef491e6ca6469165e3771c9c74358204818fd9bc5a6/pypdf-6.19.0.tar.gz", hash = "sha256:bbc43aca292369ccc6cbc8a921991ecf2538a3587ab5a116eff06c321d647155", size = 7033266, upload-time = "2026-09-16T09:32:05.946Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/e6/24c944e473c34e818cf1b98085796fcd780465ecfe17663f2bf8fee1958b/pypdf-6.21.1-py3-none-any.whl", hash = "sha256:553e2d42f5bb3bb2a785a7d3de8ef90bb7df74b5413eff5a7b4388007ea9254b", size = 405134, upload-time = "2026-10-17T10:29:50.687Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2c/c43c03eaf630435f023f1dc61ec4a4a78951ad5530a62c71cc89bde307b7/pypdf-6.19.0-py3-none-any.whl", hash = "sha256:7e5d6e730e7dae87d560a2cee218b852f6498c8be61966f3cd02ead971e48d14", size = 395480, upload-time = "2026-09-16T09:32:04.087Z" }, ] [[package]] diff --git a/vscode-extension/package-lock.json b/vscode-extension/package-lock.json index 81e9425ef4e..536d7a88f25 100644 --- a/vscode-extension/package-lock.json +++ b/vscode-extension/package-lock.json @@ -17,7 +17,7 @@ "@vscode/vsce": "^4.0.0", "esbuild": "^0.28.2", "typescript": "^5.9.3", - "vitest": "^4.1.12" + "vitest": "^4.1.11" }, "engines": { "vscode": "^1.115.0" @@ -1323,16 +1323,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.12.tgz", - "integrity": "sha512-vFlTt+YEu9WBpNjdGayNmlMVzrWPi1+sv853/7t2FU7hpO5gAFBKHzQmkCq5Xjpr+evegF2994LQT3hF3mRlqQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.12", - "@vitest/utils": "4.1.12", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -1341,13 +1341,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.12.tgz", - "integrity": "sha512-fKShNpKZzupWZuY0z7egydvL0sqW4INEZBEExMBMAa6izlENEVHwoXgdaTCaBZ5fwlr4xf2o1wXC83n9RpO+NA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.12", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1368,9 +1368,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.12.tgz", - "integrity": "sha512-XW9O8+Mxx4VpCtkYdMJLZQQwsVryP9wk7cPKDxtXFfwSak6yRh5+2fep/Y0XF/ElBvJHq8bcYdi6aeuai0opZA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -1381,13 +1381,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.12.tgz", - "integrity": "sha512-NKlVKCr5QnCe8jijdMnmkpHHU+WTqiLFR6G0pHji9uRjXK9xJl1k1nTw6F3pOOj2PDcsDt2GKQMI7wIsaT491w==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.12", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -1395,14 +1395,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.12.tgz", - "integrity": "sha512-ko1y1Qgb/slzQ9mr+b89Cuqud8WG1TKtsPD8r7ZUBdvVOGt85Af2HwCqHujrhwJq95hs/9RM7fcAPg57o1vlzw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.12", - "@vitest/utils": "4.1.12", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1411,9 +1411,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.12.tgz", - "integrity": "sha512-RGq9x4JNGNTZHEJxvN0oSiJjeSCExm0n2gZKKJZHY9cX5AVA6M7VJILBT3ZJ/tJ/0J9uWT7fETV9/btoi//vSg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -1421,13 +1421,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.12.tgz", - "integrity": "sha512-Q3awXrwOfEV+qc3WmO5ONyE0yO7Q1gQ9/oyHxGLZ/pl7N3NO/ZztqQwKLuwMkV+CA1JYSIeO3gkHnRDIo4AVaA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.12", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -3390,19 +3390,19 @@ } }, "node_modules/vitest": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.12.tgz", - "integrity": "sha512-5eWdz2yb3ajxWF3C8C4TjkXl7FGm3n2E/VlazALrQHzK7nYMSGFnCZto0bbPNl77AwkOZVLk0YnBAejUu0NiWA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.12", - "@vitest/mocker": "4.1.12", - "@vitest/pretty-format": "4.1.12", - "@vitest/runner": "4.1.12", - "@vitest/snapshot": "4.1.12", - "@vitest/spy": "4.1.12", - "@vitest/utils": "4.1.12", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -3430,12 +3430,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.12", - "@vitest/browser-preview": "4.1.12", - "@vitest/browser-webdriverio": "^4.1.11", - "@vitest/coverage-istanbul": "4.1.12", - "@vitest/coverage-v8": "4.1.12", - "@vitest/ui": "4.1.12", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/vscode-extension/package.json b/vscode-extension/package.json index a3cf2dc7a5f..431dad316a1 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -82,6 +82,6 @@ "@vscode/vsce": "^4.0.0", "esbuild": "^0.28.2", "typescript": "^5.9.3", - "vitest": "^4.1.12" + "vitest": "^4.1.11" } }