diff --git a/litellm/integrations/otel/presets/signoz.py b/litellm/integrations/otel/presets/signoz.py index 7008905481b..a2dcd3eb79e 100644 --- a/litellm/integrations/otel/presets/signoz.py +++ b/litellm/integrations/otel/presets/signoz.py @@ -1,5 +1,6 @@ """SigNoz preset — OTLP/HTTP exporter to SigNoz + GenAI vocabulary.""" +from types import MappingProxyType from typing import Final from pydantic import Field @@ -20,7 +21,7 @@ class _SigNozSettings(BaseSettings): model_config = SettingsConfigDict(case_sensitive=False, extra="ignore") # One endpoint for both SigNoz Cloud and self-hosted, with no default host, so - # nothing exports until the operator names a destination. + # nothing exports until the operator names a destination endpoint: str | None = Field(default=None, validation_alias=SIGNOZ_INGESTION_ENDPOINT_ENV) ingestion_key: str | None = Field(default=None, validation_alias="SIGNOZ_INGESTION_KEY") @@ -32,35 +33,36 @@ def signoz_preset( settings: Final = _SigNozSettings() base: Final = config_overrides or OpenTelemetryV2Config() key: Final = settings.ingestion_key + spec: Final = ExporterSpec( + kind="otlp_http", + endpoint=settings.endpoint, + headers=(f"signoz-ingestion-key={key}" if key else None), + owner=ExporterOwner.SIGNOZ, + # Cloud rejects keyless exports; a self-hosted collector accepts them + requires_headers=bool(key), + ) return base.model_copy( - update={ # mutable-ok: model_copy takes a dict of field updates - "exporters": [ # mutable-ok: matches the config's exporters list - *base.exporters, - ExporterSpec( - kind="otlp_http", - endpoint=settings.endpoint, - headers=(f"signoz-ingestion-key={key}" if key else None), - owner=ExporterOwner.SIGNOZ, - # Cloud rejects keyless exports; a self-hosted collector accepts them. - requires_headers=bool(key), - ), - ], - # SigNoz ingests the OTLP GenAI semantic conventions natively. - "mapper_names": ensure_mappers(base.mapper_names, "genai"), - } + update=MappingProxyType( + { + "exporters": (*base.exporters, spec), + "mapper_names": ensure_mappers(base.mapper_names, "genai"), + } + ) ) def signoz_dynamic_endpoint(params: StandardCallbackDynamicParams) -> str | None: """Per-request SigNoz endpoint from team/key dynamic params. - ``None`` keeps the operator's endpoint, so a team that saved only a key - still exports to the configured destination. + ``None`` keeps the operator's endpoint, so a team that saved only a key still + exports to the configured destination. """ return params.get("signoz_ingestion_endpoint") -def signoz_dynamic_headers(params: StandardCallbackDynamicParams) -> dict[str, str]: # mutable-ok: registry type +def signoz_dynamic_headers( + params: StandardCallbackDynamicParams, +) -> dict[str, str]: # mutable-ok: DYNAMIC_HEADERS_BY_CALLBACK returns a dict """Per-request SigNoz OTLP headers from team/key dynamic params.""" key: Final = params.get("signoz_ingestion_key") - return {header: value for header, value in (("signoz-ingestion-key", key),) if value} # mutable-ok: registry type + return {"signoz-ingestion-key": key} if key else {} # mutable-ok: same registry contract diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index c904cdb5ecb..57cef2b1566 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -3503,10 +3503,7 @@ class AllCallbacks(LiteLLMPydanticObjectBase): signoz: CallbackOnUI = CallbackOnUI( litellm_callback_name="signoz", ui_callback_name="SigNoz", - litellm_callback_params=[ - "SIGNOZ_INGESTION_ENDPOINT", - "SIGNOZ_INGESTION_KEY", - ], + litellm_callback_params=("SIGNOZ_INGESTION_ENDPOINT", "SIGNOZ_INGESTION_KEY"), ) diff --git a/litellm/types/utils.py b/litellm/types/utils.py index ee5d3e7c241..537da823e94 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -3307,7 +3307,7 @@ class StandardCallbackDynamicParams(TypedDict, total=False): # SigNoz dynamic params (proxy-stamped team/key callback vars only; # request-supplied values are blocked) - signoz_ingestion_endpoint: str | None # writable-ok: initialize_standard_callback_dynamic_params assigns into the dict + signoz_ingestion_endpoint: str | None # writable-ok: assigned by initialize_standard_callback_dynamic_params signoz_ingestion_key: str | None # writable-ok: initialize_standard_callback_dynamic_params assigns into the dict # Logging settings diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index d311bfa3cbc..7de3800c711 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -45388,7 +45388,7 @@ export interface operations { parameters: { query: { /** @description Specify the service being hit. */ - service: ("slack_budget_alerts" | "langfuse" | "langfuse_otel" | "slack" | "openmeter" | "webhook" | "email" | "braintrust" | "datadog" | "datadog_llm_observability" | "generic_api" | "arize" | "galileo" | "newrelic" | "sqs") | string; + service: ("slack_budget_alerts" | "langfuse" | "langfuse_otel" | "slack" | "openmeter" | "webhook" | "email" | "braintrust" | "datadog" | "datadog_llm_observability" | "generic_api" | "arize" | "galileo" | "newrelic" | "signoz" | "sqs") | string; }; header?: never; path?: never;