diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index 8b70c0807..3bb2bd07f 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -16,13 +16,12 @@ datasets, health, ingestion, + legacy, llm_connections, media, metrics, - metrics_v2, models, observations, - observations_v2, opentelemetry, organizations, projects, @@ -31,7 +30,6 @@ scim, score, score_configs, - score_v2, sessions, trace, utils, @@ -195,10 +193,8 @@ PatchMediaBody, ) from .metrics import MetricsResponse - from .metrics_v2 import MetricsV2Response from .models import CreateModelRequest, PaginatedModels - from .observations import Observations, ObservationsViews - from .observations_v2 import ObservationsV2Meta, ObservationsV2Response + from .observations import ObservationsV2Meta, ObservationsV2Response from .opentelemetry import ( OtelAttribute, OtelAttributeValue, @@ -271,13 +267,7 @@ ServiceProviderConfig, UserMeta, ) - from .score import CreateScoreRequest, CreateScoreResponse - from .score_configs import ( - CreateScoreConfigRequest, - ScoreConfigs, - UpdateScoreConfigRequest, - ) - from .score_v2 import ( + from .score import ( GetScoresResponse, GetScoresResponseData, GetScoresResponseDataBoolean, @@ -290,6 +280,11 @@ GetScoresResponseData_Numeric, GetScoresResponseTraceData, ) + from .score_configs import ( + CreateScoreConfigRequest, + ScoreConfigs, + UpdateScoreConfigRequest, + ) from .sessions import PaginatedSessions from .trace import DeleteTraceResponse, Sort, Traces _dynamic_imports: typing.Dict[str, str] = { @@ -348,8 +343,6 @@ "CreateObservationEvent": ".ingestion", "CreatePromptRequest": ".prompts", "CreateScoreConfigRequest": ".score_configs", - "CreateScoreRequest": ".score", - "CreateScoreResponse": ".score", "CreateScoreValue": ".commons", "CreateSpanBody": ".ingestion", "CreateSpanEvent": ".ingestion", @@ -374,17 +367,17 @@ "GetMediaResponse": ".media", "GetMediaUploadUrlRequest": ".media", "GetMediaUploadUrlResponse": ".media", - "GetScoresResponse": ".score_v2", - "GetScoresResponseData": ".score_v2", - "GetScoresResponseDataBoolean": ".score_v2", - "GetScoresResponseDataCategorical": ".score_v2", - "GetScoresResponseDataCorrection": ".score_v2", - "GetScoresResponseDataNumeric": ".score_v2", - "GetScoresResponseData_Boolean": ".score_v2", - "GetScoresResponseData_Categorical": ".score_v2", - "GetScoresResponseData_Correction": ".score_v2", - "GetScoresResponseData_Numeric": ".score_v2", - "GetScoresResponseTraceData": ".score_v2", + "GetScoresResponse": ".score", + "GetScoresResponseData": ".score", + "GetScoresResponseDataBoolean": ".score", + "GetScoresResponseDataCategorical": ".score", + "GetScoresResponseDataCorrection": ".score", + "GetScoresResponseDataNumeric": ".score", + "GetScoresResponseData_Boolean": ".score", + "GetScoresResponseData_Categorical": ".score", + "GetScoresResponseData_Correction": ".score", + "GetScoresResponseData_Numeric": ".score", + "GetScoresResponseTraceData": ".score", "HealthResponse": ".health", "IngestionError": ".ingestion", "IngestionEvent": ".ingestion", @@ -413,7 +406,6 @@ "MembershipsResponse": ".organizations", "MethodNotAllowedError": ".commons", "MetricsResponse": ".metrics", - "MetricsV2Response": ".metrics_v2", "Model": ".commons", "ModelPrice": ".commons", "ModelUsageUnit": ".commons", @@ -425,11 +417,9 @@ "ObservationLevel": ".commons", "ObservationType": ".ingestion", "ObservationV2": ".commons", - "Observations": ".observations", - "ObservationsV2Meta": ".observations_v2", - "ObservationsV2Response": ".observations_v2", + "ObservationsV2Meta": ".observations", + "ObservationsV2Response": ".observations", "ObservationsView": ".commons", - "ObservationsViews": ".observations", "OpenAiCompletionUsageSchema": ".ingestion", "OpenAiResponseUsageSchema": ".ingestion", "OpenAiUsage": ".ingestion", @@ -535,13 +525,12 @@ "datasets": ".datasets", "health": ".health", "ingestion": ".ingestion", + "legacy": ".legacy", "llm_connections": ".llm_connections", "media": ".media", "metrics": ".metrics", - "metrics_v2": ".metrics_v2", "models": ".models", "observations": ".observations", - "observations_v2": ".observations_v2", "opentelemetry": ".opentelemetry", "organizations": ".organizations", "projects": ".projects", @@ -550,7 +539,6 @@ "scim": ".scim", "score": ".score", "score_configs": ".score_configs", - "score_v2": ".score_v2", "sessions": ".sessions", "trace": ".trace", "utils": ".utils", @@ -640,8 +628,6 @@ def __dir__(): "CreateObservationEvent", "CreatePromptRequest", "CreateScoreConfigRequest", - "CreateScoreRequest", - "CreateScoreResponse", "CreateScoreValue", "CreateSpanBody", "CreateSpanEvent", @@ -705,7 +691,6 @@ def __dir__(): "MembershipsResponse", "MethodNotAllowedError", "MetricsResponse", - "MetricsV2Response", "Model", "ModelPrice", "ModelUsageUnit", @@ -717,11 +702,9 @@ def __dir__(): "ObservationLevel", "ObservationType", "ObservationV2", - "Observations", "ObservationsV2Meta", "ObservationsV2Response", "ObservationsView", - "ObservationsViews", "OpenAiCompletionUsageSchema", "OpenAiResponseUsageSchema", "OpenAiUsage", @@ -827,13 +810,12 @@ def __dir__(): "datasets", "health", "ingestion", + "legacy", "llm_connections", "media", "metrics", - "metrics_v2", "models", "observations", - "observations_v2", "opentelemetry", "organizations", "projects", @@ -842,7 +824,6 @@ def __dir__(): "scim", "score", "score_configs", - "score_v2", "sessions", "trace", "utils", diff --git a/langfuse/api/client.py b/langfuse/api/client.py index 041f214b9..3252313b9 100644 --- a/langfuse/api/client.py +++ b/langfuse/api/client.py @@ -25,13 +25,12 @@ from .datasets.client import AsyncDatasetsClient, DatasetsClient from .health.client import AsyncHealthClient, HealthClient from .ingestion.client import AsyncIngestionClient, IngestionClient + from .legacy.client import AsyncLegacyClient, LegacyClient from .llm_connections.client import AsyncLlmConnectionsClient, LlmConnectionsClient from .media.client import AsyncMediaClient, MediaClient from .metrics.client import AsyncMetricsClient, MetricsClient - from .metrics_v2.client import AsyncMetricsV2Client, MetricsV2Client from .models.client import AsyncModelsClient, ModelsClient from .observations.client import AsyncObservationsClient, ObservationsClient - from .observations_v2.client import AsyncObservationsV2Client, ObservationsV2Client from .opentelemetry.client import AsyncOpentelemetryClient, OpentelemetryClient from .organizations.client import AsyncOrganizationsClient, OrganizationsClient from .projects.client import AsyncProjectsClient, ProjectsClient @@ -40,7 +39,6 @@ from .scim.client import AsyncScimClient, ScimClient from .score.client import AsyncScoreClient, ScoreClient from .score_configs.client import AsyncScoreConfigsClient, ScoreConfigsClient - from .score_v2.client import AsyncScoreV2Client, ScoreV2Client from .sessions.client import AsyncSessionsClient, SessionsClient from .trace.client import AsyncTraceClient, TraceClient @@ -133,12 +131,11 @@ def __init__( self._datasets: typing.Optional[DatasetsClient] = None self._health: typing.Optional[HealthClient] = None self._ingestion: typing.Optional[IngestionClient] = None + self._legacy: typing.Optional[LegacyClient] = None self._llm_connections: typing.Optional[LlmConnectionsClient] = None self._media: typing.Optional[MediaClient] = None - self._metrics_v2: typing.Optional[MetricsV2Client] = None self._metrics: typing.Optional[MetricsClient] = None self._models: typing.Optional[ModelsClient] = None - self._observations_v2: typing.Optional[ObservationsV2Client] = None self._observations: typing.Optional[ObservationsClient] = None self._opentelemetry: typing.Optional[OpentelemetryClient] = None self._organizations: typing.Optional[OrganizationsClient] = None @@ -147,7 +144,6 @@ def __init__( self._prompts: typing.Optional[PromptsClient] = None self._scim: typing.Optional[ScimClient] = None self._score_configs: typing.Optional[ScoreConfigsClient] = None - self._score_v2: typing.Optional[ScoreV2Client] = None self._score: typing.Optional[ScoreClient] = None self._sessions: typing.Optional[SessionsClient] = None self._trace: typing.Optional[TraceClient] = None @@ -224,6 +220,14 @@ def ingestion(self): self._ingestion = IngestionClient(client_wrapper=self._client_wrapper) return self._ingestion + @property + def legacy(self): + if self._legacy is None: + from .legacy.client import LegacyClient # noqa: E402 + + self._legacy = LegacyClient(client_wrapper=self._client_wrapper) + return self._legacy + @property def llm_connections(self): if self._llm_connections is None: @@ -242,14 +246,6 @@ def media(self): self._media = MediaClient(client_wrapper=self._client_wrapper) return self._media - @property - def metrics_v2(self): - if self._metrics_v2 is None: - from .metrics_v2.client import MetricsV2Client # noqa: E402 - - self._metrics_v2 = MetricsV2Client(client_wrapper=self._client_wrapper) - return self._metrics_v2 - @property def metrics(self): if self._metrics is None: @@ -266,16 +262,6 @@ def models(self): self._models = ModelsClient(client_wrapper=self._client_wrapper) return self._models - @property - def observations_v2(self): - if self._observations_v2 is None: - from .observations_v2.client import ObservationsV2Client # noqa: E402 - - self._observations_v2 = ObservationsV2Client( - client_wrapper=self._client_wrapper - ) - return self._observations_v2 - @property def observations(self): if self._observations is None: @@ -348,14 +334,6 @@ def score_configs(self): ) return self._score_configs - @property - def score_v2(self): - if self._score_v2 is None: - from .score_v2.client import ScoreV2Client # noqa: E402 - - self._score_v2 = ScoreV2Client(client_wrapper=self._client_wrapper) - return self._score_v2 - @property def score(self): if self._score is None: @@ -469,12 +447,11 @@ def __init__( self._datasets: typing.Optional[AsyncDatasetsClient] = None self._health: typing.Optional[AsyncHealthClient] = None self._ingestion: typing.Optional[AsyncIngestionClient] = None + self._legacy: typing.Optional[AsyncLegacyClient] = None self._llm_connections: typing.Optional[AsyncLlmConnectionsClient] = None self._media: typing.Optional[AsyncMediaClient] = None - self._metrics_v2: typing.Optional[AsyncMetricsV2Client] = None self._metrics: typing.Optional[AsyncMetricsClient] = None self._models: typing.Optional[AsyncModelsClient] = None - self._observations_v2: typing.Optional[AsyncObservationsV2Client] = None self._observations: typing.Optional[AsyncObservationsClient] = None self._opentelemetry: typing.Optional[AsyncOpentelemetryClient] = None self._organizations: typing.Optional[AsyncOrganizationsClient] = None @@ -483,7 +460,6 @@ def __init__( self._prompts: typing.Optional[AsyncPromptsClient] = None self._scim: typing.Optional[AsyncScimClient] = None self._score_configs: typing.Optional[AsyncScoreConfigsClient] = None - self._score_v2: typing.Optional[AsyncScoreV2Client] = None self._score: typing.Optional[AsyncScoreClient] = None self._sessions: typing.Optional[AsyncSessionsClient] = None self._trace: typing.Optional[AsyncTraceClient] = None @@ -562,6 +538,14 @@ def ingestion(self): self._ingestion = AsyncIngestionClient(client_wrapper=self._client_wrapper) return self._ingestion + @property + def legacy(self): + if self._legacy is None: + from .legacy.client import AsyncLegacyClient # noqa: E402 + + self._legacy = AsyncLegacyClient(client_wrapper=self._client_wrapper) + return self._legacy + @property def llm_connections(self): if self._llm_connections is None: @@ -580,14 +564,6 @@ def media(self): self._media = AsyncMediaClient(client_wrapper=self._client_wrapper) return self._media - @property - def metrics_v2(self): - if self._metrics_v2 is None: - from .metrics_v2.client import AsyncMetricsV2Client # noqa: E402 - - self._metrics_v2 = AsyncMetricsV2Client(client_wrapper=self._client_wrapper) - return self._metrics_v2 - @property def metrics(self): if self._metrics is None: @@ -604,16 +580,6 @@ def models(self): self._models = AsyncModelsClient(client_wrapper=self._client_wrapper) return self._models - @property - def observations_v2(self): - if self._observations_v2 is None: - from .observations_v2.client import AsyncObservationsV2Client # noqa: E402 - - self._observations_v2 = AsyncObservationsV2Client( - client_wrapper=self._client_wrapper - ) - return self._observations_v2 - @property def observations(self): if self._observations is None: @@ -688,14 +654,6 @@ def score_configs(self): ) return self._score_configs - @property - def score_v2(self): - if self._score_v2 is None: - from .score_v2.client import AsyncScoreV2Client # noqa: E402 - - self._score_v2 = AsyncScoreV2Client(client_wrapper=self._client_wrapper) - return self._score_v2 - @property def score(self): if self._score is None: diff --git a/langfuse/api/legacy/__init__.py b/langfuse/api/legacy/__init__.py new file mode 100644 index 000000000..46ad2601a --- /dev/null +++ b/langfuse/api/legacy/__init__.py @@ -0,0 +1,61 @@ +# 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 . import metrics_v1, observations_v1, score_v1 + from .metrics_v1 import MetricsV1Response + from .observations_v1 import ObservationsV1, ObservationsV1Views + from .score_v1 import CreateScoreV1Request, CreateScoreV1Response +_dynamic_imports: typing.Dict[str, str] = { + "CreateScoreV1Request": ".score_v1", + "CreateScoreV1Response": ".score_v1", + "MetricsV1Response": ".metrics_v1", + "ObservationsV1": ".observations_v1", + "ObservationsV1Views": ".observations_v1", + "metrics_v1": ".metrics_v1", + "observations_v1": ".observations_v1", + "score_v1": ".score_v1", +} + + +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__ = [ + "CreateScoreV1Request", + "CreateScoreV1Response", + "MetricsV1Response", + "ObservationsV1", + "ObservationsV1Views", + "metrics_v1", + "observations_v1", + "score_v1", +] diff --git a/langfuse/api/legacy/client.py b/langfuse/api/legacy/client.py new file mode 100644 index 000000000..3886b9eac --- /dev/null +++ b/langfuse/api/legacy/client.py @@ -0,0 +1,105 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawLegacyClient, RawLegacyClient + +if typing.TYPE_CHECKING: + from .metrics_v1.client import AsyncMetricsV1Client, MetricsV1Client + from .observations_v1.client import AsyncObservationsV1Client, ObservationsV1Client + from .score_v1.client import AsyncScoreV1Client, ScoreV1Client + + +class LegacyClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawLegacyClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._metrics_v1: typing.Optional[MetricsV1Client] = None + self._observations_v1: typing.Optional[ObservationsV1Client] = None + self._score_v1: typing.Optional[ScoreV1Client] = None + + @property + def with_raw_response(self) -> RawLegacyClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawLegacyClient + """ + return self._raw_client + + @property + def metrics_v1(self): + if self._metrics_v1 is None: + from .metrics_v1.client import MetricsV1Client # noqa: E402 + + self._metrics_v1 = MetricsV1Client(client_wrapper=self._client_wrapper) + return self._metrics_v1 + + @property + def observations_v1(self): + if self._observations_v1 is None: + from .observations_v1.client import ObservationsV1Client # noqa: E402 + + self._observations_v1 = ObservationsV1Client( + client_wrapper=self._client_wrapper + ) + return self._observations_v1 + + @property + def score_v1(self): + if self._score_v1 is None: + from .score_v1.client import ScoreV1Client # noqa: E402 + + self._score_v1 = ScoreV1Client(client_wrapper=self._client_wrapper) + return self._score_v1 + + +class AsyncLegacyClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawLegacyClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._metrics_v1: typing.Optional[AsyncMetricsV1Client] = None + self._observations_v1: typing.Optional[AsyncObservationsV1Client] = None + self._score_v1: typing.Optional[AsyncScoreV1Client] = None + + @property + def with_raw_response(self) -> AsyncRawLegacyClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawLegacyClient + """ + return self._raw_client + + @property + def metrics_v1(self): + if self._metrics_v1 is None: + from .metrics_v1.client import AsyncMetricsV1Client # noqa: E402 + + self._metrics_v1 = AsyncMetricsV1Client(client_wrapper=self._client_wrapper) + return self._metrics_v1 + + @property + def observations_v1(self): + if self._observations_v1 is None: + from .observations_v1.client import AsyncObservationsV1Client # noqa: E402 + + self._observations_v1 = AsyncObservationsV1Client( + client_wrapper=self._client_wrapper + ) + return self._observations_v1 + + @property + def score_v1(self): + if self._score_v1 is None: + from .score_v1.client import AsyncScoreV1Client # noqa: E402 + + self._score_v1 = AsyncScoreV1Client(client_wrapper=self._client_wrapper) + return self._score_v1 diff --git a/langfuse/api/metrics_v2/__init__.py b/langfuse/api/legacy/metrics_v1/__init__.py similarity index 87% rename from langfuse/api/metrics_v2/__init__.py rename to langfuse/api/legacy/metrics_v1/__init__.py index 0421785de..0db932338 100644 --- a/langfuse/api/metrics_v2/__init__.py +++ b/langfuse/api/legacy/metrics_v1/__init__.py @@ -6,8 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import MetricsV2Response -_dynamic_imports: typing.Dict[str, str] = {"MetricsV2Response": ".types"} + from .types import MetricsV1Response +_dynamic_imports: typing.Dict[str, str] = {"MetricsV1Response": ".types"} def __getattr__(attr_name: str) -> typing.Any: @@ -37,4 +37,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["MetricsV2Response"] +__all__ = ["MetricsV1Response"] diff --git a/langfuse/api/legacy/metrics_v1/client.py b/langfuse/api/legacy/metrics_v1/client.py new file mode 100644 index 000000000..dee86a789 --- /dev/null +++ b/langfuse/api/legacy/metrics_v1/client.py @@ -0,0 +1,214 @@ +# 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 AsyncRawMetricsV1Client, RawMetricsV1Client +from .types.metrics_v1response import MetricsV1Response + + +class MetricsV1Client: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawMetricsV1Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawMetricsV1Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMetricsV1Client + """ + return self._raw_client + + def metrics( + self, *, query: str, request_options: typing.Optional[RequestOptions] = None + ) -> MetricsV1Response: + """ + Get metrics from the Langfuse project using a query object (Metrics V1, deprecated). + + Use /api/public/v2/metrics for better performance. + + For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + + Parameters + ---------- + query : str + JSON string containing the query parameters with the following structure: + ```json + { + "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "dimensions": [ // Optional. Default: [] + { + "field": string // Field to group by, e.g. "name", "userId", "sessionId" + } + ], + "metrics": [ // Required. At least one metric must be provided + { + "measure": string, // What to measure, e.g. "count", "latency", "value" + "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + } + ], + "filters": [ // Optional. Default: [] + { + "column": string, // Column to filter on + "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "value": any, // Value to compare against + "type": string, // Data type, e.g. "string", "number", "stringObject" + "key": string // Required only when filtering on metadata + } + ], + "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time + "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + }, + "fromTimestamp": string, // Required. ISO datetime string for start of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range + "orderBy": [ // Optional. Default: null + { + "field": string, // Field to order by + "direction": string // "asc" or "desc" + } + ], + "config": { // Optional. Query-specific configuration + "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 + "row_limit": number // Optional. Row limit for results (1-1000) + } + } + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MetricsV1Response + + Examples + -------- + from langfuse import LangfuseAPI + + 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.legacy.metrics_v1.metrics( + query="query", + ) + """ + _response = self._raw_client.metrics( + query=query, request_options=request_options + ) + return _response.data + + +class AsyncMetricsV1Client: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawMetricsV1Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawMetricsV1Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMetricsV1Client + """ + return self._raw_client + + async def metrics( + self, *, query: str, request_options: typing.Optional[RequestOptions] = None + ) -> MetricsV1Response: + """ + Get metrics from the Langfuse project using a query object (Metrics V1, deprecated). + + Use /api/public/v2/metrics for better performance. + + For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + + Parameters + ---------- + query : str + JSON string containing the query parameters with the following structure: + ```json + { + "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "dimensions": [ // Optional. Default: [] + { + "field": string // Field to group by, e.g. "name", "userId", "sessionId" + } + ], + "metrics": [ // Required. At least one metric must be provided + { + "measure": string, // What to measure, e.g. "count", "latency", "value" + "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + } + ], + "filters": [ // Optional. Default: [] + { + "column": string, // Column to filter on + "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "value": any, // Value to compare against + "type": string, // Data type, e.g. "string", "number", "stringObject" + "key": string // Required only when filtering on metadata + } + ], + "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time + "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + }, + "fromTimestamp": string, // Required. ISO datetime string for start of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range + "orderBy": [ // Optional. Default: null + { + "field": string, // Field to order by + "direction": string // "asc" or "desc" + } + ], + "config": { // Optional. Query-specific configuration + "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 + "row_limit": number // Optional. Row limit for results (1-1000) + } + } + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MetricsV1Response + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + 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.legacy.metrics_v1.metrics( + query="query", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.metrics( + query=query, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/legacy/metrics_v1/raw_client.py b/langfuse/api/legacy/metrics_v1/raw_client.py new file mode 100644 index 000000000..382ced9a1 --- /dev/null +++ b/langfuse/api/legacy/metrics_v1/raw_client.py @@ -0,0 +1,322 @@ +# 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 .types.metrics_v1response import MetricsV1Response + + +class RawMetricsV1Client: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def metrics( + self, *, query: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[MetricsV1Response]: + """ + Get metrics from the Langfuse project using a query object (Metrics V1, deprecated). + + Use /api/public/v2/metrics for better performance. + + For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + + Parameters + ---------- + query : str + JSON string containing the query parameters with the following structure: + ```json + { + "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "dimensions": [ // Optional. Default: [] + { + "field": string // Field to group by, e.g. "name", "userId", "sessionId" + } + ], + "metrics": [ // Required. At least one metric must be provided + { + "measure": string, // What to measure, e.g. "count", "latency", "value" + "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + } + ], + "filters": [ // Optional. Default: [] + { + "column": string, // Column to filter on + "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "value": any, // Value to compare against + "type": string, // Data type, e.g. "string", "number", "stringObject" + "key": string // Required only when filtering on metadata + } + ], + "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time + "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + }, + "fromTimestamp": string, // Required. ISO datetime string for start of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range + "orderBy": [ // Optional. Default: null + { + "field": string, // Field to order by + "direction": string // "asc" or "desc" + } + ], + "config": { // Optional. Query-specific configuration + "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 + "row_limit": number // Optional. Row limit for results (1-1000) + } + } + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MetricsV1Response] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/metrics", + method="GET", + params={ + "query": query, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MetricsV1Response, + parse_obj_as( + type_=MetricsV1Response, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 AsyncRawMetricsV1Client: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def metrics( + self, *, query: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[MetricsV1Response]: + """ + Get metrics from the Langfuse project using a query object (Metrics V1, deprecated). + + Use /api/public/v2/metrics for better performance. + + For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + + Parameters + ---------- + query : str + JSON string containing the query parameters with the following structure: + ```json + { + "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "dimensions": [ // Optional. Default: [] + { + "field": string // Field to group by, e.g. "name", "userId", "sessionId" + } + ], + "metrics": [ // Required. At least one metric must be provided + { + "measure": string, // What to measure, e.g. "count", "latency", "value" + "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + } + ], + "filters": [ // Optional. Default: [] + { + "column": string, // Column to filter on + "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "value": any, // Value to compare against + "type": string, // Data type, e.g. "string", "number", "stringObject" + "key": string // Required only when filtering on metadata + } + ], + "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time + "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + }, + "fromTimestamp": string, // Required. ISO datetime string for start of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range + "orderBy": [ // Optional. Default: null + { + "field": string, // Field to order by + "direction": string // "asc" or "desc" + } + ], + "config": { // Optional. Query-specific configuration + "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 + "row_limit": number // Optional. Row limit for results (1-1000) + } + } + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MetricsV1Response] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/metrics", + method="GET", + params={ + "query": query, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MetricsV1Response, + parse_obj_as( + type_=MetricsV1Response, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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/metrics_v2/types/__init__.py b/langfuse/api/legacy/metrics_v1/types/__init__.py similarity index 85% rename from langfuse/api/metrics_v2/types/__init__.py rename to langfuse/api/legacy/metrics_v1/types/__init__.py index b9510d24f..8f4ad17c7 100644 --- a/langfuse/api/metrics_v2/types/__init__.py +++ b/langfuse/api/legacy/metrics_v1/types/__init__.py @@ -6,8 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .metrics_v2response import MetricsV2Response -_dynamic_imports: typing.Dict[str, str] = {"MetricsV2Response": ".metrics_v2response"} + from .metrics_v1response import MetricsV1Response +_dynamic_imports: typing.Dict[str, str] = {"MetricsV1Response": ".metrics_v1response"} def __getattr__(attr_name: str) -> typing.Any: @@ -37,4 +37,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["MetricsV2Response"] +__all__ = ["MetricsV1Response"] diff --git a/langfuse/api/metrics_v2/types/metrics_v2response.py b/langfuse/api/legacy/metrics_v1/types/metrics_v1response.py similarity index 83% rename from langfuse/api/metrics_v2/types/metrics_v2response.py rename to langfuse/api/legacy/metrics_v1/types/metrics_v1response.py index 461eaf178..c0c5aee14 100644 --- a/langfuse/api/metrics_v2/types/metrics_v2response.py +++ b/langfuse/api/legacy/metrics_v1/types/metrics_v1response.py @@ -3,10 +3,10 @@ import typing import pydantic -from ...core.pydantic_utilities import UniversalBaseModel +from ....core.pydantic_utilities import UniversalBaseModel -class MetricsV2Response(UniversalBaseModel): +class MetricsV1Response(UniversalBaseModel): data: typing.List[typing.Dict[str, typing.Any]] = pydantic.Field() """ The metrics data. Each item in the list contains the metric values and dimensions requested in the query. diff --git a/langfuse/api/observations_v2/__init__.py b/langfuse/api/legacy/observations_v1/__init__.py similarity index 83% rename from langfuse/api/observations_v2/__init__.py rename to langfuse/api/legacy/observations_v1/__init__.py index 66816e540..79d74b16d 100644 --- a/langfuse/api/observations_v2/__init__.py +++ b/langfuse/api/legacy/observations_v1/__init__.py @@ -6,10 +6,10 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import ObservationsV2Meta, ObservationsV2Response + from .types import ObservationsV1, ObservationsV1Views _dynamic_imports: typing.Dict[str, str] = { - "ObservationsV2Meta": ".types", - "ObservationsV2Response": ".types", + "ObservationsV1": ".types", + "ObservationsV1Views": ".types", } @@ -40,4 +40,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["ObservationsV2Meta", "ObservationsV2Response"] +__all__ = ["ObservationsV1", "ObservationsV1Views"] diff --git a/langfuse/api/observations_v2/client.py b/langfuse/api/legacy/observations_v1/client.py similarity index 66% rename from langfuse/api/observations_v2/client.py rename to langfuse/api/legacy/observations_v1/client.py index 923a4db4b..832fddb06 100644 --- a/langfuse/api/observations_v2/client.py +++ b/langfuse/api/legacy/observations_v1/client.py @@ -3,36 +3,76 @@ import datetime as dt import typing -from ..commons.types.observation_level import ObservationLevel -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from .raw_client import AsyncRawObservationsV2Client, RawObservationsV2Client -from .types.observations_v2response import ObservationsV2Response +from ...commons.types.observation_level import ObservationLevel +from ...commons.types.observations_view import ObservationsView +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawObservationsV1Client, RawObservationsV1Client +from .types.observations_v1views import ObservationsV1Views -class ObservationsV2Client: +class ObservationsV1Client: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawObservationsV2Client(client_wrapper=client_wrapper) + self._raw_client = RawObservationsV1Client(client_wrapper=client_wrapper) @property - def with_raw_response(self) -> RawObservationsV2Client: + def with_raw_response(self) -> RawObservationsV1Client: """ Retrieves a raw implementation of this client that returns raw responses. Returns ------- - RawObservationsV2Client + RawObservationsV1Client """ return self._raw_client + def get( + self, + observation_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ObservationsView: + """ + Get an observation (Observations V1, deprecated) + + Parameters + ---------- + observation_id : str + The unique langfuse identifier of an observation, can be an event, span or generation + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationsView + + Examples + -------- + from langfuse import LangfuseAPI + + 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.legacy.observations_v1.get( + observation_id="observationId", + ) + """ + _response = self._raw_client.get( + observation_id, request_options=request_options + ) + return _response.data + def get_many( self, *, - fields: typing.Optional[str] = None, - expand_metadata: typing.Optional[str] = None, + page: typing.Optional[int] = None, limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -45,64 +85,25 @@ def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsV2Response: + ) -> ObservationsV1Views: """ - Get a list of observations with cursor-based pagination and flexible field selection. - - ## Cursor-based Pagination - This endpoint uses cursor-based pagination for efficient traversal of large datasets. - The cursor is returned in the response metadata and should be passed in subsequent requests - to retrieve the next page of results. - - ## Field Selection - Use the `fields` parameter to control which observation fields are returned: - - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type - - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId - - `time` - completionStartTime, createdAt, updatedAt - - `io` - input, output - - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) - - `model` - providedModelName, internalModelId, modelParameters - - `usage` - usageDetails, costDetails, totalCost - - `prompt` - promptId, promptName, promptVersion - - `metrics` - latency, timeToFirstToken - - If not specified, `core` and `basic` field groups are returned. - - ## Filters - Multiple filtering options are available via query parameters or the structured `filter` parameter. - When using the `filter` parameter, it takes precedence over individual query parameter filters. + Get a list of observations (Observations V1, deprecated). + + Use /api/public/v2/observations for cursor-based pagination and field selection. Parameters ---------- - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. - Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. - If not specified, `core` and `basic` field groups are returned. - Example: "basic,usage,model" - - expand_metadata : typing.Optional[str] - Comma-separated list of metadata keys to return non-truncated. - By default, metadata values over 200 characters are truncated. - Use this parameter to retrieve full values for specific keys. - Example: "key1,key2" + page : typing.Optional[int] + Page number, starts at 1. limit : typing.Optional[int] - Number of items to return per page. Maximum 1000, default 50. - - cursor : typing.Optional[str] - Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. - - parse_io_as_json : typing.Optional[bool] - **Deprecated.** Setting this to `true` will return a 400 error. - Input/output fields are always returned as raw strings. - Remove this parameter or set it to `false`. + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] - Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -163,13 +164,6 @@ def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag - - `userId` (string) - User ID - - `sessionId` (string) - Session ID - - ### Trace-Related Fields - - `traceName` (string) - Name of the parent trace - - `traceTags` (arrayOptions) - Tags from the parent trace - - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -187,13 +181,19 @@ def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name (alias: `providedModelName`) + - `model` (string) - Provided model name - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. + ### Associated Trace Fields (requires join with traces table) + - `userId` (string) - User ID from associated trace + - `traceName` (string) - Name from associated trace + - `traceEnvironment` (string) - Environment from associated trace + - `traceTags` (arrayOptions) - Tags from associated trace + ## Filter Examples ```json [ @@ -224,7 +224,7 @@ def get_many( Returns ------- - ObservationsV2Response + ObservationsV1Views Examples -------- @@ -238,14 +238,11 @@ def get_many( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.observations_v2.get_many() + client.legacy.observations_v1.get_many() """ _response = self._raw_client.get_many( - fields=fields, - expand_metadata=expand_metadata, + page=page, limit=limit, - cursor=cursor, - parse_io_as_json=parse_io_as_json, name=name, user_id=user_id, type=type, @@ -262,29 +259,76 @@ def get_many( return _response.data -class AsyncObservationsV2Client: +class AsyncObservationsV1Client: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawObservationsV2Client(client_wrapper=client_wrapper) + self._raw_client = AsyncRawObservationsV1Client(client_wrapper=client_wrapper) @property - def with_raw_response(self) -> AsyncRawObservationsV2Client: + def with_raw_response(self) -> AsyncRawObservationsV1Client: """ Retrieves a raw implementation of this client that returns raw responses. Returns ------- - AsyncRawObservationsV2Client + AsyncRawObservationsV1Client """ return self._raw_client + async def get( + self, + observation_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ObservationsView: + """ + Get an observation (Observations V1, deprecated) + + Parameters + ---------- + observation_id : str + The unique langfuse identifier of an observation, can be an event, span or generation + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationsView + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + 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.legacy.observations_v1.get( + observation_id="observationId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + observation_id, request_options=request_options + ) + return _response.data + async def get_many( self, *, - fields: typing.Optional[str] = None, - expand_metadata: typing.Optional[str] = None, + page: typing.Optional[int] = None, limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -297,64 +341,25 @@ async def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsV2Response: + ) -> ObservationsV1Views: """ - Get a list of observations with cursor-based pagination and flexible field selection. - - ## Cursor-based Pagination - This endpoint uses cursor-based pagination for efficient traversal of large datasets. - The cursor is returned in the response metadata and should be passed in subsequent requests - to retrieve the next page of results. - - ## Field Selection - Use the `fields` parameter to control which observation fields are returned: - - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type - - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId - - `time` - completionStartTime, createdAt, updatedAt - - `io` - input, output - - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) - - `model` - providedModelName, internalModelId, modelParameters - - `usage` - usageDetails, costDetails, totalCost - - `prompt` - promptId, promptName, promptVersion - - `metrics` - latency, timeToFirstToken - - If not specified, `core` and `basic` field groups are returned. - - ## Filters - Multiple filtering options are available via query parameters or the structured `filter` parameter. - When using the `filter` parameter, it takes precedence over individual query parameter filters. + Get a list of observations (Observations V1, deprecated). + + Use /api/public/v2/observations for cursor-based pagination and field selection. Parameters ---------- - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. - Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. - If not specified, `core` and `basic` field groups are returned. - Example: "basic,usage,model" - - expand_metadata : typing.Optional[str] - Comma-separated list of metadata keys to return non-truncated. - By default, metadata values over 200 characters are truncated. - Use this parameter to retrieve full values for specific keys. - Example: "key1,key2" + page : typing.Optional[int] + Page number, starts at 1. limit : typing.Optional[int] - Number of items to return per page. Maximum 1000, default 50. - - cursor : typing.Optional[str] - Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. - - parse_io_as_json : typing.Optional[bool] - **Deprecated.** Setting this to `true` will return a 400 error. - Input/output fields are always returned as raw strings. - Remove this parameter or set it to `false`. + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] - Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -415,13 +420,6 @@ async def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag - - `userId` (string) - User ID - - `sessionId` (string) - Session ID - - ### Trace-Related Fields - - `traceName` (string) - Name of the parent trace - - `traceTags` (arrayOptions) - Tags from the parent trace - - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -439,13 +437,19 @@ async def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name (alias: `providedModelName`) + - `model` (string) - Provided model name - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. + ### Associated Trace Fields (requires join with traces table) + - `userId` (string) - User ID from associated trace + - `traceName` (string) - Name from associated trace + - `traceEnvironment` (string) - Environment from associated trace + - `traceTags` (arrayOptions) - Tags from associated trace + ## Filter Examples ```json [ @@ -476,7 +480,7 @@ async def get_many( Returns ------- - ObservationsV2Response + ObservationsV1Views Examples -------- @@ -495,17 +499,14 @@ async def get_many( async def main() -> None: - await client.observations_v2.get_many() + await client.legacy.observations_v1.get_many() asyncio.run(main()) """ _response = await self._raw_client.get_many( - fields=fields, - expand_metadata=expand_metadata, + page=page, limit=limit, - cursor=cursor, - parse_io_as_json=parse_io_as_json, name=name, user_id=user_id, type=type, diff --git a/langfuse/api/observations_v2/raw_client.py b/langfuse/api/legacy/observations_v1/raw_client.py similarity index 65% rename from langfuse/api/observations_v2/raw_client.py rename to langfuse/api/legacy/observations_v1/raw_client.py index f65a45502..86478225c 100644 --- a/langfuse/api/observations_v2/raw_client.py +++ b/langfuse/api/legacy/observations_v1/raw_client.py @@ -4,33 +4,136 @@ 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 ..commons.types.observation_level import ObservationLevel -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.datetime_utils import serialize_datetime -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from .types.observations_v2response import ObservationsV2Response - - -class RawObservationsV2Client: +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 ...commons.types.observation_level import ObservationLevel +from ...commons.types.observations_view import ObservationsView +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.datetime_utils import serialize_datetime +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.jsonable_encoder import jsonable_encoder +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from .types.observations_v1views import ObservationsV1Views + + +class RawObservationsV1Client: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper + def get( + self, + observation_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObservationsView]: + """ + Get an observation (Observations V1, deprecated) + + Parameters + ---------- + observation_id : str + The unique langfuse identifier of an observation, can be an event, span or generation + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObservationsView] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/observations/{jsonable_encoder(observation_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationsView, + parse_obj_as( + type_=ObservationsView, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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, + ) + def get_many( self, *, - fields: typing.Optional[str] = None, - expand_metadata: typing.Optional[str] = None, + page: typing.Optional[int] = None, limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -43,64 +146,25 @@ def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ObservationsV2Response]: + ) -> HttpResponse[ObservationsV1Views]: """ - Get a list of observations with cursor-based pagination and flexible field selection. - - ## Cursor-based Pagination - This endpoint uses cursor-based pagination for efficient traversal of large datasets. - The cursor is returned in the response metadata and should be passed in subsequent requests - to retrieve the next page of results. - - ## Field Selection - Use the `fields` parameter to control which observation fields are returned: - - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type - - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId - - `time` - completionStartTime, createdAt, updatedAt - - `io` - input, output - - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) - - `model` - providedModelName, internalModelId, modelParameters - - `usage` - usageDetails, costDetails, totalCost - - `prompt` - promptId, promptName, promptVersion - - `metrics` - latency, timeToFirstToken - - If not specified, `core` and `basic` field groups are returned. - - ## Filters - Multiple filtering options are available via query parameters or the structured `filter` parameter. - When using the `filter` parameter, it takes precedence over individual query parameter filters. + Get a list of observations (Observations V1, deprecated). + + Use /api/public/v2/observations for cursor-based pagination and field selection. Parameters ---------- - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. - Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. - If not specified, `core` and `basic` field groups are returned. - Example: "basic,usage,model" - - expand_metadata : typing.Optional[str] - Comma-separated list of metadata keys to return non-truncated. - By default, metadata values over 200 characters are truncated. - Use this parameter to retrieve full values for specific keys. - Example: "key1,key2" + page : typing.Optional[int] + Page number, starts at 1. limit : typing.Optional[int] - Number of items to return per page. Maximum 1000, default 50. - - cursor : typing.Optional[str] - Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. - - parse_io_as_json : typing.Optional[bool] - **Deprecated.** Setting this to `true` will return a 400 error. - Input/output fields are always returned as raw strings. - Remove this parameter or set it to `false`. + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] - Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -161,13 +225,6 @@ def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag - - `userId` (string) - User ID - - `sessionId` (string) - Session ID - - ### Trace-Related Fields - - `traceName` (string) - Name of the parent trace - - `traceTags` (arrayOptions) - Tags from the parent trace - - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -185,13 +242,19 @@ def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name (alias: `providedModelName`) + - `model` (string) - Provided model name - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. + ### Associated Trace Fields (requires join with traces table) + - `userId` (string) - User ID from associated trace + - `traceName` (string) - Name from associated trace + - `traceEnvironment` (string) - Environment from associated trace + - `traceTags` (arrayOptions) - Tags from associated trace + ## Filter Examples ```json [ @@ -222,17 +285,14 @@ def get_many( Returns ------- - HttpResponse[ObservationsV2Response] + HttpResponse[ObservationsV1Views] """ _response = self._client_wrapper.httpx_client.request( - "api/public/v2/observations", + "api/public/observations", method="GET", params={ - "fields": fields, - "expandMetadata": expand_metadata, + "page": page, "limit": limit, - "cursor": cursor, - "parseIoAsJson": parse_io_as_json, "name": name, "userId": user_id, "type": type, @@ -254,9 +314,9 @@ def get_many( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationsV2Response, + ObservationsV1Views, parse_obj_as( - type_=ObservationsV2Response, # type: ignore + type_=ObservationsV1Views, # type: ignore object_=_response.json(), ), ) @@ -330,18 +390,119 @@ def get_many( ) -class AsyncRawObservationsV2Client: +class AsyncRawObservationsV1Client: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper + async def get( + self, + observation_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObservationsView]: + """ + Get an observation (Observations V1, deprecated) + + Parameters + ---------- + observation_id : str + The unique langfuse identifier of an observation, can be an event, span or generation + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObservationsView] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/observations/{jsonable_encoder(observation_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationsView, + parse_obj_as( + type_=ObservationsView, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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, + ) + async def get_many( self, *, - fields: typing.Optional[str] = None, - expand_metadata: typing.Optional[str] = None, + page: typing.Optional[int] = None, limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -354,64 +515,25 @@ async def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ObservationsV2Response]: + ) -> AsyncHttpResponse[ObservationsV1Views]: """ - Get a list of observations with cursor-based pagination and flexible field selection. - - ## Cursor-based Pagination - This endpoint uses cursor-based pagination for efficient traversal of large datasets. - The cursor is returned in the response metadata and should be passed in subsequent requests - to retrieve the next page of results. - - ## Field Selection - Use the `fields` parameter to control which observation fields are returned: - - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type - - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId - - `time` - completionStartTime, createdAt, updatedAt - - `io` - input, output - - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) - - `model` - providedModelName, internalModelId, modelParameters - - `usage` - usageDetails, costDetails, totalCost - - `prompt` - promptId, promptName, promptVersion - - `metrics` - latency, timeToFirstToken - - If not specified, `core` and `basic` field groups are returned. - - ## Filters - Multiple filtering options are available via query parameters or the structured `filter` parameter. - When using the `filter` parameter, it takes precedence over individual query parameter filters. + Get a list of observations (Observations V1, deprecated). + + Use /api/public/v2/observations for cursor-based pagination and field selection. Parameters ---------- - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. - Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. - If not specified, `core` and `basic` field groups are returned. - Example: "basic,usage,model" - - expand_metadata : typing.Optional[str] - Comma-separated list of metadata keys to return non-truncated. - By default, metadata values over 200 characters are truncated. - Use this parameter to retrieve full values for specific keys. - Example: "key1,key2" + page : typing.Optional[int] + Page number, starts at 1. limit : typing.Optional[int] - Number of items to return per page. Maximum 1000, default 50. - - cursor : typing.Optional[str] - Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. - - parse_io_as_json : typing.Optional[bool] - **Deprecated.** Setting this to `true` will return a 400 error. - Input/output fields are always returned as raw strings. - Remove this parameter or set it to `false`. + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] - Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -472,13 +594,6 @@ async def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag - - `userId` (string) - User ID - - `sessionId` (string) - Session ID - - ### Trace-Related Fields - - `traceName` (string) - Name of the parent trace - - `traceTags` (arrayOptions) - Tags from the parent trace - - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -496,13 +611,19 @@ async def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name (alias: `providedModelName`) + - `model` (string) - Provided model name - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. + ### Associated Trace Fields (requires join with traces table) + - `userId` (string) - User ID from associated trace + - `traceName` (string) - Name from associated trace + - `traceEnvironment` (string) - Environment from associated trace + - `traceTags` (arrayOptions) - Tags from associated trace + ## Filter Examples ```json [ @@ -533,17 +654,14 @@ async def get_many( Returns ------- - AsyncHttpResponse[ObservationsV2Response] + AsyncHttpResponse[ObservationsV1Views] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/v2/observations", + "api/public/observations", method="GET", params={ - "fields": fields, - "expandMetadata": expand_metadata, + "page": page, "limit": limit, - "cursor": cursor, - "parseIoAsJson": parse_io_as_json, "name": name, "userId": user_id, "type": type, @@ -565,9 +683,9 @@ async def get_many( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationsV2Response, + ObservationsV1Views, parse_obj_as( - type_=ObservationsV2Response, # type: ignore + type_=ObservationsV1Views, # type: ignore object_=_response.json(), ), ) diff --git a/langfuse/api/observations_v2/types/__init__.py b/langfuse/api/legacy/observations_v1/types/__init__.py similarity index 78% rename from langfuse/api/observations_v2/types/__init__.py rename to langfuse/api/legacy/observations_v1/types/__init__.py index 6e132aba6..8b4898e14 100644 --- a/langfuse/api/observations_v2/types/__init__.py +++ b/langfuse/api/legacy/observations_v1/types/__init__.py @@ -6,11 +6,11 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .observations_v2meta import ObservationsV2Meta - from .observations_v2response import ObservationsV2Response + from .observations_v1 import ObservationsV1 + from .observations_v1views import ObservationsV1Views _dynamic_imports: typing.Dict[str, str] = { - "ObservationsV2Meta": ".observations_v2meta", - "ObservationsV2Response": ".observations_v2response", + "ObservationsV1": ".observations_v1", + "ObservationsV1Views": ".observations_v1views", } @@ -41,4 +41,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["ObservationsV2Meta", "ObservationsV2Response"] +__all__ = ["ObservationsV1", "ObservationsV1Views"] diff --git a/langfuse/api/observations/types/observations.py b/langfuse/api/legacy/observations_v1/types/observations_v1.py similarity index 55% rename from langfuse/api/observations/types/observations.py rename to langfuse/api/legacy/observations_v1/types/observations_v1.py index 61a47cdc5..78b722ce6 100644 --- a/langfuse/api/observations/types/observations.py +++ b/langfuse/api/legacy/observations_v1/types/observations_v1.py @@ -3,12 +3,12 @@ import typing import pydantic -from ...commons.types.observation import Observation -from ...core.pydantic_utilities import UniversalBaseModel -from ...utils.pagination.types.meta_response import MetaResponse +from ....commons.types.observation import Observation +from ....core.pydantic_utilities import UniversalBaseModel +from ....utils.pagination.types.meta_response import MetaResponse -class Observations(UniversalBaseModel): +class ObservationsV1(UniversalBaseModel): data: typing.List[Observation] meta: MetaResponse diff --git a/langfuse/api/observations/types/observations_views.py b/langfuse/api/legacy/observations_v1/types/observations_v1views.py similarity index 54% rename from langfuse/api/observations/types/observations_views.py rename to langfuse/api/legacy/observations_v1/types/observations_v1views.py index ee682ed39..abbd7494e 100644 --- a/langfuse/api/observations/types/observations_views.py +++ b/langfuse/api/legacy/observations_v1/types/observations_v1views.py @@ -3,12 +3,12 @@ import typing import pydantic -from ...commons.types.observations_view import ObservationsView -from ...core.pydantic_utilities import UniversalBaseModel -from ...utils.pagination.types.meta_response import MetaResponse +from ....commons.types.observations_view import ObservationsView +from ....core.pydantic_utilities import UniversalBaseModel +from ....utils.pagination.types.meta_response import MetaResponse -class ObservationsViews(UniversalBaseModel): +class ObservationsV1Views(UniversalBaseModel): data: typing.List[ObservationsView] meta: MetaResponse diff --git a/langfuse/api/legacy/raw_client.py b/langfuse/api/legacy/raw_client.py new file mode 100644 index 000000000..0672ed01d --- /dev/null +++ b/langfuse/api/legacy/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawLegacyClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawLegacyClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/langfuse/api/legacy/score_v1/__init__.py b/langfuse/api/legacy/score_v1/__init__.py new file mode 100644 index 000000000..9ccf44e54 --- /dev/null +++ b/langfuse/api/legacy/score_v1/__init__.py @@ -0,0 +1,43 @@ +# 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 CreateScoreV1Request, CreateScoreV1Response +_dynamic_imports: typing.Dict[str, str] = { + "CreateScoreV1Request": ".types", + "CreateScoreV1Response": ".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__ = ["CreateScoreV1Request", "CreateScoreV1Response"] diff --git a/langfuse/api/legacy/score_v1/client.py b/langfuse/api/legacy/score_v1/client.py new file mode 100644 index 000000000..05e37f45a --- /dev/null +++ b/langfuse/api/legacy/score_v1/client.py @@ -0,0 +1,329 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...commons.types.create_score_value import CreateScoreValue +from ...commons.types.score_data_type import ScoreDataType +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawScoreV1Client, RawScoreV1Client +from .types.create_score_v1response import CreateScoreV1Response + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ScoreV1Client: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawScoreV1Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawScoreV1Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawScoreV1Client + """ + return self._raw_client + + def create( + self, + *, + name: str, + value: CreateScoreValue, + id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + session_id: typing.Optional[str] = OMIT, + observation_id: typing.Optional[str] = OMIT, + dataset_run_id: typing.Optional[str] = OMIT, + comment: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + environment: typing.Optional[str] = OMIT, + queue_id: typing.Optional[str] = OMIT, + data_type: typing.Optional[ScoreDataType] = OMIT, + config_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateScoreV1Response: + """ + Create a score (Score V1, deprecated; supports both trace and session scores) + + Parameters + ---------- + name : str + + value : CreateScoreValue + The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) + + id : typing.Optional[str] + + trace_id : typing.Optional[str] + + session_id : typing.Optional[str] + + observation_id : typing.Optional[str] + + dataset_run_id : typing.Optional[str] + + comment : typing.Optional[str] + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + environment : typing.Optional[str] + The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + + queue_id : typing.Optional[str] + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + + data_type : typing.Optional[ScoreDataType] + The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. + + config_id : typing.Optional[str] + Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateScoreV1Response + + Examples + -------- + from langfuse import LangfuseAPI + + 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.legacy.score_v1.create( + name="name", + value=1.1, + ) + """ + _response = self._raw_client.create( + name=name, + value=value, + id=id, + trace_id=trace_id, + session_id=session_id, + observation_id=observation_id, + dataset_run_id=dataset_run_id, + comment=comment, + metadata=metadata, + environment=environment, + queue_id=queue_id, + data_type=data_type, + config_id=config_id, + request_options=request_options, + ) + return _response.data + + def delete( + self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Delete a score (Score V1, deprecated; supports both trace and session scores) + + Parameters + ---------- + score_id : str + The unique langfuse identifier of a score + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from langfuse import LangfuseAPI + + 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.legacy.score_v1.delete( + score_id="scoreId", + ) + """ + _response = self._raw_client.delete(score_id, request_options=request_options) + return _response.data + + +class AsyncScoreV1Client: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawScoreV1Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawScoreV1Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawScoreV1Client + """ + return self._raw_client + + async def create( + self, + *, + name: str, + value: CreateScoreValue, + id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + session_id: typing.Optional[str] = OMIT, + observation_id: typing.Optional[str] = OMIT, + dataset_run_id: typing.Optional[str] = OMIT, + comment: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + environment: typing.Optional[str] = OMIT, + queue_id: typing.Optional[str] = OMIT, + data_type: typing.Optional[ScoreDataType] = OMIT, + config_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateScoreV1Response: + """ + Create a score (Score V1, deprecated; supports both trace and session scores) + + Parameters + ---------- + name : str + + value : CreateScoreValue + The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) + + id : typing.Optional[str] + + trace_id : typing.Optional[str] + + session_id : typing.Optional[str] + + observation_id : typing.Optional[str] + + dataset_run_id : typing.Optional[str] + + comment : typing.Optional[str] + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + environment : typing.Optional[str] + The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + + queue_id : typing.Optional[str] + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + + data_type : typing.Optional[ScoreDataType] + The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. + + config_id : typing.Optional[str] + Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateScoreV1Response + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + 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.legacy.score_v1.create( + name="name", + value=1.1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + name=name, + value=value, + id=id, + trace_id=trace_id, + session_id=session_id, + observation_id=observation_id, + dataset_run_id=dataset_run_id, + comment=comment, + metadata=metadata, + environment=environment, + queue_id=queue_id, + data_type=data_type, + config_id=config_id, + request_options=request_options, + ) + return _response.data + + async def delete( + self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Delete a score (Score V1, deprecated; supports both trace and session scores) + + Parameters + ---------- + score_id : str + The unique langfuse identifier of a score + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + 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.legacy.score_v1.delete( + score_id="scoreId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete( + score_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/score_v2/raw_client.py b/langfuse/api/legacy/score_v1/raw_client.py similarity index 54% rename from langfuse/api/score_v2/raw_client.py rename to langfuse/api/legacy/score_v1/raw_client.py index 2062b5bd9..e81502d98 100644 --- a/langfuse/api/score_v2/raw_client.py +++ b/langfuse/api/legacy/score_v1/raw_client.py @@ -1,170 +1,122 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt 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 ..commons.types.score import Score -from ..commons.types.score_data_type import ScoreDataType -from ..commons.types.score_source import ScoreSource -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.datetime_utils import serialize_datetime -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.jsonable_encoder import jsonable_encoder -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from .types.get_scores_response import GetScoresResponse - - -class RawScoreV2Client: +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 ...commons.types.create_score_value import CreateScoreValue +from ...commons.types.score_data_type import ScoreDataType +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.jsonable_encoder import jsonable_encoder +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from .types.create_score_v1response import CreateScoreV1Response + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawScoreV1Client: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get( + def create( self, *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - user_id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - from_timestamp: typing.Optional[dt.datetime] = None, - to_timestamp: typing.Optional[dt.datetime] = None, - environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - source: typing.Optional[ScoreSource] = None, - operator: typing.Optional[str] = None, - value: typing.Optional[float] = None, - score_ids: typing.Optional[str] = None, - config_id: typing.Optional[str] = None, - session_id: typing.Optional[str] = None, - dataset_run_id: typing.Optional[str] = None, - trace_id: typing.Optional[str] = None, - observation_id: typing.Optional[str] = None, - queue_id: typing.Optional[str] = None, - data_type: typing.Optional[ScoreDataType] = None, - trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - fields: typing.Optional[str] = None, - filter: typing.Optional[str] = None, + name: str, + value: CreateScoreValue, + id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + session_id: typing.Optional[str] = OMIT, + observation_id: typing.Optional[str] = OMIT, + dataset_run_id: typing.Optional[str] = OMIT, + comment: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + environment: typing.Optional[str] = OMIT, + queue_id: typing.Optional[str] = OMIT, + data_type: typing.Optional[ScoreDataType] = OMIT, + config_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[GetScoresResponse]: + ) -> HttpResponse[CreateScoreV1Response]: """ - Get a list of scores (supports both trace and session scores) + Create a score (Score V1, deprecated; supports both trace and session scores) Parameters ---------- - page : typing.Optional[int] - Page number, starts at 1. + name : str - limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. + value : CreateScoreValue + The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) - user_id : typing.Optional[str] - Retrieve only scores with this userId associated to the trace. + id : typing.Optional[str] - name : typing.Optional[str] - Retrieve only scores with this name. - - from_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created on or after a certain datetime (ISO 8601) - - to_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created before a certain datetime (ISO 8601) - - environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Optional filter for scores where the environment is one of the provided values. - - source : typing.Optional[ScoreSource] - Retrieve only scores from a specific source. - - operator : typing.Optional[str] - Retrieve only scores with value. - - value : typing.Optional[float] - Retrieve only scores with value. - - score_ids : typing.Optional[str] - Comma-separated list of score IDs to limit the results to. - - config_id : typing.Optional[str] - Retrieve only scores with a specific configId. + trace_id : typing.Optional[str] session_id : typing.Optional[str] - Retrieve only scores with a specific sessionId. + + observation_id : typing.Optional[str] dataset_run_id : typing.Optional[str] - Retrieve only scores with a specific datasetRunId. - trace_id : typing.Optional[str] - Retrieve only scores with a specific traceId. + comment : typing.Optional[str] - observation_id : typing.Optional[str] - Comma-separated list of observation IDs to filter scores by. + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + environment : typing.Optional[str] + The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. queue_id : typing.Optional[str] - Retrieve only scores with a specific annotation queueId. + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. data_type : typing.Optional[ScoreDataType] - Retrieve only scores with a specific dataType. - - trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Only scores linked to traces that include all of these tags will be returned. - - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. + The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. - filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. + config_id : typing.Optional[str] + Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[GetScoresResponse] + HttpResponse[CreateScoreV1Response] """ _response = self._client_wrapper.httpx_client.request( - "api/public/v2/scores", - method="GET", - params={ - "page": page, - "limit": limit, - "userId": user_id, - "name": name, - "fromTimestamp": serialize_datetime(from_timestamp) - if from_timestamp is not None - else None, - "toTimestamp": serialize_datetime(to_timestamp) - if to_timestamp is not None - else None, - "environment": environment, - "source": source, - "operator": operator, - "value": value, - "scoreIds": score_ids, - "configId": config_id, - "sessionId": session_id, - "datasetRunId": dataset_run_id, + "api/public/scores", + method="POST", + json={ + "id": id, "traceId": trace_id, + "sessionId": session_id, "observationId": observation_id, + "datasetRunId": dataset_run_id, + "name": name, + "value": convert_and_respect_annotation_metadata( + object_=value, annotation=CreateScoreValue, direction="write" + ), + "comment": comment, + "metadata": metadata, + "environment": environment, "queueId": queue_id, "dataType": data_type, - "traceTags": trace_tags, - "fields": fields, - "filter": filter, + "configId": config_id, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - GetScoresResponse, + CreateScoreV1Response, parse_obj_as( - type_=GetScoresResponse, # type: ignore + type_=CreateScoreV1Response, # type: ignore object_=_response.json(), ), ) @@ -237,11 +189,11 @@ def get( body=_response_json, ) - def get_by_id( + def delete( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[Score]: + ) -> HttpResponse[None]: """ - Get a score (supports both trace and session scores) + Delete a score (Score V1, deprecated; supports both trace and session scores) Parameters ---------- @@ -253,23 +205,16 @@ def get_by_id( Returns ------- - HttpResponse[Score] + HttpResponse[None] """ _response = self._client_wrapper.httpx_client.request( - f"api/public/v2/scores/{jsonable_encoder(score_id)}", - method="GET", + f"api/public/scores/{jsonable_encoder(score_id)}", + method="DELETE", request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - Score, - parse_obj_as( - type_=Score, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=None) if _response.status_code == 400: raise Error( headers=dict(_response.headers), @@ -339,149 +284,100 @@ def get_by_id( ) -class AsyncRawScoreV2Client: +class AsyncRawScoreV1Client: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get( + async def create( self, *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - user_id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - from_timestamp: typing.Optional[dt.datetime] = None, - to_timestamp: typing.Optional[dt.datetime] = None, - environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - source: typing.Optional[ScoreSource] = None, - operator: typing.Optional[str] = None, - value: typing.Optional[float] = None, - score_ids: typing.Optional[str] = None, - config_id: typing.Optional[str] = None, - session_id: typing.Optional[str] = None, - dataset_run_id: typing.Optional[str] = None, - trace_id: typing.Optional[str] = None, - observation_id: typing.Optional[str] = None, - queue_id: typing.Optional[str] = None, - data_type: typing.Optional[ScoreDataType] = None, - trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - fields: typing.Optional[str] = None, - filter: typing.Optional[str] = None, + name: str, + value: CreateScoreValue, + id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + session_id: typing.Optional[str] = OMIT, + observation_id: typing.Optional[str] = OMIT, + dataset_run_id: typing.Optional[str] = OMIT, + comment: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + environment: typing.Optional[str] = OMIT, + queue_id: typing.Optional[str] = OMIT, + data_type: typing.Optional[ScoreDataType] = OMIT, + config_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[GetScoresResponse]: + ) -> AsyncHttpResponse[CreateScoreV1Response]: """ - Get a list of scores (supports both trace and session scores) + Create a score (Score V1, deprecated; supports both trace and session scores) Parameters ---------- - page : typing.Optional[int] - Page number, starts at 1. + name : str - limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. + value : CreateScoreValue + The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) - user_id : typing.Optional[str] - Retrieve only scores with this userId associated to the trace. + id : typing.Optional[str] - name : typing.Optional[str] - Retrieve only scores with this name. - - from_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created on or after a certain datetime (ISO 8601) - - to_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created before a certain datetime (ISO 8601) - - environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Optional filter for scores where the environment is one of the provided values. - - source : typing.Optional[ScoreSource] - Retrieve only scores from a specific source. - - operator : typing.Optional[str] - Retrieve only scores with value. - - value : typing.Optional[float] - Retrieve only scores with value. - - score_ids : typing.Optional[str] - Comma-separated list of score IDs to limit the results to. - - config_id : typing.Optional[str] - Retrieve only scores with a specific configId. + trace_id : typing.Optional[str] session_id : typing.Optional[str] - Retrieve only scores with a specific sessionId. + + observation_id : typing.Optional[str] dataset_run_id : typing.Optional[str] - Retrieve only scores with a specific datasetRunId. - trace_id : typing.Optional[str] - Retrieve only scores with a specific traceId. + comment : typing.Optional[str] - observation_id : typing.Optional[str] - Comma-separated list of observation IDs to filter scores by. + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + environment : typing.Optional[str] + The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. queue_id : typing.Optional[str] - Retrieve only scores with a specific annotation queueId. + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. data_type : typing.Optional[ScoreDataType] - Retrieve only scores with a specific dataType. - - trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Only scores linked to traces that include all of these tags will be returned. - - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. + The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. - filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. + config_id : typing.Optional[str] + Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[GetScoresResponse] + AsyncHttpResponse[CreateScoreV1Response] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/v2/scores", - method="GET", - params={ - "page": page, - "limit": limit, - "userId": user_id, - "name": name, - "fromTimestamp": serialize_datetime(from_timestamp) - if from_timestamp is not None - else None, - "toTimestamp": serialize_datetime(to_timestamp) - if to_timestamp is not None - else None, - "environment": environment, - "source": source, - "operator": operator, - "value": value, - "scoreIds": score_ids, - "configId": config_id, - "sessionId": session_id, - "datasetRunId": dataset_run_id, + "api/public/scores", + method="POST", + json={ + "id": id, "traceId": trace_id, + "sessionId": session_id, "observationId": observation_id, + "datasetRunId": dataset_run_id, + "name": name, + "value": convert_and_respect_annotation_metadata( + object_=value, annotation=CreateScoreValue, direction="write" + ), + "comment": comment, + "metadata": metadata, + "environment": environment, "queueId": queue_id, "dataType": data_type, - "traceTags": trace_tags, - "fields": fields, - "filter": filter, + "configId": config_id, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - GetScoresResponse, + CreateScoreV1Response, parse_obj_as( - type_=GetScoresResponse, # type: ignore + type_=CreateScoreV1Response, # type: ignore object_=_response.json(), ), ) @@ -554,11 +450,11 @@ async def get( body=_response_json, ) - async def get_by_id( + async def delete( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[Score]: + ) -> AsyncHttpResponse[None]: """ - Get a score (supports both trace and session scores) + Delete a score (Score V1, deprecated; supports both trace and session scores) Parameters ---------- @@ -570,23 +466,16 @@ async def get_by_id( Returns ------- - AsyncHttpResponse[Score] + AsyncHttpResponse[None] """ _response = await self._client_wrapper.httpx_client.request( - f"api/public/v2/scores/{jsonable_encoder(score_id)}", - method="GET", + f"api/public/scores/{jsonable_encoder(score_id)}", + method="DELETE", request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - Score, - parse_obj_as( - type_=Score, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) + return AsyncHttpResponse(response=_response, data=None) if _response.status_code == 400: raise Error( headers=dict(_response.headers), diff --git a/langfuse/api/legacy/score_v1/types/__init__.py b/langfuse/api/legacy/score_v1/types/__init__.py new file mode 100644 index 000000000..0a8c42ab2 --- /dev/null +++ b/langfuse/api/legacy/score_v1/types/__init__.py @@ -0,0 +1,44 @@ +# 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 .create_score_v1request import CreateScoreV1Request + from .create_score_v1response import CreateScoreV1Response +_dynamic_imports: typing.Dict[str, str] = { + "CreateScoreV1Request": ".create_score_v1request", + "CreateScoreV1Response": ".create_score_v1response", +} + + +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__ = ["CreateScoreV1Request", "CreateScoreV1Response"] diff --git a/langfuse/api/score/types/create_score_request.py b/langfuse/api/legacy/score_v1/types/create_score_v1request.py similarity index 86% rename from langfuse/api/score/types/create_score_request.py rename to langfuse/api/legacy/score_v1/types/create_score_v1request.py index 5491031ca..e23f5effa 100644 --- a/langfuse/api/score/types/create_score_request.py +++ b/langfuse/api/legacy/score_v1/types/create_score_v1request.py @@ -4,19 +4,19 @@ import pydantic import typing_extensions -from ...commons.types.create_score_value import CreateScoreValue -from ...commons.types.score_data_type import ScoreDataType -from ...core.pydantic_utilities import UniversalBaseModel -from ...core.serialization import FieldMetadata +from ....commons.types.create_score_value import CreateScoreValue +from ....commons.types.score_data_type import ScoreDataType +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata -class CreateScoreRequest(UniversalBaseModel): +class CreateScoreV1Request(UniversalBaseModel): """ Examples -------- - from langfuse.score import CreateScoreRequest + from langfuse.legacy.score_v1 import CreateScoreV1Request - CreateScoreRequest( + CreateScoreV1Request( name="novelty", value=0.9, trace_id="cdef-1234-5678-90ab", diff --git a/langfuse/api/score/types/create_score_response.py b/langfuse/api/legacy/score_v1/types/create_score_v1response.py similarity index 74% rename from langfuse/api/score/types/create_score_response.py rename to langfuse/api/legacy/score_v1/types/create_score_v1response.py index 1c20c0f3a..0452daf16 100644 --- a/langfuse/api/score/types/create_score_response.py +++ b/langfuse/api/legacy/score_v1/types/create_score_v1response.py @@ -3,10 +3,10 @@ import typing import pydantic -from ...core.pydantic_utilities import UniversalBaseModel +from ....core.pydantic_utilities import UniversalBaseModel -class CreateScoreResponse(UniversalBaseModel): +class CreateScoreV1Response(UniversalBaseModel): id: str = pydantic.Field() """ The id of the created object in Langfuse diff --git a/langfuse/api/metrics/client.py b/langfuse/api/metrics/client.py index a7a3fe359..62acfff56 100644 --- a/langfuse/api/metrics/client.py +++ b/langfuse/api/metrics/client.py @@ -27,53 +27,157 @@ def metrics( self, *, query: str, request_options: typing.Optional[RequestOptions] = None ) -> MetricsResponse: """ - Get metrics from the Langfuse project using a query object. + Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - Consider using the [v2 metrics endpoint](/api-reference#tag/metricsv2/GET/api/public/v2/metrics) for better performance. + ## V2 Differences + - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) + - Direct access to tags and release fields on observations + - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view + - High cardinality dimensions are not supported and will return a 400 error (see below) For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + ## Available Views + + ### observations + Query observation-level data (spans, generations, events). + + **Dimensions:** + - `environment` - Deployment environment (e.g., production, staging) + - `type` - Type of observation (SPAN, GENERATION, EVENT) + - `name` - Name of the observation + - `level` - Logging level of the observation + - `version` - Version of the observation + - `tags` - User-defined tags + - `release` - Release version + - `traceName` - Name of the parent trace (backwards-compatible) + - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) + - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) + - `providedModelName` - Name of the model used + - `promptName` - Name of the prompt used + - `promptVersion` - Version of the prompt used + - `startTimeMonth` - Month of start_time in YYYY-MM format + + **Measures:** + - `count` - Total number of observations + - `latency` - Observation latency (milliseconds) + - `streamingLatency` - Generation latency from completion start to end (milliseconds) + - `inputTokens` - Sum of input tokens consumed + - `outputTokens` - Sum of output tokens produced + - `totalTokens` - Sum of all tokens consumed + - `outputTokensPerSecond` - Output tokens per second + - `tokensPerSecond` - Total tokens per second + - `inputCost` - Input cost (USD) + - `outputCost` - Output cost (USD) + - `totalCost` - Total cost (USD) + - `timeToFirstToken` - Time to first token (milliseconds) + - `countScores` - Number of scores attached to the observation + + ### scores-numeric + Query numeric and boolean score data. + + **Dimensions:** + - `environment` - Deployment environment + - `name` - Name of the score (e.g., accuracy, toxicity) + - `source` - Origin of the score (API, ANNOTATION, EVAL) + - `dataType` - Data type (NUMERIC, BOOLEAN) + - `configId` - Identifier of the score config + - `timestampMonth` - Month in YYYY-MM format + - `timestampDay` - Day in YYYY-MM-DD format + - `value` - Numeric value of the score + - `traceName` - Name of the parent trace + - `tags` - Tags + - `traceRelease` - Release version + - `traceVersion` - Version + - `observationName` - Name of the associated observation + - `observationModelName` - Model name of the associated observation + - `observationPromptName` - Prompt name of the associated observation + - `observationPromptVersion` - Prompt version of the associated observation + + **Measures:** + - `count` - Total number of scores + - `value` - Score value (for aggregations) + + ### scores-categorical + Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. + + **Measures:** + - `count` - Total number of scores + + ## High Cardinality Dimensions + The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. + Use them in filters instead. + + **observations view:** + - `id` - Use traceId filter to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `parentObservationId` - Use parentObservationId filter instead + + **scores-numeric / scores-categorical views:** + - `id` - Use specific filters to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `observationId` - Use observationId filter instead + + ## Aggregations + Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` + + ## Time Granularities + Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` + - `auto` bins the data into approximately 50 buckets based on the time range + Parameters ---------- query : str JSON string containing the query parameters with the following structure: ```json { - "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" "dimensions": [ // Optional. Default: [] { - "field": string // Field to group by, e.g. "name", "userId", "sessionId" + "field": string // Field to group by (see available dimensions above) } ], "metrics": [ // Required. At least one metric must be provided { - "measure": string, // What to measure, e.g. "count", "latency", "value" - "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + "measure": string, // What to measure (see available measures above) + "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" } ], "filters": [ // Optional. Default: [] { - "column": string, // Column to filter on - "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "column": string, // Column to filter on (any dimension field) + "operator": string, // Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject/numberObject: same as string/number with required "key" + // - boolean: "=", "<>" + // - null: "is null", "is not null" "value": any, // Value to compare against - "type": string, // Data type, e.g. "string", "number", "stringObject" - "key": string // Required only when filtering on metadata + "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) } ], "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" }, "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) "orderBy": [ // Optional. Default: null { - "field": string, // Field to order by + "field": string, // Field to order by (dimension or metric alias) "direction": string // "asc" or "desc" } ], "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 - "row_limit": number // Optional. Row limit for results (1-1000) + "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 + "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 } } ``` @@ -126,53 +230,157 @@ async def metrics( self, *, query: str, request_options: typing.Optional[RequestOptions] = None ) -> MetricsResponse: """ - Get metrics from the Langfuse project using a query object. + Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - Consider using the [v2 metrics endpoint](/api-reference#tag/metricsv2/GET/api/public/v2/metrics) for better performance. + ## V2 Differences + - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) + - Direct access to tags and release fields on observations + - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view + - High cardinality dimensions are not supported and will return a 400 error (see below) For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + ## Available Views + + ### observations + Query observation-level data (spans, generations, events). + + **Dimensions:** + - `environment` - Deployment environment (e.g., production, staging) + - `type` - Type of observation (SPAN, GENERATION, EVENT) + - `name` - Name of the observation + - `level` - Logging level of the observation + - `version` - Version of the observation + - `tags` - User-defined tags + - `release` - Release version + - `traceName` - Name of the parent trace (backwards-compatible) + - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) + - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) + - `providedModelName` - Name of the model used + - `promptName` - Name of the prompt used + - `promptVersion` - Version of the prompt used + - `startTimeMonth` - Month of start_time in YYYY-MM format + + **Measures:** + - `count` - Total number of observations + - `latency` - Observation latency (milliseconds) + - `streamingLatency` - Generation latency from completion start to end (milliseconds) + - `inputTokens` - Sum of input tokens consumed + - `outputTokens` - Sum of output tokens produced + - `totalTokens` - Sum of all tokens consumed + - `outputTokensPerSecond` - Output tokens per second + - `tokensPerSecond` - Total tokens per second + - `inputCost` - Input cost (USD) + - `outputCost` - Output cost (USD) + - `totalCost` - Total cost (USD) + - `timeToFirstToken` - Time to first token (milliseconds) + - `countScores` - Number of scores attached to the observation + + ### scores-numeric + Query numeric and boolean score data. + + **Dimensions:** + - `environment` - Deployment environment + - `name` - Name of the score (e.g., accuracy, toxicity) + - `source` - Origin of the score (API, ANNOTATION, EVAL) + - `dataType` - Data type (NUMERIC, BOOLEAN) + - `configId` - Identifier of the score config + - `timestampMonth` - Month in YYYY-MM format + - `timestampDay` - Day in YYYY-MM-DD format + - `value` - Numeric value of the score + - `traceName` - Name of the parent trace + - `tags` - Tags + - `traceRelease` - Release version + - `traceVersion` - Version + - `observationName` - Name of the associated observation + - `observationModelName` - Model name of the associated observation + - `observationPromptName` - Prompt name of the associated observation + - `observationPromptVersion` - Prompt version of the associated observation + + **Measures:** + - `count` - Total number of scores + - `value` - Score value (for aggregations) + + ### scores-categorical + Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. + + **Measures:** + - `count` - Total number of scores + + ## High Cardinality Dimensions + The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. + Use them in filters instead. + + **observations view:** + - `id` - Use traceId filter to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `parentObservationId` - Use parentObservationId filter instead + + **scores-numeric / scores-categorical views:** + - `id` - Use specific filters to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `observationId` - Use observationId filter instead + + ## Aggregations + Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` + + ## Time Granularities + Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` + - `auto` bins the data into approximately 50 buckets based on the time range + Parameters ---------- query : str JSON string containing the query parameters with the following structure: ```json { - "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" "dimensions": [ // Optional. Default: [] { - "field": string // Field to group by, e.g. "name", "userId", "sessionId" + "field": string // Field to group by (see available dimensions above) } ], "metrics": [ // Required. At least one metric must be provided { - "measure": string, // What to measure, e.g. "count", "latency", "value" - "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + "measure": string, // What to measure (see available measures above) + "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" } ], "filters": [ // Optional. Default: [] { - "column": string, // Column to filter on - "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "column": string, // Column to filter on (any dimension field) + "operator": string, // Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject/numberObject: same as string/number with required "key" + // - boolean: "=", "<>" + // - null: "is null", "is not null" "value": any, // Value to compare against - "type": string, // Data type, e.g. "string", "number", "stringObject" - "key": string // Required only when filtering on metadata + "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) } ], "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" }, "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) "orderBy": [ // Optional. Default: null { - "field": string, // Field to order by + "field": string, // Field to order by (dimension or metric alias) "direction": string // "asc" or "desc" } ], "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 - "row_limit": number // Optional. Row limit for results (1-1000) + "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 + "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 } } ``` diff --git a/langfuse/api/metrics/raw_client.py b/langfuse/api/metrics/raw_client.py index 605b095db..3baecfa6b 100644 --- a/langfuse/api/metrics/raw_client.py +++ b/langfuse/api/metrics/raw_client.py @@ -24,53 +24,157 @@ def metrics( self, *, query: str, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[MetricsResponse]: """ - Get metrics from the Langfuse project using a query object. + Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - Consider using the [v2 metrics endpoint](/api-reference#tag/metricsv2/GET/api/public/v2/metrics) for better performance. + ## V2 Differences + - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) + - Direct access to tags and release fields on observations + - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view + - High cardinality dimensions are not supported and will return a 400 error (see below) For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + ## Available Views + + ### observations + Query observation-level data (spans, generations, events). + + **Dimensions:** + - `environment` - Deployment environment (e.g., production, staging) + - `type` - Type of observation (SPAN, GENERATION, EVENT) + - `name` - Name of the observation + - `level` - Logging level of the observation + - `version` - Version of the observation + - `tags` - User-defined tags + - `release` - Release version + - `traceName` - Name of the parent trace (backwards-compatible) + - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) + - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) + - `providedModelName` - Name of the model used + - `promptName` - Name of the prompt used + - `promptVersion` - Version of the prompt used + - `startTimeMonth` - Month of start_time in YYYY-MM format + + **Measures:** + - `count` - Total number of observations + - `latency` - Observation latency (milliseconds) + - `streamingLatency` - Generation latency from completion start to end (milliseconds) + - `inputTokens` - Sum of input tokens consumed + - `outputTokens` - Sum of output tokens produced + - `totalTokens` - Sum of all tokens consumed + - `outputTokensPerSecond` - Output tokens per second + - `tokensPerSecond` - Total tokens per second + - `inputCost` - Input cost (USD) + - `outputCost` - Output cost (USD) + - `totalCost` - Total cost (USD) + - `timeToFirstToken` - Time to first token (milliseconds) + - `countScores` - Number of scores attached to the observation + + ### scores-numeric + Query numeric and boolean score data. + + **Dimensions:** + - `environment` - Deployment environment + - `name` - Name of the score (e.g., accuracy, toxicity) + - `source` - Origin of the score (API, ANNOTATION, EVAL) + - `dataType` - Data type (NUMERIC, BOOLEAN) + - `configId` - Identifier of the score config + - `timestampMonth` - Month in YYYY-MM format + - `timestampDay` - Day in YYYY-MM-DD format + - `value` - Numeric value of the score + - `traceName` - Name of the parent trace + - `tags` - Tags + - `traceRelease` - Release version + - `traceVersion` - Version + - `observationName` - Name of the associated observation + - `observationModelName` - Model name of the associated observation + - `observationPromptName` - Prompt name of the associated observation + - `observationPromptVersion` - Prompt version of the associated observation + + **Measures:** + - `count` - Total number of scores + - `value` - Score value (for aggregations) + + ### scores-categorical + Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. + + **Measures:** + - `count` - Total number of scores + + ## High Cardinality Dimensions + The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. + Use them in filters instead. + + **observations view:** + - `id` - Use traceId filter to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `parentObservationId` - Use parentObservationId filter instead + + **scores-numeric / scores-categorical views:** + - `id` - Use specific filters to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `observationId` - Use observationId filter instead + + ## Aggregations + Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` + + ## Time Granularities + Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` + - `auto` bins the data into approximately 50 buckets based on the time range + Parameters ---------- query : str JSON string containing the query parameters with the following structure: ```json { - "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" "dimensions": [ // Optional. Default: [] { - "field": string // Field to group by, e.g. "name", "userId", "sessionId" + "field": string // Field to group by (see available dimensions above) } ], "metrics": [ // Required. At least one metric must be provided { - "measure": string, // What to measure, e.g. "count", "latency", "value" - "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + "measure": string, // What to measure (see available measures above) + "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" } ], "filters": [ // Optional. Default: [] { - "column": string, // Column to filter on - "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "column": string, // Column to filter on (any dimension field) + "operator": string, // Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject/numberObject: same as string/number with required "key" + // - boolean: "=", "<>" + // - null: "is null", "is not null" "value": any, // Value to compare against - "type": string, // Data type, e.g. "string", "number", "stringObject" - "key": string // Required only when filtering on metadata + "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) } ], "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" }, "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) "orderBy": [ // Optional. Default: null { - "field": string, // Field to order by + "field": string, // Field to order by (dimension or metric alias) "direction": string // "asc" or "desc" } ], "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 - "row_limit": number // Optional. Row limit for results (1-1000) + "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 + "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 } } ``` @@ -83,7 +187,7 @@ def metrics( HttpResponse[MetricsResponse] """ _response = self._client_wrapper.httpx_client.request( - "api/public/metrics", + "api/public/v2/metrics", method="GET", params={ "query": query, @@ -177,53 +281,157 @@ async def metrics( self, *, query: str, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[MetricsResponse]: """ - Get metrics from the Langfuse project using a query object. + Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - Consider using the [v2 metrics endpoint](/api-reference#tag/metricsv2/GET/api/public/v2/metrics) for better performance. + ## V2 Differences + - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) + - Direct access to tags and release fields on observations + - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view + - High cardinality dimensions are not supported and will return a 400 error (see below) For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). + ## Available Views + + ### observations + Query observation-level data (spans, generations, events). + + **Dimensions:** + - `environment` - Deployment environment (e.g., production, staging) + - `type` - Type of observation (SPAN, GENERATION, EVENT) + - `name` - Name of the observation + - `level` - Logging level of the observation + - `version` - Version of the observation + - `tags` - User-defined tags + - `release` - Release version + - `traceName` - Name of the parent trace (backwards-compatible) + - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) + - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) + - `providedModelName` - Name of the model used + - `promptName` - Name of the prompt used + - `promptVersion` - Version of the prompt used + - `startTimeMonth` - Month of start_time in YYYY-MM format + + **Measures:** + - `count` - Total number of observations + - `latency` - Observation latency (milliseconds) + - `streamingLatency` - Generation latency from completion start to end (milliseconds) + - `inputTokens` - Sum of input tokens consumed + - `outputTokens` - Sum of output tokens produced + - `totalTokens` - Sum of all tokens consumed + - `outputTokensPerSecond` - Output tokens per second + - `tokensPerSecond` - Total tokens per second + - `inputCost` - Input cost (USD) + - `outputCost` - Output cost (USD) + - `totalCost` - Total cost (USD) + - `timeToFirstToken` - Time to first token (milliseconds) + - `countScores` - Number of scores attached to the observation + + ### scores-numeric + Query numeric and boolean score data. + + **Dimensions:** + - `environment` - Deployment environment + - `name` - Name of the score (e.g., accuracy, toxicity) + - `source` - Origin of the score (API, ANNOTATION, EVAL) + - `dataType` - Data type (NUMERIC, BOOLEAN) + - `configId` - Identifier of the score config + - `timestampMonth` - Month in YYYY-MM format + - `timestampDay` - Day in YYYY-MM-DD format + - `value` - Numeric value of the score + - `traceName` - Name of the parent trace + - `tags` - Tags + - `traceRelease` - Release version + - `traceVersion` - Version + - `observationName` - Name of the associated observation + - `observationModelName` - Model name of the associated observation + - `observationPromptName` - Prompt name of the associated observation + - `observationPromptVersion` - Prompt version of the associated observation + + **Measures:** + - `count` - Total number of scores + - `value` - Score value (for aggregations) + + ### scores-categorical + Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. + + **Measures:** + - `count` - Total number of scores + + ## High Cardinality Dimensions + The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. + Use them in filters instead. + + **observations view:** + - `id` - Use traceId filter to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `parentObservationId` - Use parentObservationId filter instead + + **scores-numeric / scores-categorical views:** + - `id` - Use specific filters to narrow down results + - `traceId` - Use traceId filter instead + - `userId` - Use userId filter instead + - `sessionId` - Use sessionId filter instead + - `observationId` - Use observationId filter instead + + ## Aggregations + Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` + + ## Time Granularities + Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` + - `auto` bins the data into approximately 50 buckets based on the time range + Parameters ---------- query : str JSON string containing the query parameters with the following structure: ```json { - "view": string, // Required. One of "traces", "observations", "scores-numeric", "scores-categorical" + "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" "dimensions": [ // Optional. Default: [] { - "field": string // Field to group by, e.g. "name", "userId", "sessionId" + "field": string // Field to group by (see available dimensions above) } ], "metrics": [ // Required. At least one metric must be provided { - "measure": string, // What to measure, e.g. "count", "latency", "value" - "aggregation": string // How to aggregate, e.g. "count", "sum", "avg", "p95", "histogram" + "measure": string, // What to measure (see available measures above) + "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" } ], "filters": [ // Optional. Default: [] { - "column": string, // Column to filter on - "operator": string, // Operator, e.g. "=", ">", "<", "contains" + "column": string, // Column to filter on (any dimension field) + "operator": string, // Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject/numberObject: same as string/number with required "key" + // - boolean: "=", "<>" + // - null: "is null", "is not null" "value": any, // Value to compare against - "type": string, // Data type, e.g. "string", "number", "stringObject" - "key": string // Required only when filtering on metadata + "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) } ], "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "minute", "hour", "day", "week", "month", "auto" + "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" }, "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range + "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) "orderBy": [ // Optional. Default: null { - "field": string, // Field to order by + "field": string, // Field to order by (dimension or metric alias) "direction": string // "asc" or "desc" } ], "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram (1-100), default: 10 - "row_limit": number // Optional. Row limit for results (1-1000) + "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 + "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 } } ``` @@ -236,7 +444,7 @@ async def metrics( AsyncHttpResponse[MetricsResponse] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/metrics", + "api/public/v2/metrics", method="GET", params={ "query": query, diff --git a/langfuse/api/metrics_v2/client.py b/langfuse/api/metrics_v2/client.py deleted file mode 100644 index d6c05914c..000000000 --- a/langfuse/api/metrics_v2/client.py +++ /dev/null @@ -1,422 +0,0 @@ -# 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 AsyncRawMetricsV2Client, RawMetricsV2Client -from .types.metrics_v2response import MetricsV2Response - - -class MetricsV2Client: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawMetricsV2Client(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawMetricsV2Client: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawMetricsV2Client - """ - return self._raw_client - - def metrics( - self, *, query: str, request_options: typing.Optional[RequestOptions] = None - ) -> MetricsV2Response: - """ - Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - - ## V2 Differences - - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) - - Direct access to tags and release fields on observations - - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view - - High cardinality dimensions are not supported and will return a 400 error (see below) - - For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). - - ## Available Views - - ### observations - Query observation-level data (spans, generations, events). - - **Dimensions:** - - `environment` - Deployment environment (e.g., production, staging) - - `type` - Type of observation (SPAN, GENERATION, EVENT) - - `name` - Name of the observation - - `level` - Logging level of the observation - - `version` - Version of the observation - - `tags` - User-defined tags - - `release` - Release version - - `traceName` - Name of the parent trace (backwards-compatible) - - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) - - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) - - `providedModelName` - Name of the model used - - `promptName` - Name of the prompt used - - `promptVersion` - Version of the prompt used - - `startTimeMonth` - Month of start_time in YYYY-MM format - - **Measures:** - - `count` - Total number of observations - - `latency` - Observation latency (milliseconds) - - `streamingLatency` - Generation latency from completion start to end (milliseconds) - - `inputTokens` - Sum of input tokens consumed - - `outputTokens` - Sum of output tokens produced - - `totalTokens` - Sum of all tokens consumed - - `outputTokensPerSecond` - Output tokens per second - - `tokensPerSecond` - Total tokens per second - - `inputCost` - Input cost (USD) - - `outputCost` - Output cost (USD) - - `totalCost` - Total cost (USD) - - `timeToFirstToken` - Time to first token (milliseconds) - - `countScores` - Number of scores attached to the observation - - ### scores-numeric - Query numeric and boolean score data. - - **Dimensions:** - - `environment` - Deployment environment - - `name` - Name of the score (e.g., accuracy, toxicity) - - `source` - Origin of the score (API, ANNOTATION, EVAL) - - `dataType` - Data type (NUMERIC, BOOLEAN) - - `configId` - Identifier of the score config - - `timestampMonth` - Month in YYYY-MM format - - `timestampDay` - Day in YYYY-MM-DD format - - `value` - Numeric value of the score - - `traceName` - Name of the parent trace - - `tags` - Tags - - `traceRelease` - Release version - - `traceVersion` - Version - - `observationName` - Name of the associated observation - - `observationModelName` - Model name of the associated observation - - `observationPromptName` - Prompt name of the associated observation - - `observationPromptVersion` - Prompt version of the associated observation - - **Measures:** - - `count` - Total number of scores - - `value` - Score value (for aggregations) - - ### scores-categorical - Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. - - **Measures:** - - `count` - Total number of scores - - ## High Cardinality Dimensions - The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. - Use them in filters instead. - - **observations view:** - - `id` - Use traceId filter to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `parentObservationId` - Use parentObservationId filter instead - - **scores-numeric / scores-categorical views:** - - `id` - Use specific filters to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `observationId` - Use observationId filter instead - - ## Aggregations - Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` - - ## Time Granularities - Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` - - `auto` bins the data into approximately 50 buckets based on the time range - - Parameters - ---------- - query : str - JSON string containing the query parameters with the following structure: - ```json - { - "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" - "dimensions": [ // Optional. Default: [] - { - "field": string // Field to group by (see available dimensions above) - } - ], - "metrics": [ // Required. At least one metric must be provided - { - "measure": string, // What to measure (see available measures above) - "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" - } - ], - "filters": [ // Optional. Default: [] - { - "column": string, // Column to filter on (any dimension field) - "operator": string, // Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject/numberObject: same as string/number with required "key" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Value to compare against - "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) - } - ], - "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" - }, - "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) - "orderBy": [ // Optional. Default: null - { - "field": string, // Field to order by (dimension or metric alias) - "direction": string // "asc" or "desc" - } - ], - "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 - "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 - } - } - ``` - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MetricsV2Response - - Examples - -------- - from langfuse import LangfuseAPI - - 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.metrics_v2.metrics( - query="query", - ) - """ - _response = self._raw_client.metrics( - query=query, request_options=request_options - ) - return _response.data - - -class AsyncMetricsV2Client: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawMetricsV2Client(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawMetricsV2Client: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawMetricsV2Client - """ - return self._raw_client - - async def metrics( - self, *, query: str, request_options: typing.Optional[RequestOptions] = None - ) -> MetricsV2Response: - """ - Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - - ## V2 Differences - - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) - - Direct access to tags and release fields on observations - - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view - - High cardinality dimensions are not supported and will return a 400 error (see below) - - For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). - - ## Available Views - - ### observations - Query observation-level data (spans, generations, events). - - **Dimensions:** - - `environment` - Deployment environment (e.g., production, staging) - - `type` - Type of observation (SPAN, GENERATION, EVENT) - - `name` - Name of the observation - - `level` - Logging level of the observation - - `version` - Version of the observation - - `tags` - User-defined tags - - `release` - Release version - - `traceName` - Name of the parent trace (backwards-compatible) - - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) - - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) - - `providedModelName` - Name of the model used - - `promptName` - Name of the prompt used - - `promptVersion` - Version of the prompt used - - `startTimeMonth` - Month of start_time in YYYY-MM format - - **Measures:** - - `count` - Total number of observations - - `latency` - Observation latency (milliseconds) - - `streamingLatency` - Generation latency from completion start to end (milliseconds) - - `inputTokens` - Sum of input tokens consumed - - `outputTokens` - Sum of output tokens produced - - `totalTokens` - Sum of all tokens consumed - - `outputTokensPerSecond` - Output tokens per second - - `tokensPerSecond` - Total tokens per second - - `inputCost` - Input cost (USD) - - `outputCost` - Output cost (USD) - - `totalCost` - Total cost (USD) - - `timeToFirstToken` - Time to first token (milliseconds) - - `countScores` - Number of scores attached to the observation - - ### scores-numeric - Query numeric and boolean score data. - - **Dimensions:** - - `environment` - Deployment environment - - `name` - Name of the score (e.g., accuracy, toxicity) - - `source` - Origin of the score (API, ANNOTATION, EVAL) - - `dataType` - Data type (NUMERIC, BOOLEAN) - - `configId` - Identifier of the score config - - `timestampMonth` - Month in YYYY-MM format - - `timestampDay` - Day in YYYY-MM-DD format - - `value` - Numeric value of the score - - `traceName` - Name of the parent trace - - `tags` - Tags - - `traceRelease` - Release version - - `traceVersion` - Version - - `observationName` - Name of the associated observation - - `observationModelName` - Model name of the associated observation - - `observationPromptName` - Prompt name of the associated observation - - `observationPromptVersion` - Prompt version of the associated observation - - **Measures:** - - `count` - Total number of scores - - `value` - Score value (for aggregations) - - ### scores-categorical - Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. - - **Measures:** - - `count` - Total number of scores - - ## High Cardinality Dimensions - The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. - Use them in filters instead. - - **observations view:** - - `id` - Use traceId filter to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `parentObservationId` - Use parentObservationId filter instead - - **scores-numeric / scores-categorical views:** - - `id` - Use specific filters to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `observationId` - Use observationId filter instead - - ## Aggregations - Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` - - ## Time Granularities - Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` - - `auto` bins the data into approximately 50 buckets based on the time range - - Parameters - ---------- - query : str - JSON string containing the query parameters with the following structure: - ```json - { - "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" - "dimensions": [ // Optional. Default: [] - { - "field": string // Field to group by (see available dimensions above) - } - ], - "metrics": [ // Required. At least one metric must be provided - { - "measure": string, // What to measure (see available measures above) - "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" - } - ], - "filters": [ // Optional. Default: [] - { - "column": string, // Column to filter on (any dimension field) - "operator": string, // Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject/numberObject: same as string/number with required "key" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Value to compare against - "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) - } - ], - "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" - }, - "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) - "orderBy": [ // Optional. Default: null - { - "field": string, // Field to order by (dimension or metric alias) - "direction": string // "asc" or "desc" - } - ], - "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 - "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 - } - } - ``` - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MetricsV2Response - - Examples - -------- - import asyncio - - from langfuse import AsyncLangfuseAPI - - 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.metrics_v2.metrics( - query="query", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.metrics( - query=query, request_options=request_options - ) - return _response.data diff --git a/langfuse/api/metrics_v2/raw_client.py b/langfuse/api/metrics_v2/raw_client.py deleted file mode 100644 index b79d55713..000000000 --- a/langfuse/api/metrics_v2/raw_client.py +++ /dev/null @@ -1,530 +0,0 @@ -# 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 .types.metrics_v2response import MetricsV2Response - - -class RawMetricsV2Client: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def metrics( - self, *, query: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[MetricsV2Response]: - """ - Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - - ## V2 Differences - - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) - - Direct access to tags and release fields on observations - - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view - - High cardinality dimensions are not supported and will return a 400 error (see below) - - For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). - - ## Available Views - - ### observations - Query observation-level data (spans, generations, events). - - **Dimensions:** - - `environment` - Deployment environment (e.g., production, staging) - - `type` - Type of observation (SPAN, GENERATION, EVENT) - - `name` - Name of the observation - - `level` - Logging level of the observation - - `version` - Version of the observation - - `tags` - User-defined tags - - `release` - Release version - - `traceName` - Name of the parent trace (backwards-compatible) - - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) - - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) - - `providedModelName` - Name of the model used - - `promptName` - Name of the prompt used - - `promptVersion` - Version of the prompt used - - `startTimeMonth` - Month of start_time in YYYY-MM format - - **Measures:** - - `count` - Total number of observations - - `latency` - Observation latency (milliseconds) - - `streamingLatency` - Generation latency from completion start to end (milliseconds) - - `inputTokens` - Sum of input tokens consumed - - `outputTokens` - Sum of output tokens produced - - `totalTokens` - Sum of all tokens consumed - - `outputTokensPerSecond` - Output tokens per second - - `tokensPerSecond` - Total tokens per second - - `inputCost` - Input cost (USD) - - `outputCost` - Output cost (USD) - - `totalCost` - Total cost (USD) - - `timeToFirstToken` - Time to first token (milliseconds) - - `countScores` - Number of scores attached to the observation - - ### scores-numeric - Query numeric and boolean score data. - - **Dimensions:** - - `environment` - Deployment environment - - `name` - Name of the score (e.g., accuracy, toxicity) - - `source` - Origin of the score (API, ANNOTATION, EVAL) - - `dataType` - Data type (NUMERIC, BOOLEAN) - - `configId` - Identifier of the score config - - `timestampMonth` - Month in YYYY-MM format - - `timestampDay` - Day in YYYY-MM-DD format - - `value` - Numeric value of the score - - `traceName` - Name of the parent trace - - `tags` - Tags - - `traceRelease` - Release version - - `traceVersion` - Version - - `observationName` - Name of the associated observation - - `observationModelName` - Model name of the associated observation - - `observationPromptName` - Prompt name of the associated observation - - `observationPromptVersion` - Prompt version of the associated observation - - **Measures:** - - `count` - Total number of scores - - `value` - Score value (for aggregations) - - ### scores-categorical - Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. - - **Measures:** - - `count` - Total number of scores - - ## High Cardinality Dimensions - The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. - Use them in filters instead. - - **observations view:** - - `id` - Use traceId filter to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `parentObservationId` - Use parentObservationId filter instead - - **scores-numeric / scores-categorical views:** - - `id` - Use specific filters to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `observationId` - Use observationId filter instead - - ## Aggregations - Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` - - ## Time Granularities - Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` - - `auto` bins the data into approximately 50 buckets based on the time range - - Parameters - ---------- - query : str - JSON string containing the query parameters with the following structure: - ```json - { - "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" - "dimensions": [ // Optional. Default: [] - { - "field": string // Field to group by (see available dimensions above) - } - ], - "metrics": [ // Required. At least one metric must be provided - { - "measure": string, // What to measure (see available measures above) - "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" - } - ], - "filters": [ // Optional. Default: [] - { - "column": string, // Column to filter on (any dimension field) - "operator": string, // Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject/numberObject: same as string/number with required "key" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Value to compare against - "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) - } - ], - "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" - }, - "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) - "orderBy": [ // Optional. Default: null - { - "field": string, // Field to order by (dimension or metric alias) - "direction": string // "asc" or "desc" - } - ], - "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 - "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 - } - } - ``` - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[MetricsV2Response] - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/v2/metrics", - method="GET", - params={ - "query": query, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - MetricsV2Response, - parse_obj_as( - type_=MetricsV2Response, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - 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 AsyncRawMetricsV2Client: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def metrics( - self, *, query: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[MetricsV2Response]: - """ - Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance. - - ## V2 Differences - - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported) - - Direct access to tags and release fields on observations - - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view - - High cardinality dimensions are not supported and will return a 400 error (see below) - - For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). - - ## Available Views - - ### observations - Query observation-level data (spans, generations, events). - - **Dimensions:** - - `environment` - Deployment environment (e.g., production, staging) - - `type` - Type of observation (SPAN, GENERATION, EVENT) - - `name` - Name of the observation - - `level` - Logging level of the observation - - `version` - Version of the observation - - `tags` - User-defined tags - - `release` - Release version - - `traceName` - Name of the parent trace (backwards-compatible) - - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release) - - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version) - - `providedModelName` - Name of the model used - - `promptName` - Name of the prompt used - - `promptVersion` - Version of the prompt used - - `startTimeMonth` - Month of start_time in YYYY-MM format - - **Measures:** - - `count` - Total number of observations - - `latency` - Observation latency (milliseconds) - - `streamingLatency` - Generation latency from completion start to end (milliseconds) - - `inputTokens` - Sum of input tokens consumed - - `outputTokens` - Sum of output tokens produced - - `totalTokens` - Sum of all tokens consumed - - `outputTokensPerSecond` - Output tokens per second - - `tokensPerSecond` - Total tokens per second - - `inputCost` - Input cost (USD) - - `outputCost` - Output cost (USD) - - `totalCost` - Total cost (USD) - - `timeToFirstToken` - Time to first token (milliseconds) - - `countScores` - Number of scores attached to the observation - - ### scores-numeric - Query numeric and boolean score data. - - **Dimensions:** - - `environment` - Deployment environment - - `name` - Name of the score (e.g., accuracy, toxicity) - - `source` - Origin of the score (API, ANNOTATION, EVAL) - - `dataType` - Data type (NUMERIC, BOOLEAN) - - `configId` - Identifier of the score config - - `timestampMonth` - Month in YYYY-MM format - - `timestampDay` - Day in YYYY-MM-DD format - - `value` - Numeric value of the score - - `traceName` - Name of the parent trace - - `tags` - Tags - - `traceRelease` - Release version - - `traceVersion` - Version - - `observationName` - Name of the associated observation - - `observationModelName` - Model name of the associated observation - - `observationPromptName` - Prompt name of the associated observation - - `observationPromptVersion` - Prompt version of the associated observation - - **Measures:** - - `count` - Total number of scores - - `value` - Score value (for aggregations) - - ### scores-categorical - Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`. - - **Measures:** - - `count` - Total number of scores - - ## High Cardinality Dimensions - The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues. - Use them in filters instead. - - **observations view:** - - `id` - Use traceId filter to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `parentObservationId` - Use parentObservationId filter instead - - **scores-numeric / scores-categorical views:** - - `id` - Use specific filters to narrow down results - - `traceId` - Use traceId filter instead - - `userId` - Use userId filter instead - - `sessionId` - Use sessionId filter instead - - `observationId` - Use observationId filter instead - - ## Aggregations - Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram` - - ## Time Granularities - Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month` - - `auto` bins the data into approximately 50 buckets based on the time range - - Parameters - ---------- - query : str - JSON string containing the query parameters with the following structure: - ```json - { - "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical" - "dimensions": [ // Optional. Default: [] - { - "field": string // Field to group by (see available dimensions above) - } - ], - "metrics": [ // Required. At least one metric must be provided - { - "measure": string, // What to measure (see available measures above) - "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram" - } - ], - "filters": [ // Optional. Default: [] - { - "column": string, // Column to filter on (any dimension field) - "operator": string, // Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject/numberObject: same as string/number with required "key" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Value to compare against - "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering) - } - ], - "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time - "granularity": string // One of "auto", "minute", "hour", "day", "week", "month" - }, - "fromTimestamp": string, // Required. ISO datetime string for start of time range - "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp) - "orderBy": [ // Optional. Default: null - { - "field": string, // Field to order by (dimension or metric alias) - "direction": string // "asc" or "desc" - } - ], - "config": { // Optional. Query-specific configuration - "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10 - "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100 - } - } - ``` - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[MetricsV2Response] - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/v2/metrics", - method="GET", - params={ - "query": query, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - MetricsV2Response, - parse_obj_as( - type_=MetricsV2Response, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - 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/observations/__init__.py b/langfuse/api/observations/__init__.py index 22b445984..66816e540 100644 --- a/langfuse/api/observations/__init__.py +++ b/langfuse/api/observations/__init__.py @@ -6,10 +6,10 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import Observations, ObservationsViews + from .types import ObservationsV2Meta, ObservationsV2Response _dynamic_imports: typing.Dict[str, str] = { - "Observations": ".types", - "ObservationsViews": ".types", + "ObservationsV2Meta": ".types", + "ObservationsV2Response": ".types", } @@ -40,4 +40,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["Observations", "ObservationsViews"] +__all__ = ["ObservationsV2Meta", "ObservationsV2Response"] diff --git a/langfuse/api/observations/client.py b/langfuse/api/observations/client.py index 6be2a71f4..ce0de0cf2 100644 --- a/langfuse/api/observations/client.py +++ b/langfuse/api/observations/client.py @@ -4,11 +4,10 @@ import typing from ..commons.types.observation_level import ObservationLevel -from ..commons.types.observations_view import ObservationsView from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.request_options import RequestOptions from .raw_client import AsyncRawObservationsClient, RawObservationsClient -from .types.observations_views import ObservationsViews +from .types.observations_v2response import ObservationsV2Response class ObservationsClient: @@ -26,53 +25,14 @@ def with_raw_response(self) -> RawObservationsClient: """ return self._raw_client - def get( - self, - observation_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsView: - """ - Get a observation - - Parameters - ---------- - observation_id : str - The unique langfuse identifier of an observation, can be an event, span or generation - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ObservationsView - - Examples - -------- - from langfuse import LangfuseAPI - - 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.observations.get( - observation_id="observationId", - ) - """ - _response = self._raw_client.get( - observation_id, request_options=request_options - ) - return _response.data - def get_many( self, *, - page: typing.Optional[int] = None, + fields: typing.Optional[str] = None, + expand_metadata: typing.Optional[str] = None, limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -85,25 +45,64 @@ def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsViews: + ) -> ObservationsV2Response: """ - Get a list of observations. - - Consider using the [v2 observations endpoint](/api-reference#tag/observationsv2/GET/api/public/v2/observations) for cursor-based pagination and field selection. + Get a list of observations with cursor-based pagination and flexible field selection. + + ## Cursor-based Pagination + This endpoint uses cursor-based pagination for efficient traversal of large datasets. + The cursor is returned in the response metadata and should be passed in subsequent requests + to retrieve the next page of results. + + ## Field Selection + Use the `fields` parameter to control which observation fields are returned: + - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type + - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId + - `time` - completionStartTime, createdAt, updatedAt + - `io` - input, output + - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) + - `model` - providedModelName, internalModelId, modelParameters + - `usage` - usageDetails, costDetails, totalCost + - `prompt` - promptId, promptName, promptVersion + - `metrics` - latency, timeToFirstToken + + If not specified, `core` and `basic` field groups are returned. + + ## Filters + Multiple filtering options are available via query parameters or the structured `filter` parameter. + When using the `filter` parameter, it takes precedence over individual query parameter filters. Parameters ---------- - page : typing.Optional[int] - Page number, starts at 1. + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. + Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. + If not specified, `core` and `basic` field groups are returned. + Example: "basic,usage,model" + + expand_metadata : typing.Optional[str] + Comma-separated list of metadata keys to return non-truncated. + By default, metadata values over 200 characters are truncated. + Use this parameter to retrieve full values for specific keys. + Example: "key1,key2" limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. + Number of items to return per page. Maximum 1000, default 50. + + cursor : typing.Optional[str] + Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. + + parse_io_as_json : typing.Optional[bool] + **Deprecated.** Setting this to `true` will return a 400 error. + Input/output fields are always returned as raw strings. + Remove this parameter or set it to `false`. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] + Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -164,6 +163,13 @@ def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag + - `userId` (string) - User ID + - `sessionId` (string) - Session ID + + ### Trace-Related Fields + - `traceName` (string) - Name of the parent trace + - `traceTags` (arrayOptions) - Tags from the parent trace + - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -181,19 +187,13 @@ def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name + - `model` (string) - Provided model name (alias: `providedModelName`) - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. - ### Associated Trace Fields (requires join with traces table) - - `userId` (string) - User ID from associated trace - - `traceName` (string) - Name from associated trace - - `traceEnvironment` (string) - Environment from associated trace - - `traceTags` (arrayOptions) - Tags from associated trace - ## Filter Examples ```json [ @@ -224,7 +224,7 @@ def get_many( Returns ------- - ObservationsViews + ObservationsV2Response Examples -------- @@ -241,8 +241,11 @@ def get_many( client.observations.get_many() """ _response = self._raw_client.get_many( - page=page, + fields=fields, + expand_metadata=expand_metadata, limit=limit, + cursor=cursor, + parse_io_as_json=parse_io_as_json, name=name, user_id=user_id, type=type, @@ -274,61 +277,14 @@ def with_raw_response(self) -> AsyncRawObservationsClient: """ return self._raw_client - async def get( - self, - observation_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsView: - """ - Get a observation - - Parameters - ---------- - observation_id : str - The unique langfuse identifier of an observation, can be an event, span or generation - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ObservationsView - - Examples - -------- - import asyncio - - from langfuse import AsyncLangfuseAPI - - 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.observations.get( - observation_id="observationId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get( - observation_id, request_options=request_options - ) - return _response.data - async def get_many( self, *, - page: typing.Optional[int] = None, + fields: typing.Optional[str] = None, + expand_metadata: typing.Optional[str] = None, limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -341,25 +297,64 @@ async def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationsViews: + ) -> ObservationsV2Response: """ - Get a list of observations. - - Consider using the [v2 observations endpoint](/api-reference#tag/observationsv2/GET/api/public/v2/observations) for cursor-based pagination and field selection. + Get a list of observations with cursor-based pagination and flexible field selection. + + ## Cursor-based Pagination + This endpoint uses cursor-based pagination for efficient traversal of large datasets. + The cursor is returned in the response metadata and should be passed in subsequent requests + to retrieve the next page of results. + + ## Field Selection + Use the `fields` parameter to control which observation fields are returned: + - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type + - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId + - `time` - completionStartTime, createdAt, updatedAt + - `io` - input, output + - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) + - `model` - providedModelName, internalModelId, modelParameters + - `usage` - usageDetails, costDetails, totalCost + - `prompt` - promptId, promptName, promptVersion + - `metrics` - latency, timeToFirstToken + + If not specified, `core` and `basic` field groups are returned. + + ## Filters + Multiple filtering options are available via query parameters or the structured `filter` parameter. + When using the `filter` parameter, it takes precedence over individual query parameter filters. Parameters ---------- - page : typing.Optional[int] - Page number, starts at 1. + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. + Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. + If not specified, `core` and `basic` field groups are returned. + Example: "basic,usage,model" + + expand_metadata : typing.Optional[str] + Comma-separated list of metadata keys to return non-truncated. + By default, metadata values over 200 characters are truncated. + Use this parameter to retrieve full values for specific keys. + Example: "key1,key2" limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. + Number of items to return per page. Maximum 1000, default 50. + + cursor : typing.Optional[str] + Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. + + parse_io_as_json : typing.Optional[bool] + **Deprecated.** Setting this to `true` will return a 400 error. + Input/output fields are always returned as raw strings. + Remove this parameter or set it to `false`. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] + Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -420,6 +415,13 @@ async def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag + - `userId` (string) - User ID + - `sessionId` (string) - Session ID + + ### Trace-Related Fields + - `traceName` (string) - Name of the parent trace + - `traceTags` (arrayOptions) - Tags from the parent trace + - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -437,19 +439,13 @@ async def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name + - `model` (string) - Provided model name (alias: `providedModelName`) - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. - ### Associated Trace Fields (requires join with traces table) - - `userId` (string) - User ID from associated trace - - `traceName` (string) - Name from associated trace - - `traceEnvironment` (string) - Environment from associated trace - - `traceTags` (arrayOptions) - Tags from associated trace - ## Filter Examples ```json [ @@ -480,7 +476,7 @@ async def get_many( Returns ------- - ObservationsViews + ObservationsV2Response Examples -------- @@ -505,8 +501,11 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._raw_client.get_many( - page=page, + fields=fields, + expand_metadata=expand_metadata, limit=limit, + cursor=cursor, + parse_io_as_json=parse_io_as_json, name=name, user_id=user_id, type=type, diff --git a/langfuse/api/observations/raw_client.py b/langfuse/api/observations/raw_client.py index 508f8b082..3ae8eab15 100644 --- a/langfuse/api/observations/raw_client.py +++ b/langfuse/api/observations/raw_client.py @@ -10,130 +10,27 @@ 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 ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.datetime_utils import serialize_datetime from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.jsonable_encoder import jsonable_encoder from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions -from .types.observations_views import ObservationsViews +from .types.observations_v2response import ObservationsV2Response class RawObservationsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get( - self, - observation_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ObservationsView]: - """ - Get a observation - - Parameters - ---------- - observation_id : str - The unique langfuse identifier of an observation, can be an event, span or generation - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[ObservationsView] - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/observations/{jsonable_encoder(observation_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - ObservationsView, - parse_obj_as( - type_=ObservationsView, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - 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, - ) - def get_many( self, *, - page: typing.Optional[int] = None, + fields: typing.Optional[str] = None, + expand_metadata: typing.Optional[str] = None, limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -146,25 +43,64 @@ def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ObservationsViews]: + ) -> HttpResponse[ObservationsV2Response]: """ - Get a list of observations. - - Consider using the [v2 observations endpoint](/api-reference#tag/observationsv2/GET/api/public/v2/observations) for cursor-based pagination and field selection. + Get a list of observations with cursor-based pagination and flexible field selection. + + ## Cursor-based Pagination + This endpoint uses cursor-based pagination for efficient traversal of large datasets. + The cursor is returned in the response metadata and should be passed in subsequent requests + to retrieve the next page of results. + + ## Field Selection + Use the `fields` parameter to control which observation fields are returned: + - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type + - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId + - `time` - completionStartTime, createdAt, updatedAt + - `io` - input, output + - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) + - `model` - providedModelName, internalModelId, modelParameters + - `usage` - usageDetails, costDetails, totalCost + - `prompt` - promptId, promptName, promptVersion + - `metrics` - latency, timeToFirstToken + + If not specified, `core` and `basic` field groups are returned. + + ## Filters + Multiple filtering options are available via query parameters or the structured `filter` parameter. + When using the `filter` parameter, it takes precedence over individual query parameter filters. Parameters ---------- - page : typing.Optional[int] - Page number, starts at 1. + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. + Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. + If not specified, `core` and `basic` field groups are returned. + Example: "basic,usage,model" + + expand_metadata : typing.Optional[str] + Comma-separated list of metadata keys to return non-truncated. + By default, metadata values over 200 characters are truncated. + Use this parameter to retrieve full values for specific keys. + Example: "key1,key2" limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. + Number of items to return per page. Maximum 1000, default 50. + + cursor : typing.Optional[str] + Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. + + parse_io_as_json : typing.Optional[bool] + **Deprecated.** Setting this to `true` will return a 400 error. + Input/output fields are always returned as raw strings. + Remove this parameter or set it to `false`. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] + Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -225,6 +161,13 @@ def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag + - `userId` (string) - User ID + - `sessionId` (string) - Session ID + + ### Trace-Related Fields + - `traceName` (string) - Name of the parent trace + - `traceTags` (arrayOptions) - Tags from the parent trace + - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -242,19 +185,13 @@ def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name + - `model` (string) - Provided model name (alias: `providedModelName`) - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. - ### Associated Trace Fields (requires join with traces table) - - `userId` (string) - User ID from associated trace - - `traceName` (string) - Name from associated trace - - `traceEnvironment` (string) - Environment from associated trace - - `traceTags` (arrayOptions) - Tags from associated trace - ## Filter Examples ```json [ @@ -285,14 +222,17 @@ def get_many( Returns ------- - HttpResponse[ObservationsViews] + HttpResponse[ObservationsV2Response] """ _response = self._client_wrapper.httpx_client.request( - "api/public/observations", + "api/public/v2/observations", method="GET", params={ - "page": page, + "fields": fields, + "expandMetadata": expand_metadata, "limit": limit, + "cursor": cursor, + "parseIoAsJson": parse_io_as_json, "name": name, "userId": user_id, "type": type, @@ -314,9 +254,9 @@ def get_many( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationsViews, + ObservationsV2Response, parse_obj_as( - type_=ObservationsViews, # type: ignore + type_=ObservationsV2Response, # type: ignore object_=_response.json(), ), ) @@ -394,115 +334,14 @@ class AsyncRawObservationsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get( - self, - observation_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ObservationsView]: - """ - Get a observation - - Parameters - ---------- - observation_id : str - The unique langfuse identifier of an observation, can be an event, span or generation - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[ObservationsView] - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/observations/{jsonable_encoder(observation_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - ObservationsView, - parse_obj_as( - type_=ObservationsView, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - 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, - ) - async def get_many( self, *, - page: typing.Optional[int] = None, + fields: typing.Optional[str] = None, + expand_metadata: typing.Optional[str] = None, limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + parse_io_as_json: typing.Optional[bool] = None, name: typing.Optional[str] = None, user_id: typing.Optional[str] = None, type: typing.Optional[str] = None, @@ -515,25 +354,64 @@ async def get_many( version: typing.Optional[str] = None, filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ObservationsViews]: + ) -> AsyncHttpResponse[ObservationsV2Response]: """ - Get a list of observations. - - Consider using the [v2 observations endpoint](/api-reference#tag/observationsv2/GET/api/public/v2/observations) for cursor-based pagination and field selection. + Get a list of observations with cursor-based pagination and flexible field selection. + + ## Cursor-based Pagination + This endpoint uses cursor-based pagination for efficient traversal of large datasets. + The cursor is returned in the response metadata and should be passed in subsequent requests + to retrieve the next page of results. + + ## Field Selection + Use the `fields` parameter to control which observation fields are returned: + - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type + - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId + - `time` - completionStartTime, createdAt, updatedAt + - `io` - input, output + - `metadata` - metadata (truncated to 200 chars by default, use `expandMetadata` to get full values) + - `model` - providedModelName, internalModelId, modelParameters + - `usage` - usageDetails, costDetails, totalCost + - `prompt` - promptId, promptName, promptVersion + - `metrics` - latency, timeToFirstToken + + If not specified, `core` and `basic` field groups are returned. + + ## Filters + Multiple filtering options are available via query parameters or the structured `filter` parameter. + When using the `filter` parameter, it takes precedence over individual query parameter filters. Parameters ---------- - page : typing.Optional[int] - Page number, starts at 1. + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. + Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics. + If not specified, `core` and `basic` field groups are returned. + Example: "basic,usage,model" + + expand_metadata : typing.Optional[str] + Comma-separated list of metadata keys to return non-truncated. + By default, metadata values over 200 characters are truncated. + Use this parameter to retrieve full values for specific keys. + Example: "key1,key2" limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. + Number of items to return per page. Maximum 1000, default 50. + + cursor : typing.Optional[str] + Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page. + + parse_io_as_json : typing.Optional[bool] + **Deprecated.** Setting this to `true` will return a 400 error. + Input/output fields are always returned as raw strings. + Remove this parameter or set it to `false`. name : typing.Optional[str] user_id : typing.Optional[str] type : typing.Optional[str] + Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL") trace_id : typing.Optional[str] @@ -594,6 +472,13 @@ async def get_many( - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) - `statusMessage` (string) - Status message - `version` (string) - Version tag + - `userId` (string) - User ID + - `sessionId` (string) - Session ID + + ### Trace-Related Fields + - `traceName` (string) - Name of the parent trace + - `traceTags` (arrayOptions) - Tags from the parent trace + - `tags` (arrayOptions) - Alias for traceTags ### Performance Metrics - `latency` (number) - Latency in seconds (calculated: end_time - start_time) @@ -611,19 +496,13 @@ async def get_many( - `totalCost` (number) - Total cost in USD ### Model Information - - `model` (string) - Provided model name + - `model` (string) - Provided model name (alias: `providedModelName`) - `promptName` (string) - Associated prompt name - `promptVersion` (number) - Associated prompt version ### Structured Data - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. - ### Associated Trace Fields (requires join with traces table) - - `userId` (string) - User ID from associated trace - - `traceName` (string) - Name from associated trace - - `traceEnvironment` (string) - Environment from associated trace - - `traceTags` (arrayOptions) - Tags from associated trace - ## Filter Examples ```json [ @@ -654,14 +533,17 @@ async def get_many( Returns ------- - AsyncHttpResponse[ObservationsViews] + AsyncHttpResponse[ObservationsV2Response] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/observations", + "api/public/v2/observations", method="GET", params={ - "page": page, + "fields": fields, + "expandMetadata": expand_metadata, "limit": limit, + "cursor": cursor, + "parseIoAsJson": parse_io_as_json, "name": name, "userId": user_id, "type": type, @@ -683,9 +565,9 @@ async def get_many( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationsViews, + ObservationsV2Response, parse_obj_as( - type_=ObservationsViews, # type: ignore + type_=ObservationsV2Response, # type: ignore object_=_response.json(), ), ) diff --git a/langfuse/api/observations/types/__init__.py b/langfuse/api/observations/types/__init__.py index 247b674a1..6e132aba6 100644 --- a/langfuse/api/observations/types/__init__.py +++ b/langfuse/api/observations/types/__init__.py @@ -6,11 +6,11 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .observations import Observations - from .observations_views import ObservationsViews + from .observations_v2meta import ObservationsV2Meta + from .observations_v2response import ObservationsV2Response _dynamic_imports: typing.Dict[str, str] = { - "Observations": ".observations", - "ObservationsViews": ".observations_views", + "ObservationsV2Meta": ".observations_v2meta", + "ObservationsV2Response": ".observations_v2response", } @@ -41,4 +41,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["Observations", "ObservationsViews"] +__all__ = ["ObservationsV2Meta", "ObservationsV2Response"] diff --git a/langfuse/api/observations_v2/types/observations_v2meta.py b/langfuse/api/observations/types/observations_v2meta.py similarity index 100% rename from langfuse/api/observations_v2/types/observations_v2meta.py rename to langfuse/api/observations/types/observations_v2meta.py diff --git a/langfuse/api/observations_v2/types/observations_v2response.py b/langfuse/api/observations/types/observations_v2response.py similarity index 100% rename from langfuse/api/observations_v2/types/observations_v2response.py rename to langfuse/api/observations/types/observations_v2response.py diff --git a/langfuse/api/score/__init__.py b/langfuse/api/score/__init__.py index 3d0c7422a..d320aecfc 100644 --- a/langfuse/api/score/__init__.py +++ b/langfuse/api/score/__init__.py @@ -6,10 +6,31 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import CreateScoreRequest, CreateScoreResponse + from .types import ( + GetScoresResponse, + GetScoresResponseData, + GetScoresResponseDataBoolean, + GetScoresResponseDataCategorical, + GetScoresResponseDataCorrection, + GetScoresResponseDataNumeric, + GetScoresResponseData_Boolean, + GetScoresResponseData_Categorical, + GetScoresResponseData_Correction, + GetScoresResponseData_Numeric, + GetScoresResponseTraceData, + ) _dynamic_imports: typing.Dict[str, str] = { - "CreateScoreRequest": ".types", - "CreateScoreResponse": ".types", + "GetScoresResponse": ".types", + "GetScoresResponseData": ".types", + "GetScoresResponseDataBoolean": ".types", + "GetScoresResponseDataCategorical": ".types", + "GetScoresResponseDataCorrection": ".types", + "GetScoresResponseDataNumeric": ".types", + "GetScoresResponseData_Boolean": ".types", + "GetScoresResponseData_Categorical": ".types", + "GetScoresResponseData_Correction": ".types", + "GetScoresResponseData_Numeric": ".types", + "GetScoresResponseTraceData": ".types", } @@ -40,4 +61,16 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["CreateScoreRequest", "CreateScoreResponse"] +__all__ = [ + "GetScoresResponse", + "GetScoresResponseData", + "GetScoresResponseDataBoolean", + "GetScoresResponseDataCategorical", + "GetScoresResponseDataCorrection", + "GetScoresResponseDataNumeric", + "GetScoresResponseData_Boolean", + "GetScoresResponseData_Categorical", + "GetScoresResponseData_Correction", + "GetScoresResponseData_Numeric", + "GetScoresResponseTraceData", +] diff --git a/langfuse/api/score/client.py b/langfuse/api/score/client.py index 7a2ba1b83..01f5f06a5 100644 --- a/langfuse/api/score/client.py +++ b/langfuse/api/score/client.py @@ -1,16 +1,15 @@ # This file was auto-generated by Fern from our API Definition. +import datetime as dt import typing -from ..commons.types.create_score_value import CreateScoreValue +from ..commons.types.score import Score from ..commons.types.score_data_type import ScoreDataType +from ..commons.types.score_source import ScoreSource from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.request_options import RequestOptions from .raw_client import AsyncRawScoreClient, RawScoreClient -from .types.create_score_response import CreateScoreResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) +from .types.get_scores_response import GetScoresResponse class ScoreClient: @@ -28,66 +27,106 @@ def with_raw_response(self) -> RawScoreClient: """ return self._raw_client - def create( + def get( self, *, - name: str, - value: CreateScoreValue, - id: typing.Optional[str] = OMIT, - trace_id: typing.Optional[str] = OMIT, - session_id: typing.Optional[str] = OMIT, - observation_id: typing.Optional[str] = OMIT, - dataset_run_id: typing.Optional[str] = OMIT, - comment: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - environment: typing.Optional[str] = OMIT, - queue_id: typing.Optional[str] = OMIT, - data_type: typing.Optional[ScoreDataType] = OMIT, - config_id: typing.Optional[str] = OMIT, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + user_id: typing.Optional[str] = None, + name: typing.Optional[str] = None, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + source: typing.Optional[ScoreSource] = None, + operator: typing.Optional[str] = None, + value: typing.Optional[float] = None, + score_ids: typing.Optional[str] = None, + config_id: typing.Optional[str] = None, + session_id: typing.Optional[str] = None, + dataset_run_id: typing.Optional[str] = None, + trace_id: typing.Optional[str] = None, + observation_id: typing.Optional[str] = None, + queue_id: typing.Optional[str] = None, + data_type: typing.Optional[ScoreDataType] = None, + trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + fields: typing.Optional[str] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> CreateScoreResponse: + ) -> GetScoresResponse: """ - Create a score (supports both trace and session scores) + Get a list of scores (supports both trace and session scores) Parameters ---------- - name : str + page : typing.Optional[int] + Page number, starts at 1. - value : CreateScoreValue - The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) + limit : typing.Optional[int] + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. - id : typing.Optional[str] + user_id : typing.Optional[str] + Retrieve only scores with this userId associated to the trace. - trace_id : typing.Optional[str] + name : typing.Optional[str] + Retrieve only scores with this name. - session_id : typing.Optional[str] + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created on or after a certain datetime (ISO 8601) - observation_id : typing.Optional[str] + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created before a certain datetime (ISO 8601) - dataset_run_id : typing.Optional[str] + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for scores where the environment is one of the provided values. + + source : typing.Optional[ScoreSource] + Retrieve only scores from a specific source. - comment : typing.Optional[str] + operator : typing.Optional[str] + Retrieve only scores with value. - metadata : typing.Optional[typing.Dict[str, typing.Any]] + value : typing.Optional[float] + Retrieve only scores with value. - environment : typing.Optional[str] - The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + score_ids : typing.Optional[str] + Comma-separated list of score IDs to limit the results to. + + config_id : typing.Optional[str] + Retrieve only scores with a specific configId. + + session_id : typing.Optional[str] + Retrieve only scores with a specific sessionId. + + dataset_run_id : typing.Optional[str] + Retrieve only scores with a specific datasetRunId. + + trace_id : typing.Optional[str] + Retrieve only scores with a specific traceId. + + observation_id : typing.Optional[str] + Comma-separated list of observation IDs to filter scores by. queue_id : typing.Optional[str] - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + Retrieve only scores with a specific annotation queueId. data_type : typing.Optional[ScoreDataType] - The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. + Retrieve only scores with a specific dataType. - config_id : typing.Optional[str] - Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. + trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Only scores linked to traces that include all of these tags will be returned. + + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. + + filter : typing.Optional[str] + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - CreateScoreResponse + GetScoresResponse Examples -------- @@ -101,34 +140,39 @@ def create( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.score.create( - name="name", - value=1.1, - ) + client.score.get() """ - _response = self._raw_client.create( + _response = self._raw_client.get( + page=page, + limit=limit, + user_id=user_id, name=name, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + environment=environment, + source=source, + operator=operator, value=value, - id=id, - trace_id=trace_id, + score_ids=score_ids, + config_id=config_id, session_id=session_id, - observation_id=observation_id, dataset_run_id=dataset_run_id, - comment=comment, - metadata=metadata, - environment=environment, + trace_id=trace_id, + observation_id=observation_id, queue_id=queue_id, data_type=data_type, - config_id=config_id, + trace_tags=trace_tags, + fields=fields, + filter=filter, request_options=request_options, ) return _response.data - def delete( + def get_by_id( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> None: + ) -> Score: """ - Delete a score (supports both trace and session scores) + Get a score (supports both trace and session scores) Parameters ---------- @@ -140,7 +184,7 @@ def delete( Returns ------- - None + Score Examples -------- @@ -154,11 +198,13 @@ def delete( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.score.delete( + client.score.get_by_id( score_id="scoreId", ) """ - _response = self._raw_client.delete(score_id, request_options=request_options) + _response = self._raw_client.get_by_id( + score_id, request_options=request_options + ) return _response.data @@ -177,66 +223,106 @@ def with_raw_response(self) -> AsyncRawScoreClient: """ return self._raw_client - async def create( + async def get( self, *, - name: str, - value: CreateScoreValue, - id: typing.Optional[str] = OMIT, - trace_id: typing.Optional[str] = OMIT, - session_id: typing.Optional[str] = OMIT, - observation_id: typing.Optional[str] = OMIT, - dataset_run_id: typing.Optional[str] = OMIT, - comment: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - environment: typing.Optional[str] = OMIT, - queue_id: typing.Optional[str] = OMIT, - data_type: typing.Optional[ScoreDataType] = OMIT, - config_id: typing.Optional[str] = OMIT, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + user_id: typing.Optional[str] = None, + name: typing.Optional[str] = None, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + source: typing.Optional[ScoreSource] = None, + operator: typing.Optional[str] = None, + value: typing.Optional[float] = None, + score_ids: typing.Optional[str] = None, + config_id: typing.Optional[str] = None, + session_id: typing.Optional[str] = None, + dataset_run_id: typing.Optional[str] = None, + trace_id: typing.Optional[str] = None, + observation_id: typing.Optional[str] = None, + queue_id: typing.Optional[str] = None, + data_type: typing.Optional[ScoreDataType] = None, + trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + fields: typing.Optional[str] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> CreateScoreResponse: + ) -> GetScoresResponse: """ - Create a score (supports both trace and session scores) + Get a list of scores (supports both trace and session scores) Parameters ---------- - name : str + page : typing.Optional[int] + Page number, starts at 1. - value : CreateScoreValue - The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) + limit : typing.Optional[int] + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. - id : typing.Optional[str] + user_id : typing.Optional[str] + Retrieve only scores with this userId associated to the trace. - trace_id : typing.Optional[str] + name : typing.Optional[str] + Retrieve only scores with this name. - session_id : typing.Optional[str] + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created on or after a certain datetime (ISO 8601) - observation_id : typing.Optional[str] + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created before a certain datetime (ISO 8601) - dataset_run_id : typing.Optional[str] + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for scores where the environment is one of the provided values. + + source : typing.Optional[ScoreSource] + Retrieve only scores from a specific source. + + operator : typing.Optional[str] + Retrieve only scores with value. - comment : typing.Optional[str] + value : typing.Optional[float] + Retrieve only scores with value. + + score_ids : typing.Optional[str] + Comma-separated list of score IDs to limit the results to. + + config_id : typing.Optional[str] + Retrieve only scores with a specific configId. + + session_id : typing.Optional[str] + Retrieve only scores with a specific sessionId. + + dataset_run_id : typing.Optional[str] + Retrieve only scores with a specific datasetRunId. - metadata : typing.Optional[typing.Dict[str, typing.Any]] + trace_id : typing.Optional[str] + Retrieve only scores with a specific traceId. - environment : typing.Optional[str] - The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + observation_id : typing.Optional[str] + Comma-separated list of observation IDs to filter scores by. queue_id : typing.Optional[str] - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + Retrieve only scores with a specific annotation queueId. data_type : typing.Optional[ScoreDataType] - The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. + Retrieve only scores with a specific dataType. - config_id : typing.Optional[str] - Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. + trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Only scores linked to traces that include all of these tags will be returned. + + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. + + filter : typing.Optional[str] + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - CreateScoreResponse + GetScoresResponse Examples -------- @@ -255,37 +341,42 @@ async def create( async def main() -> None: - await client.score.create( - name="name", - value=1.1, - ) + await client.score.get() asyncio.run(main()) """ - _response = await self._raw_client.create( + _response = await self._raw_client.get( + page=page, + limit=limit, + user_id=user_id, name=name, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + environment=environment, + source=source, + operator=operator, value=value, - id=id, - trace_id=trace_id, + score_ids=score_ids, + config_id=config_id, session_id=session_id, - observation_id=observation_id, dataset_run_id=dataset_run_id, - comment=comment, - metadata=metadata, - environment=environment, + trace_id=trace_id, + observation_id=observation_id, queue_id=queue_id, data_type=data_type, - config_id=config_id, + trace_tags=trace_tags, + fields=fields, + filter=filter, request_options=request_options, ) return _response.data - async def delete( + async def get_by_id( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> None: + ) -> Score: """ - Delete a score (supports both trace and session scores) + Get a score (supports both trace and session scores) Parameters ---------- @@ -297,7 +388,7 @@ async def delete( Returns ------- - None + Score Examples -------- @@ -316,14 +407,14 @@ async def delete( async def main() -> None: - await client.score.delete( + await client.score.get_by_id( score_id="scoreId", ) asyncio.run(main()) """ - _response = await self._raw_client.delete( + _response = await self._raw_client.get_by_id( score_id, request_options=request_options ) return _response.data diff --git a/langfuse/api/score/raw_client.py b/langfuse/api/score/raw_client.py index 7c20eee08..fe21ae775 100644 --- a/langfuse/api/score/raw_client.py +++ b/langfuse/api/score/raw_client.py @@ -1,5 +1,6 @@ # This file was auto-generated by Fern from our API Definition. +import datetime as dt import typing from json.decoder import JSONDecodeError @@ -8,115 +9,162 @@ 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 ..commons.types.create_score_value import CreateScoreValue +from ..commons.types.score import Score from ..commons.types.score_data_type import ScoreDataType +from ..commons.types.score_source import ScoreSource from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.datetime_utils import serialize_datetime from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions -from ..core.serialization import convert_and_respect_annotation_metadata -from .types.create_score_response import CreateScoreResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) +from .types.get_scores_response import GetScoresResponse class RawScoreClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def create( + def get( self, *, - name: str, - value: CreateScoreValue, - id: typing.Optional[str] = OMIT, - trace_id: typing.Optional[str] = OMIT, - session_id: typing.Optional[str] = OMIT, - observation_id: typing.Optional[str] = OMIT, - dataset_run_id: typing.Optional[str] = OMIT, - comment: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - environment: typing.Optional[str] = OMIT, - queue_id: typing.Optional[str] = OMIT, - data_type: typing.Optional[ScoreDataType] = OMIT, - config_id: typing.Optional[str] = OMIT, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + user_id: typing.Optional[str] = None, + name: typing.Optional[str] = None, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + source: typing.Optional[ScoreSource] = None, + operator: typing.Optional[str] = None, + value: typing.Optional[float] = None, + score_ids: typing.Optional[str] = None, + config_id: typing.Optional[str] = None, + session_id: typing.Optional[str] = None, + dataset_run_id: typing.Optional[str] = None, + trace_id: typing.Optional[str] = None, + observation_id: typing.Optional[str] = None, + queue_id: typing.Optional[str] = None, + data_type: typing.Optional[ScoreDataType] = None, + trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + fields: typing.Optional[str] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[CreateScoreResponse]: + ) -> HttpResponse[GetScoresResponse]: """ - Create a score (supports both trace and session scores) + Get a list of scores (supports both trace and session scores) Parameters ---------- - name : str + page : typing.Optional[int] + Page number, starts at 1. - value : CreateScoreValue - The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) + limit : typing.Optional[int] + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. - id : typing.Optional[str] + user_id : typing.Optional[str] + Retrieve only scores with this userId associated to the trace. - trace_id : typing.Optional[str] + name : typing.Optional[str] + Retrieve only scores with this name. - session_id : typing.Optional[str] + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created on or after a certain datetime (ISO 8601) - observation_id : typing.Optional[str] + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created before a certain datetime (ISO 8601) - dataset_run_id : typing.Optional[str] + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for scores where the environment is one of the provided values. + + source : typing.Optional[ScoreSource] + Retrieve only scores from a specific source. + + operator : typing.Optional[str] + Retrieve only scores with value. + + value : typing.Optional[float] + Retrieve only scores with value. + + score_ids : typing.Optional[str] + Comma-separated list of score IDs to limit the results to. - comment : typing.Optional[str] + config_id : typing.Optional[str] + Retrieve only scores with a specific configId. - metadata : typing.Optional[typing.Dict[str, typing.Any]] + session_id : typing.Optional[str] + Retrieve only scores with a specific sessionId. - environment : typing.Optional[str] - The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + dataset_run_id : typing.Optional[str] + Retrieve only scores with a specific datasetRunId. + + trace_id : typing.Optional[str] + Retrieve only scores with a specific traceId. + + observation_id : typing.Optional[str] + Comma-separated list of observation IDs to filter scores by. queue_id : typing.Optional[str] - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + Retrieve only scores with a specific annotation queueId. data_type : typing.Optional[ScoreDataType] - The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. + Retrieve only scores with a specific dataType. - config_id : typing.Optional[str] - Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. + trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Only scores linked to traces that include all of these tags will be returned. + + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. + + filter : typing.Optional[str] + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[CreateScoreResponse] + HttpResponse[GetScoresResponse] """ _response = self._client_wrapper.httpx_client.request( - "api/public/scores", - method="POST", - json={ - "id": id, - "traceId": trace_id, - "sessionId": session_id, - "observationId": observation_id, - "datasetRunId": dataset_run_id, + "api/public/v2/scores", + method="GET", + params={ + "page": page, + "limit": limit, + "userId": user_id, "name": name, - "value": convert_and_respect_annotation_metadata( - object_=value, annotation=CreateScoreValue, direction="write" - ), - "comment": comment, - "metadata": metadata, + "fromTimestamp": serialize_datetime(from_timestamp) + if from_timestamp is not None + else None, + "toTimestamp": serialize_datetime(to_timestamp) + if to_timestamp is not None + else None, "environment": environment, + "source": source, + "operator": operator, + "value": value, + "scoreIds": score_ids, + "configId": config_id, + "sessionId": session_id, + "datasetRunId": dataset_run_id, + "traceId": trace_id, + "observationId": observation_id, "queueId": queue_id, "dataType": data_type, - "configId": config_id, + "traceTags": trace_tags, + "fields": fields, + "filter": filter, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - CreateScoreResponse, + GetScoresResponse, parse_obj_as( - type_=CreateScoreResponse, # type: ignore + type_=GetScoresResponse, # type: ignore object_=_response.json(), ), ) @@ -189,11 +237,11 @@ def create( body=_response_json, ) - def delete( + def get_by_id( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[None]: + ) -> HttpResponse[Score]: """ - Delete a score (supports both trace and session scores) + Get a score (supports both trace and session scores) Parameters ---------- @@ -205,16 +253,23 @@ def delete( Returns ------- - HttpResponse[None] + HttpResponse[Score] """ _response = self._client_wrapper.httpx_client.request( - f"api/public/scores/{jsonable_encoder(score_id)}", - method="DELETE", + f"api/public/v2/scores/{jsonable_encoder(score_id)}", + method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: - return HttpResponse(response=_response, data=None) + _data = typing.cast( + Score, + parse_obj_as( + type_=Score, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: raise Error( headers=dict(_response.headers), @@ -288,96 +343,145 @@ class AsyncRawScoreClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def create( + async def get( self, *, - name: str, - value: CreateScoreValue, - id: typing.Optional[str] = OMIT, - trace_id: typing.Optional[str] = OMIT, - session_id: typing.Optional[str] = OMIT, - observation_id: typing.Optional[str] = OMIT, - dataset_run_id: typing.Optional[str] = OMIT, - comment: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - environment: typing.Optional[str] = OMIT, - queue_id: typing.Optional[str] = OMIT, - data_type: typing.Optional[ScoreDataType] = OMIT, - config_id: typing.Optional[str] = OMIT, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + user_id: typing.Optional[str] = None, + name: typing.Optional[str] = None, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + source: typing.Optional[ScoreSource] = None, + operator: typing.Optional[str] = None, + value: typing.Optional[float] = None, + score_ids: typing.Optional[str] = None, + config_id: typing.Optional[str] = None, + session_id: typing.Optional[str] = None, + dataset_run_id: typing.Optional[str] = None, + trace_id: typing.Optional[str] = None, + observation_id: typing.Optional[str] = None, + queue_id: typing.Optional[str] = None, + data_type: typing.Optional[ScoreDataType] = None, + trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + fields: typing.Optional[str] = None, + filter: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[CreateScoreResponse]: + ) -> AsyncHttpResponse[GetScoresResponse]: """ - Create a score (supports both trace and session scores) + Get a list of scores (supports both trace and session scores) Parameters ---------- - name : str + page : typing.Optional[int] + Page number, starts at 1. - value : CreateScoreValue - The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false) + limit : typing.Optional[int] + Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. - id : typing.Optional[str] + user_id : typing.Optional[str] + Retrieve only scores with this userId associated to the trace. - trace_id : typing.Optional[str] + name : typing.Optional[str] + Retrieve only scores with this name. - session_id : typing.Optional[str] + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created on or after a certain datetime (ISO 8601) - observation_id : typing.Optional[str] + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include scores created before a certain datetime (ISO 8601) - dataset_run_id : typing.Optional[str] + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for scores where the environment is one of the provided values. + + source : typing.Optional[ScoreSource] + Retrieve only scores from a specific source. - comment : typing.Optional[str] + operator : typing.Optional[str] + Retrieve only scores with value. - metadata : typing.Optional[typing.Dict[str, typing.Any]] + value : typing.Optional[float] + Retrieve only scores with value. - environment : typing.Optional[str] - The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + score_ids : typing.Optional[str] + Comma-separated list of score IDs to limit the results to. + + config_id : typing.Optional[str] + Retrieve only scores with a specific configId. + + session_id : typing.Optional[str] + Retrieve only scores with a specific sessionId. + + dataset_run_id : typing.Optional[str] + Retrieve only scores with a specific datasetRunId. + + trace_id : typing.Optional[str] + Retrieve only scores with a specific traceId. + + observation_id : typing.Optional[str] + Comma-separated list of observation IDs to filter scores by. queue_id : typing.Optional[str] - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + Retrieve only scores with a specific annotation queueId. data_type : typing.Optional[ScoreDataType] - The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric. + Retrieve only scores with a specific dataType. - config_id : typing.Optional[str] - Reference a score config on a score. The unique langfuse identifier of a score config. When passing this field, the dataType and stringValue fields are automatically populated. + trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Only scores linked to traces that include all of these tags will be returned. + + fields : typing.Optional[str] + Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. + + filter : typing.Optional[str] + A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[CreateScoreResponse] + AsyncHttpResponse[GetScoresResponse] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/scores", - method="POST", - json={ - "id": id, - "traceId": trace_id, - "sessionId": session_id, - "observationId": observation_id, - "datasetRunId": dataset_run_id, + "api/public/v2/scores", + method="GET", + params={ + "page": page, + "limit": limit, + "userId": user_id, "name": name, - "value": convert_and_respect_annotation_metadata( - object_=value, annotation=CreateScoreValue, direction="write" - ), - "comment": comment, - "metadata": metadata, + "fromTimestamp": serialize_datetime(from_timestamp) + if from_timestamp is not None + else None, + "toTimestamp": serialize_datetime(to_timestamp) + if to_timestamp is not None + else None, "environment": environment, + "source": source, + "operator": operator, + "value": value, + "scoreIds": score_ids, + "configId": config_id, + "sessionId": session_id, + "datasetRunId": dataset_run_id, + "traceId": trace_id, + "observationId": observation_id, "queueId": queue_id, "dataType": data_type, - "configId": config_id, + "traceTags": trace_tags, + "fields": fields, + "filter": filter, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - CreateScoreResponse, + GetScoresResponse, parse_obj_as( - type_=CreateScoreResponse, # type: ignore + type_=GetScoresResponse, # type: ignore object_=_response.json(), ), ) @@ -450,11 +554,11 @@ async def create( body=_response_json, ) - async def delete( + async def get_by_id( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[None]: + ) -> AsyncHttpResponse[Score]: """ - Delete a score (supports both trace and session scores) + Get a score (supports both trace and session scores) Parameters ---------- @@ -466,16 +570,23 @@ async def delete( Returns ------- - AsyncHttpResponse[None] + AsyncHttpResponse[Score] """ _response = await self._client_wrapper.httpx_client.request( - f"api/public/scores/{jsonable_encoder(score_id)}", - method="DELETE", + f"api/public/v2/scores/{jsonable_encoder(score_id)}", + method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: - return AsyncHttpResponse(response=_response, data=None) + _data = typing.cast( + Score, + parse_obj_as( + type_=Score, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: raise Error( headers=dict(_response.headers), diff --git a/langfuse/api/score/types/__init__.py b/langfuse/api/score/types/__init__.py index 4a759a978..3ee0246d4 100644 --- a/langfuse/api/score/types/__init__.py +++ b/langfuse/api/score/types/__init__.py @@ -6,11 +6,31 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .create_score_request import CreateScoreRequest - from .create_score_response import CreateScoreResponse + from .get_scores_response import GetScoresResponse + from .get_scores_response_data import ( + GetScoresResponseData, + GetScoresResponseData_Boolean, + GetScoresResponseData_Categorical, + GetScoresResponseData_Correction, + GetScoresResponseData_Numeric, + ) + from .get_scores_response_data_boolean import GetScoresResponseDataBoolean + from .get_scores_response_data_categorical import GetScoresResponseDataCategorical + from .get_scores_response_data_correction import GetScoresResponseDataCorrection + from .get_scores_response_data_numeric import GetScoresResponseDataNumeric + from .get_scores_response_trace_data import GetScoresResponseTraceData _dynamic_imports: typing.Dict[str, str] = { - "CreateScoreRequest": ".create_score_request", - "CreateScoreResponse": ".create_score_response", + "GetScoresResponse": ".get_scores_response", + "GetScoresResponseData": ".get_scores_response_data", + "GetScoresResponseDataBoolean": ".get_scores_response_data_boolean", + "GetScoresResponseDataCategorical": ".get_scores_response_data_categorical", + "GetScoresResponseDataCorrection": ".get_scores_response_data_correction", + "GetScoresResponseDataNumeric": ".get_scores_response_data_numeric", + "GetScoresResponseData_Boolean": ".get_scores_response_data", + "GetScoresResponseData_Categorical": ".get_scores_response_data", + "GetScoresResponseData_Correction": ".get_scores_response_data", + "GetScoresResponseData_Numeric": ".get_scores_response_data", + "GetScoresResponseTraceData": ".get_scores_response_trace_data", } @@ -41,4 +61,16 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["CreateScoreRequest", "CreateScoreResponse"] +__all__ = [ + "GetScoresResponse", + "GetScoresResponseData", + "GetScoresResponseDataBoolean", + "GetScoresResponseDataCategorical", + "GetScoresResponseDataCorrection", + "GetScoresResponseDataNumeric", + "GetScoresResponseData_Boolean", + "GetScoresResponseData_Categorical", + "GetScoresResponseData_Correction", + "GetScoresResponseData_Numeric", + "GetScoresResponseTraceData", +] diff --git a/langfuse/api/score_v2/types/get_scores_response.py b/langfuse/api/score/types/get_scores_response.py similarity index 100% rename from langfuse/api/score_v2/types/get_scores_response.py rename to langfuse/api/score/types/get_scores_response.py diff --git a/langfuse/api/score_v2/types/get_scores_response_data.py b/langfuse/api/score/types/get_scores_response_data.py similarity index 100% rename from langfuse/api/score_v2/types/get_scores_response_data.py rename to langfuse/api/score/types/get_scores_response_data.py diff --git a/langfuse/api/score_v2/types/get_scores_response_data_boolean.py b/langfuse/api/score/types/get_scores_response_data_boolean.py similarity index 100% rename from langfuse/api/score_v2/types/get_scores_response_data_boolean.py rename to langfuse/api/score/types/get_scores_response_data_boolean.py diff --git a/langfuse/api/score_v2/types/get_scores_response_data_categorical.py b/langfuse/api/score/types/get_scores_response_data_categorical.py similarity index 100% rename from langfuse/api/score_v2/types/get_scores_response_data_categorical.py rename to langfuse/api/score/types/get_scores_response_data_categorical.py diff --git a/langfuse/api/score_v2/types/get_scores_response_data_correction.py b/langfuse/api/score/types/get_scores_response_data_correction.py similarity index 100% rename from langfuse/api/score_v2/types/get_scores_response_data_correction.py rename to langfuse/api/score/types/get_scores_response_data_correction.py diff --git a/langfuse/api/score_v2/types/get_scores_response_data_numeric.py b/langfuse/api/score/types/get_scores_response_data_numeric.py similarity index 100% rename from langfuse/api/score_v2/types/get_scores_response_data_numeric.py rename to langfuse/api/score/types/get_scores_response_data_numeric.py diff --git a/langfuse/api/score_v2/types/get_scores_response_trace_data.py b/langfuse/api/score/types/get_scores_response_trace_data.py similarity index 100% rename from langfuse/api/score_v2/types/get_scores_response_trace_data.py rename to langfuse/api/score/types/get_scores_response_trace_data.py diff --git a/langfuse/api/score_v2/__init__.py b/langfuse/api/score_v2/__init__.py deleted file mode 100644 index d320aecfc..000000000 --- a/langfuse/api/score_v2/__init__.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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 ( - GetScoresResponse, - GetScoresResponseData, - GetScoresResponseDataBoolean, - GetScoresResponseDataCategorical, - GetScoresResponseDataCorrection, - GetScoresResponseDataNumeric, - GetScoresResponseData_Boolean, - GetScoresResponseData_Categorical, - GetScoresResponseData_Correction, - GetScoresResponseData_Numeric, - GetScoresResponseTraceData, - ) -_dynamic_imports: typing.Dict[str, str] = { - "GetScoresResponse": ".types", - "GetScoresResponseData": ".types", - "GetScoresResponseDataBoolean": ".types", - "GetScoresResponseDataCategorical": ".types", - "GetScoresResponseDataCorrection": ".types", - "GetScoresResponseDataNumeric": ".types", - "GetScoresResponseData_Boolean": ".types", - "GetScoresResponseData_Categorical": ".types", - "GetScoresResponseData_Correction": ".types", - "GetScoresResponseData_Numeric": ".types", - "GetScoresResponseTraceData": ".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__ = [ - "GetScoresResponse", - "GetScoresResponseData", - "GetScoresResponseDataBoolean", - "GetScoresResponseDataCategorical", - "GetScoresResponseDataCorrection", - "GetScoresResponseDataNumeric", - "GetScoresResponseData_Boolean", - "GetScoresResponseData_Categorical", - "GetScoresResponseData_Correction", - "GetScoresResponseData_Numeric", - "GetScoresResponseTraceData", -] diff --git a/langfuse/api/score_v2/client.py b/langfuse/api/score_v2/client.py deleted file mode 100644 index 4ee5cf372..000000000 --- a/langfuse/api/score_v2/client.py +++ /dev/null @@ -1,420 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..commons.types.score import Score -from ..commons.types.score_data_type import ScoreDataType -from ..commons.types.score_source import ScoreSource -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from .raw_client import AsyncRawScoreV2Client, RawScoreV2Client -from .types.get_scores_response import GetScoresResponse - - -class ScoreV2Client: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawScoreV2Client(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawScoreV2Client: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawScoreV2Client - """ - return self._raw_client - - def get( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - user_id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - from_timestamp: typing.Optional[dt.datetime] = None, - to_timestamp: typing.Optional[dt.datetime] = None, - environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - source: typing.Optional[ScoreSource] = None, - operator: typing.Optional[str] = None, - value: typing.Optional[float] = None, - score_ids: typing.Optional[str] = None, - config_id: typing.Optional[str] = None, - session_id: typing.Optional[str] = None, - dataset_run_id: typing.Optional[str] = None, - trace_id: typing.Optional[str] = None, - observation_id: typing.Optional[str] = None, - queue_id: typing.Optional[str] = None, - data_type: typing.Optional[ScoreDataType] = None, - trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - fields: typing.Optional[str] = None, - filter: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> GetScoresResponse: - """ - Get a list of scores (supports both trace and session scores) - - Parameters - ---------- - page : typing.Optional[int] - Page number, starts at 1. - - limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. - - user_id : typing.Optional[str] - Retrieve only scores with this userId associated to the trace. - - name : typing.Optional[str] - Retrieve only scores with this name. - - from_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created on or after a certain datetime (ISO 8601) - - to_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created before a certain datetime (ISO 8601) - - environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Optional filter for scores where the environment is one of the provided values. - - source : typing.Optional[ScoreSource] - Retrieve only scores from a specific source. - - operator : typing.Optional[str] - Retrieve only scores with value. - - value : typing.Optional[float] - Retrieve only scores with value. - - score_ids : typing.Optional[str] - Comma-separated list of score IDs to limit the results to. - - config_id : typing.Optional[str] - Retrieve only scores with a specific configId. - - session_id : typing.Optional[str] - Retrieve only scores with a specific sessionId. - - dataset_run_id : typing.Optional[str] - Retrieve only scores with a specific datasetRunId. - - trace_id : typing.Optional[str] - Retrieve only scores with a specific traceId. - - observation_id : typing.Optional[str] - Comma-separated list of observation IDs to filter scores by. - - queue_id : typing.Optional[str] - Retrieve only scores with a specific annotation queueId. - - data_type : typing.Optional[ScoreDataType] - Retrieve only scores with a specific dataType. - - trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Only scores linked to traces that include all of these tags will be returned. - - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. - - filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetScoresResponse - - Examples - -------- - from langfuse import LangfuseAPI - - 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.score_v2.get() - """ - _response = self._raw_client.get( - page=page, - limit=limit, - user_id=user_id, - name=name, - from_timestamp=from_timestamp, - to_timestamp=to_timestamp, - environment=environment, - source=source, - operator=operator, - value=value, - score_ids=score_ids, - config_id=config_id, - session_id=session_id, - dataset_run_id=dataset_run_id, - trace_id=trace_id, - observation_id=observation_id, - queue_id=queue_id, - data_type=data_type, - trace_tags=trace_tags, - fields=fields, - filter=filter, - request_options=request_options, - ) - return _response.data - - def get_by_id( - self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> Score: - """ - Get a score (supports both trace and session scores) - - Parameters - ---------- - score_id : str - The unique langfuse identifier of a score - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Score - - Examples - -------- - from langfuse import LangfuseAPI - - 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.score_v2.get_by_id( - score_id="scoreId", - ) - """ - _response = self._raw_client.get_by_id( - score_id, request_options=request_options - ) - return _response.data - - -class AsyncScoreV2Client: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawScoreV2Client(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawScoreV2Client: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawScoreV2Client - """ - return self._raw_client - - async def get( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - user_id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - from_timestamp: typing.Optional[dt.datetime] = None, - to_timestamp: typing.Optional[dt.datetime] = None, - environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - source: typing.Optional[ScoreSource] = None, - operator: typing.Optional[str] = None, - value: typing.Optional[float] = None, - score_ids: typing.Optional[str] = None, - config_id: typing.Optional[str] = None, - session_id: typing.Optional[str] = None, - dataset_run_id: typing.Optional[str] = None, - trace_id: typing.Optional[str] = None, - observation_id: typing.Optional[str] = None, - queue_id: typing.Optional[str] = None, - data_type: typing.Optional[ScoreDataType] = None, - trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, - fields: typing.Optional[str] = None, - filter: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> GetScoresResponse: - """ - Get a list of scores (supports both trace and session scores) - - Parameters - ---------- - page : typing.Optional[int] - Page number, starts at 1. - - limit : typing.Optional[int] - Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit. - - user_id : typing.Optional[str] - Retrieve only scores with this userId associated to the trace. - - name : typing.Optional[str] - Retrieve only scores with this name. - - from_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created on or after a certain datetime (ISO 8601) - - to_timestamp : typing.Optional[dt.datetime] - Optional filter to only include scores created before a certain datetime (ISO 8601) - - environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Optional filter for scores where the environment is one of the provided values. - - source : typing.Optional[ScoreSource] - Retrieve only scores from a specific source. - - operator : typing.Optional[str] - Retrieve only scores with value. - - value : typing.Optional[float] - Retrieve only scores with value. - - score_ids : typing.Optional[str] - Comma-separated list of score IDs to limit the results to. - - config_id : typing.Optional[str] - Retrieve only scores with a specific configId. - - session_id : typing.Optional[str] - Retrieve only scores with a specific sessionId. - - dataset_run_id : typing.Optional[str] - Retrieve only scores with a specific datasetRunId. - - trace_id : typing.Optional[str] - Retrieve only scores with a specific traceId. - - observation_id : typing.Optional[str] - Comma-separated list of observation IDs to filter scores by. - - queue_id : typing.Optional[str] - Retrieve only scores with a specific annotation queueId. - - data_type : typing.Optional[ScoreDataType] - Retrieve only scores with a specific dataType. - - trace_tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Only scores linked to traces that include all of these tags will be returned. - - fields : typing.Optional[str] - Comma-separated list of field groups to include in the response. Available field groups: 'score' (core score fields), 'trace' (trace properties: userId, tags, environment, sessionId). If not specified, both 'score' and 'trace' are returned by default. Example: 'score' to exclude trace data, 'score,trace' to include both. Note: When filtering by trace properties (using userId or traceTags parameters), the 'trace' field group must be included, otherwise a 400 error will be returned. - - filter : typing.Optional[str] - A JSON stringified array of filter objects. Each object requires type, column, operator, and value. Supports filtering by score metadata using the stringObject type. Example: [{"type":"stringObject","column":"metadata","key":"user_id","operator":"=","value":"abc123"}]. Supported types: stringObject (metadata key-value filtering), string, number, datetime, stringOptions, arrayOptions. Supported operators for stringObject: =, contains, does not contain, starts with, ends with. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetScoresResponse - - Examples - -------- - import asyncio - - from langfuse import AsyncLangfuseAPI - - 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.score_v2.get() - - - asyncio.run(main()) - """ - _response = await self._raw_client.get( - page=page, - limit=limit, - user_id=user_id, - name=name, - from_timestamp=from_timestamp, - to_timestamp=to_timestamp, - environment=environment, - source=source, - operator=operator, - value=value, - score_ids=score_ids, - config_id=config_id, - session_id=session_id, - dataset_run_id=dataset_run_id, - trace_id=trace_id, - observation_id=observation_id, - queue_id=queue_id, - data_type=data_type, - trace_tags=trace_tags, - fields=fields, - filter=filter, - request_options=request_options, - ) - return _response.data - - async def get_by_id( - self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> Score: - """ - Get a score (supports both trace and session scores) - - Parameters - ---------- - score_id : str - The unique langfuse identifier of a score - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Score - - Examples - -------- - import asyncio - - from langfuse import AsyncLangfuseAPI - - 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.score_v2.get_by_id( - score_id="scoreId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get_by_id( - score_id, request_options=request_options - ) - return _response.data diff --git a/langfuse/api/score_v2/types/__init__.py b/langfuse/api/score_v2/types/__init__.py deleted file mode 100644 index 3ee0246d4..000000000 --- a/langfuse/api/score_v2/types/__init__.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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 .get_scores_response import GetScoresResponse - from .get_scores_response_data import ( - GetScoresResponseData, - GetScoresResponseData_Boolean, - GetScoresResponseData_Categorical, - GetScoresResponseData_Correction, - GetScoresResponseData_Numeric, - ) - from .get_scores_response_data_boolean import GetScoresResponseDataBoolean - from .get_scores_response_data_categorical import GetScoresResponseDataCategorical - from .get_scores_response_data_correction import GetScoresResponseDataCorrection - from .get_scores_response_data_numeric import GetScoresResponseDataNumeric - from .get_scores_response_trace_data import GetScoresResponseTraceData -_dynamic_imports: typing.Dict[str, str] = { - "GetScoresResponse": ".get_scores_response", - "GetScoresResponseData": ".get_scores_response_data", - "GetScoresResponseDataBoolean": ".get_scores_response_data_boolean", - "GetScoresResponseDataCategorical": ".get_scores_response_data_categorical", - "GetScoresResponseDataCorrection": ".get_scores_response_data_correction", - "GetScoresResponseDataNumeric": ".get_scores_response_data_numeric", - "GetScoresResponseData_Boolean": ".get_scores_response_data", - "GetScoresResponseData_Categorical": ".get_scores_response_data", - "GetScoresResponseData_Correction": ".get_scores_response_data", - "GetScoresResponseData_Numeric": ".get_scores_response_data", - "GetScoresResponseTraceData": ".get_scores_response_trace_data", -} - - -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__ = [ - "GetScoresResponse", - "GetScoresResponseData", - "GetScoresResponseDataBoolean", - "GetScoresResponseDataCategorical", - "GetScoresResponseDataCorrection", - "GetScoresResponseDataNumeric", - "GetScoresResponseData_Boolean", - "GetScoresResponseData_Categorical", - "GetScoresResponseData_Correction", - "GetScoresResponseData_Numeric", - "GetScoresResponseTraceData", -]