diff --git a/langfuse/api/README.md b/langfuse/api/README.md index d7fa24a33..9e8fef6d4 100644 --- a/langfuse/api/README.md +++ b/langfuse/api/README.md @@ -3,7 +3,7 @@ [![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Langfuse%2FPython) [![pypi](https://img.shields.io/pypi/v/langfuse)](https://pypi.python.org/pypi/langfuse) -The Langfuse Python library provides convenient access to the Langfuse API from Python. +The Langfuse Python library provides convenient access to the Langfuse APIs from Python. ## Installation diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index 4f43e45f1..932a60e93 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -16,6 +16,13 @@ BasePrompt, BaseScore, BaseScoreV1, + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationDeletionResponse, + BlobStorageIntegrationFileType, + BlobStorageIntegrationResponse, + BlobStorageIntegrationType, + BlobStorageIntegrationsResponse, BooleanScore, BooleanScoreV1, BulkConfig, @@ -32,6 +39,7 @@ CreateAnnotationQueueAssignmentResponse, CreateAnnotationQueueItemRequest, CreateAnnotationQueueRequest, + CreateBlobStorageIntegrationRequest, CreateChatPromptRequest, CreateCommentRequest, CreateCommentResponse, @@ -64,6 +72,7 @@ DeleteAnnotationQueueItemResponse, DeleteDatasetItemResponse, DeleteDatasetRunResponse, + DeleteMembershipRequest, DeleteTraceResponse, EmptyResponse, Error, @@ -101,6 +110,7 @@ LlmConnection, MapValue, MediaContentType, + MembershipDeletionResponse, MembershipRequest, MembershipResponse, MembershipRole, @@ -197,6 +207,7 @@ UsageDetails, UserMeta, annotation_queues, + blob_storage_integrations, comments, commons, dataset_items, @@ -238,6 +249,13 @@ "BasePrompt", "BaseScore", "BaseScoreV1", + "BlobStorageExportFrequency", + "BlobStorageExportMode", + "BlobStorageIntegrationDeletionResponse", + "BlobStorageIntegrationFileType", + "BlobStorageIntegrationResponse", + "BlobStorageIntegrationType", + "BlobStorageIntegrationsResponse", "BooleanScore", "BooleanScoreV1", "BulkConfig", @@ -254,6 +272,7 @@ "CreateAnnotationQueueAssignmentResponse", "CreateAnnotationQueueItemRequest", "CreateAnnotationQueueRequest", + "CreateBlobStorageIntegrationRequest", "CreateChatPromptRequest", "CreateCommentRequest", "CreateCommentResponse", @@ -286,6 +305,7 @@ "DeleteAnnotationQueueItemResponse", "DeleteDatasetItemResponse", "DeleteDatasetRunResponse", + "DeleteMembershipRequest", "DeleteTraceResponse", "EmptyResponse", "Error", @@ -323,6 +343,7 @@ "LlmConnection", "MapValue", "MediaContentType", + "MembershipDeletionResponse", "MembershipRequest", "MembershipResponse", "MembershipRole", @@ -419,6 +440,7 @@ "UsageDetails", "UserMeta", "annotation_queues", + "blob_storage_integrations", "comments", "commons", "dataset_items", diff --git a/langfuse/api/client.py b/langfuse/api/client.py index f18caba1c..619e649fa 100644 --- a/langfuse/api/client.py +++ b/langfuse/api/client.py @@ -9,6 +9,10 @@ AnnotationQueuesClient, AsyncAnnotationQueuesClient, ) +from .resources.blob_storage_integrations.client import ( + AsyncBlobStorageIntegrationsClient, + BlobStorageIntegrationsClient, +) from .resources.comments.client import AsyncCommentsClient, CommentsClient from .resources.dataset_items.client import AsyncDatasetItemsClient, DatasetItemsClient from .resources.dataset_run_items.client import ( @@ -116,6 +120,9 @@ def __init__( self.annotation_queues = AnnotationQueuesClient( client_wrapper=self._client_wrapper ) + self.blob_storage_integrations = BlobStorageIntegrationsClient( + client_wrapper=self._client_wrapper + ) self.comments = CommentsClient(client_wrapper=self._client_wrapper) self.dataset_items = DatasetItemsClient(client_wrapper=self._client_wrapper) self.dataset_run_items = DatasetRunItemsClient( @@ -213,6 +220,9 @@ def __init__( self.annotation_queues = AsyncAnnotationQueuesClient( client_wrapper=self._client_wrapper ) + self.blob_storage_integrations = AsyncBlobStorageIntegrationsClient( + client_wrapper=self._client_wrapper + ) self.comments = AsyncCommentsClient(client_wrapper=self._client_wrapper) self.dataset_items = AsyncDatasetItemsClient( client_wrapper=self._client_wrapper diff --git a/langfuse/api/reference.md b/langfuse/api/reference.md index ce4c4ecd8..6b243980f 100644 --- a/langfuse/api/reference.md +++ b/langfuse/api/reference.md @@ -854,6 +854,239 @@ client.annotation_queues.delete_queue_assignment( + + + + +## BlobStorageIntegrations +
client.blob_storage_integrations.get_blob_storage_integrations() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Get all blob storage integrations for the organization (requires organization-scoped API key) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse.client import FernLangfuse + +client = FernLangfuse( + 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.blob_storage_integrations.get_blob_storage_integrations() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.blob_storage_integrations.upsert_blob_storage_integration(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create or update a blob storage integration for a specific project (requires organization-scoped API key). The configuration is validated by performing a test upload to the bucket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationFileType, + BlobStorageIntegrationType, + CreateBlobStorageIntegrationRequest, +) +from langfuse.client import FernLangfuse + +client = FernLangfuse( + 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.blob_storage_integrations.upsert_blob_storage_integration( + request=CreateBlobStorageIntegrationRequest( + project_id="projectId", + type=BlobStorageIntegrationType.S_3, + bucket_name="bucketName", + region="region", + export_frequency=BlobStorageExportFrequency.HOURLY, + enabled=True, + force_path_style=True, + file_type=BlobStorageIntegrationFileType.JSON, + export_mode=BlobStorageExportMode.FULL_HISTORY, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateBlobStorageIntegrationRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.blob_storage_integrations.delete_blob_storage_integration(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a blob storage integration by ID (requires organization-scoped API key) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse.client import FernLangfuse + +client = FernLangfuse( + 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.blob_storage_integrations.delete_blob_storage_integration( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -2207,8 +2440,9 @@ client.health.health()
-Batched ingestion for Langfuse Tracing. -If you want to use tracing via the API, such as to build your own Langfuse client implementation, this is the only API route you need to implement. +**Legacy endpoint for batch ingestion for Langfuse Observability.** + +-> Please use the OpenTelemetry endpoint (`/api/public/otel`). Learn more: https://langfuse.com/integrations/native/opentelemetry Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -2218,7 +2452,7 @@ The event.body.id is the ID of the actual trace and will be used for updates and I.e. if you want to update a trace, you'd use the same body id, but separate event IDs. Notes: -- Introduction to data model: https://langfuse.com/docs/tracing-data-model +- Introduction to data model: https://langfuse.com/docs/observability/data-model - Batch sizes are limited to 3.5 MB in total. You need to adjust the number of events per batch accordingly. - The API does not return a 4xx status code for input errors. Instead, it responds with a 207 status code, which includes a list of the encountered errors.
@@ -3523,6 +3757,84 @@ client.organizations.update_organization_membership(
+ + + + +
client.organizations.delete_organization_membership(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a membership from the organization associated with the API key (requires organization-scoped API key) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse import DeleteMembershipRequest +from langfuse.client import FernLangfuse + +client = FernLangfuse( + 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.organizations.delete_organization_membership( + request=DeleteMembershipRequest( + user_id="userId", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `DeleteMembershipRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -3686,6 +3998,93 @@ client.organizations.update_project_membership( + + + + +
client.organizations.delete_project_membership(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a membership from a specific project (requires organization-scoped API key). The user must be a member of the organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse import DeleteMembershipRequest +from langfuse.client import FernLangfuse + +client = FernLangfuse( + 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.organizations.delete_project_membership( + project_id="projectId", + request=DeleteMembershipRequest( + user_id="userId", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**project_id:** `str` + +
+
+ +
+
+ +**request:** `DeleteMembershipRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -5659,6 +6058,14 @@ client.score_v_2.get()
+**session_id:** `typing.Optional[str]` — Retrieve only scores with a specific sessionId. + +
+
+ +
+
+ **queue_id:** `typing.Optional[str]` — Retrieve only scores with a specific annotation queueId.
@@ -6406,7 +6813,7 @@ client.trace.list()
-**fields:** `typing.Optional[str]` — Comma-separated list of fields to include in the response. Available field groups are 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not provided, all fields are included. Example: 'core,scores,metrics' +**fields:** `typing.Optional[str]` — Comma-separated list of fields to include in the response. Available field groups: 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not specified, all fields are returned. Example: 'core,scores,metrics'. Note: Excluded 'observations' or 'scores' fields return empty arrays; excluded 'metrics' returns -1 for 'totalCost' and 'latency'.
diff --git a/langfuse/api/resources/__init__.py b/langfuse/api/resources/__init__.py index 062c933be..8b0f6ec76 100644 --- a/langfuse/api/resources/__init__.py +++ b/langfuse/api/resources/__init__.py @@ -2,6 +2,7 @@ from . import ( annotation_queues, + blob_storage_integrations, comments, commons, dataset_items, @@ -41,6 +42,16 @@ PaginatedAnnotationQueues, UpdateAnnotationQueueItemRequest, ) +from .blob_storage_integrations import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationDeletionResponse, + BlobStorageIntegrationFileType, + BlobStorageIntegrationResponse, + BlobStorageIntegrationType, + BlobStorageIntegrationsResponse, + CreateBlobStorageIntegrationRequest, +) from .comments import CreateCommentRequest, CreateCommentResponse, GetCommentsResponse from .commons import ( AccessDeniedError, @@ -165,6 +176,8 @@ from .models import CreateModelRequest, PaginatedModels from .observations import Observations, ObservationsViews from .organizations import ( + DeleteMembershipRequest, + MembershipDeletionResponse, MembershipRequest, MembershipResponse, MembershipRole, @@ -252,6 +265,13 @@ "BasePrompt", "BaseScore", "BaseScoreV1", + "BlobStorageExportFrequency", + "BlobStorageExportMode", + "BlobStorageIntegrationDeletionResponse", + "BlobStorageIntegrationFileType", + "BlobStorageIntegrationResponse", + "BlobStorageIntegrationType", + "BlobStorageIntegrationsResponse", "BooleanScore", "BooleanScoreV1", "BulkConfig", @@ -268,6 +288,7 @@ "CreateAnnotationQueueAssignmentResponse", "CreateAnnotationQueueItemRequest", "CreateAnnotationQueueRequest", + "CreateBlobStorageIntegrationRequest", "CreateChatPromptRequest", "CreateCommentRequest", "CreateCommentResponse", @@ -300,6 +321,7 @@ "DeleteAnnotationQueueItemResponse", "DeleteDatasetItemResponse", "DeleteDatasetRunResponse", + "DeleteMembershipRequest", "DeleteTraceResponse", "EmptyResponse", "Error", @@ -337,6 +359,7 @@ "LlmConnection", "MapValue", "MediaContentType", + "MembershipDeletionResponse", "MembershipRequest", "MembershipResponse", "MembershipRole", @@ -433,6 +456,7 @@ "UsageDetails", "UserMeta", "annotation_queues", + "blob_storage_integrations", "comments", "commons", "dataset_items", diff --git a/langfuse/api/resources/blob_storage_integrations/__init__.py b/langfuse/api/resources/blob_storage_integrations/__init__.py new file mode 100644 index 000000000..a635fba57 --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/__init__.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from .types import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationDeletionResponse, + BlobStorageIntegrationFileType, + BlobStorageIntegrationResponse, + BlobStorageIntegrationType, + BlobStorageIntegrationsResponse, + CreateBlobStorageIntegrationRequest, +) + +__all__ = [ + "BlobStorageExportFrequency", + "BlobStorageExportMode", + "BlobStorageIntegrationDeletionResponse", + "BlobStorageIntegrationFileType", + "BlobStorageIntegrationResponse", + "BlobStorageIntegrationType", + "BlobStorageIntegrationsResponse", + "CreateBlobStorageIntegrationRequest", +] diff --git a/langfuse/api/resources/blob_storage_integrations/client.py b/langfuse/api/resources/blob_storage_integrations/client.py new file mode 100644 index 000000000..73aec4fa4 --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/client.py @@ -0,0 +1,492 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.jsonable_encoder import jsonable_encoder +from ...core.pydantic_utilities import pydantic_v1 +from ...core.request_options import RequestOptions +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 .types.blob_storage_integration_deletion_response import ( + BlobStorageIntegrationDeletionResponse, +) +from .types.blob_storage_integration_response import BlobStorageIntegrationResponse +from .types.blob_storage_integrations_response import BlobStorageIntegrationsResponse +from .types.create_blob_storage_integration_request import ( + CreateBlobStorageIntegrationRequest, +) + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class BlobStorageIntegrationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_blob_storage_integrations( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> BlobStorageIntegrationsResponse: + """ + Get all blob storage integrations for the organization (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationsResponse + + Examples + -------- + from langfuse.client import FernLangfuse + + client = FernLangfuse( + 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.blob_storage_integrations.get_blob_storage_integrations() + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/integrations/blob-storage", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + BlobStorageIntegrationsResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def upsert_blob_storage_integration( + self, + *, + request: CreateBlobStorageIntegrationRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> BlobStorageIntegrationResponse: + """ + Create or update a blob storage integration for a specific project (requires organization-scoped API key). The configuration is validated by performing a test upload to the bucket. + + Parameters + ---------- + request : CreateBlobStorageIntegrationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationResponse + + Examples + -------- + from langfuse import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationFileType, + BlobStorageIntegrationType, + CreateBlobStorageIntegrationRequest, + ) + from langfuse.client import FernLangfuse + + client = FernLangfuse( + 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.blob_storage_integrations.upsert_blob_storage_integration( + request=CreateBlobStorageIntegrationRequest( + project_id="projectId", + type=BlobStorageIntegrationType.S_3, + bucket_name="bucketName", + region="region", + export_frequency=BlobStorageExportFrequency.HOURLY, + enabled=True, + force_path_style=True, + file_type=BlobStorageIntegrationFileType.JSON, + export_mode=BlobStorageExportMode.FULL_HISTORY, + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/integrations/blob-storage", + method="PUT", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + BlobStorageIntegrationResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def delete_blob_storage_integration( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> BlobStorageIntegrationDeletionResponse: + """ + Delete a blob storage integration by ID (requires organization-scoped API key) + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationDeletionResponse + + Examples + -------- + from langfuse.client import FernLangfuse + + client = FernLangfuse( + 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.blob_storage_integrations.delete_blob_storage_integration( + id="id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/integrations/blob-storage/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + BlobStorageIntegrationDeletionResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncBlobStorageIntegrationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_blob_storage_integrations( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> BlobStorageIntegrationsResponse: + """ + Get all blob storage integrations for the organization (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationsResponse + + Examples + -------- + import asyncio + + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + 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.blob_storage_integrations.get_blob_storage_integrations() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/integrations/blob-storage", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + BlobStorageIntegrationsResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def upsert_blob_storage_integration( + self, + *, + request: CreateBlobStorageIntegrationRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> BlobStorageIntegrationResponse: + """ + Create or update a blob storage integration for a specific project (requires organization-scoped API key). The configuration is validated by performing a test upload to the bucket. + + Parameters + ---------- + request : CreateBlobStorageIntegrationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationResponse + + Examples + -------- + import asyncio + + from langfuse import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationFileType, + BlobStorageIntegrationType, + CreateBlobStorageIntegrationRequest, + ) + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + 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.blob_storage_integrations.upsert_blob_storage_integration( + request=CreateBlobStorageIntegrationRequest( + project_id="projectId", + type=BlobStorageIntegrationType.S_3, + bucket_name="bucketName", + region="region", + export_frequency=BlobStorageExportFrequency.HOURLY, + enabled=True, + force_path_style=True, + file_type=BlobStorageIntegrationFileType.JSON, + export_mode=BlobStorageExportMode.FULL_HISTORY, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/integrations/blob-storage", + method="PUT", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + BlobStorageIntegrationResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def delete_blob_storage_integration( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> BlobStorageIntegrationDeletionResponse: + """ + Delete a blob storage integration by ID (requires organization-scoped API key) + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationDeletionResponse + + Examples + -------- + import asyncio + + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + 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.blob_storage_integrations.delete_blob_storage_integration( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/integrations/blob-storage/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + BlobStorageIntegrationDeletionResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/langfuse/api/resources/blob_storage_integrations/types/__init__.py b/langfuse/api/resources/blob_storage_integrations/types/__init__.py new file mode 100644 index 000000000..621196c11 --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/__init__.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from .blob_storage_export_frequency import BlobStorageExportFrequency +from .blob_storage_export_mode import BlobStorageExportMode +from .blob_storage_integration_deletion_response import ( + BlobStorageIntegrationDeletionResponse, +) +from .blob_storage_integration_file_type import BlobStorageIntegrationFileType +from .blob_storage_integration_response import BlobStorageIntegrationResponse +from .blob_storage_integration_type import BlobStorageIntegrationType +from .blob_storage_integrations_response import BlobStorageIntegrationsResponse +from .create_blob_storage_integration_request import CreateBlobStorageIntegrationRequest + +__all__ = [ + "BlobStorageExportFrequency", + "BlobStorageExportMode", + "BlobStorageIntegrationDeletionResponse", + "BlobStorageIntegrationFileType", + "BlobStorageIntegrationResponse", + "BlobStorageIntegrationType", + "BlobStorageIntegrationsResponse", + "CreateBlobStorageIntegrationRequest", +] diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_frequency.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_frequency.py new file mode 100644 index 000000000..936e0c18f --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_frequency.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import enum +import typing + +T_Result = typing.TypeVar("T_Result") + + +class BlobStorageExportFrequency(str, enum.Enum): + HOURLY = "hourly" + DAILY = "daily" + WEEKLY = "weekly" + + def visit( + self, + hourly: typing.Callable[[], T_Result], + daily: typing.Callable[[], T_Result], + weekly: typing.Callable[[], T_Result], + ) -> T_Result: + if self is BlobStorageExportFrequency.HOURLY: + return hourly() + if self is BlobStorageExportFrequency.DAILY: + return daily() + if self is BlobStorageExportFrequency.WEEKLY: + return weekly() diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_mode.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_mode.py new file mode 100644 index 000000000..1eafab79d --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_mode.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import enum +import typing + +T_Result = typing.TypeVar("T_Result") + + +class BlobStorageExportMode(str, enum.Enum): + FULL_HISTORY = "FULL_HISTORY" + FROM_TODAY = "FROM_TODAY" + FROM_CUSTOM_DATE = "FROM_CUSTOM_DATE" + + def visit( + self, + full_history: typing.Callable[[], T_Result], + from_today: typing.Callable[[], T_Result], + from_custom_date: typing.Callable[[], T_Result], + ) -> T_Result: + if self is BlobStorageExportMode.FULL_HISTORY: + return full_history() + if self is BlobStorageExportMode.FROM_TODAY: + return from_today() + if self is BlobStorageExportMode.FROM_CUSTOM_DATE: + return from_custom_date() diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_deletion_response.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_deletion_response.py new file mode 100644 index 000000000..4305cff2f --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_deletion_response.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class BlobStorageIntegrationDeletionResponse(pydantic_v1.BaseModel): + message: str + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_file_type.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_file_type.py new file mode 100644 index 000000000..a63631c6f --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_file_type.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import enum +import typing + +T_Result = typing.TypeVar("T_Result") + + +class BlobStorageIntegrationFileType(str, enum.Enum): + JSON = "JSON" + CSV = "CSV" + JSONL = "JSONL" + + def visit( + self, + json: typing.Callable[[], T_Result], + csv: typing.Callable[[], T_Result], + jsonl: typing.Callable[[], T_Result], + ) -> T_Result: + if self is BlobStorageIntegrationFileType.JSON: + return json() + if self is BlobStorageIntegrationFileType.CSV: + return csv() + if self is BlobStorageIntegrationFileType.JSONL: + return jsonl() diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_response.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_response.py new file mode 100644 index 000000000..e308e8113 --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_response.py @@ -0,0 +1,75 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .blob_storage_export_frequency import BlobStorageExportFrequency +from .blob_storage_export_mode import BlobStorageExportMode +from .blob_storage_integration_file_type import BlobStorageIntegrationFileType +from .blob_storage_integration_type import BlobStorageIntegrationType + + +class BlobStorageIntegrationResponse(pydantic_v1.BaseModel): + id: str + project_id: str = pydantic_v1.Field(alias="projectId") + type: BlobStorageIntegrationType + bucket_name: str = pydantic_v1.Field(alias="bucketName") + endpoint: typing.Optional[str] = None + region: str + access_key_id: typing.Optional[str] = pydantic_v1.Field( + alias="accessKeyId", default=None + ) + prefix: str + export_frequency: BlobStorageExportFrequency = pydantic_v1.Field( + alias="exportFrequency" + ) + enabled: bool + force_path_style: bool = pydantic_v1.Field(alias="forcePathStyle") + file_type: BlobStorageIntegrationFileType = pydantic_v1.Field(alias="fileType") + export_mode: BlobStorageExportMode = pydantic_v1.Field(alias="exportMode") + export_start_date: typing.Optional[dt.datetime] = pydantic_v1.Field( + alias="exportStartDate", default=None + ) + next_sync_at: typing.Optional[dt.datetime] = pydantic_v1.Field( + alias="nextSyncAt", default=None + ) + last_sync_at: typing.Optional[dt.datetime] = pydantic_v1.Field( + alias="lastSyncAt", default=None + ) + created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") + updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_type.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_type.py new file mode 100644 index 000000000..38bacbf85 --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_type.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import enum +import typing + +T_Result = typing.TypeVar("T_Result") + + +class BlobStorageIntegrationType(str, enum.Enum): + S_3 = "S3" + S_3_COMPATIBLE = "S3_COMPATIBLE" + AZURE_BLOB_STORAGE = "AZURE_BLOB_STORAGE" + + def visit( + self, + s_3: typing.Callable[[], T_Result], + s_3_compatible: typing.Callable[[], T_Result], + azure_blob_storage: typing.Callable[[], T_Result], + ) -> T_Result: + if self is BlobStorageIntegrationType.S_3: + return s_3() + if self is BlobStorageIntegrationType.S_3_COMPATIBLE: + return s_3_compatible() + if self is BlobStorageIntegrationType.AZURE_BLOB_STORAGE: + return azure_blob_storage() diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integrations_response.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integrations_response.py new file mode 100644 index 000000000..c6231a23e --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integrations_response.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .blob_storage_integration_response import BlobStorageIntegrationResponse + + +class BlobStorageIntegrationsResponse(pydantic_v1.BaseModel): + data: typing.List[BlobStorageIntegrationResponse] + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/blob_storage_integrations/types/create_blob_storage_integration_request.py b/langfuse/api/resources/blob_storage_integrations/types/create_blob_storage_integration_request.py new file mode 100644 index 000000000..31b5779c6 --- /dev/null +++ b/langfuse/api/resources/blob_storage_integrations/types/create_blob_storage_integration_request.py @@ -0,0 +1,108 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .blob_storage_export_frequency import BlobStorageExportFrequency +from .blob_storage_export_mode import BlobStorageExportMode +from .blob_storage_integration_file_type import BlobStorageIntegrationFileType +from .blob_storage_integration_type import BlobStorageIntegrationType + + +class CreateBlobStorageIntegrationRequest(pydantic_v1.BaseModel): + project_id: str = pydantic_v1.Field(alias="projectId") + """ + ID of the project in which to configure the blob storage integration + """ + + type: BlobStorageIntegrationType + bucket_name: str = pydantic_v1.Field(alias="bucketName") + """ + Name of the storage bucket + """ + + endpoint: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + Custom endpoint URL (required for S3_COMPATIBLE type) + """ + + region: str = pydantic_v1.Field() + """ + Storage region + """ + + access_key_id: typing.Optional[str] = pydantic_v1.Field( + alias="accessKeyId", default=None + ) + """ + Access key ID for authentication + """ + + secret_access_key: typing.Optional[str] = pydantic_v1.Field( + alias="secretAccessKey", default=None + ) + """ + Secret access key for authentication (will be encrypted when stored) + """ + + prefix: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + Path prefix for exported files (must end with forward slash if provided) + """ + + export_frequency: BlobStorageExportFrequency = pydantic_v1.Field( + alias="exportFrequency" + ) + enabled: bool = pydantic_v1.Field() + """ + Whether the integration is active + """ + + force_path_style: bool = pydantic_v1.Field(alias="forcePathStyle") + """ + Use path-style URLs for S3 requests + """ + + file_type: BlobStorageIntegrationFileType = pydantic_v1.Field(alias="fileType") + export_mode: BlobStorageExportMode = pydantic_v1.Field(alias="exportMode") + export_start_date: typing.Optional[dt.datetime] = pydantic_v1.Field( + alias="exportStartDate", default=None + ) + """ + Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE) + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/ingestion/client.py b/langfuse/api/resources/ingestion/client.py index 9d6784856..d5aa2f952 100644 --- a/langfuse/api/resources/ingestion/client.py +++ b/langfuse/api/resources/ingestion/client.py @@ -31,8 +31,9 @@ def batch( request_options: typing.Optional[RequestOptions] = None, ) -> IngestionResponse: """ - Batched ingestion for Langfuse Tracing. - If you want to use tracing via the API, such as to build your own Langfuse client implementation, this is the only API route you need to implement. + **Legacy endpoint for batch ingestion for Langfuse Observability.** + + -> Please use the OpenTelemetry endpoint (`/api/public/otel`). Learn more: https://langfuse.com/integrations/native/opentelemetry Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -42,7 +43,7 @@ def batch( I.e. if you want to update a trace, you'd use the same body id, but separate event IDs. Notes: - - Introduction to data model: https://langfuse.com/docs/tracing-data-model + - Introduction to data model: https://langfuse.com/docs/observability/data-model - Batch sizes are limited to 3.5 MB in total. You need to adjust the number of events per batch accordingly. - The API does not return a 4xx status code for input errors. Instead, it responds with a 207 status code, which includes a list of the encountered errors. @@ -148,8 +149,9 @@ async def batch( request_options: typing.Optional[RequestOptions] = None, ) -> IngestionResponse: """ - Batched ingestion for Langfuse Tracing. - If you want to use tracing via the API, such as to build your own Langfuse client implementation, this is the only API route you need to implement. + **Legacy endpoint for batch ingestion for Langfuse Observability.** + + -> Please use the OpenTelemetry endpoint (`/api/public/otel`). Learn more: https://langfuse.com/integrations/native/opentelemetry Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -159,7 +161,7 @@ async def batch( I.e. if you want to update a trace, you'd use the same body id, but separate event IDs. Notes: - - Introduction to data model: https://langfuse.com/docs/tracing-data-model + - Introduction to data model: https://langfuse.com/docs/observability/data-model - Batch sizes are limited to 3.5 MB in total. You need to adjust the number of events per batch accordingly. - The API does not return a 4xx status code for input errors. Instead, it responds with a 207 status code, which includes a list of the encountered errors. diff --git a/langfuse/api/resources/organizations/__init__.py b/langfuse/api/resources/organizations/__init__.py index 48edda3f4..5c5bfced3 100644 --- a/langfuse/api/resources/organizations/__init__.py +++ b/langfuse/api/resources/organizations/__init__.py @@ -1,6 +1,8 @@ # This file was auto-generated by Fern from our API Definition. from .types import ( + DeleteMembershipRequest, + MembershipDeletionResponse, MembershipRequest, MembershipResponse, MembershipRole, @@ -10,6 +12,8 @@ ) __all__ = [ + "DeleteMembershipRequest", + "MembershipDeletionResponse", "MembershipRequest", "MembershipResponse", "MembershipRole", diff --git a/langfuse/api/resources/organizations/client.py b/langfuse/api/resources/organizations/client.py index f7f2f5021..b60f2d2bd 100644 --- a/langfuse/api/resources/organizations/client.py +++ b/langfuse/api/resources/organizations/client.py @@ -13,6 +13,8 @@ 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 .types.delete_membership_request import DeleteMembershipRequest +from .types.membership_deletion_response import MembershipDeletionResponse from .types.membership_request import MembershipRequest from .types.membership_response import MembershipResponse from .types.memberships_response import MembershipsResponse @@ -159,6 +161,80 @@ def update_organization_membership( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def delete_organization_membership( + self, + *, + request: DeleteMembershipRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipDeletionResponse: + """ + Delete a membership from the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request : DeleteMembershipRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + Examples + -------- + from langfuse import DeleteMembershipRequest + from langfuse.client import FernLangfuse + + client = FernLangfuse( + 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.organizations.delete_organization_membership( + request=DeleteMembershipRequest( + user_id="userId", + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="DELETE", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + MembershipDeletionResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def get_project_memberships( self, project_id: str, @@ -303,6 +379,84 @@ def update_project_membership( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def delete_project_membership( + self, + project_id: str, + *, + request: DeleteMembershipRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipDeletionResponse: + """ + Delete a membership from a specific project (requires organization-scoped API key). The user must be a member of the organization. + + Parameters + ---------- + project_id : str + + request : DeleteMembershipRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + Examples + -------- + from langfuse import DeleteMembershipRequest + from langfuse.client import FernLangfuse + + client = FernLangfuse( + 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.organizations.delete_project_membership( + project_id="projectId", + request=DeleteMembershipRequest( + user_id="userId", + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="DELETE", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + MembershipDeletionResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def get_organization_projects( self, *, request_options: typing.Optional[RequestOptions] = None ) -> OrganizationProjectsResponse: @@ -519,6 +673,88 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def delete_organization_membership( + self, + *, + request: DeleteMembershipRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipDeletionResponse: + """ + Delete a membership from the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request : DeleteMembershipRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + Examples + -------- + import asyncio + + from langfuse import DeleteMembershipRequest + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + 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.organizations.delete_organization_membership( + request=DeleteMembershipRequest( + user_id="userId", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="DELETE", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + MembershipDeletionResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def get_project_memberships( self, project_id: str, @@ -679,6 +915,92 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def delete_project_membership( + self, + project_id: str, + *, + request: DeleteMembershipRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipDeletionResponse: + """ + Delete a membership from a specific project (requires organization-scoped API key). The user must be a member of the organization. + + Parameters + ---------- + project_id : str + + request : DeleteMembershipRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + Examples + -------- + import asyncio + + from langfuse import DeleteMembershipRequest + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + 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.organizations.delete_project_membership( + project_id="projectId", + request=DeleteMembershipRequest( + user_id="userId", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="DELETE", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as( + MembershipDeletionResponse, _response.json() + ) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def get_organization_projects( self, *, request_options: typing.Optional[RequestOptions] = None ) -> OrganizationProjectsResponse: diff --git a/langfuse/api/resources/organizations/types/__init__.py b/langfuse/api/resources/organizations/types/__init__.py index 4a401124d..d154f63d8 100644 --- a/langfuse/api/resources/organizations/types/__init__.py +++ b/langfuse/api/resources/organizations/types/__init__.py @@ -1,5 +1,7 @@ # This file was auto-generated by Fern from our API Definition. +from .delete_membership_request import DeleteMembershipRequest +from .membership_deletion_response import MembershipDeletionResponse from .membership_request import MembershipRequest from .membership_response import MembershipResponse from .membership_role import MembershipRole @@ -8,6 +10,8 @@ from .organization_projects_response import OrganizationProjectsResponse __all__ = [ + "DeleteMembershipRequest", + "MembershipDeletionResponse", "MembershipRequest", "MembershipResponse", "MembershipRole", diff --git a/langfuse/api/resources/organizations/types/delete_membership_request.py b/langfuse/api/resources/organizations/types/delete_membership_request.py new file mode 100644 index 000000000..6752b0aae --- /dev/null +++ b/langfuse/api/resources/organizations/types/delete_membership_request.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class DeleteMembershipRequest(pydantic_v1.BaseModel): + user_id: str = pydantic_v1.Field(alias="userId") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/organizations/types/membership_deletion_response.py b/langfuse/api/resources/organizations/types/membership_deletion_response.py new file mode 100644 index 000000000..f9c1915b7 --- /dev/null +++ b/langfuse/api/resources/organizations/types/membership_deletion_response.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class MembershipDeletionResponse(pydantic_v1.BaseModel): + message: str + user_id: str = pydantic_v1.Field(alias="userId") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/score_v_2/client.py b/langfuse/api/resources/score_v_2/client.py index 894b44f22..e927b6c2b 100644 --- a/langfuse/api/resources/score_v_2/client.py +++ b/langfuse/api/resources/score_v_2/client.py @@ -40,6 +40,7 @@ def get( value: typing.Optional[float] = None, score_ids: typing.Optional[str] = None, config_id: typing.Optional[str] = None, + session_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, @@ -86,6 +87,9 @@ def get( config_id : typing.Optional[str] Retrieve only scores with a specific configId. + session_id : typing.Optional[str] + Retrieve only scores with a specific sessionId. + queue_id : typing.Optional[str] Retrieve only scores with a specific annotation queueId. @@ -136,6 +140,7 @@ def get( "value": value, "scoreIds": score_ids, "configId": config_id, + "sessionId": session_id, "queueId": queue_id, "dataType": data_type, "traceTags": trace_tags, @@ -253,6 +258,7 @@ async def get( value: typing.Optional[float] = None, score_ids: typing.Optional[str] = None, config_id: typing.Optional[str] = None, + session_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, @@ -299,6 +305,9 @@ async def get( config_id : typing.Optional[str] Retrieve only scores with a specific configId. + session_id : typing.Optional[str] + Retrieve only scores with a specific sessionId. + queue_id : typing.Optional[str] Retrieve only scores with a specific annotation queueId. @@ -357,6 +366,7 @@ async def main() -> None: "value": value, "scoreIds": score_ids, "configId": config_id, + "sessionId": session_id, "queueId": queue_id, "dataType": data_type, "traceTags": trace_tags, diff --git a/langfuse/api/resources/trace/client.py b/langfuse/api/resources/trace/client.py index c73901123..824142a27 100644 --- a/langfuse/api/resources/trace/client.py +++ b/langfuse/api/resources/trace/client.py @@ -214,7 +214,7 @@ def list( Optional filter for traces where the environment is one of the provided values. fields : typing.Optional[str] - Comma-separated list of fields to include in the response. Available field groups are 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not provided, all fields are included. Example: 'core,scores,metrics' + Comma-separated list of fields to include in the response. Available field groups: 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not specified, all fields are returned. Example: 'core,scores,metrics'. Note: Excluded 'observations' or 'scores' fields return empty arrays; excluded 'metrics' returns -1 for 'totalCost' and 'latency'. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -565,7 +565,7 @@ async def list( Optional filter for traces where the environment is one of the provided values. fields : typing.Optional[str] - Comma-separated list of fields to include in the response. Available field groups are 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not provided, all fields are included. Example: 'core,scores,metrics' + Comma-separated list of fields to include in the response. Available field groups: 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not specified, all fields are returned. Example: 'core,scores,metrics'. Note: Excluded 'observations' or 'scores' fields return empty arrays; excluded 'metrics' returns -1 for 'totalCost' and 'latency'. request_options : typing.Optional[RequestOptions] Request-specific configuration. diff --git a/langfuse/api/tests/utils/test_http_client.py b/langfuse/api/tests/utils/test_http_client.py deleted file mode 100644 index 950fcdeb1..000000000 --- a/langfuse/api/tests/utils/test_http_client.py +++ /dev/null @@ -1,59 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from langfuse.api.core.http_client import get_request_body -from langfuse.api.core.request_options import RequestOptions - - -def get_request_options() -> RequestOptions: - return {"additional_body_parameters": {"see you": "later"}} - - -def test_get_json_request_body() -> None: - json_body, data_body = get_request_body( - json={"hello": "world"}, data=None, request_options=None, omit=None - ) - assert json_body == {"hello": "world"} - assert data_body is None - - json_body_extras, data_body_extras = get_request_body( - json={"goodbye": "world"}, - data=None, - request_options=get_request_options(), - omit=None, - ) - - assert json_body_extras == {"goodbye": "world", "see you": "later"} - assert data_body_extras is None - - -def test_get_files_request_body() -> None: - json_body, data_body = get_request_body( - json=None, data={"hello": "world"}, request_options=None, omit=None - ) - assert data_body == {"hello": "world"} - assert json_body is None - - json_body_extras, data_body_extras = get_request_body( - json=None, - data={"goodbye": "world"}, - request_options=get_request_options(), - omit=None, - ) - - assert data_body_extras == {"goodbye": "world", "see you": "later"} - assert json_body_extras is None - - -def test_get_none_request_body() -> None: - json_body, data_body = get_request_body( - json=None, data=None, request_options=None, omit=None - ) - assert data_body is None - assert json_body is None - - json_body_extras, data_body_extras = get_request_body( - json=None, data=None, request_options=get_request_options(), omit=None - ) - - assert json_body_extras == {"see you": "later"} - assert data_body_extras is None diff --git a/langfuse/api/tests/utils/test_query_encoding.py b/langfuse/api/tests/utils/test_query_encoding.py deleted file mode 100644 index 9afa0ea78..000000000 --- a/langfuse/api/tests/utils/test_query_encoding.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from langfuse.api.core.query_encoder import encode_query - - -def test_query_encoding() -> None: - assert encode_query({"hello world": "hello world"}) == { - "hello world": "hello world" - } - assert encode_query({"hello_world": {"hello": "world"}}) == { - "hello_world[hello]": "world" - } - assert encode_query( - {"hello_world": {"hello": {"world": "today"}, "test": "this"}, "hi": "there"} - ) == { - "hello_world[hello][world]": "today", - "hello_world[test]": "this", - "hi": "there", - }