From 637dd6680c6bffac0a0a8bb936b41fffc401d53e Mon Sep 17 00:00:00 2001 From: anakin87 Date: Mon, 12 Jan 2026 12:19:08 +0100 Subject: [PATCH] chore!: langfuse - drop Python 3.9 and use X|Y typing --- integrations/langfuse/pyproject.toml | 10 ++-------- .../connectors/langfuse/langfuse_connector.py | 16 +++++++-------- .../tracing/langfuse/tracer.py | 20 +++++++++---------- integrations/langfuse/tests/test_tracer.py | 3 +-- 4 files changed, 21 insertions(+), 28 deletions(-) diff --git a/integrations/langfuse/pyproject.toml b/integrations/langfuse/pyproject.toml index 3db5ecd79e..e4d4d7c65b 100644 --- a/integrations/langfuse/pyproject.toml +++ b/integrations/langfuse/pyproject.toml @@ -7,14 +7,13 @@ name = "langfuse-haystack" dynamic = ["version"] description = "Langfuse integration for Haystack" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = "Apache-2.0" keywords = [] authors = [{ name = "deepset GmbH", email = "info@deepset.ai" }] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -22,7 +21,7 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] -dependencies = ["haystack-ai>=2.17.1", "langfuse>=3.3.1, <4.0.0"] +dependencies = ["haystack-ai>=2.22.0", "langfuse>=3.3.1, <4.0.0"] [project.urls] Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse#readme" @@ -82,7 +81,6 @@ allow-direct-references = true [tool.ruff] -target-version = "py39" line-length = 120 [tool.ruff.lint] @@ -128,10 +126,6 @@ ignore = [ # Asserts "S101", ] -unfixable = [ - # Don't touch unused imports - "F401", -] [tool.ruff.lint.isort] known-first-party = ["haystack_integrations"] diff --git a/integrations/langfuse/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py b/integrations/langfuse/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py index 385c8e3ec3..08437899c1 100644 --- a/integrations/langfuse/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py +++ b/integrations/langfuse/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: Apache-2.0 -from typing import Any, Optional +from typing import Any import httpx from haystack import component, default_from_dict, default_to_dict, logging, tracing @@ -118,13 +118,13 @@ def __init__( self, name: str, public: bool = False, - public_key: Optional[Secret] = Secret.from_env_var("LANGFUSE_PUBLIC_KEY"), # noqa: B008 - secret_key: Optional[Secret] = Secret.from_env_var("LANGFUSE_SECRET_KEY"), # noqa: B008 - httpx_client: Optional[httpx.Client] = None, - span_handler: Optional[SpanHandler] = None, + public_key: Secret | None = Secret.from_env_var("LANGFUSE_PUBLIC_KEY"), # noqa: B008 + secret_key: Secret | None = Secret.from_env_var("LANGFUSE_SECRET_KEY"), # noqa: B008 + httpx_client: httpx.Client | None = None, + span_handler: SpanHandler | None = None, *, - host: Optional[str] = None, - langfuse_client_kwargs: Optional[dict[str, Any]] = None, + host: str | None = None, + langfuse_client_kwargs: dict[str, Any] | None = None, ) -> None: """ Initialize the LangfuseConnector component. @@ -172,7 +172,7 @@ def __init__( tracing.enable_tracing(self.tracer) @component.output_types(name=str, trace_url=str, trace_id=str) - def run(self, invocation_context: Optional[dict[str, Any]] = None) -> dict[str, str]: + def run(self, invocation_context: dict[str, Any] | None = None) -> dict[str, str]: """ Runs the LangfuseConnector component. diff --git a/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py b/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py index bd8a29c397..05f0bb4f48 100644 --- a/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py +++ b/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py @@ -12,7 +12,7 @@ from contextvars import ContextVar from dataclasses import dataclass from datetime import datetime -from typing import Any, Literal, Optional, cast +from typing import Any, Literal, cast from haystack import default_from_dict, default_to_dict, logging from haystack.dataclasses import ChatMessage @@ -48,7 +48,7 @@ # Internal span execution hierarchy for our tracer # Manages parent-child relationships and prevents cross-request span interleaving -span_stack_var: ContextVar[Optional[list["LangfuseSpan"]]] = ContextVar("span_stack", default=None) +span_stack_var: ContextVar[list["LangfuseSpan"] | None] = ContextVar("span_stack", default=None) class LangfuseSpan(Span): @@ -150,9 +150,9 @@ class SpanContext: name: str operation_name: str - component_type: Optional[str] + component_type: str | None tags: dict[str, Any] - parent_span: Optional[Span] + parent_span: Span | None trace_name: str = "Haystack" public: bool = False @@ -189,7 +189,7 @@ class SpanHandler(ABC): """ def __init__(self) -> None: - self.tracer: Optional[langfuse.Langfuse] = None + self.tracer: langfuse.Langfuse | None = None def init_tracer(self, tracer: langfuse.Langfuse) -> None: """ @@ -215,7 +215,7 @@ def create_span(self, context: SpanContext) -> LangfuseSpan: pass @abstractmethod - def handle(self, span: LangfuseSpan, component_type: Optional[str]) -> None: + def handle(self, span: LangfuseSpan, component_type: str | None) -> None: """ Process a span after component execution by attaching metadata and metrics. @@ -338,7 +338,7 @@ def create_span(self, context: SpanContext) -> LangfuseSpan: else: return LangfuseSpan(self.tracer.start_as_current_span(name=context.name)) - def handle(self, span: LangfuseSpan, component_type: Optional[str]) -> None: + def handle(self, span: LangfuseSpan, component_type: str | None) -> None: # If the span is at the pipeline level, we add input and output keys to the span at_pipeline_level = span.get_data().get(_PIPELINE_INPUT_KEY) is not None if at_pipeline_level: @@ -420,7 +420,7 @@ def __init__( tracer: langfuse.Langfuse, name: str = "Haystack", public: bool = False, - span_handler: Optional[SpanHandler] = None, + span_handler: SpanHandler | None = None, ) -> None: """ Initialize a LangfuseTracer instance. @@ -450,7 +450,7 @@ def __init__( @contextlib.contextmanager def trace( - self, operation_name: str, tags: Optional[dict[str, Any]] = None, parent_span: Optional[Span] = None + self, operation_name: str, tags: dict[str, Any] | None = None, parent_span: Span | None = None ) -> Iterator[Span]: tags = tags or {} span_name = tags.get(_COMPONENT_NAME_KEY, operation_name) @@ -541,7 +541,7 @@ def trace( def flush(self) -> None: self._tracer.flush() - def current_span(self) -> Optional[Span]: + def current_span(self) -> Span | None: """ Return the current active span. diff --git a/integrations/langfuse/tests/test_tracer.py b/integrations/langfuse/tests/test_tracer.py index 1b0be500c2..bac0a4d511 100644 --- a/integrations/langfuse/tests/test_tracer.py +++ b/integrations/langfuse/tests/test_tracer.py @@ -6,7 +6,6 @@ import datetime import logging import sys -from typing import Optional from unittest.mock import MagicMock, Mock, patch import pytest @@ -107,7 +106,7 @@ def flush(self): class CustomSpanHandler(DefaultSpanHandler): - def handle(self, span: LangfuseSpan, component_type: Optional[str]) -> None: + def handle(self, span: LangfuseSpan, component_type: str | None) -> None: if component_type == "OpenAIChatGenerator": output = span.get_data().get(_COMPONENT_OUTPUT_KEY, {}) replies = output.get("replies", [])