From 70b1439159ae94dd8c7c9c1ac37fa8cfb615ec90 Mon Sep 17 00:00:00 2001 From: langfuse-bot Date: Thu, 23 Jul 2026 16:34:38 +0000 Subject: [PATCH] feat(api): update API spec from langfuse/langfuse a9f0808 --- langfuse/api/__init__.py | 23 ++ langfuse/api/client.py | 19 ++ langfuse/api/commons/__init__.py | 6 + langfuse/api/commons/types/__init__.py | 6 + .../commons/types/dataset_run_with_items.py | 4 + langfuse/api/commons/types/deprecation.py | 42 +++ .../commons/types/observations_view_single.py | 19 ++ langfuse/api/commons/types/score.py | 21 +- .../api/commons/types/session_with_traces.py | 6 + .../commons/types/trace_with_full_details.py | 5 + .../types/paginated_dataset_run_items.py | 6 + .../datasets/types/paginated_dataset_runs.py | 6 + langfuse/api/feedback/__init__.py | 51 +++ langfuse/api/feedback/client.py | 184 +++++++++++ langfuse/api/feedback/errors/__init__.py | 42 +++ .../errors/feedback_not_configured_error.py | 13 + langfuse/api/feedback/raw_client.py | 291 ++++++++++++++++++ langfuse/api/feedback/types/__init__.py | 46 +++ .../feedback/types/feedback_target_type.py | 38 +++ .../feedback/types/submit_feedback_request.py | 44 +++ .../types/submit_feedback_response.py | 17 + .../metrics_v1/types/metrics_response.py | 7 + langfuse/api/legacy/observations_v1/client.py | 10 +- .../api/legacy/observations_v1/raw_client.py | 18 +- .../types/observations_views.py | 6 + .../api/scores/types/get_scores_response.py | 6 + .../api/sessions/types/paginated_sessions.py | 6 + langfuse/api/trace/types/traces.py | 6 + langfuse/api/unstable/__init__.py | 21 ++ .../api/unstable/evaluation_rules/__init__.py | 21 ++ .../api/unstable/evaluation_rules/client.py | 17 +- .../unstable/evaluation_rules/raw_client.py | 25 +- .../evaluation_rules/types/__init__.py | 21 ++ .../evaluation_rules/types/evaluation_rule.py | 89 +----- .../types/evaluation_rule_base.py | 99 ++++++ .../types/evaluation_rule_time_scope.py | 22 ++ .../types/evaluation_rules.py | 4 +- .../types/legacy_evaluation_object.py | 62 ++++ .../types/legacy_evaluation_rule.py | 47 +++ .../types/legacy_evaluation_rule_mapping.py | 50 +++ .../types/legacy_evaluation_rule_target.py | 22 ++ .../types/readable_evaluation_rule.py | 8 + 42 files changed, 1328 insertions(+), 128 deletions(-) create mode 100644 langfuse/api/commons/types/deprecation.py create mode 100644 langfuse/api/commons/types/observations_view_single.py create mode 100644 langfuse/api/feedback/__init__.py create mode 100644 langfuse/api/feedback/client.py create mode 100644 langfuse/api/feedback/errors/__init__.py create mode 100644 langfuse/api/feedback/errors/feedback_not_configured_error.py create mode 100644 langfuse/api/feedback/raw_client.py create mode 100644 langfuse/api/feedback/types/__init__.py create mode 100644 langfuse/api/feedback/types/feedback_target_type.py create mode 100644 langfuse/api/feedback/types/submit_feedback_request.py create mode 100644 langfuse/api/feedback/types/submit_feedback_response.py create mode 100644 langfuse/api/unstable/evaluation_rules/types/evaluation_rule_base.py create mode 100644 langfuse/api/unstable/evaluation_rules/types/evaluation_rule_time_scope.py create mode 100644 langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_object.py create mode 100644 langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule.py create mode 100644 langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_mapping.py create mode 100644 langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_target.py create mode 100644 langfuse/api/unstable/evaluation_rules/types/readable_evaluation_rule.py diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index baf746dbb..01110a0cc 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -15,6 +15,7 @@ dataset_run_items, datasets, experiments, + feedback, health, ingestion, legacy, @@ -95,6 +96,7 @@ DatasetRunItem, DatasetRunWithItems, DatasetStatus, + Deprecation, Error, MapValue, MethodNotAllowedError, @@ -108,6 +110,7 @@ ObservationLevel, ObservationV2, ObservationsView, + ObservationsViewSingle, PricingTier, PricingTierCondition, PricingTierInput, @@ -156,6 +159,12 @@ ExperimentsResponse, ExperimentsResponseMeta, ) + from .feedback import ( + FeedbackNotConfiguredError, + FeedbackTargetType, + SubmitFeedbackRequest, + SubmitFeedbackResponse, + ) from .health import HealthResponse, ServiceUnavailableError from .ingestion import ( BaseEvent, @@ -423,6 +432,7 @@ "DeleteLlmConnectionResponse": ".llm_connections", "DeleteMembershipRequest": ".organizations", "DeleteTraceResponse": ".trace", + "Deprecation": ".commons", "EmptyResponse": ".scim", "Error": ".commons", "Experiment": ".experiments", @@ -430,6 +440,8 @@ "ExperimentItemsResponse": ".experiments", "ExperimentsResponse": ".experiments", "ExperimentsResponseMeta": ".experiments", + "FeedbackNotConfiguredError": ".feedback", + "FeedbackTargetType": ".feedback", "FilterConfig": ".scim", "GetCommentsResponse": ".comments", "GetMediaResponse": ".media", @@ -493,6 +505,7 @@ "ObservationsV2Meta": ".observations", "ObservationsV2Response": ".observations", "ObservationsView": ".commons", + "ObservationsViewSingle": ".commons", "OpenAiCompletionUsageSchema": ".ingestion", "OpenAiResponseUsageSchema": ".ingestion", "OpenAiUsage": ".ingestion", @@ -586,6 +599,8 @@ "Session": ".commons", "SessionWithTraces": ".commons", "Sort": ".trace", + "SubmitFeedbackRequest": ".feedback", + "SubmitFeedbackResponse": ".feedback", "TextPrompt": ".prompts", "TextScore": ".commons", "TextScoreV1": ".commons", @@ -617,6 +632,7 @@ "dataset_run_items": ".dataset_run_items", "datasets": ".datasets", "experiments": ".experiments", + "feedback": ".feedback", "health": ".health", "ingestion": ".ingestion", "legacy": ".legacy", @@ -754,6 +770,7 @@ def __dir__(): "DeleteLlmConnectionResponse", "DeleteMembershipRequest", "DeleteTraceResponse", + "Deprecation", "EmptyResponse", "Error", "Experiment", @@ -761,6 +778,8 @@ def __dir__(): "ExperimentItemsResponse", "ExperimentsResponse", "ExperimentsResponseMeta", + "FeedbackNotConfiguredError", + "FeedbackTargetType", "FilterConfig", "GetCommentsResponse", "GetMediaResponse", @@ -824,6 +843,7 @@ def __dir__(): "ObservationsV2Meta", "ObservationsV2Response", "ObservationsView", + "ObservationsViewSingle", "OpenAiCompletionUsageSchema", "OpenAiResponseUsageSchema", "OpenAiUsage", @@ -917,6 +937,8 @@ def __dir__(): "Session", "SessionWithTraces", "Sort", + "SubmitFeedbackRequest", + "SubmitFeedbackResponse", "TextPrompt", "TextScore", "TextScoreV1", @@ -948,6 +970,7 @@ def __dir__(): "dataset_run_items", "datasets", "experiments", + "feedback", "health", "ingestion", "legacy", diff --git a/langfuse/api/client.py b/langfuse/api/client.py index 26b4cc994..7fc58ca40 100644 --- a/langfuse/api/client.py +++ b/langfuse/api/client.py @@ -24,6 +24,7 @@ ) from .datasets.client import AsyncDatasetsClient, DatasetsClient from .experiments.client import AsyncExperimentsClient, ExperimentsClient + from .feedback.client import AsyncFeedbackClient, FeedbackClient from .health.client import AsyncHealthClient, HealthClient from .ingestion.client import AsyncIngestionClient, IngestionClient from .legacy.client import AsyncLegacyClient, LegacyClient @@ -133,6 +134,7 @@ def __init__( self._dataset_run_items: typing.Optional[DatasetRunItemsClient] = None self._datasets: typing.Optional[DatasetsClient] = None self._experiments: typing.Optional[ExperimentsClient] = None + self._feedback: typing.Optional[FeedbackClient] = None self._health: typing.Optional[HealthClient] = None self._ingestion: typing.Optional[IngestionClient] = None self._legacy: typing.Optional[LegacyClient] = None @@ -218,6 +220,14 @@ def experiments(self): self._experiments = ExperimentsClient(client_wrapper=self._client_wrapper) return self._experiments + @property + def feedback(self): + if self._feedback is None: + from .feedback.client import FeedbackClient # noqa: E402 + + self._feedback = FeedbackClient(client_wrapper=self._client_wrapper) + return self._feedback + @property def health(self): if self._health is None: @@ -476,6 +486,7 @@ def __init__( self._dataset_run_items: typing.Optional[AsyncDatasetRunItemsClient] = None self._datasets: typing.Optional[AsyncDatasetsClient] = None self._experiments: typing.Optional[AsyncExperimentsClient] = None + self._feedback: typing.Optional[AsyncFeedbackClient] = None self._health: typing.Optional[AsyncHealthClient] = None self._ingestion: typing.Optional[AsyncIngestionClient] = None self._legacy: typing.Optional[AsyncLegacyClient] = None @@ -565,6 +576,14 @@ def experiments(self): ) return self._experiments + @property + def feedback(self): + if self._feedback is None: + from .feedback.client import AsyncFeedbackClient # noqa: E402 + + self._feedback = AsyncFeedbackClient(client_wrapper=self._client_wrapper) + return self._feedback + @property def health(self): if self._health is None: diff --git a/langfuse/api/commons/__init__.py b/langfuse/api/commons/__init__.py index a35064081..5d5f56bcd 100644 --- a/langfuse/api/commons/__init__.py +++ b/langfuse/api/commons/__init__.py @@ -27,6 +27,7 @@ DatasetRunItem, DatasetRunWithItems, DatasetStatus, + Deprecation, MapValue, Model, ModelPrice, @@ -37,6 +38,7 @@ ObservationLevel, ObservationV2, ObservationsView, + ObservationsViewSingle, PricingTier, PricingTierCondition, PricingTierInput, @@ -94,6 +96,7 @@ "DatasetRunItem": ".types", "DatasetRunWithItems": ".types", "DatasetStatus": ".types", + "Deprecation": ".types", "Error": ".errors", "MapValue": ".types", "MethodNotAllowedError": ".errors", @@ -107,6 +110,7 @@ "ObservationLevel": ".types", "ObservationV2": ".types", "ObservationsView": ".types", + "ObservationsViewSingle": ".types", "PricingTier": ".types", "PricingTierCondition": ".types", "PricingTierInput": ".types", @@ -187,6 +191,7 @@ def __dir__(): "DatasetRunItem", "DatasetRunWithItems", "DatasetStatus", + "Deprecation", "Error", "MapValue", "MethodNotAllowedError", @@ -200,6 +205,7 @@ def __dir__(): "ObservationLevel", "ObservationV2", "ObservationsView", + "ObservationsViewSingle", "PricingTier", "PricingTierCondition", "PricingTierInput", diff --git a/langfuse/api/commons/types/__init__.py b/langfuse/api/commons/types/__init__.py index 12faf307f..7932fec73 100644 --- a/langfuse/api/commons/types/__init__.py +++ b/langfuse/api/commons/types/__init__.py @@ -26,6 +26,7 @@ from .dataset_run_item import DatasetRunItem from .dataset_run_with_items import DatasetRunWithItems from .dataset_status import DatasetStatus + from .deprecation import Deprecation from .map_value import MapValue from .model import Model from .model_price import ModelPrice @@ -36,6 +37,7 @@ from .observation_level import ObservationLevel from .observation_v2 import ObservationV2 from .observations_view import ObservationsView + from .observations_view_single import ObservationsViewSingle from .pricing_tier import PricingTier from .pricing_tier_condition import PricingTierCondition from .pricing_tier_input import PricingTierInput @@ -88,6 +90,7 @@ "DatasetRunItem": ".dataset_run_item", "DatasetRunWithItems": ".dataset_run_with_items", "DatasetStatus": ".dataset_status", + "Deprecation": ".deprecation", "MapValue": ".map_value", "Model": ".model", "ModelPrice": ".model_price", @@ -98,6 +101,7 @@ "ObservationLevel": ".observation_level", "ObservationV2": ".observation_v2", "ObservationsView": ".observations_view", + "ObservationsViewSingle": ".observations_view_single", "PricingTier": ".pricing_tier", "PricingTierCondition": ".pricing_tier_condition", "PricingTierInput": ".pricing_tier_input", @@ -176,6 +180,7 @@ def __dir__(): "DatasetRunItem", "DatasetRunWithItems", "DatasetStatus", + "Deprecation", "MapValue", "Model", "ModelPrice", @@ -186,6 +191,7 @@ def __dir__(): "ObservationLevel", "ObservationV2", "ObservationsView", + "ObservationsViewSingle", "PricingTier", "PricingTierCondition", "PricingTierInput", diff --git a/langfuse/api/commons/types/dataset_run_with_items.py b/langfuse/api/commons/types/dataset_run_with_items.py index b5995dd30..b16660b97 100644 --- a/langfuse/api/commons/types/dataset_run_with_items.py +++ b/langfuse/api/commons/types/dataset_run_with_items.py @@ -7,12 +7,16 @@ from ...core.serialization import FieldMetadata from .dataset_run import DatasetRun from .dataset_run_item import DatasetRunItem +from .deprecation import Deprecation class DatasetRunWithItems(DatasetRun): dataset_run_items: typing_extensions.Annotated[ typing.List[DatasetRunItem], FieldMetadata(alias="datasetRunItems") ] + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/commons/types/deprecation.py b/langfuse/api/commons/types/deprecation.py new file mode 100644 index 000000000..18c7bf616 --- /dev/null +++ b/langfuse/api/commons/types/deprecation.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class Deprecation(UniversalBaseModel): + """ + Migration signal returned by deprecated endpoints. Optional fields are omitted when they have no value. + """ + + message: str = pydantic.Field() + """ + Human- and agent-readable summary of the deprecation and its replacement. + """ + + replacement: typing.Optional[str] = pydantic.Field(default=None) + """ + The replacement endpoint, e.g. "GET /api/public/v2/observations". Omitted when the endpoint is being removed without a direct replacement. + """ + + docs_url: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="docsUrl") + ] = pydantic.Field(default=None) + """ + Link to the migration documentation (markdown), when available. + """ + + sunset_at: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sunsetAt") + ] = pydantic.Field(default=None) + """ + ISO date after which the endpoint may stop working, when a removal date is committed. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/observations_view_single.py b/langfuse/api/commons/types/observations_view_single.py new file mode 100644 index 000000000..4c7d68ace --- /dev/null +++ b/langfuse/api/commons/types/observations_view_single.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .deprecation import Deprecation +from .observations_view import ObservationsView + + +class ObservationsViewSingle(ObservationsView): + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/score.py b/langfuse/api/commons/types/score.py index 33c901c5f..7b31ae39a 100644 --- a/langfuse/api/commons/types/score.py +++ b/langfuse/api/commons/types/score.py @@ -9,10 +9,21 @@ import typing_extensions from ...core.pydantic_utilities import UniversalBaseModel from ...core.serialization import FieldMetadata +from .deprecation import Deprecation from .score_source import ScoreSource -class Score_Numeric(UniversalBaseModel): +class Base(UniversalBaseModel): + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class Score_Numeric(Base): data_type: typing_extensions.Annotated[ typing.Literal["NUMERIC"], FieldMetadata(alias="dataType") ] = "NUMERIC" @@ -57,7 +68,7 @@ class Score_Numeric(UniversalBaseModel): ) -class Score_Categorical(UniversalBaseModel): +class Score_Categorical(Base): data_type: typing_extensions.Annotated[ typing.Literal["CATEGORICAL"], FieldMetadata(alias="dataType") ] = "CATEGORICAL" @@ -103,7 +114,7 @@ class Score_Categorical(UniversalBaseModel): ) -class Score_Boolean(UniversalBaseModel): +class Score_Boolean(Base): data_type: typing_extensions.Annotated[ typing.Literal["BOOLEAN"], FieldMetadata(alias="dataType") ] = "BOOLEAN" @@ -149,7 +160,7 @@ class Score_Boolean(UniversalBaseModel): ) -class Score_Correction(UniversalBaseModel): +class Score_Correction(Base): data_type: typing_extensions.Annotated[ typing.Literal["CORRECTION"], FieldMetadata(alias="dataType") ] = "CORRECTION" @@ -195,7 +206,7 @@ class Score_Correction(UniversalBaseModel): ) -class Score_Text(UniversalBaseModel): +class Score_Text(Base): data_type: typing_extensions.Annotated[ typing.Literal["TEXT"], FieldMetadata(alias="dataType") ] = "TEXT" diff --git a/langfuse/api/commons/types/session_with_traces.py b/langfuse/api/commons/types/session_with_traces.py index d04eef54e..e5159ade0 100644 --- a/langfuse/api/commons/types/session_with_traces.py +++ b/langfuse/api/commons/types/session_with_traces.py @@ -3,12 +3,18 @@ import typing import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .deprecation import Deprecation from .session import Session from .trace import Trace class SessionWithTraces(Session): traces: typing.List[Trace] + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/commons/types/trace_with_full_details.py b/langfuse/api/commons/types/trace_with_full_details.py index f14757ce5..227730cf4 100644 --- a/langfuse/api/commons/types/trace_with_full_details.py +++ b/langfuse/api/commons/types/trace_with_full_details.py @@ -5,6 +5,7 @@ import pydantic import typing_extensions from ...core.serialization import FieldMetadata +from .deprecation import Deprecation from .observations_view import ObservationsView from .score_v1 import ScoreV1 from .trace import Trace @@ -40,6 +41,10 @@ class TraceWithFullDetails(Trace): List of scores """ + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True ) diff --git a/langfuse/api/dataset_run_items/types/paginated_dataset_run_items.py b/langfuse/api/dataset_run_items/types/paginated_dataset_run_items.py index 24f7c1996..e3d3a4889 100644 --- a/langfuse/api/dataset_run_items/types/paginated_dataset_run_items.py +++ b/langfuse/api/dataset_run_items/types/paginated_dataset_run_items.py @@ -3,14 +3,20 @@ import typing import pydantic +import typing_extensions from ...commons.types.dataset_run_item import DatasetRunItem +from ...commons.types.deprecation import Deprecation from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from ...utils.pagination.types.meta_response import MetaResponse class PaginatedDatasetRunItems(UniversalBaseModel): data: typing.List[DatasetRunItem] meta: MetaResponse + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/datasets/types/paginated_dataset_runs.py b/langfuse/api/datasets/types/paginated_dataset_runs.py index 85cd54c55..d70a9bf35 100644 --- a/langfuse/api/datasets/types/paginated_dataset_runs.py +++ b/langfuse/api/datasets/types/paginated_dataset_runs.py @@ -3,14 +3,20 @@ import typing import pydantic +import typing_extensions from ...commons.types.dataset_run import DatasetRun +from ...commons.types.deprecation import Deprecation from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from ...utils.pagination.types.meta_response import MetaResponse class PaginatedDatasetRuns(UniversalBaseModel): data: typing.List[DatasetRun] meta: MetaResponse + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/feedback/__init__.py b/langfuse/api/feedback/__init__.py new file mode 100644 index 000000000..94e9b1e8f --- /dev/null +++ b/langfuse/api/feedback/__init__.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import FeedbackTargetType, SubmitFeedbackRequest, SubmitFeedbackResponse + from .errors import FeedbackNotConfiguredError +_dynamic_imports: typing.Dict[str, str] = { + "FeedbackNotConfiguredError": ".errors", + "FeedbackTargetType": ".types", + "SubmitFeedbackRequest": ".types", + "SubmitFeedbackResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "FeedbackNotConfiguredError", + "FeedbackTargetType", + "SubmitFeedbackRequest", + "SubmitFeedbackResponse", +] diff --git a/langfuse/api/feedback/client.py b/langfuse/api/feedback/client.py new file mode 100644 index 000000000..abdfd22f0 --- /dev/null +++ b/langfuse/api/feedback/client.py @@ -0,0 +1,184 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawFeedbackClient, RawFeedbackClient +from .types.feedback_target_type import FeedbackTargetType +from .types.submit_feedback_response import SubmitFeedbackResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class FeedbackClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawFeedbackClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawFeedbackClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawFeedbackClient + """ + return self._raw_client + + def submit( + self, + *, + target_type: FeedbackTargetType, + target: str, + feedback: str, + goal: typing.Optional[str] = OMIT, + reference_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SubmitFeedbackResponse: + """ + Submit explicit user-approved feedback about Langfuse skills, MCP tools, CLI, docs, or public API. Do not include secrets, credentials, customer data, trace payloads, or unrelated use-case details. + + Parameters + ---------- + target_type : FeedbackTargetType + Category of the thing the feedback is about. + + target : str + The specific instance within targetType: the skill name, MCP tool name, CLI command, API endpoint path, or docs page path (e.g. 'queryMetrics', '/docs/mcp'). An identifier, not a sentence. Must be between 1 and 200 characters. + + feedback : str + Concise feedback text approved by the user. Must be between 1 and 3000 characters. + + goal : typing.Optional[str] + Optional user-approved goal or use case they were trying to achieve. Must be between 1 and 1500 characters when provided. Do not include secrets, customer data, trace payloads, or broad unrelated context. + + reference_url : typing.Optional[str] + Optional HTTP(S) reference URL. Langfuse stores it as text for triage and does not fetch it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubmitFeedbackResponse + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.feedback import FeedbackTargetType + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.feedback.submit( + target_type=FeedbackTargetType.SKILL, + target="target", + feedback="feedback", + ) + """ + _response = self._raw_client.submit( + target_type=target_type, + target=target, + feedback=feedback, + goal=goal, + reference_url=reference_url, + request_options=request_options, + ) + return _response.data + + +class AsyncFeedbackClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawFeedbackClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawFeedbackClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawFeedbackClient + """ + return self._raw_client + + async def submit( + self, + *, + target_type: FeedbackTargetType, + target: str, + feedback: str, + goal: typing.Optional[str] = OMIT, + reference_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SubmitFeedbackResponse: + """ + Submit explicit user-approved feedback about Langfuse skills, MCP tools, CLI, docs, or public API. Do not include secrets, credentials, customer data, trace payloads, or unrelated use-case details. + + Parameters + ---------- + target_type : FeedbackTargetType + Category of the thing the feedback is about. + + target : str + The specific instance within targetType: the skill name, MCP tool name, CLI command, API endpoint path, or docs page path (e.g. 'queryMetrics', '/docs/mcp'). An identifier, not a sentence. Must be between 1 and 200 characters. + + feedback : str + Concise feedback text approved by the user. Must be between 1 and 3000 characters. + + goal : typing.Optional[str] + Optional user-approved goal or use case they were trying to achieve. Must be between 1 and 1500 characters when provided. Do not include secrets, customer data, trace payloads, or broad unrelated context. + + reference_url : typing.Optional[str] + Optional HTTP(S) reference URL. Langfuse stores it as text for triage and does not fetch it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubmitFeedbackResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.feedback import FeedbackTargetType + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.feedback.submit( + target_type=FeedbackTargetType.SKILL, + target="target", + feedback="feedback", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.submit( + target_type=target_type, + target=target, + feedback=feedback, + goal=goal, + reference_url=reference_url, + request_options=request_options, + ) + return _response.data diff --git a/langfuse/api/feedback/errors/__init__.py b/langfuse/api/feedback/errors/__init__.py new file mode 100644 index 000000000..ab6f609ab --- /dev/null +++ b/langfuse/api/feedback/errors/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .feedback_not_configured_error import FeedbackNotConfiguredError +_dynamic_imports: typing.Dict[str, str] = { + "FeedbackNotConfiguredError": ".feedback_not_configured_error" +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["FeedbackNotConfiguredError"] diff --git a/langfuse/api/feedback/errors/feedback_not_configured_error.py b/langfuse/api/feedback/errors/feedback_not_configured_error.py new file mode 100644 index 000000000..d92c60fe8 --- /dev/null +++ b/langfuse/api/feedback/errors/feedback_not_configured_error.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class FeedbackNotConfiguredError(ApiError): + def __init__(self, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__( + status_code=409, + headers=headers, + ) diff --git a/langfuse/api/feedback/raw_client.py b/langfuse/api/feedback/raw_client.py new file mode 100644 index 000000000..6a1862370 --- /dev/null +++ b/langfuse/api/feedback/raw_client.py @@ -0,0 +1,291 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..commons.errors.access_denied_error import AccessDeniedError +from ..commons.errors.error import Error +from ..commons.errors.method_not_allowed_error import MethodNotAllowedError +from ..commons.errors.not_found_error import NotFoundError +from ..commons.errors.unauthorized_error import UnauthorizedError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from .errors.feedback_not_configured_error import FeedbackNotConfiguredError +from .types.feedback_target_type import FeedbackTargetType +from .types.submit_feedback_response import SubmitFeedbackResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawFeedbackClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def submit( + self, + *, + target_type: FeedbackTargetType, + target: str, + feedback: str, + goal: typing.Optional[str] = OMIT, + reference_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SubmitFeedbackResponse]: + """ + Submit explicit user-approved feedback about Langfuse skills, MCP tools, CLI, docs, or public API. Do not include secrets, credentials, customer data, trace payloads, or unrelated use-case details. + + Parameters + ---------- + target_type : FeedbackTargetType + Category of the thing the feedback is about. + + target : str + The specific instance within targetType: the skill name, MCP tool name, CLI command, API endpoint path, or docs page path (e.g. 'queryMetrics', '/docs/mcp'). An identifier, not a sentence. Must be between 1 and 200 characters. + + feedback : str + Concise feedback text approved by the user. Must be between 1 and 3000 characters. + + goal : typing.Optional[str] + Optional user-approved goal or use case they were trying to achieve. Must be between 1 and 1500 characters when provided. Do not include secrets, customer data, trace payloads, or broad unrelated context. + + reference_url : typing.Optional[str] + Optional HTTP(S) reference URL. Langfuse stores it as text for triage and does not fetch it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SubmitFeedbackResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/feedback", + method="POST", + json={ + "targetType": target_type, + "target": target, + "feedback": feedback, + "goal": goal, + "referenceUrl": reference_url, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SubmitFeedbackResponse, + parse_obj_as( + type_=SubmitFeedbackResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 409: + raise FeedbackNotConfiguredError(headers=dict(_response.headers)) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + +class AsyncRawFeedbackClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def submit( + self, + *, + target_type: FeedbackTargetType, + target: str, + feedback: str, + goal: typing.Optional[str] = OMIT, + reference_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SubmitFeedbackResponse]: + """ + Submit explicit user-approved feedback about Langfuse skills, MCP tools, CLI, docs, or public API. Do not include secrets, credentials, customer data, trace payloads, or unrelated use-case details. + + Parameters + ---------- + target_type : FeedbackTargetType + Category of the thing the feedback is about. + + target : str + The specific instance within targetType: the skill name, MCP tool name, CLI command, API endpoint path, or docs page path (e.g. 'queryMetrics', '/docs/mcp'). An identifier, not a sentence. Must be between 1 and 200 characters. + + feedback : str + Concise feedback text approved by the user. Must be between 1 and 3000 characters. + + goal : typing.Optional[str] + Optional user-approved goal or use case they were trying to achieve. Must be between 1 and 1500 characters when provided. Do not include secrets, customer data, trace payloads, or broad unrelated context. + + reference_url : typing.Optional[str] + Optional HTTP(S) reference URL. Langfuse stores it as text for triage and does not fetch it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SubmitFeedbackResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/feedback", + method="POST", + json={ + "targetType": target_type, + "target": target, + "feedback": feedback, + "goal": goal, + "referenceUrl": reference_url, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SubmitFeedbackResponse, + parse_obj_as( + type_=SubmitFeedbackResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 409: + raise FeedbackNotConfiguredError(headers=dict(_response.headers)) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) diff --git a/langfuse/api/feedback/types/__init__.py b/langfuse/api/feedback/types/__init__.py new file mode 100644 index 000000000..6ba86cbb5 --- /dev/null +++ b/langfuse/api/feedback/types/__init__.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .feedback_target_type import FeedbackTargetType + from .submit_feedback_request import SubmitFeedbackRequest + from .submit_feedback_response import SubmitFeedbackResponse +_dynamic_imports: typing.Dict[str, str] = { + "FeedbackTargetType": ".feedback_target_type", + "SubmitFeedbackRequest": ".submit_feedback_request", + "SubmitFeedbackResponse": ".submit_feedback_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["FeedbackTargetType", "SubmitFeedbackRequest", "SubmitFeedbackResponse"] diff --git a/langfuse/api/feedback/types/feedback_target_type.py b/langfuse/api/feedback/types/feedback_target_type.py new file mode 100644 index 000000000..16d673075 --- /dev/null +++ b/langfuse/api/feedback/types/feedback_target_type.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core import enum + +T_Result = typing.TypeVar("T_Result") + + +class FeedbackTargetType(enum.StrEnum): + SKILL = "skill" + MCP_TOOL = "mcp-tool" + CLI = "cli" + DOCS = "docs" + PUBLIC_API = "public-api" + OTHER = "other" + + def visit( + self, + skill: typing.Callable[[], T_Result], + mcp_tool: typing.Callable[[], T_Result], + cli: typing.Callable[[], T_Result], + docs: typing.Callable[[], T_Result], + public_api: typing.Callable[[], T_Result], + other: typing.Callable[[], T_Result], + ) -> T_Result: + if self is FeedbackTargetType.SKILL: + return skill() + if self is FeedbackTargetType.MCP_TOOL: + return mcp_tool() + if self is FeedbackTargetType.CLI: + return cli() + if self is FeedbackTargetType.DOCS: + return docs() + if self is FeedbackTargetType.PUBLIC_API: + return public_api() + if self is FeedbackTargetType.OTHER: + return other() diff --git a/langfuse/api/feedback/types/submit_feedback_request.py b/langfuse/api/feedback/types/submit_feedback_request.py new file mode 100644 index 000000000..3c77fe1e4 --- /dev/null +++ b/langfuse/api/feedback/types/submit_feedback_request.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .feedback_target_type import FeedbackTargetType + + +class SubmitFeedbackRequest(UniversalBaseModel): + target_type: typing_extensions.Annotated[ + FeedbackTargetType, FieldMetadata(alias="targetType") + ] = pydantic.Field() + """ + Category of the thing the feedback is about. + """ + + target: str = pydantic.Field() + """ + The specific instance within targetType: the skill name, MCP tool name, CLI command, API endpoint path, or docs page path (e.g. 'queryMetrics', '/docs/mcp'). An identifier, not a sentence. Must be between 1 and 200 characters. + """ + + feedback: str = pydantic.Field() + """ + Concise feedback text approved by the user. Must be between 1 and 3000 characters. + """ + + goal: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional user-approved goal or use case they were trying to achieve. Must be between 1 and 1500 characters when provided. Do not include secrets, customer data, trace payloads, or broad unrelated context. + """ + + reference_url: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="referenceUrl") + ] = pydantic.Field(default=None) + """ + Optional HTTP(S) reference URL. Langfuse stores it as text for triage and does not fetch it. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/feedback/types/submit_feedback_response.py b/langfuse/api/feedback/types/submit_feedback_response.py new file mode 100644 index 000000000..f6e1fce9a --- /dev/null +++ b/langfuse/api/feedback/types/submit_feedback_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class SubmitFeedbackResponse(UniversalBaseModel): + id: str = pydantic.Field() + """ + Correlation ID for the submitted feedback. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/legacy/metrics_v1/types/metrics_response.py b/langfuse/api/legacy/metrics_v1/types/metrics_response.py index 734c47ffd..546f19fd6 100644 --- a/langfuse/api/legacy/metrics_v1/types/metrics_response.py +++ b/langfuse/api/legacy/metrics_v1/types/metrics_response.py @@ -3,7 +3,10 @@ import typing import pydantic +import typing_extensions +from ....commons.types.deprecation import Deprecation from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata class MetricsResponse(UniversalBaseModel): @@ -14,6 +17,10 @@ class MetricsResponse(UniversalBaseModel): Histograms will return an array with [lower, upper, height] tuples. """ + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True ) diff --git a/langfuse/api/legacy/observations_v1/client.py b/langfuse/api/legacy/observations_v1/client.py index 40d5df6b8..631874321 100644 --- a/langfuse/api/legacy/observations_v1/client.py +++ b/langfuse/api/legacy/observations_v1/client.py @@ -4,7 +4,7 @@ import typing from ...commons.types.observation_level import ObservationLevel -from ...commons.types.observations_view import ObservationsView +from ...commons.types.observations_view_single import ObservationsViewSingle from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions from .raw_client import AsyncRawObservationsV1Client, RawObservationsV1Client @@ -31,7 +31,7 @@ def get( observation_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsView: + ) -> ObservationsViewSingle: """ Get a observation @@ -45,7 +45,7 @@ def get( Returns ------- - ObservationsView + ObservationsViewSingle Examples -------- @@ -279,7 +279,7 @@ async def get( observation_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsView: + ) -> ObservationsViewSingle: """ Get a observation @@ -293,7 +293,7 @@ async def get( Returns ------- - ObservationsView + ObservationsViewSingle Examples -------- diff --git a/langfuse/api/legacy/observations_v1/raw_client.py b/langfuse/api/legacy/observations_v1/raw_client.py index 61ecf409d..f68d62937 100644 --- a/langfuse/api/legacy/observations_v1/raw_client.py +++ b/langfuse/api/legacy/observations_v1/raw_client.py @@ -10,7 +10,7 @@ from ...commons.errors.not_found_error import NotFoundError from ...commons.errors.unauthorized_error import UnauthorizedError from ...commons.types.observation_level import ObservationLevel -from ...commons.types.observations_view import ObservationsView +from ...commons.types.observations_view_single import ObservationsViewSingle from ...core.api_error import ApiError from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.datetime_utils import serialize_datetime @@ -30,7 +30,7 @@ def get( observation_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ObservationsView]: + ) -> HttpResponse[ObservationsViewSingle]: """ Get a observation @@ -44,7 +44,7 @@ def get( Returns ------- - HttpResponse[ObservationsView] + HttpResponse[ObservationsViewSingle] """ _response = self._client_wrapper.httpx_client.request( f"api/public/observations/{jsonable_encoder(observation_id)}", @@ -54,9 +54,9 @@ def get( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationsView, + ObservationsViewSingle, parse_obj_as( - type_=ObservationsView, # type: ignore + type_=ObservationsViewSingle, # type: ignore object_=_response.json(), ), ) @@ -399,7 +399,7 @@ async def get( observation_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ObservationsView]: + ) -> AsyncHttpResponse[ObservationsViewSingle]: """ Get a observation @@ -413,7 +413,7 @@ async def get( Returns ------- - AsyncHttpResponse[ObservationsView] + AsyncHttpResponse[ObservationsViewSingle] """ _response = await self._client_wrapper.httpx_client.request( f"api/public/observations/{jsonable_encoder(observation_id)}", @@ -423,9 +423,9 @@ async def get( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationsView, + ObservationsViewSingle, parse_obj_as( - type_=ObservationsView, # type: ignore + type_=ObservationsViewSingle, # type: ignore object_=_response.json(), ), ) diff --git a/langfuse/api/legacy/observations_v1/types/observations_views.py b/langfuse/api/legacy/observations_v1/types/observations_views.py index b6d294c7d..bb1da2541 100644 --- a/langfuse/api/legacy/observations_v1/types/observations_views.py +++ b/langfuse/api/legacy/observations_v1/types/observations_views.py @@ -3,14 +3,20 @@ import typing import pydantic +import typing_extensions +from ....commons.types.deprecation import Deprecation from ....commons.types.observations_view import ObservationsView from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata from ....utils.pagination.types.meta_response import MetaResponse class ObservationsViews(UniversalBaseModel): data: typing.List[ObservationsView] meta: MetaResponse + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/scores/types/get_scores_response.py b/langfuse/api/scores/types/get_scores_response.py index 0ca4d0e40..70f9c7a51 100644 --- a/langfuse/api/scores/types/get_scores_response.py +++ b/langfuse/api/scores/types/get_scores_response.py @@ -3,7 +3,10 @@ import typing import pydantic +import typing_extensions +from ...commons.types.deprecation import Deprecation from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from ...utils.pagination.types.meta_response import MetaResponse from .get_scores_response_data import GetScoresResponseData @@ -11,6 +14,9 @@ class GetScoresResponse(UniversalBaseModel): data: typing.List[GetScoresResponseData] meta: MetaResponse + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/sessions/types/paginated_sessions.py b/langfuse/api/sessions/types/paginated_sessions.py index 5d7bd3886..cc1ef68b3 100644 --- a/langfuse/api/sessions/types/paginated_sessions.py +++ b/langfuse/api/sessions/types/paginated_sessions.py @@ -3,14 +3,20 @@ import typing import pydantic +import typing_extensions +from ...commons.types.deprecation import Deprecation from ...commons.types.session import Session from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from ...utils.pagination.types.meta_response import MetaResponse class PaginatedSessions(UniversalBaseModel): data: typing.List[Session] meta: MetaResponse + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/trace/types/traces.py b/langfuse/api/trace/types/traces.py index b559118e1..3ab791cf9 100644 --- a/langfuse/api/trace/types/traces.py +++ b/langfuse/api/trace/types/traces.py @@ -3,14 +3,20 @@ import typing import pydantic +import typing_extensions +from ...commons.types.deprecation import Deprecation from ...commons.types.trace_with_details import TraceWithDetails from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from ...utils.pagination.types.meta_response import MetaResponse class Traces(UniversalBaseModel): data: typing.List[TraceWithDetails] meta: MetaResponse + deprecation: typing_extensions.Annotated[ + typing.Optional[Deprecation], FieldMetadata(alias="_deprecation") + ] = None model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/unstable/__init__.py b/langfuse/api/unstable/__init__.py index f294aba5f..8fe7fdc80 100644 --- a/langfuse/api/unstable/__init__.py +++ b/langfuse/api/unstable/__init__.py @@ -125,11 +125,18 @@ CreateLlmAsJudgeEvaluationRuleRequest, DeleteEvaluationRuleResponse, EvaluationRule, + EvaluationRuleBase, EvaluationRuleEvaluator, EvaluationRuleEvaluatorReference, + EvaluationRuleTimeScope, EvaluationRules, + LegacyEvaluationObject, + LegacyEvaluationRule, + LegacyEvaluationRuleMapping, + LegacyEvaluationRuleTarget, LlmAsJudgeEvaluationRuleEvaluatorReference, LlmAsJudgeEvaluatorType, + ReadableEvaluationRule, UpdateEvaluationRuleRequest, ) from .evaluators import ( @@ -199,6 +206,7 @@ "DeleteEvaluatorResponse": ".evaluators", "EvaluationRule": ".evaluation_rules", "EvaluationRuleArrayOptionsFilterOperator": ".commons", + "EvaluationRuleBase": ".evaluation_rules", "EvaluationRuleBooleanFilterOperator": ".commons", "EvaluationRuleEvaluator": ".evaluation_rules", "EvaluationRuleEvaluatorReference": ".evaluation_rules", @@ -221,6 +229,7 @@ "EvaluationRuleStatus": ".commons", "EvaluationRuleStringFilterOperator": ".commons", "EvaluationRuleTarget": ".commons", + "EvaluationRuleTimeScope": ".evaluation_rules", "EvaluationRules": ".evaluation_rules", "Evaluator": ".evaluators", "EvaluatorBase": ".evaluators", @@ -237,6 +246,10 @@ "Evaluator_LlmAsJudge": ".evaluators", "Evaluators": ".evaluators", "InternalServerError": ".errors", + "LegacyEvaluationObject": ".evaluation_rules", + "LegacyEvaluationRule": ".evaluation_rules", + "LegacyEvaluationRuleMapping": ".evaluation_rules", + "LegacyEvaluationRuleTarget": ".evaluation_rules", "LlmAsJudgeEvaluationRuleEvaluatorReference": ".evaluation_rules", "LlmAsJudgeEvaluator": ".evaluators", "LlmAsJudgeEvaluatorType": ".evaluation_rules", @@ -258,6 +271,7 @@ "PublicEvaluatorOutputDefinition_Categorical": ".commons", "PublicEvaluatorOutputDefinition_Numeric": ".commons", "PublicNumericEvaluatorOutputDefinition": ".commons", + "ReadableEvaluationRule": ".evaluation_rules", "StringEvaluationRuleFilter": ".commons", "StringObjectEvaluationRuleFilter": ".commons", "StringOptionsEvaluationRuleFilter": ".commons", @@ -357,6 +371,7 @@ def __dir__(): "DeleteEvaluatorResponse", "EvaluationRule", "EvaluationRuleArrayOptionsFilterOperator", + "EvaluationRuleBase", "EvaluationRuleBooleanFilterOperator", "EvaluationRuleEvaluator", "EvaluationRuleEvaluatorReference", @@ -379,6 +394,7 @@ def __dir__(): "EvaluationRuleStatus", "EvaluationRuleStringFilterOperator", "EvaluationRuleTarget", + "EvaluationRuleTimeScope", "EvaluationRules", "Evaluator", "EvaluatorBase", @@ -395,6 +411,10 @@ def __dir__(): "Evaluator_LlmAsJudge", "Evaluators", "InternalServerError", + "LegacyEvaluationObject", + "LegacyEvaluationRule", + "LegacyEvaluationRuleMapping", + "LegacyEvaluationRuleTarget", "LlmAsJudgeEvaluationRuleEvaluatorReference", "LlmAsJudgeEvaluator", "LlmAsJudgeEvaluatorType", @@ -416,6 +436,7 @@ def __dir__(): "PublicEvaluatorOutputDefinition_Categorical", "PublicEvaluatorOutputDefinition_Numeric", "PublicNumericEvaluatorOutputDefinition", + "ReadableEvaluationRule", "StringEvaluationRuleFilter", "StringObjectEvaluationRuleFilter", "StringOptionsEvaluationRuleFilter", diff --git a/langfuse/api/unstable/evaluation_rules/__init__.py b/langfuse/api/unstable/evaluation_rules/__init__.py index 8541bdcc8..c8031798c 100644 --- a/langfuse/api/unstable/evaluation_rules/__init__.py +++ b/langfuse/api/unstable/evaluation_rules/__init__.py @@ -13,11 +13,18 @@ CreateLlmAsJudgeEvaluationRuleRequest, DeleteEvaluationRuleResponse, EvaluationRule, + EvaluationRuleBase, EvaluationRuleEvaluator, EvaluationRuleEvaluatorReference, + EvaluationRuleTimeScope, EvaluationRules, + LegacyEvaluationObject, + LegacyEvaluationRule, + LegacyEvaluationRuleMapping, + LegacyEvaluationRuleTarget, LlmAsJudgeEvaluationRuleEvaluatorReference, LlmAsJudgeEvaluatorType, + ReadableEvaluationRule, UpdateEvaluationRuleRequest, ) _dynamic_imports: typing.Dict[str, str] = { @@ -27,11 +34,18 @@ "CreateLlmAsJudgeEvaluationRuleRequest": ".types", "DeleteEvaluationRuleResponse": ".types", "EvaluationRule": ".types", + "EvaluationRuleBase": ".types", "EvaluationRuleEvaluator": ".types", "EvaluationRuleEvaluatorReference": ".types", + "EvaluationRuleTimeScope": ".types", "EvaluationRules": ".types", + "LegacyEvaluationObject": ".types", + "LegacyEvaluationRule": ".types", + "LegacyEvaluationRuleMapping": ".types", + "LegacyEvaluationRuleTarget": ".types", "LlmAsJudgeEvaluationRuleEvaluatorReference": ".types", "LlmAsJudgeEvaluatorType": ".types", + "ReadableEvaluationRule": ".types", "UpdateEvaluationRuleRequest": ".types", } @@ -70,10 +84,17 @@ def __dir__(): "CreateLlmAsJudgeEvaluationRuleRequest", "DeleteEvaluationRuleResponse", "EvaluationRule", + "EvaluationRuleBase", "EvaluationRuleEvaluator", "EvaluationRuleEvaluatorReference", + "EvaluationRuleTimeScope", "EvaluationRules", + "LegacyEvaluationObject", + "LegacyEvaluationRule", + "LegacyEvaluationRuleMapping", + "LegacyEvaluationRuleTarget", "LlmAsJudgeEvaluationRuleEvaluatorReference", "LlmAsJudgeEvaluatorType", + "ReadableEvaluationRule", "UpdateEvaluationRuleRequest", ] diff --git a/langfuse/api/unstable/evaluation_rules/client.py b/langfuse/api/unstable/evaluation_rules/client.py index aa0cefbdf..a1206fbb4 100644 --- a/langfuse/api/unstable/evaluation_rules/client.py +++ b/langfuse/api/unstable/evaluation_rules/client.py @@ -13,6 +13,7 @@ from .types.evaluation_rule import EvaluationRule from .types.evaluation_rule_evaluator_reference import EvaluationRuleEvaluatorReference from .types.evaluation_rules import EvaluationRules +from .types.readable_evaluation_rule import ReadableEvaluationRule # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -159,7 +160,7 @@ def list( """ List evaluation rules in the authenticated project. - Each item describes one live evaluation rule and its effective runtime status. + This includes legacy `trace` and `dataset` rules so they can be inspected and migrated to v4 rules. Legacy rules are read-only through this API; create, update, and delete continue to support only `observation` and `experiment` rules. Parameters ---------- @@ -200,11 +201,11 @@ def get( evaluation_rule_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> EvaluationRule: + ) -> ReadableEvaluationRule: """ Get one evaluation rule by its identifier. - Use this endpoint to inspect the current evaluator, target, mapping, filters, and effective runtime status. + Use this endpoint to inspect the current evaluator, target, mapping, filters, execution timing, and effective runtime status. Legacy `trace` and `dataset` rules are returned for migration and are read-only through this API. Parameters ---------- @@ -216,7 +217,7 @@ def get( Returns ------- - EvaluationRule + ReadableEvaluationRule Examples -------- @@ -538,7 +539,7 @@ async def list( """ List evaluation rules in the authenticated project. - Each item describes one live evaluation rule and its effective runtime status. + This includes legacy `trace` and `dataset` rules so they can be inspected and migrated to v4 rules. Legacy rules are read-only through this API; create, update, and delete continue to support only `observation` and `experiment` rules. Parameters ---------- @@ -587,11 +588,11 @@ async def get( evaluation_rule_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> EvaluationRule: + ) -> ReadableEvaluationRule: """ Get one evaluation rule by its identifier. - Use this endpoint to inspect the current evaluator, target, mapping, filters, and effective runtime status. + Use this endpoint to inspect the current evaluator, target, mapping, filters, execution timing, and effective runtime status. Legacy `trace` and `dataset` rules are returned for migration and are read-only through this API. Parameters ---------- @@ -603,7 +604,7 @@ async def get( Returns ------- - EvaluationRule + ReadableEvaluationRule Examples -------- diff --git a/langfuse/api/unstable/evaluation_rules/raw_client.py b/langfuse/api/unstable/evaluation_rules/raw_client.py index 7115cbe70..803f76d63 100644 --- a/langfuse/api/unstable/evaluation_rules/raw_client.py +++ b/langfuse/api/unstable/evaluation_rules/raw_client.py @@ -49,6 +49,7 @@ from .types.evaluation_rule import EvaluationRule from .types.evaluation_rule_evaluator_reference import EvaluationRuleEvaluatorReference from .types.evaluation_rules import EvaluationRules +from .types.readable_evaluation_rule import ReadableEvaluationRule # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -311,7 +312,7 @@ def list( """ List evaluation rules in the authenticated project. - Each item describes one live evaluation rule and its effective runtime status. + This includes legacy `trace` and `dataset` rules so they can be inspected and migrated to v4 rules. Legacy rules are read-only through this API; create, update, and delete continue to support only `observation` and `experiment` rules. Parameters ---------- @@ -486,11 +487,11 @@ def get( evaluation_rule_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EvaluationRule]: + ) -> HttpResponse[ReadableEvaluationRule]: """ Get one evaluation rule by its identifier. - Use this endpoint to inspect the current evaluator, target, mapping, filters, and effective runtime status. + Use this endpoint to inspect the current evaluator, target, mapping, filters, execution timing, and effective runtime status. Legacy `trace` and `dataset` rules are returned for migration and are read-only through this API. Parameters ---------- @@ -502,7 +503,7 @@ def get( Returns ------- - HttpResponse[EvaluationRule] + HttpResponse[ReadableEvaluationRule] """ _response = self._client_wrapper.httpx_client.request( f"api/public/unstable/evaluation-rules/{jsonable_encoder(evaluation_rule_id)}", @@ -512,9 +513,9 @@ def get( try: if 200 <= _response.status_code < 300: _data = typing.cast( - EvaluationRule, + ReadableEvaluationRule, parse_obj_as( - type_=EvaluationRule, # type: ignore + type_=ReadableEvaluationRule, # type: ignore object_=_response.json(), ), ) @@ -1374,7 +1375,7 @@ async def list( """ List evaluation rules in the authenticated project. - Each item describes one live evaluation rule and its effective runtime status. + This includes legacy `trace` and `dataset` rules so they can be inspected and migrated to v4 rules. Legacy rules are read-only through this API; create, update, and delete continue to support only `observation` and `experiment` rules. Parameters ---------- @@ -1549,11 +1550,11 @@ async def get( evaluation_rule_id: str, *, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EvaluationRule]: + ) -> AsyncHttpResponse[ReadableEvaluationRule]: """ Get one evaluation rule by its identifier. - Use this endpoint to inspect the current evaluator, target, mapping, filters, and effective runtime status. + Use this endpoint to inspect the current evaluator, target, mapping, filters, execution timing, and effective runtime status. Legacy `trace` and `dataset` rules are returned for migration and are read-only through this API. Parameters ---------- @@ -1565,7 +1566,7 @@ async def get( Returns ------- - AsyncHttpResponse[EvaluationRule] + AsyncHttpResponse[ReadableEvaluationRule] """ _response = await self._client_wrapper.httpx_client.request( f"api/public/unstable/evaluation-rules/{jsonable_encoder(evaluation_rule_id)}", @@ -1575,9 +1576,9 @@ async def get( try: if 200 <= _response.status_code < 300: _data = typing.cast( - EvaluationRule, + ReadableEvaluationRule, parse_obj_as( - type_=EvaluationRule, # type: ignore + type_=ReadableEvaluationRule, # type: ignore object_=_response.json(), ), ) diff --git a/langfuse/api/unstable/evaluation_rules/types/__init__.py b/langfuse/api/unstable/evaluation_rules/types/__init__.py index a1cdeb967..cd5d4a832 100644 --- a/langfuse/api/unstable/evaluation_rules/types/__init__.py +++ b/langfuse/api/unstable/evaluation_rules/types/__init__.py @@ -16,13 +16,20 @@ ) from .delete_evaluation_rule_response import DeleteEvaluationRuleResponse from .evaluation_rule import EvaluationRule + from .evaluation_rule_base import EvaluationRuleBase from .evaluation_rule_evaluator import EvaluationRuleEvaluator from .evaluation_rule_evaluator_reference import EvaluationRuleEvaluatorReference + from .evaluation_rule_time_scope import EvaluationRuleTimeScope from .evaluation_rules import EvaluationRules + from .legacy_evaluation_object import LegacyEvaluationObject + from .legacy_evaluation_rule import LegacyEvaluationRule + from .legacy_evaluation_rule_mapping import LegacyEvaluationRuleMapping + from .legacy_evaluation_rule_target import LegacyEvaluationRuleTarget from .llm_as_judge_evaluation_rule_evaluator_reference import ( LlmAsJudgeEvaluationRuleEvaluatorReference, ) from .llm_as_judge_evaluator_type import LlmAsJudgeEvaluatorType + from .readable_evaluation_rule import ReadableEvaluationRule from .update_evaluation_rule_request import UpdateEvaluationRuleRequest _dynamic_imports: typing.Dict[str, str] = { "CodeEvaluationRuleEvaluatorReference": ".code_evaluation_rule_evaluator_reference", @@ -31,11 +38,18 @@ "CreateLlmAsJudgeEvaluationRuleRequest": ".create_llm_as_judge_evaluation_rule_request", "DeleteEvaluationRuleResponse": ".delete_evaluation_rule_response", "EvaluationRule": ".evaluation_rule", + "EvaluationRuleBase": ".evaluation_rule_base", "EvaluationRuleEvaluator": ".evaluation_rule_evaluator", "EvaluationRuleEvaluatorReference": ".evaluation_rule_evaluator_reference", + "EvaluationRuleTimeScope": ".evaluation_rule_time_scope", "EvaluationRules": ".evaluation_rules", + "LegacyEvaluationObject": ".legacy_evaluation_object", + "LegacyEvaluationRule": ".legacy_evaluation_rule", + "LegacyEvaluationRuleMapping": ".legacy_evaluation_rule_mapping", + "LegacyEvaluationRuleTarget": ".legacy_evaluation_rule_target", "LlmAsJudgeEvaluationRuleEvaluatorReference": ".llm_as_judge_evaluation_rule_evaluator_reference", "LlmAsJudgeEvaluatorType": ".llm_as_judge_evaluator_type", + "ReadableEvaluationRule": ".readable_evaluation_rule", "UpdateEvaluationRuleRequest": ".update_evaluation_rule_request", } @@ -74,10 +88,17 @@ def __dir__(): "CreateLlmAsJudgeEvaluationRuleRequest", "DeleteEvaluationRuleResponse", "EvaluationRule", + "EvaluationRuleBase", "EvaluationRuleEvaluator", "EvaluationRuleEvaluatorReference", + "EvaluationRuleTimeScope", "EvaluationRules", + "LegacyEvaluationObject", + "LegacyEvaluationRule", + "LegacyEvaluationRuleMapping", + "LegacyEvaluationRuleTarget", "LlmAsJudgeEvaluationRuleEvaluatorReference", "LlmAsJudgeEvaluatorType", + "ReadableEvaluationRule", "UpdateEvaluationRuleRequest", ] diff --git a/langfuse/api/unstable/evaluation_rules/types/evaluation_rule.py b/langfuse/api/unstable/evaluation_rules/types/evaluation_rule.py index 418004090..e7a098fb0 100644 --- a/langfuse/api/unstable/evaluation_rules/types/evaluation_rule.py +++ b/langfuse/api/unstable/evaluation_rules/types/evaluation_rule.py @@ -1,35 +1,16 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt import typing import pydantic -import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata from ...commons.types.evaluation_rule_filter import EvaluationRuleFilter from ...commons.types.evaluation_rule_mapping import EvaluationRuleMapping -from ...commons.types.evaluation_rule_status import EvaluationRuleStatus from ...commons.types.evaluation_rule_target import EvaluationRuleTarget -from .evaluation_rule_evaluator import EvaluationRuleEvaluator +from .evaluation_rule_base import EvaluationRuleBase -class EvaluationRule(UniversalBaseModel): +class EvaluationRule(EvaluationRuleBase): """ - Live evaluation rule for incoming data. - - An evaluation rule answers: - - which evaluator should be used - - which target objects should trigger scoring - - how often scoring should run - - which target fields should populate each evaluator variable - - whether the deployment is active, inactive, or paused - - Important status semantics: - - `enabled` is the desired on/off setting from the client - - `status` is the effective runtime state after Langfuse applies validation and blocking rules - - `enabled=true` with `status=paused` means the rule should run, but Langfuse has paused it until the underlying problem is fixed - Examples -------- import datetime @@ -88,63 +69,11 @@ class EvaluationRule(UniversalBaseModel): ) """ - id: str = pydantic.Field() - """ - Stable evaluation rule identifier. - """ - - name: str = pydantic.Field() - """ - Human-readable deployment name. This is independent from the evaluator name. - """ - - evaluator: EvaluationRuleEvaluator = pydantic.Field() - """ - Evaluator currently used by this rule. - - `name` and `scope` identify the evaluator family conceptually. - `id` is the currently active evaluator version in that family. - If you create a newer project version with the same evaluator name later, existing evaluation rules are moved to it automatically. - """ - target: EvaluationRuleTarget = pydantic.Field() """ Target object type that should trigger scoring. """ - enabled: bool = pydantic.Field() - """ - Desired enabled state configured by the client. - """ - - status: EvaluationRuleStatus = pydantic.Field() - """ - Effective runtime status after Langfuse applies validation and blocking rules. - """ - - paused_reason: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="pausedReason") - ] = pydantic.Field(default=None) - """ - Machine-readable reason when `status=paused`, otherwise `null`. - """ - - paused_message: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="pausedMessage") - ] = pydantic.Field(default=None) - """ - Human-readable explanation when `status=paused`, otherwise `null`. - """ - - sampling: float = pydantic.Field() - """ - Fraction of matching target objects that should be evaluated. - - Must be greater than `0` and less than or equal to `1`. - - `1` means evaluate every matching target. - - `0.25` means evaluate approximately 25% of matching targets. - """ - filter: typing.List[EvaluationRuleFilter] = pydantic.Field() """ List of filter conditions used to decide whether a target should be evaluated. @@ -155,20 +84,6 @@ class EvaluationRule(UniversalBaseModel): Variable mappings used to populate evaluator runtime variables from the live target object. """ - created_at: typing_extensions.Annotated[ - dt.datetime, FieldMetadata(alias="createdAt") - ] = pydantic.Field() - """ - Timestamp when the evaluation rule was created. - """ - - updated_at: typing_extensions.Annotated[ - dt.datetime, FieldMetadata(alias="updatedAt") - ] = pydantic.Field() - """ - Timestamp when the evaluation rule was last updated. - """ - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True ) diff --git a/langfuse/api/unstable/evaluation_rules/types/evaluation_rule_base.py b/langfuse/api/unstable/evaluation_rules/types/evaluation_rule_base.py new file mode 100644 index 000000000..e504f250b --- /dev/null +++ b/langfuse/api/unstable/evaluation_rules/types/evaluation_rule_base.py @@ -0,0 +1,99 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata +from ...commons.types.evaluation_rule_status import EvaluationRuleStatus +from .evaluation_rule_evaluator import EvaluationRuleEvaluator + + +class EvaluationRuleBase(UniversalBaseModel): + """ + Live evaluation rule for incoming data. + + An evaluation rule answers: + - which evaluator should be used + - which target objects should trigger scoring + - how often scoring should run + - which target fields should populate each evaluator variable + - whether the deployment is active, inactive, or paused + + Important status semantics: + - `enabled` is the desired on/off setting from the client + - `status` is the effective runtime state after Langfuse applies validation and blocking rules + - `enabled=true` with `status=paused` means the rule should run, but Langfuse has paused it until the underlying problem is fixed + """ + + id: str = pydantic.Field() + """ + Stable evaluation rule identifier. + """ + + name: str = pydantic.Field() + """ + Human-readable deployment name. This is independent from the evaluator name. + """ + + evaluator: EvaluationRuleEvaluator = pydantic.Field() + """ + Evaluator currently used by this rule. + + `name` and `scope` identify the evaluator family conceptually. + `id` is the currently active evaluator version in that family. + If you create a newer project version with the same evaluator name later, existing evaluation rules are moved to it automatically. + """ + + enabled: bool = pydantic.Field() + """ + Desired enabled state configured by the client. + """ + + status: EvaluationRuleStatus = pydantic.Field() + """ + Effective runtime status after Langfuse applies validation and blocking rules. + """ + + paused_reason: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="pausedReason") + ] = pydantic.Field(default=None) + """ + Machine-readable reason when `status=paused`, otherwise `null`. + """ + + paused_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="pausedMessage") + ] = pydantic.Field(default=None) + """ + Human-readable explanation when `status=paused`, otherwise `null`. + """ + + sampling: float = pydantic.Field() + """ + Fraction of matching target objects that should be evaluated. + + Must be greater than `0` and less than or equal to `1`. + - `1` means evaluate every matching target. + - `0.25` means evaluate approximately 25% of matching targets. + """ + + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] = pydantic.Field() + """ + Timestamp when the evaluation rule was created. + """ + + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] = pydantic.Field() + """ + Timestamp when the evaluation rule was last updated. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/evaluation_rules/types/evaluation_rule_time_scope.py b/langfuse/api/unstable/evaluation_rules/types/evaluation_rule_time_scope.py new file mode 100644 index 000000000..887769bbb --- /dev/null +++ b/langfuse/api/unstable/evaluation_rules/types/evaluation_rule_time_scope.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class EvaluationRuleTimeScope(enum.StrEnum): + NEW = "NEW" + EXISTING = "EXISTING" + + def visit( + self, + new: typing.Callable[[], T_Result], + existing: typing.Callable[[], T_Result], + ) -> T_Result: + if self is EvaluationRuleTimeScope.NEW: + return new() + if self is EvaluationRuleTimeScope.EXISTING: + return existing() diff --git a/langfuse/api/unstable/evaluation_rules/types/evaluation_rules.py b/langfuse/api/unstable/evaluation_rules/types/evaluation_rules.py index cd1f74c6d..b6d8ddc01 100644 --- a/langfuse/api/unstable/evaluation_rules/types/evaluation_rules.py +++ b/langfuse/api/unstable/evaluation_rules/types/evaluation_rules.py @@ -5,7 +5,7 @@ import pydantic from ....core.pydantic_utilities import UniversalBaseModel from ....utils.pagination.types.meta_response import MetaResponse -from .evaluation_rule import EvaluationRule +from .readable_evaluation_rule import ReadableEvaluationRule class EvaluationRules(UniversalBaseModel): @@ -13,7 +13,7 @@ class EvaluationRules(UniversalBaseModel): Paginated list of evaluation rules. """ - data: typing.List[EvaluationRule] = pydantic.Field() + data: typing.List[ReadableEvaluationRule] = pydantic.Field() """ Evaluation rules in the current page. """ diff --git a/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_object.py b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_object.py new file mode 100644 index 000000000..0cf92f67e --- /dev/null +++ b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_object.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class LegacyEvaluationObject(enum.StrEnum): + TRACE = "trace" + SPAN = "span" + GENERATION = "generation" + EVENT = "event" + AGENT = "agent" + TOOL = "tool" + CHAIN = "chain" + RETRIEVER = "retriever" + EVALUATOR = "evaluator" + EMBEDDING = "embedding" + GUARDRAIL = "guardrail" + DATASET_ITEM = "dataset_item" + + def visit( + self, + trace: typing.Callable[[], T_Result], + span: typing.Callable[[], T_Result], + generation: typing.Callable[[], T_Result], + event: typing.Callable[[], T_Result], + agent: typing.Callable[[], T_Result], + tool: typing.Callable[[], T_Result], + chain: typing.Callable[[], T_Result], + retriever: typing.Callable[[], T_Result], + evaluator: typing.Callable[[], T_Result], + embedding: typing.Callable[[], T_Result], + guardrail: typing.Callable[[], T_Result], + dataset_item: typing.Callable[[], T_Result], + ) -> T_Result: + if self is LegacyEvaluationObject.TRACE: + return trace() + if self is LegacyEvaluationObject.SPAN: + return span() + if self is LegacyEvaluationObject.GENERATION: + return generation() + if self is LegacyEvaluationObject.EVENT: + return event() + if self is LegacyEvaluationObject.AGENT: + return agent() + if self is LegacyEvaluationObject.TOOL: + return tool() + if self is LegacyEvaluationObject.CHAIN: + return chain() + if self is LegacyEvaluationObject.RETRIEVER: + return retriever() + if self is LegacyEvaluationObject.EVALUATOR: + return evaluator() + if self is LegacyEvaluationObject.EMBEDDING: + return embedding() + if self is LegacyEvaluationObject.GUARDRAIL: + return guardrail() + if self is LegacyEvaluationObject.DATASET_ITEM: + return dataset_item() diff --git a/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule.py b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule.py new file mode 100644 index 000000000..b9738f8b7 --- /dev/null +++ b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.serialization import FieldMetadata +from ...commons.types.evaluation_rule_filter import EvaluationRuleFilter +from .evaluation_rule_base import EvaluationRuleBase +from .evaluation_rule_time_scope import EvaluationRuleTimeScope +from .legacy_evaluation_rule_mapping import LegacyEvaluationRuleMapping +from .legacy_evaluation_rule_target import LegacyEvaluationRuleTarget + + +class LegacyEvaluationRule(EvaluationRuleBase): + """ + Legacy trace- or dataset-level evaluation rule returned by list and get for migration. + + This resource is read-only through the unstable public API. Its mapping preserves the trace, dataset item, or named observation that each evaluator variable previously read from. Its filters use the persisted legacy filter format so migration clients can read the configuration without losing information. + """ + + target: LegacyEvaluationRuleTarget + delay: int = pydantic.Field() + """ + Delay in milliseconds before the legacy evaluation job runs. + """ + + time_scope: typing_extensions.Annotated[ + typing.List[EvaluationRuleTimeScope], FieldMetadata(alias="timeScope") + ] = pydantic.Field() + """ + Whether the legacy rule evaluates newly ingested data, existing data, or both. + """ + + filter: typing.List[EvaluationRuleFilter] = pydantic.Field() + """ + Stored filters used by the legacy trace or dataset rule. + """ + + mapping: typing.List[LegacyEvaluationRuleMapping] = pydantic.Field() + """ + Stored variable mappings, including the trace, dataset item, or named observation selected for each variable. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_mapping.py b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_mapping.py new file mode 100644 index 000000000..79a8856ca --- /dev/null +++ b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_mapping.py @@ -0,0 +1,50 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata +from .legacy_evaluation_object import LegacyEvaluationObject + + +class LegacyEvaluationRuleMapping(UniversalBaseModel): + """ + Maps one evaluator variable to a trace, dataset item, or field on a named observation in a legacy rule. + """ + + variable: str = pydantic.Field() + """ + Evaluator prompt variable populated by this mapping. + """ + + langfuse_object: typing_extensions.Annotated[ + LegacyEvaluationObject, FieldMetadata(alias="langfuseObject") + ] = pydantic.Field() + """ + Trace, dataset item, or observation type from which the value is read. + """ + + object_name: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="objectName") + ] = pydantic.Field(default=None) + """ + Observation name to match, or `null` when `langfuseObject` is `trace` or `dataset_item`. + """ + + source: str = pydantic.Field() + """ + Stored field selected from the trace, dataset item, or observation. + """ + + json_path: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="jsonPath") + ] = pydantic.Field(default=None) + """ + Optional JSONPath selector applied to the selected field. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_target.py b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_target.py new file mode 100644 index 000000000..c90d59399 --- /dev/null +++ b/langfuse/api/unstable/evaluation_rules/types/legacy_evaluation_rule_target.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class LegacyEvaluationRuleTarget(enum.StrEnum): + TRACE = "trace" + DATASET = "dataset" + + def visit( + self, + trace: typing.Callable[[], T_Result], + dataset: typing.Callable[[], T_Result], + ) -> T_Result: + if self is LegacyEvaluationRuleTarget.TRACE: + return trace() + if self is LegacyEvaluationRuleTarget.DATASET: + return dataset() diff --git a/langfuse/api/unstable/evaluation_rules/types/readable_evaluation_rule.py b/langfuse/api/unstable/evaluation_rules/types/readable_evaluation_rule.py new file mode 100644 index 000000000..b7eb2ccd0 --- /dev/null +++ b/langfuse/api/unstable/evaluation_rules/types/readable_evaluation_rule.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .evaluation_rule import EvaluationRule +from .legacy_evaluation_rule import LegacyEvaluationRule + +ReadableEvaluationRule = typing.Union[EvaluationRule, LegacyEvaluationRule]