revert: separate dependency fixes from Chinese localization

Revert merge 3a3bc917fb relative to its first parent.

Restore the exact premerge Chinese localization tree from 9baf20e312. Preserve the reviewed security and dependency fixes on their separate branch for an independent PR.
This commit is contained in:
J_Sinclair 2026-10-23 18:00:35 +08:00
parent 3a3bc917fb
commit 490e1baad1
24 changed files with 511 additions and 1094 deletions

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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,
)

View file

@ -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)
)

View file

@ -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]:

View file

@ -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)))

View file

@ -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",

View file

@ -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)

View file

@ -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<token>``. 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()

View file

@ -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"

View file

@ -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"

View file

@ -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):

View file

@ -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:

View file

@ -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)

View file

@ -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):

View file

@ -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):

View file

@ -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"

View file

@ -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",

246
uv.lock generated
View file

@ -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]]

View file

@ -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"

View file

@ -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"
}
}