mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-30 01:52:18 +00:00
fix(otel v2): one span scope control per backend in the admin UI
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
c6b3fccc6f
commit
f3555a72be
7 changed files with 21 additions and 43 deletions
|
|
@ -21,11 +21,7 @@
|
|||
"type": "select",
|
||||
"ui_name": "Span Scope",
|
||||
"description": "full sends the whole request trace, no_internal drops the proxy's own auth, cache and database spans, llm_only sends just the model-call spans",
|
||||
"options": [
|
||||
"full",
|
||||
"no_internal",
|
||||
"llm_only"
|
||||
],
|
||||
"options": ["full", "no_internal", "llm_only"],
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
|
|
@ -275,22 +271,7 @@
|
|||
"type": "select",
|
||||
"ui_name": "Span Scope",
|
||||
"description": "full sends the whole request trace, no_internal drops the proxy's own auth, cache and database spans, llm_only sends just the model-call spans",
|
||||
"options": [
|
||||
"full",
|
||||
"no_internal",
|
||||
"llm_only"
|
||||
],
|
||||
"required": false
|
||||
},
|
||||
"otel_span_scope": {
|
||||
"type": "select",
|
||||
"ui_name": "Span Scope",
|
||||
"description": "full sends the whole request trace, no_internal drops the proxy's own auth, cache and database spans, llm_only sends just the model-call spans",
|
||||
"options": [
|
||||
"full",
|
||||
"no_internal",
|
||||
"llm_only"
|
||||
],
|
||||
"options": ["full", "no_internal", "llm_only"],
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
|
|
@ -357,11 +338,7 @@
|
|||
"type": "select",
|
||||
"ui_name": "Span Scope",
|
||||
"description": "full sends the whole request trace, no_internal drops the proxy's own auth, cache and database spans, llm_only sends just the model-call spans",
|
||||
"options": [
|
||||
"full",
|
||||
"no_internal",
|
||||
"llm_only"
|
||||
],
|
||||
"options": ["full", "no_internal", "llm_only"],
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
|
|
@ -416,10 +393,7 @@
|
|||
"type": "select",
|
||||
"ui_name": "Export Protocol",
|
||||
"description": "OTLP wire format for trace exports. Use http/json for collectors that cannot decode protobuf",
|
||||
"options": [
|
||||
"http/protobuf",
|
||||
"http/json"
|
||||
],
|
||||
"options": ["http/protobuf", "http/json"],
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import os
|
||||
from collections.abc import Mapping
|
||||
from contextvars import ContextVar, Token
|
||||
from typing import TYPE_CHECKING, Final
|
||||
from typing import TYPE_CHECKING, Final, get_args
|
||||
|
||||
from opentelemetry import baggage
|
||||
from opentelemetry.context import Context, get_current
|
||||
|
|
@ -22,7 +22,7 @@ from opentelemetry.trace.propagation.tracecontext import (
|
|||
|
||||
from litellm.constants import OTEL_TENANT_SPAN_SCOPE_ENV
|
||||
from litellm.integrations.otel.model.semconv import HTTP
|
||||
from litellm.types.utils import OTEL_SPAN_SCOPES, OtelSpanScope
|
||||
from litellm.types.utils import OtelSpanScope
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.integrations.otel.model.destination import OtelDestination
|
||||
|
|
@ -402,13 +402,18 @@ def tenant_destinations_are_additive() -> bool:
|
|||
return isinstance(configured, str) and configured.strip().lower() == ADDITIVE_DESTINATION_MODE
|
||||
|
||||
|
||||
_SCOPES: Final[tuple[OtelSpanScope, ...]] = get_args(OtelSpanScope)
|
||||
|
||||
|
||||
def tenant_span_scope_default() -> OtelSpanScope:
|
||||
"""The ``span_scope`` a tenant destination gets when its own callback vars name none."""
|
||||
import litellm
|
||||
|
||||
configured: Final = litellm.otel_tenant_span_scope or os.environ.get(OTEL_TENANT_SPAN_SCOPE_ENV)
|
||||
if isinstance(configured, str) and configured.strip().lower() in OTEL_SPAN_SCOPES:
|
||||
return configured.strip().lower() # pyright: ignore[reportReturnType] # membership check narrows to the literal
|
||||
normalized: Final = configured.strip().lower() if isinstance(configured, str) else None
|
||||
for scope in _SCOPES:
|
||||
if scope == normalized:
|
||||
return scope
|
||||
return "full"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ async def add_team_callbacks(
|
|||
- langfuse_host: The host for the Langfuse callback
|
||||
- langfuse_environment: The tracing environment for the Langfuse callback (lowercase; falls back to LANGFUSE_TRACING_ENVIRONMENT)
|
||||
- langfuse_span_scope: For langfuse_otel, "full" (default) sends the whole request trace, "no_internal" holds back the proxy's own auth, cache and database spans, "llm_only" sends only the model-call spans
|
||||
- otel_span_scope: For langfuse_otel, arize, weave_otel and newrelic, the same three scopes; a langfuse_otel entry may set either name, and otel_span_scope wins when they disagree; defaults to litellm_settings.otel_tenant_span_scope
|
||||
- otel_span_scope: For langfuse_otel, arize, weave_otel and newrelic, the same three scopes; a langfuse_otel entry may set either name; different values on one entry are rejected; defaults to litellm_settings.otel_tenant_span_scope
|
||||
- gcs_bucket_name: The name of the GCS bucket
|
||||
- gcs_path_service_account: The path to the GCS service account
|
||||
- langsmith_api_key: The API key for the Langsmith callback
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
"tests/e2e/ui/tests/integrationCritical/projectDetachment.spec.ts::project creation and explicit detachment preserve saved scope and restore serving",
|
||||
"tests/e2e/ui/tests/integrationCritical/otelSpanScope.spec.ts::team logging settings persist otel_span_scope no_internal through the callback editor",
|
||||
"tests/e2e/ui/tests/integrationCritical/otelSpanScope.spec.ts::team logging settings persist langfuse_span_scope no_internal through the callback editor",
|
||||
"tests/e2e/ui/tests/integrationCritical/mcpUserEnvVars.spec.ts::per-user MCP env var stays updatable and clearable from the card after it is set",
|
||||
"tests/e2e/ui/tests/integrationCritical/mcpUserEnvVars.spec.ts::cancelling the clear confirmation keeps the stored value and sends no delete",
|
||||
"tests/e2e/ui/tests/integrationCritical/mcpUserEnvVars.spec.ts::pressing Enter on Update opens the credentials modal instead of the server editor",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Page } from "../../fixtures/pages";
|
|||
import { navigateToPage } from "../../helpers/navigation";
|
||||
import { captureRequestBody, readBack } from "../../helpers/roundTrip";
|
||||
|
||||
test("team logging settings persist otel_span_scope no_internal through the callback editor", async ({
|
||||
test("team logging settings persist langfuse_span_scope no_internal through the callback editor", async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
|
|
@ -40,7 +40,7 @@ test("team logging settings persist otel_span_scope no_internal through the call
|
|||
.getByRole("option", { name: /Langfuse OTEL/ })
|
||||
.click();
|
||||
const spanScope = page.getByRole("combobox", {
|
||||
name: "otel span scope",
|
||||
name: "langfuse span scope",
|
||||
});
|
||||
await spanScope.click();
|
||||
await expect(
|
||||
|
|
@ -61,7 +61,7 @@ test("team logging settings persist otel_span_scope no_internal through the call
|
|||
{
|
||||
callback_name: "langfuse_otel",
|
||||
callback_type: "success",
|
||||
callback_vars: { otel_span_scope: "no_internal" },
|
||||
callback_vars: { langfuse_span_scope: "no_internal" },
|
||||
},
|
||||
]);
|
||||
await expect(
|
||||
|
|
@ -71,7 +71,7 @@ test("team logging settings persist otel_span_scope no_internal through the call
|
|||
team_info: { metadata: { logging: any[] } };
|
||||
}>(page, `/team/info?team_id=${team.team_id}`);
|
||||
expect(
|
||||
info.team_info.metadata.logging[0].callback_vars.otel_span_scope,
|
||||
info.team_info.metadata.logging[0].callback_vars.langfuse_span_scope,
|
||||
).toBe("no_internal");
|
||||
} finally {
|
||||
await post("/team/delete", { team_ids: [team.team_id] });
|
||||
|
|
|
|||
|
|
@ -136,11 +136,9 @@ export const CALLBACK_CONFIGS: CallbackConfig[] = [
|
|||
langfuse_host: "text",
|
||||
langfuse_environment: "text",
|
||||
langfuse_span_scope: "select",
|
||||
otel_span_scope: "select",
|
||||
},
|
||||
dynamic_param_options: {
|
||||
langfuse_span_scope: ["full", "no_internal", "llm_only"],
|
||||
otel_span_scope: ["full", "no_internal", "llm_only"],
|
||||
},
|
||||
description: "Langfuse v3 OTEL Logging Integration",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ describe("LoggingSettings", () => {
|
|||
expect(mockOnChange).toHaveBeenCalledWith([expect.objectContaining({ callback_type: "failure" })]);
|
||||
});
|
||||
|
||||
it("offers the Langfuse OTEL span scope as a pick between full and llm_only rather than free text", async () => {
|
||||
it("offers the Langfuse OTEL span scope as a pick between full, no_internal and llm_only rather than free text", async () => {
|
||||
const user = userEvent.setup({ delay: null });
|
||||
const mockOnChange = vi.fn();
|
||||
const initialValue = [
|
||||
|
|
@ -230,6 +230,7 @@ describe("LoggingSettings", () => {
|
|||
renderWithProviders(<LoggingSettings value={initialValue} onChange={mockOnChange} />);
|
||||
|
||||
expect(screen.queryByPlaceholderText("os.environ/LANGFUSE_SPAN_SCOPE")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("combobox", { name: "otel span scope" })).not.toBeInTheDocument();
|
||||
await user.click(screen.getByRole("combobox", { name: "langfuse span scope" }));
|
||||
expect((await screen.findAllByRole("option")).map((option) => option.textContent)).toEqual([
|
||||
"full",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue