From 08f3b06d82d2ef9289bf912a759bf0224d7ed5be Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 7 Jul 2025 15:53:43 -0700 Subject: [PATCH] [Feat] Bump langfuse python SDK version and `LANGFUSE_TRACING_ENVIRONMENT` (#12376) * bump langfuse to 2.59.7 * _set_langfuse_specific_attributes * fix files * test_set_langfuse_environment_attribute --- .circleci/config.yml | 8 +++--- .../observability/langfuse_integration.md | 2 +- .../integrations/langfuse/langfuse_otel.py | 28 ++++++++++++++++++- litellm/types/integrations/langfuse_otel.py | 8 ++++-- requirements.txt | 2 +- .../integrations/test_langfuse_otel.py | 16 +++++++++++ 6 files changed, 55 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 92a26af32f..413415ae8a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -88,7 +88,7 @@ jobs: pip install langchain pip install lunary==0.2.5 pip install "azure-identity==1.16.1" - pip install "langfuse==2.45.0" + pip install "langfuse==2.59.7" pip install "logfire==0.29.0" pip install numpydoc pip install traceloop-sdk==0.21.1 @@ -211,7 +211,7 @@ jobs: pip install langchain pip install lunary==0.2.5 pip install "azure-identity==1.16.1" - pip install "langfuse==2.45.0" + pip install "langfuse==2.59.7" pip install "logfire==0.29.0" pip install numpydoc pip install traceloop-sdk==0.21.1 @@ -318,7 +318,7 @@ jobs: pip install langchain pip install lunary==0.2.5 pip install "azure-identity==1.16.1" - pip install "langfuse==2.45.0" + pip install "langfuse==2.59.7" pip install "logfire==0.29.0" pip install numpydoc pip install traceloop-sdk==0.21.1 @@ -591,7 +591,7 @@ jobs: pip install langchain pip install lunary==0.2.5 pip install "azure-identity==1.16.1" - pip install "langfuse==2.45.0" + pip install "langfuse==2.59.7" pip install "logfire==0.29.0" pip install numpydoc pip install traceloop-sdk==0.21.1 diff --git a/docs/my-website/docs/observability/langfuse_integration.md b/docs/my-website/docs/observability/langfuse_integration.md index b6d8c83652..c1cf6165b3 100644 --- a/docs/my-website/docs/observability/langfuse_integration.md +++ b/docs/my-website/docs/observability/langfuse_integration.md @@ -28,7 +28,7 @@ For Langfuse v3, we recommend using the [Langfuse OTEL](./langfuse_otel_integrat ### Pre-Requisites Ensure you have run `pip install langfuse` for this integration ```shell -pip install langfuse==2.45.0 litellm +pip install langfuse==2.59.7 litellm ``` ### Quick Start diff --git a/litellm/integrations/langfuse/langfuse_otel.py b/litellm/integrations/langfuse/langfuse_otel.py index 7fc222ff6c..f257da6d95 100644 --- a/litellm/integrations/langfuse/langfuse_otel.py +++ b/litellm/integrations/langfuse/langfuse_otel.py @@ -5,7 +5,10 @@ from urllib.parse import quote from litellm._logging import verbose_logger from litellm.integrations.arize import _utils -from litellm.types.integrations.langfuse_otel import LangfuseOtelConfig +from litellm.types.integrations.langfuse_otel import ( + LangfuseOtelConfig, + LangfuseSpanAttributes, +) if TYPE_CHECKING: from opentelemetry.trace import Span as _Span @@ -28,6 +31,7 @@ LANGFUSE_CLOUD_EU_ENDPOINT = "https://cloud.langfuse.com/api/public/otel" LANGFUSE_CLOUD_US_ENDPOINT = "https://us.cloud.langfuse.com/api/public/otel" + class LangfuseOtelLogger: @staticmethod def set_langfuse_otel_attributes(span: Span, kwargs, response_obj): @@ -36,7 +40,29 @@ class LangfuseOtelLogger: Uses the same attribute setting logic as Arize Phoenix for consistency. """ _utils.set_attributes(span, kwargs, response_obj) + + ######################################################### + # Set Langfuse specific attributes eg Langfuse Environment + ######################################################### + LangfuseOtelLogger._set_langfuse_specific_attributes( + span=span, + kwargs=kwargs + ) return + + @staticmethod + def _set_langfuse_specific_attributes(span: Span, kwargs): + """ + Sets Langfuse specific attributes to the span. + """ + from litellm.integrations.arize._utils import safe_set_attribute + langfuse_environment = os.environ.get("LANGFUSE_TRACING_ENVIRONMENT", None) + if langfuse_environment: + safe_set_attribute( + span=span, + key=LangfuseSpanAttributes.LANGFUSE_ENVIRONMENT.value, + value=langfuse_environment + ) @staticmethod def get_langfuse_otel_config() -> LangfuseOtelConfig: diff --git a/litellm/types/integrations/langfuse_otel.py b/litellm/types/integrations/langfuse_otel.py index 389b013501..d4b6130454 100644 --- a/litellm/types/integrations/langfuse_otel.py +++ b/litellm/types/integrations/langfuse_otel.py @@ -1,4 +1,5 @@ -from typing import TYPE_CHECKING, Literal, Optional, Any +from enum import Enum +from typing import TYPE_CHECKING, Any, Literal, Optional from pydantic import BaseModel @@ -10,4 +11,7 @@ else: class LangfuseOtelConfig(BaseModel): otlp_auth_headers: Optional[str] = None - protocol: Protocol = "otlp_http" \ No newline at end of file + protocol: Protocol = "otlp_http" + +class LangfuseSpanAttributes(Enum): + LANGFUSE_ENVIRONMENT = "langfuse.environment" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 391c627a95..df8d9c4242 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ anthropic[vertex]==0.54.0 mcp==1.9.3 # for MCP server google-generativeai==0.5.0 # for vertex ai calls async_generator==1.10.0 # for async ollama calls -langfuse==2.45.0 # for langfuse self-hosted logging +langfuse==2.59.7 # for langfuse self-hosted logging prometheus_client==0.20.0 # for /metrics endpoint on proxy ddtrace==2.19.0 # for advanced DD tracing / profiling orjson==3.10.12 # fast /embedding responses diff --git a/tests/test_litellm/integrations/test_langfuse_otel.py b/tests/test_litellm/integrations/test_langfuse_otel.py index 9712552916..cf33299f62 100644 --- a/tests/test_litellm/integrations/test_langfuse_otel.py +++ b/tests/test_litellm/integrations/test_langfuse_otel.py @@ -81,6 +81,22 @@ class TestLangfuseOtelIntegration: mock_set_attributes.assert_called_once_with(mock_span, mock_kwargs, mock_response) + def test_set_langfuse_environment_attribute(self): + """Test that Langfuse environment is set correctly when environment variable is present.""" + mock_span = MagicMock() + mock_kwargs = {"test": "kwargs"} + test_env = "staging" + + with patch.dict(os.environ, {'LANGFUSE_TRACING_ENVIRONMENT': test_env}): + with patch('litellm.integrations.arize._utils.safe_set_attribute') as mock_safe_set_attribute: + LangfuseOtelLogger._set_langfuse_specific_attributes(mock_span, mock_kwargs) + + mock_safe_set_attribute.assert_called_once_with( + span=mock_span, + key="langfuse.environment", + value=test_env + ) + if __name__ == "__main__": pytest.main([__file__]) \ No newline at end of file