diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 344225dcb..d4f1b6775 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,7 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" name: Test on Python version ${{ matrix.python-version }} steps: diff --git a/langfuse/_client/attributes.py b/langfuse/_client/attributes.py index 343c70cdb..3d67ba9b6 100644 --- a/langfuse/_client/attributes.py +++ b/langfuse/_client/attributes.py @@ -19,8 +19,9 @@ ObservationTypeSpanLike, ) from langfuse._utils.serializer import EventSerializer +from langfuse.api import MapValue from langfuse.model import PromptClient -from langfuse.types import MapValue, SpanLevel +from langfuse.types import SpanLevel class LangfuseOtelSpanAttributes: diff --git a/langfuse/_client/client.py b/langfuse/_client/client.py index a3f653ada..a2a18b74f 100644 --- a/langfuse/_client/client.py +++ b/langfuse/_client/client.py @@ -45,7 +45,7 @@ ObservationTypeSpanLike, get_observation_types_list, ) -from langfuse._client.datasets import DatasetClient, DatasetItemClient +from langfuse._client.datasets import DatasetClient from langfuse._client.environment_variables import ( LANGFUSE_BASE_URL, LANGFUSE_DEBUG, @@ -78,14 +78,19 @@ from langfuse._utils import _get_timestamp from langfuse._utils.parse_error import handle_fern_exception from langfuse._utils.prompt_cache import PromptCache -from langfuse.api.resources.commons.errors.error import Error -from langfuse.api.resources.commons.errors.not_found_error import NotFoundError -from langfuse.api.resources.ingestion.types.score_body import ScoreBody -from langfuse.api.resources.prompts.types import ( - CreatePromptRequest_Chat, - CreatePromptRequest_Text, +from langfuse.api import ( + CreateChatPromptRequest, + CreateChatPromptType, + CreateTextPromptRequest, + Dataset, + DatasetItem, + DatasetStatus, + Error, + MapValue, + NotFoundError, Prompt_Chat, Prompt_Text, + ScoreBody, ) from langfuse.batch_evaluation import ( BatchEvaluationResult, @@ -112,13 +117,6 @@ ChatMessageDict, ChatMessageWithPlaceholdersDict, ChatPromptClient, - CreateDatasetItemRequest, - CreateDatasetRequest, - CreateDatasetRunItemRequest, - Dataset, - DatasetItem, - DatasetStatus, - MapValue, PromptClient, TextPromptClient, ) @@ -1635,67 +1633,6 @@ def update_current_span( status_message=status_message, ) - def update_current_trace( - self, - *, - name: Optional[str] = None, - user_id: Optional[str] = None, - session_id: Optional[str] = None, - version: Optional[str] = None, - input: Optional[Any] = None, - output: Optional[Any] = None, - metadata: Optional[Any] = None, - tags: Optional[List[str]] = None, - public: Optional[bool] = None, - ) -> None: - """Update the current trace with additional information. - - Args: - name: Updated name for the Langfuse trace - user_id: ID of the user who initiated the Langfuse trace - session_id: Session identifier for grouping related Langfuse traces - version: Version identifier for the application or service - input: Input data for the overall Langfuse trace - output: Output data from the overall Langfuse trace - metadata: Additional metadata to associate with the Langfuse trace - tags: List of tags to categorize the Langfuse trace - public: Whether the Langfuse trace should be publicly accessible - - See Also: - :func:`langfuse.propagate_attributes`: Recommended replacement - """ - if not self._tracing_enabled: - langfuse_logger.debug( - "Operation skipped: update_current_trace - Tracing is disabled or client is in no-op mode." - ) - return - - current_otel_span = self._get_current_otel_span() - - if current_otel_span is not None and current_otel_span.is_recording(): - existing_observation_type = current_otel_span.attributes.get( # type: ignore[attr-defined] - LangfuseOtelSpanAttributes.OBSERVATION_TYPE, "span" - ) - # We need to preserve the class to keep the correct observation type - span_class = self._get_span_class(existing_observation_type) - span = span_class( - otel_span=current_otel_span, - langfuse_client=self, - environment=self._environment, - ) - - span.update_trace( - name=name, - user_id=user_id, - session_id=session_id, - version=version, - input=input, - output=output, - metadata=metadata, - tags=tags, - public=public, - ) - def create_event( self, *, @@ -2057,7 +1994,7 @@ def create_score( try: new_body = ScoreBody( id=score_id, - sessionId=session_id, + session_id=session_id, datasetRunId=dataset_run_id, traceId=trace_id, observationId=observation_id, @@ -2453,9 +2390,9 @@ def get_dataset( page += 1 - items = [DatasetItemClient(i, langfuse=self) for i in dataset_items] - - return DatasetClient(dataset, items=items) + return DatasetClient( + dataset=dataset, items=dataset_items, langfuse_client=self + ) except Error as e: handle_fern_exception(e) @@ -2818,14 +2755,12 @@ async def _process_experiment_item( # creates multiple event loops across different threads dataset_run_item = await asyncio.to_thread( self.api.dataset_run_items.create, - request=CreateDatasetRunItemRequest( - runName=experiment_run_name, - runDescription=experiment_description, - metadata=experiment_metadata, - datasetItemId=item.id, # type: ignore - traceId=trace_id, - observationId=span.id, - ), + run_name=experiment_run_name, + run_description=experiment_description, + metadata=experiment_metadata, + dataset_item_id=item.id, # type: ignore + trace_id=trace_id, + observation_id=span.id, ) dataset_run_id = dataset_run_item.dataset_run_id @@ -3271,16 +3206,17 @@ def create_dataset( Dataset: The created dataset as returned by the Langfuse API. """ try: - body = CreateDatasetRequest( + langfuse_logger.debug(f"Creating datasets {name}") + + result = self.api.datasets.create( name=name, description=description, metadata=metadata, - inputSchema=input_schema, - expectedOutputSchema=expected_output_schema, + input_schema=input_schema, + expected_output_schema=expected_output_schema, ) - langfuse_logger.debug(f"Creating datasets {body}") - return self.api.datasets.create(request=body) + return cast(Dataset, result) except Error as e: handle_fern_exception(e) @@ -3331,18 +3267,20 @@ def create_dataset_item( ``` """ try: - body = CreateDatasetItemRequest( - datasetName=dataset_name, + langfuse_logger.debug(f"Creating dataset item for dataset {dataset_name}") + + result = self.api.dataset_items.create( + dataset_name=dataset_name, input=input, - expectedOutput=expected_output, + expected_output=expected_output, metadata=metadata, - sourceTraceId=source_trace_id, - sourceObservationId=source_observation_id, + source_trace_id=source_trace_id, + source_observation_id=source_observation_id, status=status, id=id, ) - langfuse_logger.debug(f"Creating dataset item {body}") - return self.api.dataset_items.create(request=body) + + return cast(DatasetItem, result) except Error as e: handle_fern_exception(e) raise e @@ -3704,15 +3642,15 @@ def create_prompt( raise ValueError( "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes." ) - request: Union[CreatePromptRequest_Chat, CreatePromptRequest_Text] = ( - CreatePromptRequest_Chat( + request: Union[CreateChatPromptRequest, CreateTextPromptRequest] = ( + CreateChatPromptRequest( name=name, prompt=cast(Any, prompt), labels=labels, tags=tags, config=config or {}, - commitMessage=commit_message, - type="chat", + commit_message=commit_message, + type=CreateChatPromptType.CHAT, ) ) server_prompt = self.api.prompts.create(request=request) @@ -3725,14 +3663,13 @@ def create_prompt( if not isinstance(prompt, str): raise ValueError("For 'text' type, 'prompt' must be a string.") - request = CreatePromptRequest_Text( + request = CreateTextPromptRequest( name=name, prompt=prompt, labels=labels, tags=tags, config=config or {}, - commitMessage=commit_message, - type="text", + commit_message=commit_message, ) server_prompt = self.api.prompts.create(request=request) diff --git a/langfuse/_client/datasets.py b/langfuse/_client/datasets.py index 0a9a0312c..7124e5c86 100644 --- a/langfuse/_client/datasets.py +++ b/langfuse/_client/datasets.py @@ -1,9 +1,10 @@ import datetime as dt -import logging -from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional - -from opentelemetry.util._decorator import _agnosticcontextmanager +from typing import TYPE_CHECKING, Any, Dict, List, Optional +from langfuse.api import ( + Dataset, + DatasetItem, +) from langfuse.batch_evaluation import CompositeEvaluatorFunction from langfuse.experiment import ( EvaluatorFunction, @@ -11,138 +12,11 @@ RunEvaluatorFunction, TaskFunction, ) -from langfuse.model import ( - CreateDatasetRunItemRequest, - Dataset, - DatasetItem, - DatasetStatus, -) - -from .span import LangfuseSpan if TYPE_CHECKING: from langfuse._client.client import Langfuse -class DatasetItemClient: - """Class for managing dataset items in Langfuse. - - Args: - id (str): Unique identifier of the dataset item. - status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'. - input (Any): Input data of the dataset item. - expected_output (Optional[Any]): Expected output of the dataset item. - metadata (Optional[Any]): Additional metadata of the dataset item. - source_trace_id (Optional[str]): Identifier of the source trace. - source_observation_id (Optional[str]): Identifier of the source observation. - dataset_id (str): Identifier of the dataset to which this item belongs. - dataset_name (str): Name of the dataset to which this item belongs. - created_at (datetime): Timestamp of dataset item creation. - updated_at (datetime): Timestamp of the last update to the dataset item. - langfuse (Langfuse): Instance of Langfuse client for API interactions. - - Example: - ```python - from langfuse import Langfuse - - langfuse = Langfuse() - - dataset = langfuse.get_dataset("") - - for item in dataset.items: - # Generate a completion using the input of every item - completion, generation = llm_app.run(item.input) - - # Evaluate the completion - generation.score( - name="example-score", - value=1 - ) - ``` - """ - - log = logging.getLogger("langfuse") - - id: str - status: DatasetStatus - input: Any - expected_output: Optional[Any] - metadata: Optional[Any] - source_trace_id: Optional[str] - source_observation_id: Optional[str] - dataset_id: str - dataset_name: str - created_at: dt.datetime - updated_at: dt.datetime - - langfuse: "Langfuse" - - def __init__(self, dataset_item: DatasetItem, langfuse: "Langfuse"): - """Initialize the DatasetItemClient.""" - self.id = dataset_item.id - self.status = dataset_item.status - self.input = dataset_item.input - self.expected_output = dataset_item.expected_output - self.metadata = dataset_item.metadata - self.source_trace_id = dataset_item.source_trace_id - self.source_observation_id = dataset_item.source_observation_id - self.dataset_id = dataset_item.dataset_id - self.dataset_name = dataset_item.dataset_name - self.created_at = dataset_item.created_at - self.updated_at = dataset_item.updated_at - - self.langfuse = langfuse - - @_agnosticcontextmanager - def run( - self, - *, - run_name: str, - run_metadata: Optional[Any] = None, - run_description: Optional[str] = None, - ) -> Generator[LangfuseSpan, None, None]: - """Create a context manager for the dataset item run that links the execution to a Langfuse trace. - - This method is a context manager that creates a trace for the dataset run and yields a span - that can be used to track the execution of the run. - - Args: - run_name (str): The name of the dataset run. - run_metadata (Optional[Any]): Additional metadata to include in dataset run. - run_description (Optional[str]): Description of the dataset run. - - Yields: - span: A LangfuseSpan that can be used to trace the execution of the run. - """ - trace_name = f"Dataset run: {run_name}" - - with self.langfuse.start_as_current_span(name=trace_name) as span: - span.update_trace( - name=trace_name, - metadata={ - "dataset_item_id": self.id, - "run_name": run_name, - "dataset_id": self.dataset_id, - }, - ) - - self.log.debug( - f"Creating dataset run item: run_name={run_name} id={self.id} trace_id={span.trace_id}" - ) - - self.langfuse.api.dataset_run_items.create( - request=CreateDatasetRunItemRequest( - runName=run_name, - datasetItemId=self.id, - traceId=span.trace_id, - metadata=run_metadata, - runDescription=run_description, - ) - ) - - yield span - - class DatasetClient: """Class for managing datasets in Langfuse. @@ -154,7 +28,7 @@ class DatasetClient: project_id (str): Identifier of the project to which the dataset belongs. created_at (datetime): Timestamp of dataset creation. updated_at (datetime): Timestamp of the last update to the dataset. - items (List[DatasetItemClient]): List of dataset items associated with the dataset. + items (List[DatasetItem]): List of dataset items associated with the dataset. Example: Print the input of each dataset item in a dataset. @@ -177,9 +51,13 @@ class DatasetClient: metadata: Optional[Any] created_at: dt.datetime updated_at: dt.datetime - items: List[DatasetItemClient] + input_schema: Optional[Any] + expected_output_schema: Optional[Any] + items: List[DatasetItem] - def __init__(self, dataset: Dataset, items: List[DatasetItemClient]): + def __init__( + self, *, dataset: Dataset, items: List[DatasetItem], langfuse_client: "Langfuse" + ): """Initialize the DatasetClient.""" self.id = dataset.id self.name = dataset.name @@ -188,14 +66,10 @@ def __init__(self, dataset: Dataset, items: List[DatasetItemClient]): self.metadata = dataset.metadata self.created_at = dataset.created_at self.updated_at = dataset.updated_at + self.input_schema = dataset.input_schema + self.expected_output_schema = dataset.expected_output_schema self.items = items - self._langfuse: Optional["Langfuse"] = None - - def _get_langfuse_client(self) -> Optional["Langfuse"]: - """Get the Langfuse client from the first item.""" - if self._langfuse is None and self.items: - self._langfuse = self.items[0].langfuse - return self._langfuse + self._langfuse_client: "Langfuse" = langfuse_client def run_experiment( self, @@ -406,11 +280,7 @@ def content_diversity(*, item_results, **kwargs): - This method works in both sync and async contexts (Jupyter notebooks, web apps, etc.) - Async execution is handled automatically with smart event loop detection """ - langfuse_client = self._get_langfuse_client() - if not langfuse_client: - raise ValueError("No Langfuse client available. Dataset items are empty.") - - return langfuse_client.run_experiment( + return self._langfuse_client.run_experiment( name=name, run_name=run_name, description=description, diff --git a/langfuse/_client/resource_manager.py b/langfuse/_client/resource_manager.py index 08c008234..1a74285c3 100644 --- a/langfuse/_client/resource_manager.py +++ b/langfuse/_client/resource_manager.py @@ -42,7 +42,7 @@ from langfuse._utils.environment import get_common_release_envs from langfuse._utils.prompt_cache import PromptCache from langfuse._utils.request import LangfuseClient -from langfuse.api.client import AsyncFernLangfuse, FernLangfuse +from langfuse.api import AsyncLangfuseAPI, LangfuseAPI from langfuse.logger import langfuse_logger from langfuse.types import MaskFunction @@ -213,7 +213,7 @@ def _initialize_instance( client_headers = additional_headers if additional_headers else {} self.httpx_client = httpx.Client(timeout=timeout, headers=client_headers) - self.api = FernLangfuse( + self.api = LangfuseAPI( base_url=base_url, username=self.public_key, password=secret_key, @@ -223,7 +223,7 @@ def _initialize_instance( httpx_client=self.httpx_client, timeout=timeout, ) - self.async_api = AsyncFernLangfuse( + self.async_api = AsyncLangfuseAPI( base_url=base_url, username=self.public_key, password=secret_key, diff --git a/langfuse/_client/span.py b/langfuse/_client/span.py index 2f0000e41..450aa0fa2 100644 --- a/langfuse/_client/span.py +++ b/langfuse/_client/span.py @@ -20,7 +20,6 @@ TYPE_CHECKING, Any, Dict, - List, Literal, Optional, Type, @@ -42,7 +41,6 @@ LangfuseOtelSpanAttributes, create_generation_attributes, create_span_attributes, - create_trace_attributes, ) from langfuse._client.constants import ( ObservationTypeGenerationLike, @@ -51,8 +49,9 @@ ObservationTypeSpanLike, get_observation_types_list, ) +from langfuse.api import MapValue, ScoreDataType from langfuse.logger import langfuse_logger -from langfuse.types import MapValue, ScoreDataType, SpanLevel +from langfuse.types import SpanLevel # Factory mapping for observation classes # Note: "event" is handled separately due to special instantiation logic @@ -208,64 +207,6 @@ def end(self, *, end_time: Optional[int] = None) -> "LangfuseObservationWrapper" return self - def update_trace( - self, - *, - name: Optional[str] = None, - user_id: Optional[str] = None, - session_id: Optional[str] = None, - version: Optional[str] = None, - input: Optional[Any] = None, - output: Optional[Any] = None, - metadata: Optional[Any] = None, - tags: Optional[List[str]] = None, - public: Optional[bool] = None, - ) -> "LangfuseObservationWrapper": - """Update the trace that this span belongs to. - - Args: - name: Updated name for the trace - user_id: ID of the user who initiated the trace - session_id: Session identifier for grouping related traces - version: Version identifier for the application or service - input: Input data for the overall trace - output: Output data from the overall trace - metadata: Additional metadata to associate with the trace - tags: List of tags to categorize the trace - public: Whether the trace should be publicly accessible - - See Also: - :func:`langfuse.propagate_attributes`: Recommended replacement - """ - if not self._otel_span.is_recording(): - return self - - media_processed_input = self._process_media_and_apply_mask( - data=input, field="input", span=self._otel_span - ) - media_processed_output = self._process_media_and_apply_mask( - data=output, field="output", span=self._otel_span - ) - media_processed_metadata = self._process_media_and_apply_mask( - data=metadata, field="metadata", span=self._otel_span - ) - - attributes = create_trace_attributes( - name=name, - user_id=user_id, - session_id=session_id, - version=version, - input=media_processed_input, - output=media_processed_output, - metadata=media_processed_metadata, - tags=tags, - public=public, - ) - - self._otel_span.set_attributes(attributes) - - return self - @overload def score( self, @@ -273,7 +214,9 @@ def score( name: str, value: float, score_id: Optional[str] = None, - data_type: Optional[Literal["NUMERIC", "BOOLEAN"]] = None, + data_type: Optional[ + Literal[ScoreDataType.NUMERIC, ScoreDataType.BOOLEAN] + ] = None, comment: Optional[str] = None, config_id: Optional[str] = None, timestamp: Optional[datetime] = None, @@ -286,7 +229,9 @@ def score( name: str, value: str, score_id: Optional[str] = None, - data_type: Optional[Literal["CATEGORICAL"]] = "CATEGORICAL", + data_type: Optional[ + Literal[ScoreDataType.CATEGORICAL] + ] = ScoreDataType.CATEGORICAL, comment: Optional[str] = None, config_id: Optional[str] = None, timestamp: Optional[datetime] = None, @@ -351,7 +296,9 @@ def score_trace( name: str, value: float, score_id: Optional[str] = None, - data_type: Optional[Literal["NUMERIC", "BOOLEAN"]] = None, + data_type: Optional[ + Literal[ScoreDataType.NUMERIC, ScoreDataType.BOOLEAN] + ] = None, comment: Optional[str] = None, config_id: Optional[str] = None, timestamp: Optional[datetime] = None, @@ -364,7 +311,9 @@ def score_trace( name: str, value: str, score_id: Optional[str] = None, - data_type: Optional[Literal["CATEGORICAL"]] = "CATEGORICAL", + data_type: Optional[ + Literal[ScoreDataType.CATEGORICAL] + ] = ScoreDataType.CATEGORICAL, comment: Optional[str] = None, config_id: Optional[str] = None, timestamp: Optional[datetime] = None, diff --git a/langfuse/_task_manager/media_manager.py b/langfuse/_task_manager/media_manager.py index 1a32e3d60..99312c669 100644 --- a/langfuse/_task_manager/media_manager.py +++ b/langfuse/_task_manager/media_manager.py @@ -10,10 +10,8 @@ from langfuse._client.environment_variables import LANGFUSE_MEDIA_UPLOAD_ENABLED from langfuse._utils import _get_timestamp -from langfuse.api import GetMediaUploadUrlRequest, PatchMediaBody -from langfuse.api.client import FernLangfuse +from langfuse.api import LangfuseAPI, MediaContentType from langfuse.api.core import ApiError -from langfuse.api.resources.media.types.media_content_type import MediaContentType from langfuse.media import LangfuseMedia from .media_upload_queue import UploadMediaJob @@ -28,7 +26,7 @@ class MediaManager: def __init__( self, *, - api_client: FernLangfuse, + api_client: LangfuseAPI, media_upload_queue: Queue, max_retries: Optional[int] = 3, ): @@ -219,14 +217,12 @@ def _process_upload_media_job( ) -> None: upload_url_response = self._request_with_backoff( self._api_client.media.get_upload_url, - request=GetMediaUploadUrlRequest( - contentLength=data["content_length"], - contentType=cast(MediaContentType, data["content_type"]), - sha256Hash=data["content_sha256_hash"], - field=data["field"], - traceId=data["trace_id"], - observationId=data["observation_id"], - ), + content_length=data["content_length"], + content_type=cast(MediaContentType, data["content_type"]), + sha256hash=data["content_sha256_hash"], + field=data["field"], + trace_id=data["trace_id"], + observation_id=data["observation_id"], ) upload_url = upload_url_response.upload_url @@ -266,12 +262,10 @@ def _process_upload_media_job( self._request_with_backoff( self._api_client.media.patch, media_id=data["media_id"], - request=PatchMediaBody( - uploadedAt=_get_timestamp(), - uploadHttpStatus=upload_response.status_code, - uploadHttpError=upload_response.text, - uploadTimeMs=upload_time_ms, - ), + uploaded_at=_get_timestamp(), + upload_http_status=upload_response.status_code, + upload_http_error=upload_response.text, + upload_time_ms=upload_time_ms, ) self._log.debug( diff --git a/langfuse/_task_manager/score_ingestion_consumer.py b/langfuse/_task_manager/score_ingestion_consumer.py index 1a5b61f91..f53ea08f0 100644 --- a/langfuse/_task_manager/score_ingestion_consumer.py +++ b/langfuse/_task_manager/score_ingestion_consumer.py @@ -7,23 +7,19 @@ from typing import Any, List, Optional import backoff - -from ..version import __version__ as langfuse_version - -try: - import pydantic.v1 as pydantic -except ImportError: - import pydantic # type: ignore +from pydantic import BaseModel from langfuse._utils.parse_error import handle_exception from langfuse._utils.request import APIError, LangfuseClient from langfuse._utils.serializer import EventSerializer +from ..version import __version__ as langfuse_version + MAX_EVENT_SIZE_BYTES = int(os.environ.get("LANGFUSE_MAX_EVENT_SIZE_BYTES", 1_000_000)) MAX_BATCH_SIZE_BYTES = int(os.environ.get("LANGFUSE_MAX_BATCH_SIZE_BYTES", 2_500_000)) -class ScoreIngestionMetadata(pydantic.BaseModel): +class ScoreIngestionMetadata(BaseModel): batch_size: int sdk_name: str sdk_version: str @@ -78,8 +74,8 @@ def _next(self) -> list: ) # convert pydantic models to dicts - if "body" in event and isinstance(event["body"], pydantic.BaseModel): - event["body"] = event["body"].dict(exclude_none=True) + if "body" in event and isinstance(event["body"], BaseModel): + event["body"] = event["body"].model_dump(exclude_none=True) item_size = self._get_item_size(event) @@ -156,7 +152,7 @@ def _upload_batch(self, batch: List[Any]) -> None: sdk_name="python", sdk_version=langfuse_version, public_key=self._public_key, - ).dict() + ).model_dump() @backoff.on_exception( backoff.expo, Exception, max_tries=self._max_retries, logger=None diff --git a/langfuse/_utils/parse_error.py b/langfuse/_utils/parse_error.py index cb5749f93..4fd8d6a69 100644 --- a/langfuse/_utils/parse_error.py +++ b/langfuse/_utils/parse_error.py @@ -3,17 +3,17 @@ # our own api errors from langfuse._utils.request import APIError, APIErrors -from langfuse.api.core import ApiError # fern api errors -from langfuse.api.resources.commons.errors import ( +from langfuse.api import ( AccessDeniedError, Error, MethodNotAllowedError, NotFoundError, + ServiceUnavailableError, UnauthorizedError, ) -from langfuse.api.resources.health.errors import ServiceUnavailableError +from langfuse.api.core import ApiError SUPPORT_URL = "https://langfuse.com/support" API_DOCS_URL = "https://api.reference.langfuse.com" diff --git a/langfuse/_utils/serializer.py b/langfuse/_utils/serializer.py index 1350ea00c..c2dad3312 100644 --- a/langfuse/_utils/serializer.py +++ b/langfuse/_utils/serializer.py @@ -1,5 +1,6 @@ """@private""" +import datetime as dt import enum import math from asyncio import Queue @@ -14,7 +15,6 @@ from pydantic import BaseModel -from langfuse.api.core import pydantic_utilities, serialize_datetime from langfuse.media import LangfuseMedia # Attempt to import Serializable @@ -98,17 +98,13 @@ def default(self, obj: Any) -> Any: return obj.isoformat() if isinstance(obj, BaseModel): - obj.model_rebuild() if pydantic_utilities.IS_PYDANTIC_V2 else obj.update_forward_refs() # This method forces the OpenAI model to instantiate its serializer to avoid errors when serializing + obj.model_rebuild() # For LlamaIndex models, we need to rebuild the raw model as well if they include OpenAI models if isinstance(raw := getattr(obj, "raw", None), BaseModel): - raw.model_rebuild() if pydantic_utilities.IS_PYDANTIC_V2 else raw.update_forward_refs() + raw.model_rebuild() - return ( - obj.model_dump() - if pydantic_utilities.IS_PYDANTIC_V2 - else obj.dict() - ) + return obj.model_dump() if isinstance(obj, Path): return str(obj) @@ -188,3 +184,22 @@ def is_js_safe_integer(value: int) -> bool: min_safe_int = -(2**53) + 1 return min_safe_int <= value <= max_safe_int + + +def serialize_datetime(v: dt.datetime) -> str: + def _serialize_zoned_datetime(v: dt.datetime) -> str: + if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname( + None + ): + # UTC is a special case where we use "Z" at the end instead of "+00:00" + return v.isoformat().replace("+00:00", "Z") + else: + # Delegate to the typical +/- offset format + return v.isoformat() + + if v.tzinfo is not None: + return _serialize_zoned_datetime(v) + else: + local_tz = dt.datetime.now().astimezone().tzinfo + localized_dt = v.replace(tzinfo=local_tz) + return _serialize_zoned_datetime(localized_dt) diff --git a/langfuse/api/.fern/metadata.json b/langfuse/api/.fern/metadata.json new file mode 100644 index 000000000..4f1a155c9 --- /dev/null +++ b/langfuse/api/.fern/metadata.json @@ -0,0 +1,14 @@ +{ + "cliVersion": "3.30.3", + "generatorName": "fernapi/fern-python-sdk", + "generatorVersion": "4.46.2", + "generatorConfig": { + "pydantic_config": { + "enum_type": "python_enums", + "version": "v2" + }, + "client": { + "class_name": "LangfuseAPI" + } + } +} \ No newline at end of file diff --git a/langfuse/api/README.md b/langfuse/api/README.md deleted file mode 100644 index 47de89ea3..000000000 --- a/langfuse/api/README.md +++ /dev/null @@ -1,161 +0,0 @@ -# Langfuse Python Library - -[![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 APIs from Python. - -## Table of Contents - -- [Installation](#installation) -- [Usage](#usage) -- [Async Client](#async-client) -- [Exception Handling](#exception-handling) -- [Advanced](#advanced) - - [Retries](#retries) - - [Timeouts](#timeouts) - - [Custom Client](#custom-client) -- [Contributing](#contributing) - -## Installation - -```sh -pip install langfuse -``` - -## Usage - -Instantiate and use the client with the following: - -```python -from langfuse import CreateAnnotationQueueRequest -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.annotation_queues.create_queue( - request=CreateAnnotationQueueRequest( - name="name", - score_config_ids=["scoreConfigIds", "scoreConfigIds"], - ), -) -``` - -## Async Client - -The SDK also exports an `async` client so that you can make non-blocking calls to our API. - -```python -import asyncio - -from langfuse import CreateAnnotationQueueRequest -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.annotation_queues.create_queue( - request=CreateAnnotationQueueRequest( - name="name", - score_config_ids=["scoreConfigIds", "scoreConfigIds"], - ), - ) - - -asyncio.run(main()) -``` - -## Exception Handling - -When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error -will be thrown. - -```python -from .api_error import ApiError - -try: - client.annotation_queues.create_queue(...) -except ApiError as e: - print(e.status_code) - print(e.body) -``` - -## Advanced - -### Retries - -The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long -as the request is deemed retriable and the number of retry attempts has not grown larger than the configured -retry limit (default: 2). - -A request is deemed retriable when any of the following HTTP status codes is returned: - -- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) -- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) -- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) - -Use the `max_retries` request option to configure this behavior. - -```python -client.annotation_queues.create_queue(...,{ - max_retries=1 -}) -``` - -### Timeouts - -The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - -```python - -from langfuse.client import FernLangfuse - -client = FernLangfuse(..., { timeout=20.0 }, ) - - -# Override timeout for a specific method -client.annotation_queues.create_queue(...,{ - timeout_in_seconds=1 -}) -``` - -### Custom Client - -You can override the `httpx` client to customize it for your use-case. Some common use-cases include support for proxies -and transports. -```python -import httpx -from langfuse.client import FernLangfuse - -client = FernLangfuse( - ..., - http_client=httpx.Client( - proxies="http://my.test.proxy.example.com", - transport=httpx.HTTPTransport(local_address="0.0.0.0"), - ), -) -``` - -## Contributing - -While we value open-source contributions to this SDK, this library is generated programmatically. -Additions made directly to this library would have to be moved over to our generation code, -otherwise they would be overwritten upon the next generated release. Feel free to open a PR as -a proof of concept, but know that we will not be able to merge it as-is. We suggest opening -an issue first to discuss with us! - -On the other hand, contributions to the README are always very welcome! diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index c958219d5..dc72246e3 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -1,254 +1,570 @@ # This file was auto-generated by Fern from our API Definition. -from .resources import ( - AccessDeniedError, - AnnotationQueue, - AnnotationQueueAssignmentRequest, - AnnotationQueueItem, - AnnotationQueueObjectType, - AnnotationQueueStatus, - ApiKeyDeletionResponse, - ApiKeyList, - ApiKeyResponse, - ApiKeySummary, - AuthenticationScheme, - BaseEvent, - BasePrompt, - BaseScore, - BaseScoreV1, - BlobStorageExportFrequency, - BlobStorageExportMode, - BlobStorageIntegrationDeletionResponse, - BlobStorageIntegrationFileType, - BlobStorageIntegrationResponse, - BlobStorageIntegrationType, - BlobStorageIntegrationsResponse, - BooleanScore, - BooleanScoreV1, - BulkConfig, - CategoricalScore, - CategoricalScoreV1, - ChatMessage, - ChatMessageWithPlaceholders, - ChatMessageWithPlaceholders_Chatmessage, - ChatMessageWithPlaceholders_Placeholder, - ChatPrompt, - Comment, - CommentObjectType, - ConfigCategory, - CreateAnnotationQueueAssignmentResponse, - CreateAnnotationQueueItemRequest, - CreateAnnotationQueueRequest, - CreateBlobStorageIntegrationRequest, - CreateChatPromptRequest, - CreateCommentRequest, - CreateCommentResponse, - CreateDatasetItemRequest, - CreateDatasetRequest, - CreateDatasetRunItemRequest, - CreateEventBody, - CreateEventEvent, - CreateGenerationBody, - CreateGenerationEvent, - CreateModelRequest, - CreateObservationEvent, - CreatePromptRequest, - CreatePromptRequest_Chat, - CreatePromptRequest_Text, - CreateScoreConfigRequest, - CreateScoreRequest, - CreateScoreResponse, - CreateScoreValue, - CreateSpanBody, - CreateSpanEvent, - CreateTextPromptRequest, - Dataset, - DatasetItem, - DatasetRun, - DatasetRunItem, - DatasetRunWithItems, - DatasetStatus, - DeleteAnnotationQueueAssignmentResponse, - DeleteAnnotationQueueItemResponse, - DeleteDatasetItemResponse, - DeleteDatasetRunResponse, - DeleteMembershipRequest, - DeleteTraceResponse, - EmptyResponse, - Error, - FilterConfig, - GetCommentsResponse, - GetMediaResponse, - GetMediaUploadUrlRequest, - GetMediaUploadUrlResponse, - GetScoresResponse, - GetScoresResponseData, - GetScoresResponseDataBoolean, - GetScoresResponseDataCategorical, - GetScoresResponseDataNumeric, - GetScoresResponseData_Boolean, - GetScoresResponseData_Categorical, - GetScoresResponseData_Numeric, - GetScoresResponseTraceData, - HealthResponse, - IngestionError, - IngestionEvent, - IngestionEvent_EventCreate, - IngestionEvent_GenerationCreate, - IngestionEvent_GenerationUpdate, - IngestionEvent_ObservationCreate, - IngestionEvent_ObservationUpdate, - IngestionEvent_ScoreCreate, - IngestionEvent_SdkLog, - IngestionEvent_SpanCreate, - IngestionEvent_SpanUpdate, - IngestionEvent_TraceCreate, - IngestionResponse, - IngestionSuccess, - IngestionUsage, - LlmAdapter, - LlmConnection, - MapValue, - MediaContentType, - MembershipDeletionResponse, - MembershipRequest, - MembershipResponse, - MembershipRole, - MembershipsResponse, - MethodNotAllowedError, - MetricsResponse, - Model, - ModelPrice, - ModelUsageUnit, - NotFoundError, - NumericScore, - NumericScoreV1, - Observation, - ObservationBody, - ObservationLevel, - ObservationType, - Observations, - ObservationsView, - ObservationsViews, - OpenAiCompletionUsageSchema, - OpenAiResponseUsageSchema, - OpenAiUsage, - OptionalObservationBody, - OrganizationApiKey, - OrganizationApiKeysResponse, - OrganizationProject, - OrganizationProjectsResponse, - OtelAttribute, - OtelAttributeValue, - OtelResource, - OtelResourceSpan, - OtelScope, - OtelScopeSpan, - OtelSpan, - OtelTraceResponse, - PaginatedAnnotationQueueItems, - PaginatedAnnotationQueues, - PaginatedDatasetItems, - PaginatedDatasetRunItems, - PaginatedDatasetRuns, - PaginatedDatasets, - PaginatedLlmConnections, - PaginatedModels, - PaginatedSessions, - PatchMediaBody, - PlaceholderMessage, - PricingTier, - PricingTierCondition, - PricingTierInput, - PricingTierOperator, - Project, - ProjectDeletionResponse, - Projects, - Prompt, - PromptMeta, - PromptMetaListResponse, - PromptType, - Prompt_Chat, - Prompt_Text, - ResourceMeta, - ResourceType, - ResourceTypesResponse, - SchemaExtension, - SchemaResource, - SchemasResponse, - ScimEmail, - ScimFeatureSupport, - ScimName, - ScimUser, - ScimUsersListResponse, - Score, - ScoreBody, - ScoreConfig, - ScoreConfigs, - ScoreDataType, - ScoreEvent, - ScoreSource, - ScoreV1, - ScoreV1_Boolean, - ScoreV1_Categorical, - ScoreV1_Numeric, - Score_Boolean, - Score_Categorical, - Score_Numeric, - SdkLogBody, - SdkLogEvent, - ServiceProviderConfig, - ServiceUnavailableError, - Session, - SessionWithTraces, - Sort, - TextPrompt, - Trace, - TraceBody, - TraceEvent, - TraceWithDetails, - TraceWithFullDetails, - Traces, - UnauthorizedError, - UpdateAnnotationQueueItemRequest, - UpdateEventBody, - UpdateGenerationBody, - UpdateGenerationEvent, - UpdateObservationEvent, - UpdateScoreConfigRequest, - UpdateSpanBody, - UpdateSpanEvent, - UpsertLlmConnectionRequest, - Usage, - UsageDetails, - UserMeta, - annotation_queues, - blob_storage_integrations, - comments, - commons, - dataset_items, - dataset_run_items, - datasets, - health, - ingestion, - llm_connections, - media, - metrics, - models, - observations, - opentelemetry, - organizations, - projects, - prompt_version, - prompts, - scim, - score, - score_configs, - score_v_2, - sessions, - trace, - utils, -) +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import ( + annotation_queues, + blob_storage_integrations, + comments, + commons, + dataset_items, + dataset_run_items, + datasets, + health, + ingestion, + llm_connections, + media, + metrics, + metrics_v2, + models, + observations, + observations_v2, + opentelemetry, + organizations, + projects, + prompt_version, + prompts, + scim, + score, + score_configs, + score_v2, + sessions, + trace, + utils, + ) + from .annotation_queues import ( + AnnotationQueue, + AnnotationQueueAssignmentRequest, + AnnotationQueueItem, + AnnotationQueueObjectType, + AnnotationQueueStatus, + CreateAnnotationQueueAssignmentResponse, + CreateAnnotationQueueItemRequest, + CreateAnnotationQueueRequest, + DeleteAnnotationQueueAssignmentResponse, + DeleteAnnotationQueueItemResponse, + PaginatedAnnotationQueueItems, + PaginatedAnnotationQueues, + UpdateAnnotationQueueItemRequest, + ) + from .blob_storage_integrations import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationDeletionResponse, + BlobStorageIntegrationFileType, + BlobStorageIntegrationResponse, + BlobStorageIntegrationType, + BlobStorageIntegrationsResponse, + CreateBlobStorageIntegrationRequest, + ) + from .client import AsyncLangfuseAPI, LangfuseAPI + from .comments import ( + CreateCommentRequest, + CreateCommentResponse, + GetCommentsResponse, + ) + from .commons import ( + AccessDeniedError, + BaseScore, + BaseScoreV1, + BooleanScore, + BooleanScoreV1, + CategoricalScore, + CategoricalScoreV1, + Comment, + CommentObjectType, + ConfigCategory, + CreateScoreValue, + Dataset, + DatasetItem, + DatasetRun, + DatasetRunItem, + DatasetRunWithItems, + DatasetStatus, + Error, + MapValue, + MethodNotAllowedError, + Model, + ModelPrice, + ModelUsageUnit, + NotFoundError, + NumericScore, + NumericScoreV1, + Observation, + ObservationLevel, + ObservationsView, + PricingTier, + PricingTierCondition, + PricingTierInput, + PricingTierOperator, + Score, + ScoreConfig, + ScoreDataType, + ScoreSource, + ScoreV1, + ScoreV1_Boolean, + ScoreV1_Categorical, + ScoreV1_Numeric, + Score_Boolean, + Score_Categorical, + Score_Numeric, + Session, + SessionWithTraces, + Trace, + TraceWithDetails, + TraceWithFullDetails, + UnauthorizedError, + Usage, + ) + from .dataset_items import ( + CreateDatasetItemRequest, + DeleteDatasetItemResponse, + PaginatedDatasetItems, + ) + from .dataset_run_items import CreateDatasetRunItemRequest, PaginatedDatasetRunItems + from .datasets import ( + CreateDatasetRequest, + DeleteDatasetRunResponse, + PaginatedDatasetRuns, + PaginatedDatasets, + ) + from .health import HealthResponse, ServiceUnavailableError + from .ingestion import ( + BaseEvent, + CreateEventBody, + CreateEventEvent, + CreateGenerationBody, + CreateGenerationEvent, + CreateObservationEvent, + CreateSpanBody, + CreateSpanEvent, + IngestionError, + IngestionEvent, + IngestionEvent_EventCreate, + IngestionEvent_GenerationCreate, + IngestionEvent_GenerationUpdate, + IngestionEvent_ObservationCreate, + IngestionEvent_ObservationUpdate, + IngestionEvent_ScoreCreate, + IngestionEvent_SdkLog, + IngestionEvent_SpanCreate, + IngestionEvent_SpanUpdate, + IngestionEvent_TraceCreate, + IngestionResponse, + IngestionSuccess, + IngestionUsage, + ObservationBody, + ObservationType, + OpenAiCompletionUsageSchema, + OpenAiResponseUsageSchema, + OpenAiUsage, + OptionalObservationBody, + ScoreBody, + ScoreEvent, + SdkLogBody, + SdkLogEvent, + TraceBody, + TraceEvent, + UpdateEventBody, + UpdateGenerationBody, + UpdateGenerationEvent, + UpdateObservationEvent, + UpdateSpanBody, + UpdateSpanEvent, + UsageDetails, + ) + from .llm_connections import ( + LlmAdapter, + LlmConnection, + PaginatedLlmConnections, + UpsertLlmConnectionRequest, + ) + from .media import ( + GetMediaResponse, + GetMediaUploadUrlRequest, + GetMediaUploadUrlResponse, + MediaContentType, + 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 .opentelemetry import ( + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, + OtelTraceResponse, + ) + from .organizations import ( + DeleteMembershipRequest, + MembershipDeletionResponse, + MembershipRequest, + MembershipResponse, + MembershipRole, + MembershipsResponse, + OrganizationApiKey, + OrganizationApiKeysResponse, + OrganizationProject, + OrganizationProjectsResponse, + ) + from .projects import ( + ApiKeyDeletionResponse, + ApiKeyList, + ApiKeyResponse, + ApiKeySummary, + Project, + ProjectDeletionResponse, + Projects, + ) + from .prompts import ( + BasePrompt, + ChatMessage, + ChatMessageWithPlaceholders, + ChatPrompt, + CreateChatPromptRequest, + CreateChatPromptType, + CreatePromptRequest, + CreateTextPromptRequest, + CreateTextPromptType, + PlaceholderMessage, + Prompt, + PromptMeta, + PromptMetaListResponse, + PromptType, + Prompt_Chat, + Prompt_Text, + TextPrompt, + ) + from .scim import ( + AuthenticationScheme, + BulkConfig, + EmptyResponse, + FilterConfig, + ResourceMeta, + ResourceType, + ResourceTypesResponse, + SchemaExtension, + SchemaResource, + SchemasResponse, + ScimEmail, + ScimFeatureSupport, + ScimName, + ScimUser, + ScimUsersListResponse, + ServiceProviderConfig, + UserMeta, + ) + from .score import CreateScoreRequest, CreateScoreResponse + from .score_configs import ( + CreateScoreConfigRequest, + ScoreConfigs, + UpdateScoreConfigRequest, + ) + from .score_v2 import ( + GetScoresResponse, + GetScoresResponseData, + GetScoresResponseDataBoolean, + GetScoresResponseDataCategorical, + GetScoresResponseDataNumeric, + GetScoresResponseData_Boolean, + GetScoresResponseData_Categorical, + GetScoresResponseData_Numeric, + GetScoresResponseTraceData, + ) + from .sessions import PaginatedSessions + from .trace import DeleteTraceResponse, Sort, Traces +_dynamic_imports: typing.Dict[str, str] = { + "AccessDeniedError": ".commons", + "AnnotationQueue": ".annotation_queues", + "AnnotationQueueAssignmentRequest": ".annotation_queues", + "AnnotationQueueItem": ".annotation_queues", + "AnnotationQueueObjectType": ".annotation_queues", + "AnnotationQueueStatus": ".annotation_queues", + "ApiKeyDeletionResponse": ".projects", + "ApiKeyList": ".projects", + "ApiKeyResponse": ".projects", + "ApiKeySummary": ".projects", + "AsyncLangfuseAPI": ".client", + "AuthenticationScheme": ".scim", + "BaseEvent": ".ingestion", + "BasePrompt": ".prompts", + "BaseScore": ".commons", + "BaseScoreV1": ".commons", + "BlobStorageExportFrequency": ".blob_storage_integrations", + "BlobStorageExportMode": ".blob_storage_integrations", + "BlobStorageIntegrationDeletionResponse": ".blob_storage_integrations", + "BlobStorageIntegrationFileType": ".blob_storage_integrations", + "BlobStorageIntegrationResponse": ".blob_storage_integrations", + "BlobStorageIntegrationType": ".blob_storage_integrations", + "BlobStorageIntegrationsResponse": ".blob_storage_integrations", + "BooleanScore": ".commons", + "BooleanScoreV1": ".commons", + "BulkConfig": ".scim", + "CategoricalScore": ".commons", + "CategoricalScoreV1": ".commons", + "ChatMessage": ".prompts", + "ChatMessageWithPlaceholders": ".prompts", + "ChatPrompt": ".prompts", + "Comment": ".commons", + "CommentObjectType": ".commons", + "ConfigCategory": ".commons", + "CreateAnnotationQueueAssignmentResponse": ".annotation_queues", + "CreateAnnotationQueueItemRequest": ".annotation_queues", + "CreateAnnotationQueueRequest": ".annotation_queues", + "CreateBlobStorageIntegrationRequest": ".blob_storage_integrations", + "CreateChatPromptRequest": ".prompts", + "CreateChatPromptType": ".prompts", + "CreateCommentRequest": ".comments", + "CreateCommentResponse": ".comments", + "CreateDatasetItemRequest": ".dataset_items", + "CreateDatasetRequest": ".datasets", + "CreateDatasetRunItemRequest": ".dataset_run_items", + "CreateEventBody": ".ingestion", + "CreateEventEvent": ".ingestion", + "CreateGenerationBody": ".ingestion", + "CreateGenerationEvent": ".ingestion", + "CreateModelRequest": ".models", + "CreateObservationEvent": ".ingestion", + "CreatePromptRequest": ".prompts", + "CreateScoreConfigRequest": ".score_configs", + "CreateScoreRequest": ".score", + "CreateScoreResponse": ".score", + "CreateScoreValue": ".commons", + "CreateSpanBody": ".ingestion", + "CreateSpanEvent": ".ingestion", + "CreateTextPromptRequest": ".prompts", + "CreateTextPromptType": ".prompts", + "Dataset": ".commons", + "DatasetItem": ".commons", + "DatasetRun": ".commons", + "DatasetRunItem": ".commons", + "DatasetRunWithItems": ".commons", + "DatasetStatus": ".commons", + "DeleteAnnotationQueueAssignmentResponse": ".annotation_queues", + "DeleteAnnotationQueueItemResponse": ".annotation_queues", + "DeleteDatasetItemResponse": ".dataset_items", + "DeleteDatasetRunResponse": ".datasets", + "DeleteMembershipRequest": ".organizations", + "DeleteTraceResponse": ".trace", + "EmptyResponse": ".scim", + "Error": ".commons", + "FilterConfig": ".scim", + "GetCommentsResponse": ".comments", + "GetMediaResponse": ".media", + "GetMediaUploadUrlRequest": ".media", + "GetMediaUploadUrlResponse": ".media", + "GetScoresResponse": ".score_v2", + "GetScoresResponseData": ".score_v2", + "GetScoresResponseDataBoolean": ".score_v2", + "GetScoresResponseDataCategorical": ".score_v2", + "GetScoresResponseDataNumeric": ".score_v2", + "GetScoresResponseData_Boolean": ".score_v2", + "GetScoresResponseData_Categorical": ".score_v2", + "GetScoresResponseData_Numeric": ".score_v2", + "GetScoresResponseTraceData": ".score_v2", + "HealthResponse": ".health", + "IngestionError": ".ingestion", + "IngestionEvent": ".ingestion", + "IngestionEvent_EventCreate": ".ingestion", + "IngestionEvent_GenerationCreate": ".ingestion", + "IngestionEvent_GenerationUpdate": ".ingestion", + "IngestionEvent_ObservationCreate": ".ingestion", + "IngestionEvent_ObservationUpdate": ".ingestion", + "IngestionEvent_ScoreCreate": ".ingestion", + "IngestionEvent_SdkLog": ".ingestion", + "IngestionEvent_SpanCreate": ".ingestion", + "IngestionEvent_SpanUpdate": ".ingestion", + "IngestionEvent_TraceCreate": ".ingestion", + "IngestionResponse": ".ingestion", + "IngestionSuccess": ".ingestion", + "IngestionUsage": ".ingestion", + "LangfuseAPI": ".client", + "LlmAdapter": ".llm_connections", + "LlmConnection": ".llm_connections", + "MapValue": ".commons", + "MediaContentType": ".media", + "MembershipDeletionResponse": ".organizations", + "MembershipRequest": ".organizations", + "MembershipResponse": ".organizations", + "MembershipRole": ".organizations", + "MembershipsResponse": ".organizations", + "MethodNotAllowedError": ".commons", + "MetricsResponse": ".metrics", + "MetricsV2Response": ".metrics_v2", + "Model": ".commons", + "ModelPrice": ".commons", + "ModelUsageUnit": ".commons", + "NotFoundError": ".commons", + "NumericScore": ".commons", + "NumericScoreV1": ".commons", + "Observation": ".commons", + "ObservationBody": ".ingestion", + "ObservationLevel": ".commons", + "ObservationType": ".ingestion", + "Observations": ".observations", + "ObservationsV2Meta": ".observations_v2", + "ObservationsV2Response": ".observations_v2", + "ObservationsView": ".commons", + "ObservationsViews": ".observations", + "OpenAiCompletionUsageSchema": ".ingestion", + "OpenAiResponseUsageSchema": ".ingestion", + "OpenAiUsage": ".ingestion", + "OptionalObservationBody": ".ingestion", + "OrganizationApiKey": ".organizations", + "OrganizationApiKeysResponse": ".organizations", + "OrganizationProject": ".organizations", + "OrganizationProjectsResponse": ".organizations", + "OtelAttribute": ".opentelemetry", + "OtelAttributeValue": ".opentelemetry", + "OtelResource": ".opentelemetry", + "OtelResourceSpan": ".opentelemetry", + "OtelScope": ".opentelemetry", + "OtelScopeSpan": ".opentelemetry", + "OtelSpan": ".opentelemetry", + "OtelTraceResponse": ".opentelemetry", + "PaginatedAnnotationQueueItems": ".annotation_queues", + "PaginatedAnnotationQueues": ".annotation_queues", + "PaginatedDatasetItems": ".dataset_items", + "PaginatedDatasetRunItems": ".dataset_run_items", + "PaginatedDatasetRuns": ".datasets", + "PaginatedDatasets": ".datasets", + "PaginatedLlmConnections": ".llm_connections", + "PaginatedModels": ".models", + "PaginatedSessions": ".sessions", + "PatchMediaBody": ".media", + "PlaceholderMessage": ".prompts", + "PricingTier": ".commons", + "PricingTierCondition": ".commons", + "PricingTierInput": ".commons", + "PricingTierOperator": ".commons", + "Project": ".projects", + "ProjectDeletionResponse": ".projects", + "Projects": ".projects", + "Prompt": ".prompts", + "PromptMeta": ".prompts", + "PromptMetaListResponse": ".prompts", + "PromptType": ".prompts", + "Prompt_Chat": ".prompts", + "Prompt_Text": ".prompts", + "ResourceMeta": ".scim", + "ResourceType": ".scim", + "ResourceTypesResponse": ".scim", + "SchemaExtension": ".scim", + "SchemaResource": ".scim", + "SchemasResponse": ".scim", + "ScimEmail": ".scim", + "ScimFeatureSupport": ".scim", + "ScimName": ".scim", + "ScimUser": ".scim", + "ScimUsersListResponse": ".scim", + "Score": ".commons", + "ScoreBody": ".ingestion", + "ScoreConfig": ".commons", + "ScoreConfigs": ".score_configs", + "ScoreDataType": ".commons", + "ScoreEvent": ".ingestion", + "ScoreSource": ".commons", + "ScoreV1": ".commons", + "ScoreV1_Boolean": ".commons", + "ScoreV1_Categorical": ".commons", + "ScoreV1_Numeric": ".commons", + "Score_Boolean": ".commons", + "Score_Categorical": ".commons", + "Score_Numeric": ".commons", + "SdkLogBody": ".ingestion", + "SdkLogEvent": ".ingestion", + "ServiceProviderConfig": ".scim", + "ServiceUnavailableError": ".health", + "Session": ".commons", + "SessionWithTraces": ".commons", + "Sort": ".trace", + "TextPrompt": ".prompts", + "Trace": ".commons", + "TraceBody": ".ingestion", + "TraceEvent": ".ingestion", + "TraceWithDetails": ".commons", + "TraceWithFullDetails": ".commons", + "Traces": ".trace", + "UnauthorizedError": ".commons", + "UpdateAnnotationQueueItemRequest": ".annotation_queues", + "UpdateEventBody": ".ingestion", + "UpdateGenerationBody": ".ingestion", + "UpdateGenerationEvent": ".ingestion", + "UpdateObservationEvent": ".ingestion", + "UpdateScoreConfigRequest": ".score_configs", + "UpdateSpanBody": ".ingestion", + "UpdateSpanEvent": ".ingestion", + "UpsertLlmConnectionRequest": ".llm_connections", + "Usage": ".commons", + "UsageDetails": ".ingestion", + "UserMeta": ".scim", + "annotation_queues": ".annotation_queues", + "blob_storage_integrations": ".blob_storage_integrations", + "comments": ".comments", + "commons": ".commons", + "dataset_items": ".dataset_items", + "dataset_run_items": ".dataset_run_items", + "datasets": ".datasets", + "health": ".health", + "ingestion": ".ingestion", + "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", + "prompt_version": ".prompt_version", + "prompts": ".prompts", + "scim": ".scim", + "score": ".score", + "score_configs": ".score_configs", + "score_v2": ".score_v2", + "sessions": ".sessions", + "trace": ".trace", + "utils": ".utils", +} + + +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__ = [ "AccessDeniedError", @@ -261,6 +577,7 @@ "ApiKeyList", "ApiKeyResponse", "ApiKeySummary", + "AsyncLangfuseAPI", "AuthenticationScheme", "BaseEvent", "BasePrompt", @@ -280,8 +597,6 @@ "CategoricalScoreV1", "ChatMessage", "ChatMessageWithPlaceholders", - "ChatMessageWithPlaceholders_Chatmessage", - "ChatMessageWithPlaceholders_Placeholder", "ChatPrompt", "Comment", "CommentObjectType", @@ -291,6 +606,7 @@ "CreateAnnotationQueueRequest", "CreateBlobStorageIntegrationRequest", "CreateChatPromptRequest", + "CreateChatPromptType", "CreateCommentRequest", "CreateCommentResponse", "CreateDatasetItemRequest", @@ -303,8 +619,6 @@ "CreateModelRequest", "CreateObservationEvent", "CreatePromptRequest", - "CreatePromptRequest_Chat", - "CreatePromptRequest_Text", "CreateScoreConfigRequest", "CreateScoreRequest", "CreateScoreResponse", @@ -312,6 +626,7 @@ "CreateSpanBody", "CreateSpanEvent", "CreateTextPromptRequest", + "CreateTextPromptType", "Dataset", "DatasetItem", "DatasetRun", @@ -356,6 +671,7 @@ "IngestionResponse", "IngestionSuccess", "IngestionUsage", + "LangfuseAPI", "LlmAdapter", "LlmConnection", "MapValue", @@ -367,6 +683,7 @@ "MembershipsResponse", "MethodNotAllowedError", "MetricsResponse", + "MetricsV2Response", "Model", "ModelPrice", "ModelUsageUnit", @@ -378,6 +695,8 @@ "ObservationLevel", "ObservationType", "Observations", + "ObservationsV2Meta", + "ObservationsV2Response", "ObservationsView", "ObservationsViews", "OpenAiCompletionUsageSchema", @@ -484,8 +803,10 @@ "llm_connections", "media", "metrics", + "metrics_v2", "models", "observations", + "observations_v2", "opentelemetry", "organizations", "projects", @@ -494,7 +815,7 @@ "scim", "score", "score_configs", - "score_v_2", + "score_v2", "sessions", "trace", "utils", diff --git a/langfuse/api/annotation_queues/__init__.py b/langfuse/api/annotation_queues/__init__.py new file mode 100644 index 000000000..119661e05 --- /dev/null +++ b/langfuse/api/annotation_queues/__init__.py @@ -0,0 +1,82 @@ +# 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 ( + AnnotationQueue, + AnnotationQueueAssignmentRequest, + AnnotationQueueItem, + AnnotationQueueObjectType, + AnnotationQueueStatus, + CreateAnnotationQueueAssignmentResponse, + CreateAnnotationQueueItemRequest, + CreateAnnotationQueueRequest, + DeleteAnnotationQueueAssignmentResponse, + DeleteAnnotationQueueItemResponse, + PaginatedAnnotationQueueItems, + PaginatedAnnotationQueues, + UpdateAnnotationQueueItemRequest, + ) +_dynamic_imports: typing.Dict[str, str] = { + "AnnotationQueue": ".types", + "AnnotationQueueAssignmentRequest": ".types", + "AnnotationQueueItem": ".types", + "AnnotationQueueObjectType": ".types", + "AnnotationQueueStatus": ".types", + "CreateAnnotationQueueAssignmentResponse": ".types", + "CreateAnnotationQueueItemRequest": ".types", + "CreateAnnotationQueueRequest": ".types", + "DeleteAnnotationQueueAssignmentResponse": ".types", + "DeleteAnnotationQueueItemResponse": ".types", + "PaginatedAnnotationQueueItems": ".types", + "PaginatedAnnotationQueues": ".types", + "UpdateAnnotationQueueItemRequest": ".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__ = [ + "AnnotationQueue", + "AnnotationQueueAssignmentRequest", + "AnnotationQueueItem", + "AnnotationQueueObjectType", + "AnnotationQueueStatus", + "CreateAnnotationQueueAssignmentResponse", + "CreateAnnotationQueueItemRequest", + "CreateAnnotationQueueRequest", + "DeleteAnnotationQueueAssignmentResponse", + "DeleteAnnotationQueueItemResponse", + "PaginatedAnnotationQueueItems", + "PaginatedAnnotationQueues", + "UpdateAnnotationQueueItemRequest", +] diff --git a/langfuse/api/annotation_queues/client.py b/langfuse/api/annotation_queues/client.py new file mode 100644 index 000000000..6fb3d5682 --- /dev/null +++ b/langfuse/api/annotation_queues/client.py @@ -0,0 +1,1111 @@ +# 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 AsyncRawAnnotationQueuesClient, RawAnnotationQueuesClient +from .types.annotation_queue import AnnotationQueue +from .types.annotation_queue_item import AnnotationQueueItem +from .types.annotation_queue_object_type import AnnotationQueueObjectType +from .types.annotation_queue_status import AnnotationQueueStatus +from .types.create_annotation_queue_assignment_response import ( + CreateAnnotationQueueAssignmentResponse, +) +from .types.delete_annotation_queue_assignment_response import ( + DeleteAnnotationQueueAssignmentResponse, +) +from .types.delete_annotation_queue_item_response import ( + DeleteAnnotationQueueItemResponse, +) +from .types.paginated_annotation_queue_items import PaginatedAnnotationQueueItems +from .types.paginated_annotation_queues import PaginatedAnnotationQueues + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class AnnotationQueuesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAnnotationQueuesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAnnotationQueuesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAnnotationQueuesClient + """ + return self._raw_client + + def list_queues( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedAnnotationQueues: + """ + Get all annotation queues + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedAnnotationQueues + + 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.annotation_queues.list_queues() + """ + _response = self._raw_client.list_queues( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + def create_queue( + self, + *, + name: str, + score_config_ids: typing.Sequence[str], + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueue: + """ + Create an annotation queue + + Parameters + ---------- + name : str + + score_config_ids : typing.Sequence[str] + + description : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueue + + 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.annotation_queues.create_queue( + name="name", + score_config_ids=["scoreConfigIds", "scoreConfigIds"], + ) + """ + _response = self._raw_client.create_queue( + name=name, + score_config_ids=score_config_ids, + description=description, + request_options=request_options, + ) + return _response.data + + def get_queue( + self, queue_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AnnotationQueue: + """ + Get an annotation queue by ID + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueue + + 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.annotation_queues.get_queue( + queue_id="queueId", + ) + """ + _response = self._raw_client.get_queue( + queue_id, request_options=request_options + ) + return _response.data + + def list_queue_items( + self, + queue_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedAnnotationQueueItems: + """ + Get items for a specific annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + status : typing.Optional[AnnotationQueueStatus] + Filter by status + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedAnnotationQueueItems + + 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.annotation_queues.list_queue_items( + queue_id="queueId", + ) + """ + _response = self._raw_client.list_queue_items( + queue_id, + status=status, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data + + def get_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueueItem: + """ + Get a specific item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueueItem + + 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.annotation_queues.get_queue_item( + queue_id="queueId", + item_id="itemId", + ) + """ + _response = self._raw_client.get_queue_item( + queue_id, item_id, request_options=request_options + ) + return _response.data + + def create_queue_item( + self, + queue_id: str, + *, + object_id: str, + object_type: AnnotationQueueObjectType, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueueItem: + """ + Add an item to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + object_id : str + + object_type : AnnotationQueueObjectType + + status : typing.Optional[AnnotationQueueStatus] + Defaults to PENDING for new queue items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueueItem + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.annotation_queues import AnnotationQueueObjectType + + 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.annotation_queues.create_queue_item( + queue_id="queueId", + object_id="objectId", + object_type=AnnotationQueueObjectType.TRACE, + ) + """ + _response = self._raw_client.create_queue_item( + queue_id, + object_id=object_id, + object_type=object_type, + status=status, + request_options=request_options, + ) + return _response.data + + def update_queue_item( + self, + queue_id: str, + item_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueueItem: + """ + Update an annotation queue item + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + status : typing.Optional[AnnotationQueueStatus] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueueItem + + 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.annotation_queues.update_queue_item( + queue_id="queueId", + item_id="itemId", + ) + """ + _response = self._raw_client.update_queue_item( + queue_id, item_id, status=status, request_options=request_options + ) + return _response.data + + def delete_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteAnnotationQueueItemResponse: + """ + Remove an item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteAnnotationQueueItemResponse + + 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.annotation_queues.delete_queue_item( + queue_id="queueId", + item_id="itemId", + ) + """ + _response = self._raw_client.delete_queue_item( + queue_id, item_id, request_options=request_options + ) + return _response.data + + def create_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateAnnotationQueueAssignmentResponse: + """ + Create an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateAnnotationQueueAssignmentResponse + + 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.annotation_queues.create_queue_assignment( + queue_id="queueId", + user_id="userId", + ) + """ + _response = self._raw_client.create_queue_assignment( + queue_id, user_id=user_id, request_options=request_options + ) + return _response.data + + def delete_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteAnnotationQueueAssignmentResponse: + """ + Delete an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteAnnotationQueueAssignmentResponse + + 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.annotation_queues.delete_queue_assignment( + queue_id="queueId", + user_id="userId", + ) + """ + _response = self._raw_client.delete_queue_assignment( + queue_id, user_id=user_id, request_options=request_options + ) + return _response.data + + +class AsyncAnnotationQueuesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAnnotationQueuesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAnnotationQueuesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAnnotationQueuesClient + """ + return self._raw_client + + async def list_queues( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedAnnotationQueues: + """ + Get all annotation queues + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedAnnotationQueues + + 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.annotation_queues.list_queues() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_queues( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def create_queue( + self, + *, + name: str, + score_config_ids: typing.Sequence[str], + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueue: + """ + Create an annotation queue + + Parameters + ---------- + name : str + + score_config_ids : typing.Sequence[str] + + description : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueue + + 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.annotation_queues.create_queue( + name="name", + score_config_ids=["scoreConfigIds", "scoreConfigIds"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_queue( + name=name, + score_config_ids=score_config_ids, + description=description, + request_options=request_options, + ) + return _response.data + + async def get_queue( + self, queue_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AnnotationQueue: + """ + Get an annotation queue by ID + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueue + + 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.annotation_queues.get_queue( + queue_id="queueId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_queue( + queue_id, request_options=request_options + ) + return _response.data + + async def list_queue_items( + self, + queue_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedAnnotationQueueItems: + """ + Get items for a specific annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + status : typing.Optional[AnnotationQueueStatus] + Filter by status + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedAnnotationQueueItems + + 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.annotation_queues.list_queue_items( + queue_id="queueId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_queue_items( + queue_id, + status=status, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data + + async def get_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueueItem: + """ + Get a specific item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueueItem + + 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.annotation_queues.get_queue_item( + queue_id="queueId", + item_id="itemId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_queue_item( + queue_id, item_id, request_options=request_options + ) + return _response.data + + async def create_queue_item( + self, + queue_id: str, + *, + object_id: str, + object_type: AnnotationQueueObjectType, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueueItem: + """ + Add an item to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + object_id : str + + object_type : AnnotationQueueObjectType + + status : typing.Optional[AnnotationQueueStatus] + Defaults to PENDING for new queue items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueueItem + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.annotation_queues import AnnotationQueueObjectType + + 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.annotation_queues.create_queue_item( + queue_id="queueId", + object_id="objectId", + object_type=AnnotationQueueObjectType.TRACE, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_queue_item( + queue_id, + object_id=object_id, + object_type=object_type, + status=status, + request_options=request_options, + ) + return _response.data + + async def update_queue_item( + self, + queue_id: str, + item_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AnnotationQueueItem: + """ + Update an annotation queue item + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + status : typing.Optional[AnnotationQueueStatus] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AnnotationQueueItem + + 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.annotation_queues.update_queue_item( + queue_id="queueId", + item_id="itemId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_queue_item( + queue_id, item_id, status=status, request_options=request_options + ) + return _response.data + + async def delete_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteAnnotationQueueItemResponse: + """ + Remove an item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteAnnotationQueueItemResponse + + 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.annotation_queues.delete_queue_item( + queue_id="queueId", + item_id="itemId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_queue_item( + queue_id, item_id, request_options=request_options + ) + return _response.data + + async def create_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateAnnotationQueueAssignmentResponse: + """ + Create an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateAnnotationQueueAssignmentResponse + + 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.annotation_queues.create_queue_assignment( + queue_id="queueId", + user_id="userId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_queue_assignment( + queue_id, user_id=user_id, request_options=request_options + ) + return _response.data + + async def delete_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteAnnotationQueueAssignmentResponse: + """ + Delete an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteAnnotationQueueAssignmentResponse + + 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.annotation_queues.delete_queue_assignment( + queue_id="queueId", + user_id="userId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_queue_assignment( + queue_id, user_id=user_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/annotation_queues/raw_client.py b/langfuse/api/annotation_queues/raw_client.py new file mode 100644 index 000000000..451095061 --- /dev/null +++ b/langfuse/api/annotation_queues/raw_client.py @@ -0,0 +1,2288 @@ +# 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.jsonable_encoder import jsonable_encoder +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from .types.annotation_queue import AnnotationQueue +from .types.annotation_queue_item import AnnotationQueueItem +from .types.annotation_queue_object_type import AnnotationQueueObjectType +from .types.annotation_queue_status import AnnotationQueueStatus +from .types.create_annotation_queue_assignment_response import ( + CreateAnnotationQueueAssignmentResponse, +) +from .types.delete_annotation_queue_assignment_response import ( + DeleteAnnotationQueueAssignmentResponse, +) +from .types.delete_annotation_queue_item_response import ( + DeleteAnnotationQueueItemResponse, +) +from .types.paginated_annotation_queue_items import PaginatedAnnotationQueueItems +from .types.paginated_annotation_queues import PaginatedAnnotationQueues + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawAnnotationQueuesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_queues( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedAnnotationQueues]: + """ + Get all annotation queues + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedAnnotationQueues] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/annotation-queues", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedAnnotationQueues, + parse_obj_as( + type_=PaginatedAnnotationQueues, # 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 create_queue( + self, + *, + name: str, + score_config_ids: typing.Sequence[str], + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AnnotationQueue]: + """ + Create an annotation queue + + Parameters + ---------- + name : str + + score_config_ids : typing.Sequence[str] + + description : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AnnotationQueue] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/annotation-queues", + method="POST", + json={ + "name": name, + "description": description, + "scoreConfigIds": score_config_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueue, + parse_obj_as( + type_=AnnotationQueue, # 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_queue( + self, queue_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[AnnotationQueue]: + """ + Get an annotation queue by ID + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AnnotationQueue] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueue, + parse_obj_as( + type_=AnnotationQueue, # 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 list_queue_items( + self, + queue_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedAnnotationQueueItems]: + """ + Get items for a specific annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + status : typing.Optional[AnnotationQueueStatus] + Filter by status + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedAnnotationQueueItems] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", + method="GET", + params={ + "status": status, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedAnnotationQueueItems, + parse_obj_as( + type_=PaginatedAnnotationQueueItems, # 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_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AnnotationQueueItem]: + """ + Get a specific item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AnnotationQueueItem] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueueItem, + parse_obj_as( + type_=AnnotationQueueItem, # 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 create_queue_item( + self, + queue_id: str, + *, + object_id: str, + object_type: AnnotationQueueObjectType, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AnnotationQueueItem]: + """ + Add an item to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + object_id : str + + object_type : AnnotationQueueObjectType + + status : typing.Optional[AnnotationQueueStatus] + Defaults to PENDING for new queue items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AnnotationQueueItem] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", + method="POST", + json={ + "objectId": object_id, + "objectType": object_type, + "status": status, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueueItem, + parse_obj_as( + type_=AnnotationQueueItem, # 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 update_queue_item( + self, + queue_id: str, + item_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AnnotationQueueItem]: + """ + Update an annotation queue item + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + status : typing.Optional[AnnotationQueueStatus] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AnnotationQueueItem] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", + method="PATCH", + json={ + "status": status, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueueItem, + parse_obj_as( + type_=AnnotationQueueItem, # 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 delete_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteAnnotationQueueItemResponse]: + """ + Remove an item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteAnnotationQueueItemResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteAnnotationQueueItemResponse, + parse_obj_as( + type_=DeleteAnnotationQueueItemResponse, # 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 create_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CreateAnnotationQueueAssignmentResponse]: + """ + Create an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CreateAnnotationQueueAssignmentResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", + method="POST", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateAnnotationQueueAssignmentResponse, + parse_obj_as( + type_=CreateAnnotationQueueAssignmentResponse, # 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 delete_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteAnnotationQueueAssignmentResponse]: + """ + Delete an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteAnnotationQueueAssignmentResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", + method="DELETE", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteAnnotationQueueAssignmentResponse, + parse_obj_as( + type_=DeleteAnnotationQueueAssignmentResponse, # 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 AsyncRawAnnotationQueuesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_queues( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedAnnotationQueues]: + """ + Get all annotation queues + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedAnnotationQueues] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/annotation-queues", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedAnnotationQueues, + parse_obj_as( + type_=PaginatedAnnotationQueues, # 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 create_queue( + self, + *, + name: str, + score_config_ids: typing.Sequence[str], + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AnnotationQueue]: + """ + Create an annotation queue + + Parameters + ---------- + name : str + + score_config_ids : typing.Sequence[str] + + description : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AnnotationQueue] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/annotation-queues", + method="POST", + json={ + "name": name, + "description": description, + "scoreConfigIds": score_config_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueue, + parse_obj_as( + type_=AnnotationQueue, # 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_queue( + self, queue_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[AnnotationQueue]: + """ + Get an annotation queue by ID + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AnnotationQueue] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueue, + parse_obj_as( + type_=AnnotationQueue, # 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 list_queue_items( + self, + queue_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedAnnotationQueueItems]: + """ + Get items for a specific annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + status : typing.Optional[AnnotationQueueStatus] + Filter by status + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedAnnotationQueueItems] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", + method="GET", + params={ + "status": status, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedAnnotationQueueItems, + parse_obj_as( + type_=PaginatedAnnotationQueueItems, # 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_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AnnotationQueueItem]: + """ + Get a specific item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AnnotationQueueItem] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueueItem, + parse_obj_as( + type_=AnnotationQueueItem, # 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 create_queue_item( + self, + queue_id: str, + *, + object_id: str, + object_type: AnnotationQueueObjectType, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AnnotationQueueItem]: + """ + Add an item to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + object_id : str + + object_type : AnnotationQueueObjectType + + status : typing.Optional[AnnotationQueueStatus] + Defaults to PENDING for new queue items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AnnotationQueueItem] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", + method="POST", + json={ + "objectId": object_id, + "objectType": object_type, + "status": status, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueueItem, + parse_obj_as( + type_=AnnotationQueueItem, # 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 update_queue_item( + self, + queue_id: str, + item_id: str, + *, + status: typing.Optional[AnnotationQueueStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AnnotationQueueItem]: + """ + Update an annotation queue item + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + status : typing.Optional[AnnotationQueueStatus] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AnnotationQueueItem] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", + method="PATCH", + json={ + "status": status, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AnnotationQueueItem, + parse_obj_as( + type_=AnnotationQueueItem, # 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 delete_queue_item( + self, + queue_id: str, + item_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteAnnotationQueueItemResponse]: + """ + Remove an item from an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + item_id : str + The unique identifier of the annotation queue item + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteAnnotationQueueItemResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteAnnotationQueueItemResponse, + parse_obj_as( + type_=DeleteAnnotationQueueItemResponse, # 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 create_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CreateAnnotationQueueAssignmentResponse]: + """ + Create an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CreateAnnotationQueueAssignmentResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", + method="POST", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateAnnotationQueueAssignmentResponse, + parse_obj_as( + type_=CreateAnnotationQueueAssignmentResponse, # 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 delete_queue_assignment( + self, + queue_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteAnnotationQueueAssignmentResponse]: + """ + Delete an assignment for a user to an annotation queue + + Parameters + ---------- + queue_id : str + The unique identifier of the annotation queue + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteAnnotationQueueAssignmentResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", + method="DELETE", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteAnnotationQueueAssignmentResponse, + parse_obj_as( + type_=DeleteAnnotationQueueAssignmentResponse, # 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/annotation_queues/types/__init__.py b/langfuse/api/annotation_queues/types/__init__.py new file mode 100644 index 000000000..0d34bb763 --- /dev/null +++ b/langfuse/api/annotation_queues/types/__init__.py @@ -0,0 +1,84 @@ +# 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 .annotation_queue import AnnotationQueue + from .annotation_queue_assignment_request import AnnotationQueueAssignmentRequest + from .annotation_queue_item import AnnotationQueueItem + from .annotation_queue_object_type import AnnotationQueueObjectType + from .annotation_queue_status import AnnotationQueueStatus + from .create_annotation_queue_assignment_response import ( + CreateAnnotationQueueAssignmentResponse, + ) + from .create_annotation_queue_item_request import CreateAnnotationQueueItemRequest + from .create_annotation_queue_request import CreateAnnotationQueueRequest + from .delete_annotation_queue_assignment_response import ( + DeleteAnnotationQueueAssignmentResponse, + ) + from .delete_annotation_queue_item_response import DeleteAnnotationQueueItemResponse + from .paginated_annotation_queue_items import PaginatedAnnotationQueueItems + from .paginated_annotation_queues import PaginatedAnnotationQueues + from .update_annotation_queue_item_request import UpdateAnnotationQueueItemRequest +_dynamic_imports: typing.Dict[str, str] = { + "AnnotationQueue": ".annotation_queue", + "AnnotationQueueAssignmentRequest": ".annotation_queue_assignment_request", + "AnnotationQueueItem": ".annotation_queue_item", + "AnnotationQueueObjectType": ".annotation_queue_object_type", + "AnnotationQueueStatus": ".annotation_queue_status", + "CreateAnnotationQueueAssignmentResponse": ".create_annotation_queue_assignment_response", + "CreateAnnotationQueueItemRequest": ".create_annotation_queue_item_request", + "CreateAnnotationQueueRequest": ".create_annotation_queue_request", + "DeleteAnnotationQueueAssignmentResponse": ".delete_annotation_queue_assignment_response", + "DeleteAnnotationQueueItemResponse": ".delete_annotation_queue_item_response", + "PaginatedAnnotationQueueItems": ".paginated_annotation_queue_items", + "PaginatedAnnotationQueues": ".paginated_annotation_queues", + "UpdateAnnotationQueueItemRequest": ".update_annotation_queue_item_request", +} + + +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__ = [ + "AnnotationQueue", + "AnnotationQueueAssignmentRequest", + "AnnotationQueueItem", + "AnnotationQueueObjectType", + "AnnotationQueueStatus", + "CreateAnnotationQueueAssignmentResponse", + "CreateAnnotationQueueItemRequest", + "CreateAnnotationQueueRequest", + "DeleteAnnotationQueueAssignmentResponse", + "DeleteAnnotationQueueItemResponse", + "PaginatedAnnotationQueueItems", + "PaginatedAnnotationQueues", + "UpdateAnnotationQueueItemRequest", +] diff --git a/langfuse/api/annotation_queues/types/annotation_queue.py b/langfuse/api/annotation_queues/types/annotation_queue.py new file mode 100644 index 000000000..89cc5d407 --- /dev/null +++ b/langfuse/api/annotation_queues/types/annotation_queue.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class AnnotationQueue(UniversalBaseModel): + id: str + name: str + description: typing.Optional[str] = None + score_config_ids: typing_extensions.Annotated[ + typing.List[str], FieldMetadata(alias="scoreConfigIds") + ] + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/annotation_queue_assignment_request.py b/langfuse/api/annotation_queues/types/annotation_queue_assignment_request.py new file mode 100644 index 000000000..e25e4a327 --- /dev/null +++ b/langfuse/api/annotation_queues/types/annotation_queue_assignment_request.py @@ -0,0 +1,16 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class AnnotationQueueAssignmentRequest(UniversalBaseModel): + user_id: typing_extensions.Annotated[str, FieldMetadata(alias="userId")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/annotation_queue_item.py b/langfuse/api/annotation_queues/types/annotation_queue_item.py new file mode 100644 index 000000000..9c4b622d8 --- /dev/null +++ b/langfuse/api/annotation_queues/types/annotation_queue_item.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .annotation_queue_object_type import AnnotationQueueObjectType +from .annotation_queue_status import AnnotationQueueStatus + + +class AnnotationQueueItem(UniversalBaseModel): + id: str + queue_id: typing_extensions.Annotated[str, FieldMetadata(alias="queueId")] + object_id: typing_extensions.Annotated[str, FieldMetadata(alias="objectId")] + object_type: typing_extensions.Annotated[ + AnnotationQueueObjectType, FieldMetadata(alias="objectType") + ] + status: AnnotationQueueStatus + completed_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="completedAt") + ] = None + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/annotation_queues/types/annotation_queue_object_type.py b/langfuse/api/annotation_queues/types/annotation_queue_object_type.py similarity index 89% rename from langfuse/api/resources/annotation_queues/types/annotation_queue_object_type.py rename to langfuse/api/annotation_queues/types/annotation_queue_object_type.py index 6e63a7015..af8b95e89 100644 --- a/langfuse/api/resources/annotation_queues/types/annotation_queue_object_type.py +++ b/langfuse/api/annotation_queues/types/annotation_queue_object_type.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class AnnotationQueueObjectType(str, enum.Enum): +class AnnotationQueueObjectType(enum.StrEnum): TRACE = "TRACE" OBSERVATION = "OBSERVATION" SESSION = "SESSION" diff --git a/langfuse/api/resources/annotation_queues/types/annotation_queue_status.py b/langfuse/api/annotation_queues/types/annotation_queue_status.py similarity index 87% rename from langfuse/api/resources/annotation_queues/types/annotation_queue_status.py rename to langfuse/api/annotation_queues/types/annotation_queue_status.py index cf075f38a..a11fe3ea8 100644 --- a/langfuse/api/resources/annotation_queues/types/annotation_queue_status.py +++ b/langfuse/api/annotation_queues/types/annotation_queue_status.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class AnnotationQueueStatus(str, enum.Enum): +class AnnotationQueueStatus(enum.StrEnum): PENDING = "PENDING" COMPLETED = "COMPLETED" diff --git a/langfuse/api/annotation_queues/types/create_annotation_queue_assignment_response.py b/langfuse/api/annotation_queues/types/create_annotation_queue_assignment_response.py new file mode 100644 index 000000000..8f040d3e4 --- /dev/null +++ b/langfuse/api/annotation_queues/types/create_annotation_queue_assignment_response.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class CreateAnnotationQueueAssignmentResponse(UniversalBaseModel): + user_id: typing_extensions.Annotated[str, FieldMetadata(alias="userId")] + queue_id: typing_extensions.Annotated[str, FieldMetadata(alias="queueId")] + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/create_annotation_queue_item_request.py b/langfuse/api/annotation_queues/types/create_annotation_queue_item_request.py new file mode 100644 index 000000000..b81287ce5 --- /dev/null +++ b/langfuse/api/annotation_queues/types/create_annotation_queue_item_request.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .annotation_queue_object_type import AnnotationQueueObjectType +from .annotation_queue_status import AnnotationQueueStatus + + +class CreateAnnotationQueueItemRequest(UniversalBaseModel): + object_id: typing_extensions.Annotated[str, FieldMetadata(alias="objectId")] + object_type: typing_extensions.Annotated[ + AnnotationQueueObjectType, FieldMetadata(alias="objectType") + ] + status: typing.Optional[AnnotationQueueStatus] = pydantic.Field(default=None) + """ + Defaults to PENDING for new queue items + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/create_annotation_queue_request.py b/langfuse/api/annotation_queues/types/create_annotation_queue_request.py new file mode 100644 index 000000000..1415ad1a7 --- /dev/null +++ b/langfuse/api/annotation_queues/types/create_annotation_queue_request.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class CreateAnnotationQueueRequest(UniversalBaseModel): + name: str + description: typing.Optional[str] = None + score_config_ids: typing_extensions.Annotated[ + typing.List[str], FieldMetadata(alias="scoreConfigIds") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/delete_annotation_queue_assignment_response.py b/langfuse/api/annotation_queues/types/delete_annotation_queue_assignment_response.py new file mode 100644 index 000000000..547e2847e --- /dev/null +++ b/langfuse/api/annotation_queues/types/delete_annotation_queue_assignment_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class DeleteAnnotationQueueAssignmentResponse(UniversalBaseModel): + success: bool + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/delete_annotation_queue_item_response.py b/langfuse/api/annotation_queues/types/delete_annotation_queue_item_response.py new file mode 100644 index 000000000..ededb3a49 --- /dev/null +++ b/langfuse/api/annotation_queues/types/delete_annotation_queue_item_response.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class DeleteAnnotationQueueItemResponse(UniversalBaseModel): + success: bool + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/paginated_annotation_queue_items.py b/langfuse/api/annotation_queues/types/paginated_annotation_queue_items.py new file mode 100644 index 000000000..140d2efb9 --- /dev/null +++ b/langfuse/api/annotation_queues/types/paginated_annotation_queue_items.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse +from .annotation_queue_item import AnnotationQueueItem + + +class PaginatedAnnotationQueueItems(UniversalBaseModel): + data: typing.List[AnnotationQueueItem] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/paginated_annotation_queues.py b/langfuse/api/annotation_queues/types/paginated_annotation_queues.py new file mode 100644 index 000000000..e07a71ac7 --- /dev/null +++ b/langfuse/api/annotation_queues/types/paginated_annotation_queues.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse +from .annotation_queue import AnnotationQueue + + +class PaginatedAnnotationQueues(UniversalBaseModel): + data: typing.List[AnnotationQueue] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/annotation_queues/types/update_annotation_queue_item_request.py b/langfuse/api/annotation_queues/types/update_annotation_queue_item_request.py new file mode 100644 index 000000000..8f2c2f898 --- /dev/null +++ b/langfuse/api/annotation_queues/types/update_annotation_queue_item_request.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .annotation_queue_status import AnnotationQueueStatus + + +class UpdateAnnotationQueueItemRequest(UniversalBaseModel): + status: typing.Optional[AnnotationQueueStatus] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/blob_storage_integrations/__init__.py b/langfuse/api/blob_storage_integrations/__init__.py new file mode 100644 index 000000000..abd0d9e84 --- /dev/null +++ b/langfuse/api/blob_storage_integrations/__init__.py @@ -0,0 +1,67 @@ +# 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 ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationDeletionResponse, + BlobStorageIntegrationFileType, + BlobStorageIntegrationResponse, + BlobStorageIntegrationType, + BlobStorageIntegrationsResponse, + CreateBlobStorageIntegrationRequest, + ) +_dynamic_imports: typing.Dict[str, str] = { + "BlobStorageExportFrequency": ".types", + "BlobStorageExportMode": ".types", + "BlobStorageIntegrationDeletionResponse": ".types", + "BlobStorageIntegrationFileType": ".types", + "BlobStorageIntegrationResponse": ".types", + "BlobStorageIntegrationType": ".types", + "BlobStorageIntegrationsResponse": ".types", + "CreateBlobStorageIntegrationRequest": ".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__ = [ + "BlobStorageExportFrequency", + "BlobStorageExportMode", + "BlobStorageIntegrationDeletionResponse", + "BlobStorageIntegrationFileType", + "BlobStorageIntegrationResponse", + "BlobStorageIntegrationType", + "BlobStorageIntegrationsResponse", + "CreateBlobStorageIntegrationRequest", +] diff --git a/langfuse/api/blob_storage_integrations/client.py b/langfuse/api/blob_storage_integrations/client.py new file mode 100644 index 000000000..6b1f6a677 --- /dev/null +++ b/langfuse/api/blob_storage_integrations/client.py @@ -0,0 +1,463 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import ( + AsyncRawBlobStorageIntegrationsClient, + RawBlobStorageIntegrationsClient, +) +from .types.blob_storage_export_frequency import BlobStorageExportFrequency +from .types.blob_storage_export_mode import BlobStorageExportMode +from .types.blob_storage_integration_deletion_response import ( + BlobStorageIntegrationDeletionResponse, +) +from .types.blob_storage_integration_file_type import BlobStorageIntegrationFileType +from .types.blob_storage_integration_response import BlobStorageIntegrationResponse +from .types.blob_storage_integration_type import BlobStorageIntegrationType +from .types.blob_storage_integrations_response import BlobStorageIntegrationsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class BlobStorageIntegrationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawBlobStorageIntegrationsClient( + client_wrapper=client_wrapper + ) + + @property + def with_raw_response(self) -> RawBlobStorageIntegrationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawBlobStorageIntegrationsClient + """ + return self._raw_client + + 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 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.blob_storage_integrations.get_blob_storage_integrations() + """ + _response = self._raw_client.get_blob_storage_integrations( + request_options=request_options + ) + return _response.data + + def upsert_blob_storage_integration( + self, + *, + project_id: str, + type: BlobStorageIntegrationType, + bucket_name: str, + region: str, + export_frequency: BlobStorageExportFrequency, + enabled: bool, + force_path_style: bool, + file_type: BlobStorageIntegrationFileType, + export_mode: BlobStorageExportMode, + endpoint: typing.Optional[str] = OMIT, + access_key_id: typing.Optional[str] = OMIT, + secret_access_key: typing.Optional[str] = OMIT, + prefix: typing.Optional[str] = OMIT, + export_start_date: typing.Optional[dt.datetime] = OMIT, + 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 + ---------- + project_id : str + ID of the project in which to configure the blob storage integration + + type : BlobStorageIntegrationType + + bucket_name : str + Name of the storage bucket + + region : str + Storage region + + export_frequency : BlobStorageExportFrequency + + enabled : bool + Whether the integration is active + + force_path_style : bool + Use path-style URLs for S3 requests + + file_type : BlobStorageIntegrationFileType + + export_mode : BlobStorageExportMode + + endpoint : typing.Optional[str] + Custom endpoint URL (required for S3_COMPATIBLE type) + + access_key_id : typing.Optional[str] + Access key ID for authentication + + secret_access_key : typing.Optional[str] + Secret access key for authentication (will be encrypted when stored) + + prefix : typing.Optional[str] + Path prefix for exported files (must end with forward slash if provided) + + export_start_date : typing.Optional[dt.datetime] + Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationResponse + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.blob_storage_integrations import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationFileType, + BlobStorageIntegrationType, + ) + + 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.blob_storage_integrations.upsert_blob_storage_integration( + project_id="projectId", + type=BlobStorageIntegrationType.S3, + 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._raw_client.upsert_blob_storage_integration( + project_id=project_id, + type=type, + bucket_name=bucket_name, + region=region, + export_frequency=export_frequency, + enabled=enabled, + force_path_style=force_path_style, + file_type=file_type, + export_mode=export_mode, + endpoint=endpoint, + access_key_id=access_key_id, + secret_access_key=secret_access_key, + prefix=prefix, + export_start_date=export_start_date, + request_options=request_options, + ) + return _response.data + + 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 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.blob_storage_integrations.delete_blob_storage_integration( + id="id", + ) + """ + _response = self._raw_client.delete_blob_storage_integration( + id, request_options=request_options + ) + return _response.data + + +class AsyncBlobStorageIntegrationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawBlobStorageIntegrationsClient( + client_wrapper=client_wrapper + ) + + @property + def with_raw_response(self) -> AsyncRawBlobStorageIntegrationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawBlobStorageIntegrationsClient + """ + return self._raw_client + + 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 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.blob_storage_integrations.get_blob_storage_integrations() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_blob_storage_integrations( + request_options=request_options + ) + return _response.data + + async def upsert_blob_storage_integration( + self, + *, + project_id: str, + type: BlobStorageIntegrationType, + bucket_name: str, + region: str, + export_frequency: BlobStorageExportFrequency, + enabled: bool, + force_path_style: bool, + file_type: BlobStorageIntegrationFileType, + export_mode: BlobStorageExportMode, + endpoint: typing.Optional[str] = OMIT, + access_key_id: typing.Optional[str] = OMIT, + secret_access_key: typing.Optional[str] = OMIT, + prefix: typing.Optional[str] = OMIT, + export_start_date: typing.Optional[dt.datetime] = OMIT, + 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 + ---------- + project_id : str + ID of the project in which to configure the blob storage integration + + type : BlobStorageIntegrationType + + bucket_name : str + Name of the storage bucket + + region : str + Storage region + + export_frequency : BlobStorageExportFrequency + + enabled : bool + Whether the integration is active + + force_path_style : bool + Use path-style URLs for S3 requests + + file_type : BlobStorageIntegrationFileType + + export_mode : BlobStorageExportMode + + endpoint : typing.Optional[str] + Custom endpoint URL (required for S3_COMPATIBLE type) + + access_key_id : typing.Optional[str] + Access key ID for authentication + + secret_access_key : typing.Optional[str] + Secret access key for authentication (will be encrypted when stored) + + prefix : typing.Optional[str] + Path prefix for exported files (must end with forward slash if provided) + + export_start_date : typing.Optional[dt.datetime] + Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BlobStorageIntegrationResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.blob_storage_integrations import ( + BlobStorageExportFrequency, + BlobStorageExportMode, + BlobStorageIntegrationFileType, + BlobStorageIntegrationType, + ) + + 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.blob_storage_integrations.upsert_blob_storage_integration( + project_id="projectId", + type=BlobStorageIntegrationType.S3, + 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._raw_client.upsert_blob_storage_integration( + project_id=project_id, + type=type, + bucket_name=bucket_name, + region=region, + export_frequency=export_frequency, + enabled=enabled, + force_path_style=force_path_style, + file_type=file_type, + export_mode=export_mode, + endpoint=endpoint, + access_key_id=access_key_id, + secret_access_key=secret_access_key, + prefix=prefix, + export_start_date=export_start_date, + request_options=request_options, + ) + return _response.data + + 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 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.blob_storage_integrations.delete_blob_storage_integration( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_blob_storage_integration( + id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/blob_storage_integrations/raw_client.py b/langfuse/api/blob_storage_integrations/raw_client.py new file mode 100644 index 000000000..00ee72316 --- /dev/null +++ b/langfuse/api/blob_storage_integrations/raw_client.py @@ -0,0 +1,773 @@ +# 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 ..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 .types.blob_storage_export_frequency import BlobStorageExportFrequency +from .types.blob_storage_export_mode import BlobStorageExportMode +from .types.blob_storage_integration_deletion_response import ( + BlobStorageIntegrationDeletionResponse, +) +from .types.blob_storage_integration_file_type import BlobStorageIntegrationFileType +from .types.blob_storage_integration_response import BlobStorageIntegrationResponse +from .types.blob_storage_integration_type import BlobStorageIntegrationType +from .types.blob_storage_integrations_response import BlobStorageIntegrationsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawBlobStorageIntegrationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_blob_storage_integrations( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[BlobStorageIntegrationsResponse]: + """ + Get all blob storage integrations for the organization (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[BlobStorageIntegrationsResponse] + """ + _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: + _data = typing.cast( + BlobStorageIntegrationsResponse, + parse_obj_as( + type_=BlobStorageIntegrationsResponse, # 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 upsert_blob_storage_integration( + self, + *, + project_id: str, + type: BlobStorageIntegrationType, + bucket_name: str, + region: str, + export_frequency: BlobStorageExportFrequency, + enabled: bool, + force_path_style: bool, + file_type: BlobStorageIntegrationFileType, + export_mode: BlobStorageExportMode, + endpoint: typing.Optional[str] = OMIT, + access_key_id: typing.Optional[str] = OMIT, + secret_access_key: typing.Optional[str] = OMIT, + prefix: typing.Optional[str] = OMIT, + export_start_date: typing.Optional[dt.datetime] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[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 + ---------- + project_id : str + ID of the project in which to configure the blob storage integration + + type : BlobStorageIntegrationType + + bucket_name : str + Name of the storage bucket + + region : str + Storage region + + export_frequency : BlobStorageExportFrequency + + enabled : bool + Whether the integration is active + + force_path_style : bool + Use path-style URLs for S3 requests + + file_type : BlobStorageIntegrationFileType + + export_mode : BlobStorageExportMode + + endpoint : typing.Optional[str] + Custom endpoint URL (required for S3_COMPATIBLE type) + + access_key_id : typing.Optional[str] + Access key ID for authentication + + secret_access_key : typing.Optional[str] + Secret access key for authentication (will be encrypted when stored) + + prefix : typing.Optional[str] + Path prefix for exported files (must end with forward slash if provided) + + export_start_date : typing.Optional[dt.datetime] + Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[BlobStorageIntegrationResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/integrations/blob-storage", + method="PUT", + json={ + "projectId": project_id, + "type": type, + "bucketName": bucket_name, + "endpoint": endpoint, + "region": region, + "accessKeyId": access_key_id, + "secretAccessKey": secret_access_key, + "prefix": prefix, + "exportFrequency": export_frequency, + "enabled": enabled, + "forcePathStyle": force_path_style, + "fileType": file_type, + "exportMode": export_mode, + "exportStartDate": export_start_date, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + BlobStorageIntegrationResponse, + parse_obj_as( + type_=BlobStorageIntegrationResponse, # 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 delete_blob_storage_integration( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[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 + ------- + HttpResponse[BlobStorageIntegrationDeletionResponse] + """ + _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: + _data = typing.cast( + BlobStorageIntegrationDeletionResponse, + parse_obj_as( + type_=BlobStorageIntegrationDeletionResponse, # 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 AsyncRawBlobStorageIntegrationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_blob_storage_integrations( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[BlobStorageIntegrationsResponse]: + """ + Get all blob storage integrations for the organization (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[BlobStorageIntegrationsResponse] + """ + _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: + _data = typing.cast( + BlobStorageIntegrationsResponse, + parse_obj_as( + type_=BlobStorageIntegrationsResponse, # 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 upsert_blob_storage_integration( + self, + *, + project_id: str, + type: BlobStorageIntegrationType, + bucket_name: str, + region: str, + export_frequency: BlobStorageExportFrequency, + enabled: bool, + force_path_style: bool, + file_type: BlobStorageIntegrationFileType, + export_mode: BlobStorageExportMode, + endpoint: typing.Optional[str] = OMIT, + access_key_id: typing.Optional[str] = OMIT, + secret_access_key: typing.Optional[str] = OMIT, + prefix: typing.Optional[str] = OMIT, + export_start_date: typing.Optional[dt.datetime] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[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 + ---------- + project_id : str + ID of the project in which to configure the blob storage integration + + type : BlobStorageIntegrationType + + bucket_name : str + Name of the storage bucket + + region : str + Storage region + + export_frequency : BlobStorageExportFrequency + + enabled : bool + Whether the integration is active + + force_path_style : bool + Use path-style URLs for S3 requests + + file_type : BlobStorageIntegrationFileType + + export_mode : BlobStorageExportMode + + endpoint : typing.Optional[str] + Custom endpoint URL (required for S3_COMPATIBLE type) + + access_key_id : typing.Optional[str] + Access key ID for authentication + + secret_access_key : typing.Optional[str] + Secret access key for authentication (will be encrypted when stored) + + prefix : typing.Optional[str] + Path prefix for exported files (must end with forward slash if provided) + + export_start_date : typing.Optional[dt.datetime] + Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[BlobStorageIntegrationResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/integrations/blob-storage", + method="PUT", + json={ + "projectId": project_id, + "type": type, + "bucketName": bucket_name, + "endpoint": endpoint, + "region": region, + "accessKeyId": access_key_id, + "secretAccessKey": secret_access_key, + "prefix": prefix, + "exportFrequency": export_frequency, + "enabled": enabled, + "forcePathStyle": force_path_style, + "fileType": file_type, + "exportMode": export_mode, + "exportStartDate": export_start_date, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + BlobStorageIntegrationResponse, + parse_obj_as( + type_=BlobStorageIntegrationResponse, # 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 delete_blob_storage_integration( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[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 + ------- + AsyncHttpResponse[BlobStorageIntegrationDeletionResponse] + """ + _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: + _data = typing.cast( + BlobStorageIntegrationDeletionResponse, + parse_obj_as( + type_=BlobStorageIntegrationDeletionResponse, # 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/blob_storage_integrations/types/__init__.py b/langfuse/api/blob_storage_integrations/types/__init__.py new file mode 100644 index 000000000..cc19f1a6d --- /dev/null +++ b/langfuse/api/blob_storage_integrations/types/__init__.py @@ -0,0 +1,69 @@ +# 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 .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, + ) +_dynamic_imports: typing.Dict[str, str] = { + "BlobStorageExportFrequency": ".blob_storage_export_frequency", + "BlobStorageExportMode": ".blob_storage_export_mode", + "BlobStorageIntegrationDeletionResponse": ".blob_storage_integration_deletion_response", + "BlobStorageIntegrationFileType": ".blob_storage_integration_file_type", + "BlobStorageIntegrationResponse": ".blob_storage_integration_response", + "BlobStorageIntegrationType": ".blob_storage_integration_type", + "BlobStorageIntegrationsResponse": ".blob_storage_integrations_response", + "CreateBlobStorageIntegrationRequest": ".create_blob_storage_integration_request", +} + + +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__ = [ + "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/blob_storage_integrations/types/blob_storage_export_frequency.py similarity index 89% rename from langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_frequency.py rename to langfuse/api/blob_storage_integrations/types/blob_storage_export_frequency.py index 936e0c18f..bcc7fc6d5 100644 --- a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_frequency.py +++ b/langfuse/api/blob_storage_integrations/types/blob_storage_export_frequency.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class BlobStorageExportFrequency(str, enum.Enum): +class BlobStorageExportFrequency(enum.StrEnum): HOURLY = "hourly" DAILY = "daily" WEEKLY = "weekly" diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_mode.py b/langfuse/api/blob_storage_integrations/types/blob_storage_export_mode.py similarity index 91% rename from langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_mode.py rename to langfuse/api/blob_storage_integrations/types/blob_storage_export_mode.py index 1eafab79d..d692c0fb9 100644 --- a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_export_mode.py +++ b/langfuse/api/blob_storage_integrations/types/blob_storage_export_mode.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class BlobStorageExportMode(str, enum.Enum): +class BlobStorageExportMode(enum.StrEnum): FULL_HISTORY = "FULL_HISTORY" FROM_TODAY = "FROM_TODAY" FROM_CUSTOM_DATE = "FROM_CUSTOM_DATE" diff --git a/langfuse/api/blob_storage_integrations/types/blob_storage_integration_deletion_response.py b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_deletion_response.py new file mode 100644 index 000000000..d0c8655b1 --- /dev/null +++ b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_deletion_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class BlobStorageIntegrationDeletionResponse(UniversalBaseModel): + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_file_type.py b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_file_type.py similarity index 88% rename from langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_file_type.py rename to langfuse/api/blob_storage_integrations/types/blob_storage_integration_file_type.py index a63631c6f..52998e5e4 100644 --- a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_file_type.py +++ b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_file_type.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class BlobStorageIntegrationFileType(str, enum.Enum): +class BlobStorageIntegrationFileType(enum.StrEnum): JSON = "JSON" CSV = "CSV" JSONL = "JSONL" diff --git a/langfuse/api/blob_storage_integrations/types/blob_storage_integration_response.py b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_response.py new file mode 100644 index 000000000..08529ee67 --- /dev/null +++ b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_response.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .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(UniversalBaseModel): + id: str + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] + type: BlobStorageIntegrationType + bucket_name: typing_extensions.Annotated[str, FieldMetadata(alias="bucketName")] + endpoint: typing.Optional[str] = None + region: str + access_key_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="accessKeyId") + ] = None + prefix: str + export_frequency: typing_extensions.Annotated[ + BlobStorageExportFrequency, FieldMetadata(alias="exportFrequency") + ] + enabled: bool + force_path_style: typing_extensions.Annotated[ + bool, FieldMetadata(alias="forcePathStyle") + ] + file_type: typing_extensions.Annotated[ + BlobStorageIntegrationFileType, FieldMetadata(alias="fileType") + ] + export_mode: typing_extensions.Annotated[ + BlobStorageExportMode, FieldMetadata(alias="exportMode") + ] + export_start_date: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="exportStartDate") + ] = None + next_sync_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="nextSyncAt") + ] = None + last_sync_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="lastSyncAt") + ] = None + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/blob_storage_integrations/types/blob_storage_integration_type.py b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_type.py new file mode 100644 index 000000000..66828a62d --- /dev/null +++ b/langfuse/api/blob_storage_integrations/types/blob_storage_integration_type.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core import enum + +T_Result = typing.TypeVar("T_Result") + + +class BlobStorageIntegrationType(enum.StrEnum): + S3 = "S3" + S3COMPATIBLE = "S3_COMPATIBLE" + AZURE_BLOB_STORAGE = "AZURE_BLOB_STORAGE" + + def visit( + self, + s3: typing.Callable[[], T_Result], + s3compatible: typing.Callable[[], T_Result], + azure_blob_storage: typing.Callable[[], T_Result], + ) -> T_Result: + if self is BlobStorageIntegrationType.S3: + return s3() + if self is BlobStorageIntegrationType.S3COMPATIBLE: + return s3compatible() + if self is BlobStorageIntegrationType.AZURE_BLOB_STORAGE: + return azure_blob_storage() diff --git a/langfuse/api/blob_storage_integrations/types/blob_storage_integrations_response.py b/langfuse/api/blob_storage_integrations/types/blob_storage_integrations_response.py new file mode 100644 index 000000000..0f3f5cdd7 --- /dev/null +++ b/langfuse/api/blob_storage_integrations/types/blob_storage_integrations_response.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .blob_storage_integration_response import BlobStorageIntegrationResponse + + +class BlobStorageIntegrationsResponse(UniversalBaseModel): + data: typing.List[BlobStorageIntegrationResponse] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/blob_storage_integrations/types/create_blob_storage_integration_request.py b/langfuse/api/blob_storage_integrations/types/create_blob_storage_integration_request.py new file mode 100644 index 000000000..3a4d9fa06 --- /dev/null +++ b/langfuse/api/blob_storage_integrations/types/create_blob_storage_integration_request.py @@ -0,0 +1,91 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .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(UniversalBaseModel): + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] = ( + pydantic.Field() + ) + """ + ID of the project in which to configure the blob storage integration + """ + + type: BlobStorageIntegrationType + bucket_name: typing_extensions.Annotated[str, FieldMetadata(alias="bucketName")] = ( + pydantic.Field() + ) + """ + Name of the storage bucket + """ + + endpoint: typing.Optional[str] = pydantic.Field(default=None) + """ + Custom endpoint URL (required for S3_COMPATIBLE type) + """ + + region: str = pydantic.Field() + """ + Storage region + """ + + access_key_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="accessKeyId") + ] = pydantic.Field(default=None) + """ + Access key ID for authentication + """ + + secret_access_key: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="secretAccessKey") + ] = pydantic.Field(default=None) + """ + Secret access key for authentication (will be encrypted when stored) + """ + + prefix: typing.Optional[str] = pydantic.Field(default=None) + """ + Path prefix for exported files (must end with forward slash if provided) + """ + + export_frequency: typing_extensions.Annotated[ + BlobStorageExportFrequency, FieldMetadata(alias="exportFrequency") + ] + enabled: bool = pydantic.Field() + """ + Whether the integration is active + """ + + force_path_style: typing_extensions.Annotated[ + bool, FieldMetadata(alias="forcePathStyle") + ] = pydantic.Field() + """ + Use path-style URLs for S3 requests + """ + + file_type: typing_extensions.Annotated[ + BlobStorageIntegrationFileType, FieldMetadata(alias="fileType") + ] + export_mode: typing_extensions.Annotated[ + BlobStorageExportMode, FieldMetadata(alias="exportMode") + ] + export_start_date: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="exportStartDate") + ] = pydantic.Field(default=None) + """ + Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE) + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/client.py b/langfuse/api/client.py index 646279b5a..041f214b9 100644 --- a/langfuse/api/client.py +++ b/langfuse/api/client.py @@ -1,58 +1,51 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import typing import httpx - from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from .resources.annotation_queues.client import ( - 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 ( - AsyncDatasetRunItemsClient, - DatasetRunItemsClient, -) -from .resources.datasets.client import AsyncDatasetsClient, DatasetsClient -from .resources.health.client import AsyncHealthClient, HealthClient -from .resources.ingestion.client import AsyncIngestionClient, IngestionClient -from .resources.llm_connections.client import ( - AsyncLlmConnectionsClient, - LlmConnectionsClient, -) -from .resources.media.client import AsyncMediaClient, MediaClient -from .resources.metrics.client import AsyncMetricsClient, MetricsClient -from .resources.models.client import AsyncModelsClient, ModelsClient -from .resources.observations.client import AsyncObservationsClient, ObservationsClient -from .resources.opentelemetry.client import ( - AsyncOpentelemetryClient, - OpentelemetryClient, -) -from .resources.organizations.client import ( - AsyncOrganizationsClient, - OrganizationsClient, -) -from .resources.projects.client import AsyncProjectsClient, ProjectsClient -from .resources.prompt_version.client import ( - AsyncPromptVersionClient, - PromptVersionClient, -) -from .resources.prompts.client import AsyncPromptsClient, PromptsClient -from .resources.scim.client import AsyncScimClient, ScimClient -from .resources.score.client import AsyncScoreClient, ScoreClient -from .resources.score_configs.client import AsyncScoreConfigsClient, ScoreConfigsClient -from .resources.score_v_2.client import AsyncScoreV2Client, ScoreV2Client -from .resources.sessions.client import AsyncSessionsClient, SessionsClient -from .resources.trace.client import AsyncTraceClient, TraceClient - - -class FernLangfuse: + +if typing.TYPE_CHECKING: + from .annotation_queues.client import ( + AnnotationQueuesClient, + AsyncAnnotationQueuesClient, + ) + from .blob_storage_integrations.client import ( + AsyncBlobStorageIntegrationsClient, + BlobStorageIntegrationsClient, + ) + from .comments.client import AsyncCommentsClient, CommentsClient + from .dataset_items.client import AsyncDatasetItemsClient, DatasetItemsClient + from .dataset_run_items.client import ( + AsyncDatasetRunItemsClient, + DatasetRunItemsClient, + ) + from .datasets.client import AsyncDatasetsClient, DatasetsClient + from .health.client import AsyncHealthClient, HealthClient + from .ingestion.client import AsyncIngestionClient, IngestionClient + 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 + from .prompt_version.client import AsyncPromptVersionClient, PromptVersionClient + from .prompts.client import AsyncPromptsClient, PromptsClient + 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 + + +class LangfuseAPI: """ Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. @@ -66,6 +59,9 @@ class FernLangfuse: x_langfuse_public_key : typing.Optional[str] username : typing.Optional[typing.Union[str, typing.Callable[[], str]]] password : typing.Optional[typing.Union[str, typing.Callable[[], str]]] + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + timeout : typing.Optional[float] The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. @@ -77,9 +73,9 @@ class FernLangfuse: Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -98,12 +94,17 @@ def __init__( x_langfuse_public_key: typing.Optional[str] = None, username: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, password: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.Client] = None, ): _defaulted_timeout = ( - timeout if timeout is not None else 60 if httpx_client is None else None + timeout + if timeout is not None + else 60 + if httpx_client is None + else httpx_client.timeout.read ) self._client_wrapper = SyncClientWrapper( base_url=base_url, @@ -112,6 +113,7 @@ def __init__( x_langfuse_public_key=x_langfuse_public_key, username=username, password=password, + headers=headers, httpx_client=httpx_client if httpx_client is not None else httpx.Client( @@ -121,39 +123,265 @@ def __init__( else httpx.Client(timeout=_defaulted_timeout), timeout=_defaulted_timeout, ) - 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( - client_wrapper=self._client_wrapper - ) - self.datasets = DatasetsClient(client_wrapper=self._client_wrapper) - self.health = HealthClient(client_wrapper=self._client_wrapper) - self.ingestion = IngestionClient(client_wrapper=self._client_wrapper) - self.llm_connections = LlmConnectionsClient(client_wrapper=self._client_wrapper) - self.media = MediaClient(client_wrapper=self._client_wrapper) - self.metrics = MetricsClient(client_wrapper=self._client_wrapper) - self.models = ModelsClient(client_wrapper=self._client_wrapper) - self.observations = ObservationsClient(client_wrapper=self._client_wrapper) - self.opentelemetry = OpentelemetryClient(client_wrapper=self._client_wrapper) - self.organizations = OrganizationsClient(client_wrapper=self._client_wrapper) - self.projects = ProjectsClient(client_wrapper=self._client_wrapper) - self.prompt_version = PromptVersionClient(client_wrapper=self._client_wrapper) - self.prompts = PromptsClient(client_wrapper=self._client_wrapper) - self.scim = ScimClient(client_wrapper=self._client_wrapper) - self.score_configs = ScoreConfigsClient(client_wrapper=self._client_wrapper) - self.score_v_2 = ScoreV2Client(client_wrapper=self._client_wrapper) - self.score = ScoreClient(client_wrapper=self._client_wrapper) - self.sessions = SessionsClient(client_wrapper=self._client_wrapper) - self.trace = TraceClient(client_wrapper=self._client_wrapper) - - -class AsyncFernLangfuse: + self._annotation_queues: typing.Optional[AnnotationQueuesClient] = None + self._blob_storage_integrations: typing.Optional[ + BlobStorageIntegrationsClient + ] = None + self._comments: typing.Optional[CommentsClient] = None + self._dataset_items: typing.Optional[DatasetItemsClient] = None + self._dataset_run_items: typing.Optional[DatasetRunItemsClient] = None + self._datasets: typing.Optional[DatasetsClient] = None + self._health: typing.Optional[HealthClient] = None + self._ingestion: typing.Optional[IngestionClient] = 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 + self._projects: typing.Optional[ProjectsClient] = None + self._prompt_version: typing.Optional[PromptVersionClient] = None + 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 + + @property + def annotation_queues(self): + if self._annotation_queues is None: + from .annotation_queues.client import AnnotationQueuesClient # noqa: E402 + + self._annotation_queues = AnnotationQueuesClient( + client_wrapper=self._client_wrapper + ) + return self._annotation_queues + + @property + def blob_storage_integrations(self): + if self._blob_storage_integrations is None: + from .blob_storage_integrations.client import BlobStorageIntegrationsClient # noqa: E402 + + self._blob_storage_integrations = BlobStorageIntegrationsClient( + client_wrapper=self._client_wrapper + ) + return self._blob_storage_integrations + + @property + def comments(self): + if self._comments is None: + from .comments.client import CommentsClient # noqa: E402 + + self._comments = CommentsClient(client_wrapper=self._client_wrapper) + return self._comments + + @property + def dataset_items(self): + if self._dataset_items is None: + from .dataset_items.client import DatasetItemsClient # noqa: E402 + + self._dataset_items = DatasetItemsClient( + client_wrapper=self._client_wrapper + ) + return self._dataset_items + + @property + def dataset_run_items(self): + if self._dataset_run_items is None: + from .dataset_run_items.client import DatasetRunItemsClient # noqa: E402 + + self._dataset_run_items = DatasetRunItemsClient( + client_wrapper=self._client_wrapper + ) + return self._dataset_run_items + + @property + def datasets(self): + if self._datasets is None: + from .datasets.client import DatasetsClient # noqa: E402 + + self._datasets = DatasetsClient(client_wrapper=self._client_wrapper) + return self._datasets + + @property + def health(self): + if self._health is None: + from .health.client import HealthClient # noqa: E402 + + self._health = HealthClient(client_wrapper=self._client_wrapper) + return self._health + + @property + def ingestion(self): + if self._ingestion is None: + from .ingestion.client import IngestionClient # noqa: E402 + + self._ingestion = IngestionClient(client_wrapper=self._client_wrapper) + return self._ingestion + + @property + def llm_connections(self): + if self._llm_connections is None: + from .llm_connections.client import LlmConnectionsClient # noqa: E402 + + self._llm_connections = LlmConnectionsClient( + client_wrapper=self._client_wrapper + ) + return self._llm_connections + + @property + def media(self): + if self._media is None: + from .media.client import MediaClient # noqa: E402 + + 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: + from .metrics.client import MetricsClient # noqa: E402 + + self._metrics = MetricsClient(client_wrapper=self._client_wrapper) + return self._metrics + + @property + def models(self): + if self._models is None: + from .models.client import ModelsClient # noqa: E402 + + 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: + from .observations.client import ObservationsClient # noqa: E402 + + self._observations = ObservationsClient(client_wrapper=self._client_wrapper) + return self._observations + + @property + def opentelemetry(self): + if self._opentelemetry is None: + from .opentelemetry.client import OpentelemetryClient # noqa: E402 + + self._opentelemetry = OpentelemetryClient( + client_wrapper=self._client_wrapper + ) + return self._opentelemetry + + @property + def organizations(self): + if self._organizations is None: + from .organizations.client import OrganizationsClient # noqa: E402 + + self._organizations = OrganizationsClient( + client_wrapper=self._client_wrapper + ) + return self._organizations + + @property + def projects(self): + if self._projects is None: + from .projects.client import ProjectsClient # noqa: E402 + + self._projects = ProjectsClient(client_wrapper=self._client_wrapper) + return self._projects + + @property + def prompt_version(self): + if self._prompt_version is None: + from .prompt_version.client import PromptVersionClient # noqa: E402 + + self._prompt_version = PromptVersionClient( + client_wrapper=self._client_wrapper + ) + return self._prompt_version + + @property + def prompts(self): + if self._prompts is None: + from .prompts.client import PromptsClient # noqa: E402 + + self._prompts = PromptsClient(client_wrapper=self._client_wrapper) + return self._prompts + + @property + def scim(self): + if self._scim is None: + from .scim.client import ScimClient # noqa: E402 + + self._scim = ScimClient(client_wrapper=self._client_wrapper) + return self._scim + + @property + def score_configs(self): + if self._score_configs is None: + from .score_configs.client import ScoreConfigsClient # noqa: E402 + + self._score_configs = ScoreConfigsClient( + client_wrapper=self._client_wrapper + ) + 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: + from .score.client import ScoreClient # noqa: E402 + + self._score = ScoreClient(client_wrapper=self._client_wrapper) + return self._score + + @property + def sessions(self): + if self._sessions is None: + from .sessions.client import SessionsClient # noqa: E402 + + self._sessions = SessionsClient(client_wrapper=self._client_wrapper) + return self._sessions + + @property + def trace(self): + if self._trace is None: + from .trace.client import TraceClient # noqa: E402 + + self._trace = TraceClient(client_wrapper=self._client_wrapper) + return self._trace + + +class AsyncLangfuseAPI: """ Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. @@ -167,6 +395,9 @@ class AsyncFernLangfuse: x_langfuse_public_key : typing.Optional[str] username : typing.Optional[typing.Union[str, typing.Callable[[], str]]] password : typing.Optional[typing.Union[str, typing.Callable[[], str]]] + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + timeout : typing.Optional[float] The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. @@ -178,9 +409,9 @@ class AsyncFernLangfuse: Examples -------- - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -199,12 +430,17 @@ def __init__( x_langfuse_public_key: typing.Optional[str] = None, username: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, password: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.AsyncClient] = None, ): _defaulted_timeout = ( - timeout if timeout is not None else 60 if httpx_client is None else None + timeout + if timeout is not None + else 60 + if httpx_client is None + else httpx_client.timeout.read ) self._client_wrapper = AsyncClientWrapper( base_url=base_url, @@ -213,6 +449,7 @@ def __init__( x_langfuse_public_key=x_langfuse_public_key, username=username, password=password, + headers=headers, httpx_client=httpx_client if httpx_client is not None else httpx.AsyncClient( @@ -222,45 +459,263 @@ def __init__( else httpx.AsyncClient(timeout=_defaulted_timeout), timeout=_defaulted_timeout, ) - 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 - ) - self.dataset_run_items = AsyncDatasetRunItemsClient( - client_wrapper=self._client_wrapper - ) - self.datasets = AsyncDatasetsClient(client_wrapper=self._client_wrapper) - self.health = AsyncHealthClient(client_wrapper=self._client_wrapper) - self.ingestion = AsyncIngestionClient(client_wrapper=self._client_wrapper) - self.llm_connections = AsyncLlmConnectionsClient( - client_wrapper=self._client_wrapper - ) - self.media = AsyncMediaClient(client_wrapper=self._client_wrapper) - self.metrics = AsyncMetricsClient(client_wrapper=self._client_wrapper) - self.models = AsyncModelsClient(client_wrapper=self._client_wrapper) - self.observations = AsyncObservationsClient(client_wrapper=self._client_wrapper) - self.opentelemetry = AsyncOpentelemetryClient( - client_wrapper=self._client_wrapper - ) - self.organizations = AsyncOrganizationsClient( - client_wrapper=self._client_wrapper - ) - self.projects = AsyncProjectsClient(client_wrapper=self._client_wrapper) - self.prompt_version = AsyncPromptVersionClient( - client_wrapper=self._client_wrapper - ) - self.prompts = AsyncPromptsClient(client_wrapper=self._client_wrapper) - self.scim = AsyncScimClient(client_wrapper=self._client_wrapper) - self.score_configs = AsyncScoreConfigsClient( - client_wrapper=self._client_wrapper - ) - self.score_v_2 = AsyncScoreV2Client(client_wrapper=self._client_wrapper) - self.score = AsyncScoreClient(client_wrapper=self._client_wrapper) - self.sessions = AsyncSessionsClient(client_wrapper=self._client_wrapper) - self.trace = AsyncTraceClient(client_wrapper=self._client_wrapper) + self._annotation_queues: typing.Optional[AsyncAnnotationQueuesClient] = None + self._blob_storage_integrations: typing.Optional[ + AsyncBlobStorageIntegrationsClient + ] = None + self._comments: typing.Optional[AsyncCommentsClient] = None + self._dataset_items: typing.Optional[AsyncDatasetItemsClient] = None + self._dataset_run_items: typing.Optional[AsyncDatasetRunItemsClient] = None + self._datasets: typing.Optional[AsyncDatasetsClient] = None + self._health: typing.Optional[AsyncHealthClient] = None + self._ingestion: typing.Optional[AsyncIngestionClient] = 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 + self._projects: typing.Optional[AsyncProjectsClient] = None + self._prompt_version: typing.Optional[AsyncPromptVersionClient] = None + 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 + + @property + def annotation_queues(self): + if self._annotation_queues is None: + from .annotation_queues.client import AsyncAnnotationQueuesClient # noqa: E402 + + self._annotation_queues = AsyncAnnotationQueuesClient( + client_wrapper=self._client_wrapper + ) + return self._annotation_queues + + @property + def blob_storage_integrations(self): + if self._blob_storage_integrations is None: + from .blob_storage_integrations.client import ( + AsyncBlobStorageIntegrationsClient, + ) # noqa: E402 + + self._blob_storage_integrations = AsyncBlobStorageIntegrationsClient( + client_wrapper=self._client_wrapper + ) + return self._blob_storage_integrations + + @property + def comments(self): + if self._comments is None: + from .comments.client import AsyncCommentsClient # noqa: E402 + + self._comments = AsyncCommentsClient(client_wrapper=self._client_wrapper) + return self._comments + + @property + def dataset_items(self): + if self._dataset_items is None: + from .dataset_items.client import AsyncDatasetItemsClient # noqa: E402 + + self._dataset_items = AsyncDatasetItemsClient( + client_wrapper=self._client_wrapper + ) + return self._dataset_items + + @property + def dataset_run_items(self): + if self._dataset_run_items is None: + from .dataset_run_items.client import AsyncDatasetRunItemsClient # noqa: E402 + + self._dataset_run_items = AsyncDatasetRunItemsClient( + client_wrapper=self._client_wrapper + ) + return self._dataset_run_items + + @property + def datasets(self): + if self._datasets is None: + from .datasets.client import AsyncDatasetsClient # noqa: E402 + + self._datasets = AsyncDatasetsClient(client_wrapper=self._client_wrapper) + return self._datasets + + @property + def health(self): + if self._health is None: + from .health.client import AsyncHealthClient # noqa: E402 + + self._health = AsyncHealthClient(client_wrapper=self._client_wrapper) + return self._health + + @property + def ingestion(self): + if self._ingestion is None: + from .ingestion.client import AsyncIngestionClient # noqa: E402 + + self._ingestion = AsyncIngestionClient(client_wrapper=self._client_wrapper) + return self._ingestion + + @property + def llm_connections(self): + if self._llm_connections is None: + from .llm_connections.client import AsyncLlmConnectionsClient # noqa: E402 + + self._llm_connections = AsyncLlmConnectionsClient( + client_wrapper=self._client_wrapper + ) + return self._llm_connections + + @property + def media(self): + if self._media is None: + from .media.client import AsyncMediaClient # noqa: E402 + + 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: + from .metrics.client import AsyncMetricsClient # noqa: E402 + + self._metrics = AsyncMetricsClient(client_wrapper=self._client_wrapper) + return self._metrics + + @property + def models(self): + if self._models is None: + from .models.client import AsyncModelsClient # noqa: E402 + + 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: + from .observations.client import AsyncObservationsClient # noqa: E402 + + self._observations = AsyncObservationsClient( + client_wrapper=self._client_wrapper + ) + return self._observations + + @property + def opentelemetry(self): + if self._opentelemetry is None: + from .opentelemetry.client import AsyncOpentelemetryClient # noqa: E402 + + self._opentelemetry = AsyncOpentelemetryClient( + client_wrapper=self._client_wrapper + ) + return self._opentelemetry + + @property + def organizations(self): + if self._organizations is None: + from .organizations.client import AsyncOrganizationsClient # noqa: E402 + + self._organizations = AsyncOrganizationsClient( + client_wrapper=self._client_wrapper + ) + return self._organizations + + @property + def projects(self): + if self._projects is None: + from .projects.client import AsyncProjectsClient # noqa: E402 + + self._projects = AsyncProjectsClient(client_wrapper=self._client_wrapper) + return self._projects + + @property + def prompt_version(self): + if self._prompt_version is None: + from .prompt_version.client import AsyncPromptVersionClient # noqa: E402 + + self._prompt_version = AsyncPromptVersionClient( + client_wrapper=self._client_wrapper + ) + return self._prompt_version + + @property + def prompts(self): + if self._prompts is None: + from .prompts.client import AsyncPromptsClient # noqa: E402 + + self._prompts = AsyncPromptsClient(client_wrapper=self._client_wrapper) + return self._prompts + + @property + def scim(self): + if self._scim is None: + from .scim.client import AsyncScimClient # noqa: E402 + + self._scim = AsyncScimClient(client_wrapper=self._client_wrapper) + return self._scim + + @property + def score_configs(self): + if self._score_configs is None: + from .score_configs.client import AsyncScoreConfigsClient # noqa: E402 + + self._score_configs = AsyncScoreConfigsClient( + client_wrapper=self._client_wrapper + ) + 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: + from .score.client import AsyncScoreClient # noqa: E402 + + self._score = AsyncScoreClient(client_wrapper=self._client_wrapper) + return self._score + + @property + def sessions(self): + if self._sessions is None: + from .sessions.client import AsyncSessionsClient # noqa: E402 + + self._sessions = AsyncSessionsClient(client_wrapper=self._client_wrapper) + return self._sessions + + @property + def trace(self): + if self._trace is None: + from .trace.client import AsyncTraceClient # noqa: E402 + + self._trace = AsyncTraceClient(client_wrapper=self._client_wrapper) + return self._trace diff --git a/langfuse/api/comments/__init__.py b/langfuse/api/comments/__init__.py new file mode 100644 index 000000000..0588586c7 --- /dev/null +++ b/langfuse/api/comments/__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 .types import CreateCommentRequest, CreateCommentResponse, GetCommentsResponse +_dynamic_imports: typing.Dict[str, str] = { + "CreateCommentRequest": ".types", + "CreateCommentResponse": ".types", + "GetCommentsResponse": ".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__ = ["CreateCommentRequest", "CreateCommentResponse", "GetCommentsResponse"] diff --git a/langfuse/api/comments/client.py b/langfuse/api/comments/client.py new file mode 100644 index 000000000..f5e92ff36 --- /dev/null +++ b/langfuse/api/comments/client.py @@ -0,0 +1,407 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..commons.types.comment import Comment +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawCommentsClient, RawCommentsClient +from .types.create_comment_response import CreateCommentResponse +from .types.get_comments_response import GetCommentsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CommentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCommentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCommentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCommentsClient + """ + return self._raw_client + + def create( + self, + *, + project_id: str, + object_type: str, + object_id: str, + content: str, + author_user_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateCommentResponse: + """ + Create a comment. Comments may be attached to different object types (trace, observation, session, prompt). + + Parameters + ---------- + project_id : str + The id of the project to attach the comment to. + + object_type : str + The type of the object to attach the comment to (trace, observation, session, prompt). + + object_id : str + The id of the object to attach the comment to. If this does not reference a valid existing object, an error will be thrown. + + content : str + The content of the comment. May include markdown. Currently limited to 5000 characters. + + author_user_id : typing.Optional[str] + The id of the user who created the comment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateCommentResponse + + 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.comments.create( + project_id="projectId", + object_type="objectType", + object_id="objectId", + content="content", + ) + """ + _response = self._raw_client.create( + project_id=project_id, + object_type=object_type, + object_id=object_id, + content=content, + author_user_id=author_user_id, + request_options=request_options, + ) + return _response.data + + def get( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + object_type: typing.Optional[str] = None, + object_id: typing.Optional[str] = None, + author_user_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetCommentsResponse: + """ + Get all comments + + 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 + + object_type : typing.Optional[str] + Filter comments by object type (trace, observation, session, prompt). + + object_id : typing.Optional[str] + Filter comments by object id. If objectType is not provided, an error will be thrown. + + author_user_id : typing.Optional[str] + Filter comments by author user id. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetCommentsResponse + + 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.comments.get() + """ + _response = self._raw_client.get( + page=page, + limit=limit, + object_type=object_type, + object_id=object_id, + author_user_id=author_user_id, + request_options=request_options, + ) + return _response.data + + def get_by_id( + self, + comment_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> Comment: + """ + Get a comment by id + + Parameters + ---------- + comment_id : str + The unique langfuse identifier of a comment + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Comment + + 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.comments.get_by_id( + comment_id="commentId", + ) + """ + _response = self._raw_client.get_by_id( + comment_id, request_options=request_options + ) + return _response.data + + +class AsyncCommentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCommentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCommentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCommentsClient + """ + return self._raw_client + + async def create( + self, + *, + project_id: str, + object_type: str, + object_id: str, + content: str, + author_user_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateCommentResponse: + """ + Create a comment. Comments may be attached to different object types (trace, observation, session, prompt). + + Parameters + ---------- + project_id : str + The id of the project to attach the comment to. + + object_type : str + The type of the object to attach the comment to (trace, observation, session, prompt). + + object_id : str + The id of the object to attach the comment to. If this does not reference a valid existing object, an error will be thrown. + + content : str + The content of the comment. May include markdown. Currently limited to 5000 characters. + + author_user_id : typing.Optional[str] + The id of the user who created the comment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateCommentResponse + + 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.comments.create( + project_id="projectId", + object_type="objectType", + object_id="objectId", + content="content", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + project_id=project_id, + object_type=object_type, + object_id=object_id, + content=content, + author_user_id=author_user_id, + request_options=request_options, + ) + return _response.data + + async def get( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + object_type: typing.Optional[str] = None, + object_id: typing.Optional[str] = None, + author_user_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetCommentsResponse: + """ + Get all comments + + 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 + + object_type : typing.Optional[str] + Filter comments by object type (trace, observation, session, prompt). + + object_id : typing.Optional[str] + Filter comments by object id. If objectType is not provided, an error will be thrown. + + author_user_id : typing.Optional[str] + Filter comments by author user id. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetCommentsResponse + + 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.comments.get() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + page=page, + limit=limit, + object_type=object_type, + object_id=object_id, + author_user_id=author_user_id, + request_options=request_options, + ) + return _response.data + + async def get_by_id( + self, + comment_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> Comment: + """ + Get a comment by id + + Parameters + ---------- + comment_id : str + The unique langfuse identifier of a comment + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Comment + + 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.comments.get_by_id( + comment_id="commentId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_by_id( + comment_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/comments/raw_client.py b/langfuse/api/comments/raw_client.py new file mode 100644 index 000000000..0bb39539a --- /dev/null +++ b/langfuse/api/comments/raw_client.py @@ -0,0 +1,750 @@ +# 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 ..commons.types.comment import Comment +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 .types.create_comment_response import CreateCommentResponse +from .types.get_comments_response import GetCommentsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCommentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + *, + project_id: str, + object_type: str, + object_id: str, + content: str, + author_user_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CreateCommentResponse]: + """ + Create a comment. Comments may be attached to different object types (trace, observation, session, prompt). + + Parameters + ---------- + project_id : str + The id of the project to attach the comment to. + + object_type : str + The type of the object to attach the comment to (trace, observation, session, prompt). + + object_id : str + The id of the object to attach the comment to. If this does not reference a valid existing object, an error will be thrown. + + content : str + The content of the comment. May include markdown. Currently limited to 5000 characters. + + author_user_id : typing.Optional[str] + The id of the user who created the comment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CreateCommentResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/comments", + method="POST", + json={ + "projectId": project_id, + "objectType": object_type, + "objectId": object_id, + "content": content, + "authorUserId": author_user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateCommentResponse, + parse_obj_as( + type_=CreateCommentResponse, # 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( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + object_type: typing.Optional[str] = None, + object_id: typing.Optional[str] = None, + author_user_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[GetCommentsResponse]: + """ + Get all comments + + 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 + + object_type : typing.Optional[str] + Filter comments by object type (trace, observation, session, prompt). + + object_id : typing.Optional[str] + Filter comments by object id. If objectType is not provided, an error will be thrown. + + author_user_id : typing.Optional[str] + Filter comments by author user id. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetCommentsResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/comments", + method="GET", + params={ + "page": page, + "limit": limit, + "objectType": object_type, + "objectId": object_id, + "authorUserId": author_user_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetCommentsResponse, + parse_obj_as( + type_=GetCommentsResponse, # 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_by_id( + self, + comment_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Comment]: + """ + Get a comment by id + + Parameters + ---------- + comment_id : str + The unique langfuse identifier of a comment + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Comment] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/comments/{jsonable_encoder(comment_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Comment, + parse_obj_as( + type_=Comment, # 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 AsyncRawCommentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + *, + project_id: str, + object_type: str, + object_id: str, + content: str, + author_user_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CreateCommentResponse]: + """ + Create a comment. Comments may be attached to different object types (trace, observation, session, prompt). + + Parameters + ---------- + project_id : str + The id of the project to attach the comment to. + + object_type : str + The type of the object to attach the comment to (trace, observation, session, prompt). + + object_id : str + The id of the object to attach the comment to. If this does not reference a valid existing object, an error will be thrown. + + content : str + The content of the comment. May include markdown. Currently limited to 5000 characters. + + author_user_id : typing.Optional[str] + The id of the user who created the comment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CreateCommentResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/comments", + method="POST", + json={ + "projectId": project_id, + "objectType": object_type, + "objectId": object_id, + "content": content, + "authorUserId": author_user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateCommentResponse, + parse_obj_as( + type_=CreateCommentResponse, # 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( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + object_type: typing.Optional[str] = None, + object_id: typing.Optional[str] = None, + author_user_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[GetCommentsResponse]: + """ + Get all comments + + 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 + + object_type : typing.Optional[str] + Filter comments by object type (trace, observation, session, prompt). + + object_id : typing.Optional[str] + Filter comments by object id. If objectType is not provided, an error will be thrown. + + author_user_id : typing.Optional[str] + Filter comments by author user id. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetCommentsResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/comments", + method="GET", + params={ + "page": page, + "limit": limit, + "objectType": object_type, + "objectId": object_id, + "authorUserId": author_user_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetCommentsResponse, + parse_obj_as( + type_=GetCommentsResponse, # 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_by_id( + self, + comment_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Comment]: + """ + Get a comment by id + + Parameters + ---------- + comment_id : str + The unique langfuse identifier of a comment + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Comment] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/comments/{jsonable_encoder(comment_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Comment, + parse_obj_as( + type_=Comment, # 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/comments/types/__init__.py b/langfuse/api/comments/types/__init__.py new file mode 100644 index 000000000..4936025a0 --- /dev/null +++ b/langfuse/api/comments/types/__init__.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_comment_request import CreateCommentRequest + from .create_comment_response import CreateCommentResponse + from .get_comments_response import GetCommentsResponse +_dynamic_imports: typing.Dict[str, str] = { + "CreateCommentRequest": ".create_comment_request", + "CreateCommentResponse": ".create_comment_response", + "GetCommentsResponse": ".get_comments_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateCommentRequest", "CreateCommentResponse", "GetCommentsResponse"] diff --git a/langfuse/api/comments/types/create_comment_request.py b/langfuse/api/comments/types/create_comment_request.py new file mode 100644 index 000000000..56ef2794d --- /dev/null +++ b/langfuse/api/comments/types/create_comment_request.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class CreateCommentRequest(UniversalBaseModel): + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] = ( + pydantic.Field() + ) + """ + The id of the project to attach the comment to. + """ + + object_type: typing_extensions.Annotated[str, FieldMetadata(alias="objectType")] = ( + pydantic.Field() + ) + """ + The type of the object to attach the comment to (trace, observation, session, prompt). + """ + + object_id: typing_extensions.Annotated[str, FieldMetadata(alias="objectId")] = ( + pydantic.Field() + ) + """ + The id of the object to attach the comment to. If this does not reference a valid existing object, an error will be thrown. + """ + + content: str = pydantic.Field() + """ + The content of the comment. May include markdown. Currently limited to 5000 characters. + """ + + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = pydantic.Field(default=None) + """ + The id of the user who created the comment. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/comments/types/create_comment_response.py b/langfuse/api/comments/types/create_comment_response.py new file mode 100644 index 000000000..d080349b0 --- /dev/null +++ b/langfuse/api/comments/types/create_comment_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class CreateCommentResponse(UniversalBaseModel): + id: str = pydantic.Field() + """ + The id of the created object in Langfuse + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/comments/types/get_comments_response.py b/langfuse/api/comments/types/get_comments_response.py new file mode 100644 index 000000000..f275210e8 --- /dev/null +++ b/langfuse/api/comments/types/get_comments_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.comment import Comment +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class GetCommentsResponse(UniversalBaseModel): + data: typing.List[Comment] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/__init__.py b/langfuse/api/commons/__init__.py new file mode 100644 index 000000000..273e6c76c --- /dev/null +++ b/langfuse/api/commons/__init__.py @@ -0,0 +1,198 @@ +# 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 ( + BaseScore, + BaseScoreV1, + BooleanScore, + BooleanScoreV1, + CategoricalScore, + CategoricalScoreV1, + Comment, + CommentObjectType, + ConfigCategory, + CreateScoreValue, + Dataset, + DatasetItem, + DatasetRun, + DatasetRunItem, + DatasetRunWithItems, + DatasetStatus, + MapValue, + Model, + ModelPrice, + ModelUsageUnit, + NumericScore, + NumericScoreV1, + Observation, + ObservationLevel, + ObservationsView, + PricingTier, + PricingTierCondition, + PricingTierInput, + PricingTierOperator, + Score, + ScoreConfig, + ScoreDataType, + ScoreSource, + ScoreV1, + ScoreV1_Boolean, + ScoreV1_Categorical, + ScoreV1_Numeric, + Score_Boolean, + Score_Categorical, + Score_Numeric, + Session, + SessionWithTraces, + Trace, + TraceWithDetails, + TraceWithFullDetails, + Usage, + ) + from .errors import ( + AccessDeniedError, + Error, + MethodNotAllowedError, + NotFoundError, + UnauthorizedError, + ) +_dynamic_imports: typing.Dict[str, str] = { + "AccessDeniedError": ".errors", + "BaseScore": ".types", + "BaseScoreV1": ".types", + "BooleanScore": ".types", + "BooleanScoreV1": ".types", + "CategoricalScore": ".types", + "CategoricalScoreV1": ".types", + "Comment": ".types", + "CommentObjectType": ".types", + "ConfigCategory": ".types", + "CreateScoreValue": ".types", + "Dataset": ".types", + "DatasetItem": ".types", + "DatasetRun": ".types", + "DatasetRunItem": ".types", + "DatasetRunWithItems": ".types", + "DatasetStatus": ".types", + "Error": ".errors", + "MapValue": ".types", + "MethodNotAllowedError": ".errors", + "Model": ".types", + "ModelPrice": ".types", + "ModelUsageUnit": ".types", + "NotFoundError": ".errors", + "NumericScore": ".types", + "NumericScoreV1": ".types", + "Observation": ".types", + "ObservationLevel": ".types", + "ObservationsView": ".types", + "PricingTier": ".types", + "PricingTierCondition": ".types", + "PricingTierInput": ".types", + "PricingTierOperator": ".types", + "Score": ".types", + "ScoreConfig": ".types", + "ScoreDataType": ".types", + "ScoreSource": ".types", + "ScoreV1": ".types", + "ScoreV1_Boolean": ".types", + "ScoreV1_Categorical": ".types", + "ScoreV1_Numeric": ".types", + "Score_Boolean": ".types", + "Score_Categorical": ".types", + "Score_Numeric": ".types", + "Session": ".types", + "SessionWithTraces": ".types", + "Trace": ".types", + "TraceWithDetails": ".types", + "TraceWithFullDetails": ".types", + "UnauthorizedError": ".errors", + "Usage": ".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__ = [ + "AccessDeniedError", + "BaseScore", + "BaseScoreV1", + "BooleanScore", + "BooleanScoreV1", + "CategoricalScore", + "CategoricalScoreV1", + "Comment", + "CommentObjectType", + "ConfigCategory", + "CreateScoreValue", + "Dataset", + "DatasetItem", + "DatasetRun", + "DatasetRunItem", + "DatasetRunWithItems", + "DatasetStatus", + "Error", + "MapValue", + "MethodNotAllowedError", + "Model", + "ModelPrice", + "ModelUsageUnit", + "NotFoundError", + "NumericScore", + "NumericScoreV1", + "Observation", + "ObservationLevel", + "ObservationsView", + "PricingTier", + "PricingTierCondition", + "PricingTierInput", + "PricingTierOperator", + "Score", + "ScoreConfig", + "ScoreDataType", + "ScoreSource", + "ScoreV1", + "ScoreV1_Boolean", + "ScoreV1_Categorical", + "ScoreV1_Numeric", + "Score_Boolean", + "Score_Categorical", + "Score_Numeric", + "Session", + "SessionWithTraces", + "Trace", + "TraceWithDetails", + "TraceWithFullDetails", + "UnauthorizedError", + "Usage", +] diff --git a/langfuse/api/commons/errors/__init__.py b/langfuse/api/commons/errors/__init__.py new file mode 100644 index 000000000..c633139f0 --- /dev/null +++ b/langfuse/api/commons/errors/__init__.py @@ -0,0 +1,56 @@ +# 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 .access_denied_error import AccessDeniedError + from .error import Error + from .method_not_allowed_error import MethodNotAllowedError + from .not_found_error import NotFoundError + from .unauthorized_error import UnauthorizedError +_dynamic_imports: typing.Dict[str, str] = { + "AccessDeniedError": ".access_denied_error", + "Error": ".error", + "MethodNotAllowedError": ".method_not_allowed_error", + "NotFoundError": ".not_found_error", + "UnauthorizedError": ".unauthorized_error", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "AccessDeniedError", + "Error", + "MethodNotAllowedError", + "NotFoundError", + "UnauthorizedError", +] diff --git a/langfuse/api/commons/errors/access_denied_error.py b/langfuse/api/commons/errors/access_denied_error.py new file mode 100644 index 000000000..156403fb7 --- /dev/null +++ b/langfuse/api/commons/errors/access_denied_error.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class AccessDeniedError(ApiError): + def __init__( + self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None + ): + super().__init__(status_code=403, headers=headers, body=body) diff --git a/langfuse/api/commons/errors/error.py b/langfuse/api/commons/errors/error.py new file mode 100644 index 000000000..5a8bd9639 --- /dev/null +++ b/langfuse/api/commons/errors/error.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class Error(ApiError): + def __init__( + self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None + ): + super().__init__(status_code=400, headers=headers, body=body) diff --git a/langfuse/api/commons/errors/method_not_allowed_error.py b/langfuse/api/commons/errors/method_not_allowed_error.py new file mode 100644 index 000000000..436dd29dd --- /dev/null +++ b/langfuse/api/commons/errors/method_not_allowed_error.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class MethodNotAllowedError(ApiError): + def __init__( + self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None + ): + super().__init__(status_code=405, headers=headers, body=body) diff --git a/langfuse/api/commons/errors/not_found_error.py b/langfuse/api/commons/errors/not_found_error.py new file mode 100644 index 000000000..66b5bfc55 --- /dev/null +++ b/langfuse/api/commons/errors/not_found_error.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class NotFoundError(ApiError): + def __init__( + self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None + ): + super().__init__(status_code=404, headers=headers, body=body) diff --git a/langfuse/api/commons/errors/unauthorized_error.py b/langfuse/api/commons/errors/unauthorized_error.py new file mode 100644 index 000000000..e71a01c5d --- /dev/null +++ b/langfuse/api/commons/errors/unauthorized_error.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class UnauthorizedError(ApiError): + def __init__( + self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None + ): + super().__init__(status_code=401, headers=headers, body=body) diff --git a/langfuse/api/commons/types/__init__.py b/langfuse/api/commons/types/__init__.py new file mode 100644 index 000000000..f4ebf4317 --- /dev/null +++ b/langfuse/api/commons/types/__init__.py @@ -0,0 +1,173 @@ +# 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 .base_score import BaseScore + from .base_score_v1 import BaseScoreV1 + from .boolean_score import BooleanScore + from .boolean_score_v1 import BooleanScoreV1 + from .categorical_score import CategoricalScore + from .categorical_score_v1 import CategoricalScoreV1 + from .comment import Comment + from .comment_object_type import CommentObjectType + from .config_category import ConfigCategory + from .create_score_value import CreateScoreValue + from .dataset import Dataset + from .dataset_item import DatasetItem + from .dataset_run import DatasetRun + from .dataset_run_item import DatasetRunItem + from .dataset_run_with_items import DatasetRunWithItems + from .dataset_status import DatasetStatus + from .map_value import MapValue + from .model import Model + from .model_price import ModelPrice + from .model_usage_unit import ModelUsageUnit + from .numeric_score import NumericScore + from .numeric_score_v1 import NumericScoreV1 + from .observation import Observation + from .observation_level import ObservationLevel + from .observations_view import ObservationsView + from .pricing_tier import PricingTier + from .pricing_tier_condition import PricingTierCondition + from .pricing_tier_input import PricingTierInput + from .pricing_tier_operator import PricingTierOperator + from .score import Score, Score_Boolean, Score_Categorical, Score_Numeric + from .score_config import ScoreConfig + from .score_data_type import ScoreDataType + from .score_source import ScoreSource + from .score_v1 import ScoreV1, ScoreV1_Boolean, ScoreV1_Categorical, ScoreV1_Numeric + from .session import Session + from .session_with_traces import SessionWithTraces + from .trace import Trace + from .trace_with_details import TraceWithDetails + from .trace_with_full_details import TraceWithFullDetails + from .usage import Usage +_dynamic_imports: typing.Dict[str, str] = { + "BaseScore": ".base_score", + "BaseScoreV1": ".base_score_v1", + "BooleanScore": ".boolean_score", + "BooleanScoreV1": ".boolean_score_v1", + "CategoricalScore": ".categorical_score", + "CategoricalScoreV1": ".categorical_score_v1", + "Comment": ".comment", + "CommentObjectType": ".comment_object_type", + "ConfigCategory": ".config_category", + "CreateScoreValue": ".create_score_value", + "Dataset": ".dataset", + "DatasetItem": ".dataset_item", + "DatasetRun": ".dataset_run", + "DatasetRunItem": ".dataset_run_item", + "DatasetRunWithItems": ".dataset_run_with_items", + "DatasetStatus": ".dataset_status", + "MapValue": ".map_value", + "Model": ".model", + "ModelPrice": ".model_price", + "ModelUsageUnit": ".model_usage_unit", + "NumericScore": ".numeric_score", + "NumericScoreV1": ".numeric_score_v1", + "Observation": ".observation", + "ObservationLevel": ".observation_level", + "ObservationsView": ".observations_view", + "PricingTier": ".pricing_tier", + "PricingTierCondition": ".pricing_tier_condition", + "PricingTierInput": ".pricing_tier_input", + "PricingTierOperator": ".pricing_tier_operator", + "Score": ".score", + "ScoreConfig": ".score_config", + "ScoreDataType": ".score_data_type", + "ScoreSource": ".score_source", + "ScoreV1": ".score_v1", + "ScoreV1_Boolean": ".score_v1", + "ScoreV1_Categorical": ".score_v1", + "ScoreV1_Numeric": ".score_v1", + "Score_Boolean": ".score", + "Score_Categorical": ".score", + "Score_Numeric": ".score", + "Session": ".session", + "SessionWithTraces": ".session_with_traces", + "Trace": ".trace", + "TraceWithDetails": ".trace_with_details", + "TraceWithFullDetails": ".trace_with_full_details", + "Usage": ".usage", +} + + +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__ = [ + "BaseScore", + "BaseScoreV1", + "BooleanScore", + "BooleanScoreV1", + "CategoricalScore", + "CategoricalScoreV1", + "Comment", + "CommentObjectType", + "ConfigCategory", + "CreateScoreValue", + "Dataset", + "DatasetItem", + "DatasetRun", + "DatasetRunItem", + "DatasetRunWithItems", + "DatasetStatus", + "MapValue", + "Model", + "ModelPrice", + "ModelUsageUnit", + "NumericScore", + "NumericScoreV1", + "Observation", + "ObservationLevel", + "ObservationsView", + "PricingTier", + "PricingTierCondition", + "PricingTierInput", + "PricingTierOperator", + "Score", + "ScoreConfig", + "ScoreDataType", + "ScoreSource", + "ScoreV1", + "ScoreV1_Boolean", + "ScoreV1_Categorical", + "ScoreV1_Numeric", + "Score_Boolean", + "Score_Categorical", + "Score_Numeric", + "Session", + "SessionWithTraces", + "Trace", + "TraceWithDetails", + "TraceWithFullDetails", + "Usage", +] diff --git a/langfuse/api/commons/types/base_score.py b/langfuse/api/commons/types/base_score.py new file mode 100644 index 000000000..8115d768a --- /dev/null +++ b/langfuse/api/commons/types/base_score.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .score_source import ScoreSource + + +class BaseScore(UniversalBaseModel): + id: str + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + source: ScoreSource + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = pydantic.Field(default=None) + """ + Reference a score config on a score. When set, config and score name must be equal and value must comply to optionally defined numerical range + """ + + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = pydantic.Field(default=None) + """ + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + """ + + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + The environment from which this score originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/base_score_v1.py b/langfuse/api/commons/types/base_score_v1.py new file mode 100644 index 000000000..ec805424d --- /dev/null +++ b/langfuse/api/commons/types/base_score_v1.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .score_source import ScoreSource + + +class BaseScoreV1(UniversalBaseModel): + id: str + trace_id: typing_extensions.Annotated[str, FieldMetadata(alias="traceId")] + name: str + source: ScoreSource + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = pydantic.Field(default=None) + """ + Reference a score config on a score. When set, config and score name must be equal and value must comply to optionally defined numerical range + """ + + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = pydantic.Field(default=None) + """ + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + """ + + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + The environment from which this score originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/boolean_score.py b/langfuse/api/commons/types/boolean_score.py new file mode 100644 index 000000000..2f65cf338 --- /dev/null +++ b/langfuse/api/commons/types/boolean_score.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .base_score import BaseScore + + +class BooleanScore(BaseScore): + value: float = pydantic.Field() + """ + The numeric value of the score. Equals 1 for "True" and 0 for "False" + """ + + string_value: typing_extensions.Annotated[ + str, FieldMetadata(alias="stringValue") + ] = pydantic.Field() + """ + The string representation of the score value. Is inferred from the numeric value and equals "True" or "False" + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/boolean_score_v1.py b/langfuse/api/commons/types/boolean_score_v1.py new file mode 100644 index 000000000..cf5425255 --- /dev/null +++ b/langfuse/api/commons/types/boolean_score_v1.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .base_score_v1 import BaseScoreV1 + + +class BooleanScoreV1(BaseScoreV1): + value: float = pydantic.Field() + """ + The numeric value of the score. Equals 1 for "True" and 0 for "False" + """ + + string_value: typing_extensions.Annotated[ + str, FieldMetadata(alias="stringValue") + ] = pydantic.Field() + """ + The string representation of the score value. Is inferred from the numeric value and equals "True" or "False" + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/categorical_score.py b/langfuse/api/commons/types/categorical_score.py new file mode 100644 index 000000000..a12ac58c3 --- /dev/null +++ b/langfuse/api/commons/types/categorical_score.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .base_score import BaseScore + + +class CategoricalScore(BaseScore): + value: float = pydantic.Field() + """ + Represents the numeric category mapping of the stringValue. If no config is linked, defaults to 0. + """ + + string_value: typing_extensions.Annotated[ + str, FieldMetadata(alias="stringValue") + ] = pydantic.Field() + """ + The string representation of the score value. If no config is linked, can be any string. Otherwise, must map to a config category + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/categorical_score_v1.py b/langfuse/api/commons/types/categorical_score_v1.py new file mode 100644 index 000000000..8f98af1a8 --- /dev/null +++ b/langfuse/api/commons/types/categorical_score_v1.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .base_score_v1 import BaseScoreV1 + + +class CategoricalScoreV1(BaseScoreV1): + value: float = pydantic.Field() + """ + Represents the numeric category mapping of the stringValue. If no config is linked, defaults to 0. + """ + + string_value: typing_extensions.Annotated[ + str, FieldMetadata(alias="stringValue") + ] = pydantic.Field() + """ + The string representation of the score value. If no config is linked, can be any string. Otherwise, must map to a config category + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/comment.py b/langfuse/api/commons/types/comment.py new file mode 100644 index 000000000..d1926ba5d --- /dev/null +++ b/langfuse/api/commons/types/comment.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .comment_object_type import CommentObjectType + + +class Comment(UniversalBaseModel): + id: str + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + object_type: typing_extensions.Annotated[ + CommentObjectType, FieldMetadata(alias="objectType") + ] + object_id: typing_extensions.Annotated[str, FieldMetadata(alias="objectId")] + content: str + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/comment_object_type.py b/langfuse/api/commons/types/comment_object_type.py similarity index 92% rename from langfuse/api/resources/commons/types/comment_object_type.py rename to langfuse/api/commons/types/comment_object_type.py index 9c6c134c6..fbd0d4e49 100644 --- a/langfuse/api/resources/commons/types/comment_object_type.py +++ b/langfuse/api/commons/types/comment_object_type.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class CommentObjectType(str, enum.Enum): +class CommentObjectType(enum.StrEnum): TRACE = "TRACE" OBSERVATION = "OBSERVATION" SESSION = "SESSION" diff --git a/langfuse/api/commons/types/config_category.py b/langfuse/api/commons/types/config_category.py new file mode 100644 index 000000000..4ca546e35 --- /dev/null +++ b/langfuse/api/commons/types/config_category.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ConfigCategory(UniversalBaseModel): + value: float + label: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/create_score_value.py b/langfuse/api/commons/types/create_score_value.py similarity index 100% rename from langfuse/api/resources/commons/types/create_score_value.py rename to langfuse/api/commons/types/create_score_value.py diff --git a/langfuse/api/commons/types/dataset.py b/langfuse/api/commons/types/dataset.py new file mode 100644 index 000000000..296b26d7c --- /dev/null +++ b/langfuse/api/commons/types/dataset.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class Dataset(UniversalBaseModel): + id: str + name: str + description: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + input_schema: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="inputSchema") + ] = pydantic.Field(default=None) + """ + JSON Schema for validating dataset item inputs + """ + + expected_output_schema: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="expectedOutputSchema") + ] = pydantic.Field(default=None) + """ + JSON Schema for validating dataset item expected outputs + """ + + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/dataset_item.py b/langfuse/api/commons/types/dataset_item.py new file mode 100644 index 000000000..d3970eb9a --- /dev/null +++ b/langfuse/api/commons/types/dataset_item.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .dataset_status import DatasetStatus + + +class DatasetItem(UniversalBaseModel): + id: str + status: DatasetStatus + input: typing.Optional[typing.Any] = None + expected_output: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="expectedOutput") + ] = None + metadata: typing.Optional[typing.Any] = None + source_trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sourceTraceId") + ] = None + source_observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sourceObservationId") + ] = None + dataset_id: typing_extensions.Annotated[str, FieldMetadata(alias="datasetId")] + dataset_name: typing_extensions.Annotated[str, FieldMetadata(alias="datasetName")] + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/dataset_run.py b/langfuse/api/commons/types/dataset_run.py new file mode 100644 index 000000000..2cf159ca9 --- /dev/null +++ b/langfuse/api/commons/types/dataset_run.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class DatasetRun(UniversalBaseModel): + id: str = pydantic.Field() + """ + Unique identifier of the dataset run + """ + + name: str = pydantic.Field() + """ + Name of the dataset run + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Description of the run + """ + + metadata: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + Metadata of the dataset run + """ + + dataset_id: typing_extensions.Annotated[str, FieldMetadata(alias="datasetId")] = ( + pydantic.Field() + ) + """ + Id of the associated dataset + """ + + dataset_name: typing_extensions.Annotated[ + str, FieldMetadata(alias="datasetName") + ] = pydantic.Field() + """ + Name of the associated dataset + """ + + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] = pydantic.Field() + """ + The date and time when the dataset run was created + """ + + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] = pydantic.Field() + """ + The date and time when the dataset run was last updated + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/dataset_run_item.py b/langfuse/api/commons/types/dataset_run_item.py new file mode 100644 index 000000000..0093ffdca --- /dev/null +++ b/langfuse/api/commons/types/dataset_run_item.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class DatasetRunItem(UniversalBaseModel): + id: str + dataset_run_id: typing_extensions.Annotated[ + str, FieldMetadata(alias="datasetRunId") + ] + dataset_run_name: typing_extensions.Annotated[ + str, FieldMetadata(alias="datasetRunName") + ] + dataset_item_id: typing_extensions.Annotated[ + str, FieldMetadata(alias="datasetItemId") + ] + trace_id: typing_extensions.Annotated[str, FieldMetadata(alias="traceId")] + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/dataset_run_with_items.py b/langfuse/api/commons/types/dataset_run_with_items.py new file mode 100644 index 000000000..b5995dd30 --- /dev/null +++ b/langfuse/api/commons/types/dataset_run_with_items.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .dataset_run import DatasetRun +from .dataset_run_item import DatasetRunItem + + +class DatasetRunWithItems(DatasetRun): + dataset_run_items: typing_extensions.Annotated[ + typing.List[DatasetRunItem], FieldMetadata(alias="datasetRunItems") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/dataset_status.py b/langfuse/api/commons/types/dataset_status.py similarity index 88% rename from langfuse/api/resources/commons/types/dataset_status.py rename to langfuse/api/commons/types/dataset_status.py index 09eac62fe..f8e681aeb 100644 --- a/langfuse/api/resources/commons/types/dataset_status.py +++ b/langfuse/api/commons/types/dataset_status.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class DatasetStatus(str, enum.Enum): +class DatasetStatus(enum.StrEnum): ACTIVE = "ACTIVE" ARCHIVED = "ARCHIVED" diff --git a/langfuse/api/resources/commons/types/map_value.py b/langfuse/api/commons/types/map_value.py similarity index 88% rename from langfuse/api/resources/commons/types/map_value.py rename to langfuse/api/commons/types/map_value.py index e1e771a9b..46115a967 100644 --- a/langfuse/api/resources/commons/types/map_value.py +++ b/langfuse/api/commons/types/map_value.py @@ -5,6 +5,7 @@ MapValue = typing.Union[ typing.Optional[str], typing.Optional[int], + typing.Optional[float], typing.Optional[bool], typing.Optional[typing.List[str]], ] diff --git a/langfuse/api/resources/commons/types/model.py b/langfuse/api/commons/types/model.py similarity index 55% rename from langfuse/api/resources/commons/types/model.py rename to langfuse/api/commons/types/model.py index 1b83c2696..2a722e095 100644 --- a/langfuse/api/resources/commons/types/model.py +++ b/langfuse/api/commons/types/model.py @@ -3,14 +3,16 @@ import datetime as dt import typing -from ....core.datetime_utils import serialize_datetime -from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from .model_price import ModelPrice from .model_usage_unit import ModelUsageUnit from .pricing_tier import PricingTier -class Model(pydantic_v1.BaseModel): +class Model(UniversalBaseModel): """ Model definition used for transforming usage into USD cost and/or tokenization. @@ -24,70 +26,78 @@ class Model(pydantic_v1.BaseModel): """ id: str - model_name: str = pydantic_v1.Field(alias="modelName") + model_name: typing_extensions.Annotated[str, FieldMetadata(alias="modelName")] = ( + pydantic.Field() + ) """ Name of the model definition. If multiple with the same name exist, they are applied in the following order: (1) custom over built-in, (2) newest according to startTime where model.startTime 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} + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/model_price.py b/langfuse/api/commons/types/model_price.py new file mode 100644 index 000000000..a2e2288fa --- /dev/null +++ b/langfuse/api/commons/types/model_price.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ModelPrice(UniversalBaseModel): + price: float + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/model_usage_unit.py b/langfuse/api/commons/types/model_usage_unit.py similarity index 94% rename from langfuse/api/resources/commons/types/model_usage_unit.py rename to langfuse/api/commons/types/model_usage_unit.py index 35253f92e..281223960 100644 --- a/langfuse/api/resources/commons/types/model_usage_unit.py +++ b/langfuse/api/commons/types/model_usage_unit.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class ModelUsageUnit(str, enum.Enum): +class ModelUsageUnit(enum.StrEnum): """ Unit of usage in Langfuse """ diff --git a/langfuse/api/commons/types/numeric_score.py b/langfuse/api/commons/types/numeric_score.py new file mode 100644 index 000000000..63ced82ab --- /dev/null +++ b/langfuse/api/commons/types/numeric_score.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_score import BaseScore + + +class NumericScore(BaseScore): + value: float = pydantic.Field() + """ + The numeric value of the score + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/numeric_score_v1.py b/langfuse/api/commons/types/numeric_score_v1.py new file mode 100644 index 000000000..872c89aca --- /dev/null +++ b/langfuse/api/commons/types/numeric_score_v1.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_score_v1 import BaseScoreV1 + + +class NumericScoreV1(BaseScoreV1): + value: float = pydantic.Field() + """ + The numeric value of the score + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/observation.py b/langfuse/api/commons/types/observation.py new file mode 100644 index 000000000..814e0c7f0 --- /dev/null +++ b/langfuse/api/commons/types/observation.py @@ -0,0 +1,144 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .map_value import MapValue +from .observation_level import ObservationLevel +from .usage import Usage + + +class Observation(UniversalBaseModel): + id: str = pydantic.Field() + """ + The unique identifier of the observation + """ + + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = pydantic.Field(default=None) + """ + The trace ID associated with the observation + """ + + type: str = pydantic.Field() + """ + The type of the observation + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the observation + """ + + start_time: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="startTime") + ] = pydantic.Field() + """ + The start time of the observation + """ + + end_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="endTime") + ] = pydantic.Field(default=None) + """ + The end time of the observation. + """ + + completion_start_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="completionStartTime") + ] = pydantic.Field(default=None) + """ + The completion start time of the observation + """ + + model: typing.Optional[str] = pydantic.Field(default=None) + """ + The model used for the observation + """ + + model_parameters: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, MapValue]], + FieldMetadata(alias="modelParameters"), + ] = pydantic.Field(default=None) + """ + The parameters of the model used for the observation + """ + + input: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + The input data of the observation + """ + + version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the observation + """ + + metadata: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + Additional metadata of the observation + """ + + output: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + The output data of the observation + """ + + usage: typing.Optional[Usage] = pydantic.Field(default=None) + """ + (Deprecated. Use usageDetails and costDetails instead.) The usage data of the observation + """ + + level: ObservationLevel = pydantic.Field() + """ + The level of the observation + """ + + status_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="statusMessage") + ] = pydantic.Field(default=None) + """ + The status message of the observation + """ + + parent_observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="parentObservationId") + ] = pydantic.Field(default=None) + """ + The parent observation ID + """ + + prompt_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="promptId") + ] = pydantic.Field(default=None) + """ + The prompt ID associated with the observation + """ + + usage_details: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, int]], FieldMetadata(alias="usageDetails") + ] = pydantic.Field(default=None) + """ + The usage details of the observation. Key is the name of the usage metric, value is the number of units consumed. The total key is the sum of all (non-total) usage metrics or the total value ingested. + """ + + cost_details: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, float]], FieldMetadata(alias="costDetails") + ] = pydantic.Field(default=None) + """ + The cost details of the observation. Key is the name of the cost metric, value is the cost in USD. The total key is the sum of all (non-total) cost metrics or the total value ingested. + """ + + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + The environment from which this observation originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/observation_level.py b/langfuse/api/commons/types/observation_level.py similarity index 91% rename from langfuse/api/resources/commons/types/observation_level.py rename to langfuse/api/commons/types/observation_level.py index c33e87b59..8629ef31d 100644 --- a/langfuse/api/resources/commons/types/observation_level.py +++ b/langfuse/api/commons/types/observation_level.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class ObservationLevel(str, enum.Enum): +class ObservationLevel(enum.StrEnum): DEBUG = "DEBUG" DEFAULT = "DEFAULT" WARNING = "WARNING" diff --git a/langfuse/api/commons/types/observations_view.py b/langfuse/api/commons/types/observations_view.py new file mode 100644 index 000000000..fc97c27e7 --- /dev/null +++ b/langfuse/api/commons/types/observations_view.py @@ -0,0 +1,89 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .observation import Observation + + +class ObservationsView(Observation): + prompt_name: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="promptName") + ] = pydantic.Field(default=None) + """ + The name of the prompt associated with the observation + """ + + prompt_version: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="promptVersion") + ] = pydantic.Field(default=None) + """ + The version of the prompt associated with the observation + """ + + model_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="modelId") + ] = pydantic.Field(default=None) + """ + The unique identifier of the model + """ + + input_price: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="inputPrice") + ] = pydantic.Field(default=None) + """ + The price of the input in USD + """ + + output_price: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="outputPrice") + ] = pydantic.Field(default=None) + """ + The price of the output in USD. + """ + + total_price: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="totalPrice") + ] = pydantic.Field(default=None) + """ + The total price in USD. + """ + + calculated_input_cost: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="calculatedInputCost") + ] = pydantic.Field(default=None) + """ + (Deprecated. Use usageDetails and costDetails instead.) The calculated cost of the input in USD + """ + + calculated_output_cost: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="calculatedOutputCost") + ] = pydantic.Field(default=None) + """ + (Deprecated. Use usageDetails and costDetails instead.) The calculated cost of the output in USD + """ + + calculated_total_cost: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="calculatedTotalCost") + ] = pydantic.Field(default=None) + """ + (Deprecated. Use usageDetails and costDetails instead.) The calculated total cost in USD + """ + + latency: typing.Optional[float] = pydantic.Field(default=None) + """ + The latency in seconds. + """ + + time_to_first_token: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="timeToFirstToken") + ] = pydantic.Field(default=None) + """ + The time to the first token in seconds + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/pricing_tier.py b/langfuse/api/commons/types/pricing_tier.py similarity index 66% rename from langfuse/api/resources/commons/types/pricing_tier.py rename to langfuse/api/commons/types/pricing_tier.py index 031d142c0..49b99096b 100644 --- a/langfuse/api/resources/commons/types/pricing_tier.py +++ b/langfuse/api/commons/types/pricing_tier.py @@ -1,14 +1,15 @@ # 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 +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from .pricing_tier_condition import PricingTierCondition -class PricingTier(pydantic_v1.BaseModel): +class PricingTier(UniversalBaseModel): """ Pricing tier definition with conditional pricing based on usage thresholds. @@ -29,19 +30,21 @@ class PricingTier(pydantic_v1.BaseModel): Every model must have exactly one default tier to ensure cost calculation always succeeds. """ - id: str = pydantic_v1.Field() + id: str = pydantic.Field() """ Unique identifier for the pricing tier """ - name: str = pydantic_v1.Field() + name: str = pydantic.Field() """ Name of the pricing tier for display and identification purposes. Examples: "Standard", "High Volume Tier", "Large Context", "Extended Context Tier" """ - is_default: bool = pydantic_v1.Field(alias="isDefault") + is_default: typing_extensions.Annotated[bool, FieldMetadata(alias="isDefault")] = ( + pydantic.Field() + ) """ Whether this is the default tier. Every model must have exactly one default tier with priority 0 and no conditions. @@ -49,7 +52,7 @@ class PricingTier(pydantic_v1.BaseModel): It typically represents the base pricing for standard usage patterns. """ - priority: int = pydantic_v1.Field() + priority: int = pydantic.Field() """ Priority for tier matching evaluation. Lower numbers = higher priority (evaluated first). @@ -63,7 +66,7 @@ class PricingTier(pydantic_v1.BaseModel): This ensures more specific conditions are checked before general ones. """ - conditions: typing.List[PricingTierCondition] = pydantic_v1.Field() + conditions: typing.List[PricingTierCondition] = pydantic.Field() """ Array of conditions that must ALL be met for this tier to match (AND logic). @@ -73,7 +76,7 @@ class PricingTier(pydantic_v1.BaseModel): Multiple conditions enable complex matching scenarios (e.g., "high input tokens AND low output tokens"). """ - prices: typing.Dict[str, float] = pydantic_v1.Field() + prices: typing.Dict[str, float] = pydantic.Field() """ Prices (USD) by usage type for this tier. @@ -83,35 +86,6 @@ class PricingTier(pydantic_v1.BaseModel): Example: {"input": 0.000003, "output": 0.000015} means $3 per million input tokens and $15 per million output tokens. """ - 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} + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/pricing_tier_condition.py b/langfuse/api/commons/types/pricing_tier_condition.py similarity index 57% rename from langfuse/api/resources/commons/types/pricing_tier_condition.py rename to langfuse/api/commons/types/pricing_tier_condition.py index 8b89fe116..9ebbba0af 100644 --- a/langfuse/api/resources/commons/types/pricing_tier_condition.py +++ b/langfuse/api/commons/types/pricing_tier_condition.py @@ -1,14 +1,15 @@ # 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 +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from .pricing_tier_operator import PricingTierOperator -class PricingTierCondition(pydantic_v1.BaseModel): +class PricingTierCondition(UniversalBaseModel): """ Condition for matching a pricing tier based on usage details. Used to implement tiered pricing models where costs vary based on usage thresholds. @@ -24,7 +25,9 @@ class PricingTierCondition(pydantic_v1.BaseModel): - Volume-based pricing: Different rates based on total request or token count """ - usage_detail_pattern: str = pydantic_v1.Field(alias="usageDetailPattern") + usage_detail_pattern: typing_extensions.Annotated[ + str, FieldMetadata(alias="usageDetailPattern") + ] = pydantic.Field() """ Regex pattern to match against usage detail keys. All matching keys' values are summed for threshold comparison. @@ -36,7 +39,7 @@ class PricingTierCondition(pydantic_v1.BaseModel): The pattern is case-insensitive by default. If no keys match, the sum is treated as zero. """ - operator: PricingTierOperator = pydantic_v1.Field() + operator: PricingTierOperator = pydantic.Field() """ Comparison operator to apply between the summed value and the threshold. @@ -48,45 +51,18 @@ class PricingTierCondition(pydantic_v1.BaseModel): - neq: not equal (sum != threshold) """ - value: float = pydantic_v1.Field() + value: float = pydantic.Field() """ Threshold value for comparison. For token-based pricing, this is typically the token count threshold (e.g., 200000 for a 200K token threshold). """ - case_sensitive: bool = pydantic_v1.Field(alias="caseSensitive") + case_sensitive: typing_extensions.Annotated[ + bool, FieldMetadata(alias="caseSensitive") + ] = pydantic.Field() """ Whether the regex pattern matching is case-sensitive. Default is false (case-insensitive matching). """ - 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} + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/pricing_tier_input.py b/langfuse/api/commons/types/pricing_tier_input.py similarity index 59% rename from langfuse/api/resources/commons/types/pricing_tier_input.py rename to langfuse/api/commons/types/pricing_tier_input.py index 00dfdb37b..c25d7f716 100644 --- a/langfuse/api/resources/commons/types/pricing_tier_input.py +++ b/langfuse/api/commons/types/pricing_tier_input.py @@ -1,14 +1,15 @@ # 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 +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from .pricing_tier_condition import PricingTierCondition -class PricingTierInput(pydantic_v1.BaseModel): +class PricingTierInput(UniversalBaseModel): """ Input schema for creating a pricing tier. The tier ID will be automatically generated server-side. @@ -21,14 +22,16 @@ class PricingTierInput(pydantic_v1.BaseModel): See PricingTier for detailed information about how tiers work and why they're useful. """ - name: str = pydantic_v1.Field() + name: str = pydantic.Field() """ Name of the pricing tier for display and identification purposes. Must be unique within the model. Common patterns: "Standard", "High Volume Tier", "Extended Context" """ - is_default: bool = pydantic_v1.Field(alias="isDefault") + is_default: typing_extensions.Annotated[bool, FieldMetadata(alias="isDefault")] = ( + pydantic.Field() + ) """ Whether this is the default tier. Exactly one tier per model must be marked as default. @@ -40,7 +43,7 @@ class PricingTierInput(pydantic_v1.BaseModel): The default tier acts as a fallback when no conditional tiers match. """ - priority: int = pydantic_v1.Field() + priority: int = pydantic.Field() """ Priority for tier matching evaluation. Lower numbers = higher priority (evaluated first). @@ -48,7 +51,7 @@ class PricingTierInput(pydantic_v1.BaseModel): Conditional tiers should use priority 1, 2, 3, etc. based on their specificity. """ - conditions: typing.List[PricingTierCondition] = pydantic_v1.Field() + conditions: typing.List[PricingTierCondition] = pydantic.Field() """ Array of conditions that must ALL be met for this tier to match (AND logic). @@ -58,7 +61,7 @@ class PricingTierInput(pydantic_v1.BaseModel): Each condition specifies a regex pattern, operator, and threshold value for matching against usage details. """ - prices: typing.Dict[str, float] = pydantic_v1.Field() + prices: typing.Dict[str, float] = pydantic.Field() """ Prices (USD) by usage type for this tier. At least one price must be defined. @@ -68,35 +71,6 @@ class PricingTierInput(pydantic_v1.BaseModel): Example: {"input": 0.000003, "output": 0.000015} represents $3 per million input tokens and $15 per million output tokens. """ - 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} + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/pricing_tier_operator.py b/langfuse/api/commons/types/pricing_tier_operator.py similarity index 93% rename from langfuse/api/resources/commons/types/pricing_tier_operator.py rename to langfuse/api/commons/types/pricing_tier_operator.py index c5af10199..6d40799ec 100644 --- a/langfuse/api/resources/commons/types/pricing_tier_operator.py +++ b/langfuse/api/commons/types/pricing_tier_operator.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class PricingTierOperator(str, enum.Enum): +class PricingTierOperator(enum.StrEnum): """ Comparison operators for pricing tier conditions """ diff --git a/langfuse/api/commons/types/score.py b/langfuse/api/commons/types/score.py new file mode 100644 index 000000000..777b6874e --- /dev/null +++ b/langfuse/api/commons/types/score.py @@ -0,0 +1,155 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .score_source import ScoreSource + + +class Score_Numeric(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["NUMERIC"], FieldMetadata(alias="dataType") + ] = "NUMERIC" + value: float + id: str + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + source: ScoreSource + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class Score_Categorical(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["CATEGORICAL"], FieldMetadata(alias="dataType") + ] = "CATEGORICAL" + value: float + string_value: typing_extensions.Annotated[str, FieldMetadata(alias="stringValue")] + id: str + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + source: ScoreSource + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class Score_Boolean(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["BOOLEAN"], FieldMetadata(alias="dataType") + ] = "BOOLEAN" + value: float + string_value: typing_extensions.Annotated[str, FieldMetadata(alias="stringValue")] + id: str + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + source: ScoreSource + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +Score = typing_extensions.Annotated[ + typing.Union[Score_Numeric, Score_Categorical, Score_Boolean], + pydantic.Field(discriminator="data_type"), +] diff --git a/langfuse/api/commons/types/score_config.py b/langfuse/api/commons/types/score_config.py new file mode 100644 index 000000000..2cd30b585 --- /dev/null +++ b/langfuse/api/commons/types/score_config.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .config_category import ConfigCategory +from .score_data_type import ScoreDataType + + +class ScoreConfig(UniversalBaseModel): + """ + Configuration for a score + """ + + id: str + name: str + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] + data_type: typing_extensions.Annotated[ + ScoreDataType, FieldMetadata(alias="dataType") + ] + is_archived: typing_extensions.Annotated[ + bool, FieldMetadata(alias="isArchived") + ] = pydantic.Field() + """ + Whether the score config is archived. Defaults to false + """ + + min_value: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="minValue") + ] = pydantic.Field(default=None) + """ + Sets minimum value for numerical scores. If not set, the minimum value defaults to -∞ + """ + + max_value: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="maxValue") + ] = pydantic.Field(default=None) + """ + Sets maximum value for numerical scores. If not set, the maximum value defaults to +∞ + """ + + categories: typing.Optional[typing.List[ConfigCategory]] = pydantic.Field( + default=None + ) + """ + Configures custom categories for categorical scores + """ + + description: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/commons/types/score_data_type.py b/langfuse/api/commons/types/score_data_type.py similarity index 91% rename from langfuse/api/resources/commons/types/score_data_type.py rename to langfuse/api/commons/types/score_data_type.py index c2eed12cd..a94d68d7c 100644 --- a/langfuse/api/resources/commons/types/score_data_type.py +++ b/langfuse/api/commons/types/score_data_type.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class ScoreDataType(str, enum.Enum): +class ScoreDataType(enum.StrEnum): NUMERIC = "NUMERIC" BOOLEAN = "BOOLEAN" CATEGORICAL = "CATEGORICAL" diff --git a/langfuse/api/resources/commons/types/score_source.py b/langfuse/api/commons/types/score_source.py similarity index 90% rename from langfuse/api/resources/commons/types/score_source.py rename to langfuse/api/commons/types/score_source.py index 699f078b7..ee1398a9a 100644 --- a/langfuse/api/resources/commons/types/score_source.py +++ b/langfuse/api/commons/types/score_source.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class ScoreSource(str, enum.Enum): +class ScoreSource(enum.StrEnum): ANNOTATION = "ANNOTATION" API = "API" EVAL = "EVAL" diff --git a/langfuse/api/commons/types/score_v1.py b/langfuse/api/commons/types/score_v1.py new file mode 100644 index 000000000..85b843b35 --- /dev/null +++ b/langfuse/api/commons/types/score_v1.py @@ -0,0 +1,131 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .score_source import ScoreSource + + +class ScoreV1_Numeric(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["NUMERIC"], FieldMetadata(alias="dataType") + ] = "NUMERIC" + value: float + id: str + trace_id: typing_extensions.Annotated[str, FieldMetadata(alias="traceId")] + name: str + source: ScoreSource + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class ScoreV1_Categorical(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["CATEGORICAL"], FieldMetadata(alias="dataType") + ] = "CATEGORICAL" + value: float + string_value: typing_extensions.Annotated[str, FieldMetadata(alias="stringValue")] + id: str + trace_id: typing_extensions.Annotated[str, FieldMetadata(alias="traceId")] + name: str + source: ScoreSource + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class ScoreV1_Boolean(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["BOOLEAN"], FieldMetadata(alias="dataType") + ] = "BOOLEAN" + value: float + string_value: typing_extensions.Annotated[str, FieldMetadata(alias="stringValue")] + id: str + trace_id: typing_extensions.Annotated[str, FieldMetadata(alias="traceId")] + name: str + source: ScoreSource + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +ScoreV1 = typing_extensions.Annotated[ + typing.Union[ScoreV1_Numeric, ScoreV1_Categorical, ScoreV1_Boolean], + pydantic.Field(discriminator="data_type"), +] diff --git a/langfuse/api/commons/types/session.py b/langfuse/api/commons/types/session.py new file mode 100644 index 000000000..ccb21a9ad --- /dev/null +++ b/langfuse/api/commons/types/session.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class Session(UniversalBaseModel): + id: str + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + The environment from which this session originated. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/session_with_traces.py b/langfuse/api/commons/types/session_with_traces.py new file mode 100644 index 000000000..d04eef54e --- /dev/null +++ b/langfuse/api/commons/types/session_with_traces.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .session import Session +from .trace import Trace + + +class SessionWithTraces(Session): + traces: typing.List[Trace] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/trace.py b/langfuse/api/commons/types/trace.py new file mode 100644 index 000000000..fb4555d49 --- /dev/null +++ b/langfuse/api/commons/types/trace.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class Trace(UniversalBaseModel): + id: str = pydantic.Field() + """ + The unique identifier of a trace + """ + + timestamp: dt.datetime = pydantic.Field() + """ + The timestamp when the trace was created + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the trace + """ + + input: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + The input data of the trace. Can be any JSON. + """ + + output: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + The output data of the trace. Can be any JSON. + """ + + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = pydantic.Field(default=None) + """ + The session identifier associated with the trace + """ + + release: typing.Optional[str] = pydantic.Field(default=None) + """ + The release version of the application when the trace was created + """ + + version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the trace + """ + + user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="userId") + ] = pydantic.Field(default=None) + """ + The user identifier associated with the trace + """ + + metadata: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + The metadata associated with the trace. Can be any JSON. + """ + + tags: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The tags associated with the trace. Can be an array of strings or null. + """ + + public: typing.Optional[bool] = pydantic.Field(default=None) + """ + Public traces are accessible via url without login + """ + + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + The environment from which this trace originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/trace_with_details.py b/langfuse/api/commons/types/trace_with_details.py new file mode 100644 index 000000000..df4e12da5 --- /dev/null +++ b/langfuse/api/commons/types/trace_with_details.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .trace import Trace + + +class TraceWithDetails(Trace): + html_path: typing_extensions.Annotated[str, FieldMetadata(alias="htmlPath")] = ( + pydantic.Field() + ) + """ + Path of trace in Langfuse UI + """ + + latency: float = pydantic.Field() + """ + Latency of trace in seconds + """ + + total_cost: typing_extensions.Annotated[float, FieldMetadata(alias="totalCost")] = ( + pydantic.Field() + ) + """ + Cost of trace in USD + """ + + observations: typing.List[str] = pydantic.Field() + """ + List of observation ids + """ + + scores: typing.List[str] = pydantic.Field() + """ + List of score ids + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/trace_with_full_details.py b/langfuse/api/commons/types/trace_with_full_details.py new file mode 100644 index 000000000..288ce8624 --- /dev/null +++ b/langfuse/api/commons/types/trace_with_full_details.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .observations_view import ObservationsView +from .score_v1 import ScoreV1 +from .trace import Trace + + +class TraceWithFullDetails(Trace): + html_path: typing_extensions.Annotated[str, FieldMetadata(alias="htmlPath")] = ( + pydantic.Field() + ) + """ + Path of trace in Langfuse UI + """ + + latency: float = pydantic.Field() + """ + Latency of trace in seconds + """ + + total_cost: typing_extensions.Annotated[float, FieldMetadata(alias="totalCost")] = ( + pydantic.Field() + ) + """ + Cost of trace in USD + """ + + observations: typing.List[ObservationsView] = pydantic.Field() + """ + List of observations + """ + + scores: typing.List[ScoreV1] = pydantic.Field() + """ + List of scores + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/commons/types/usage.py b/langfuse/api/commons/types/usage.py new file mode 100644 index 000000000..bce8057c2 --- /dev/null +++ b/langfuse/api/commons/types/usage.py @@ -0,0 +1,56 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .model_usage_unit import ModelUsageUnit + + +class Usage(UniversalBaseModel): + """ + (Deprecated. Use usageDetails and costDetails instead.) Standard interface for usage and cost + """ + + input: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of input units (e.g. tokens) + """ + + output: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of output units (e.g. tokens) + """ + + total: typing.Optional[int] = pydantic.Field(default=None) + """ + Defaults to input+output if not set + """ + + unit: typing.Optional[ModelUsageUnit] = None + input_cost: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="inputCost") + ] = pydantic.Field(default=None) + """ + USD input cost + """ + + output_cost: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="outputCost") + ] = pydantic.Field(default=None) + """ + USD output cost + """ + + total_cost: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="totalCost") + ] = pydantic.Field(default=None) + """ + USD total cost, defaults to input+output + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/core/__init__.py b/langfuse/api/core/__init__.py index 58ad52ad2..91742cd87 100644 --- a/langfuse/api/core/__init__.py +++ b/langfuse/api/core/__init__.py @@ -1,30 +1,111 @@ # This file was auto-generated by Fern from our API Definition. -from .api_error import ApiError -from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper -from .datetime_utils import serialize_datetime -from .file import File, convert_file_dict_to_httpx_tuples -from .http_client import AsyncHttpClient, HttpClient -from .jsonable_encoder import jsonable_encoder -from .pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .query_encoder import encode_query -from .remove_none_from_dict import remove_none_from_dict -from .request_options import RequestOptions +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .api_error import ApiError + from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .datetime_utils import serialize_datetime + from .file import File, convert_file_dict_to_httpx_tuples, with_content_type + from .http_client import AsyncHttpClient, HttpClient + from .http_response import AsyncHttpResponse, HttpResponse + from .jsonable_encoder import jsonable_encoder + from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, + ) + from .query_encoder import encode_query + from .remove_none_from_dict import remove_none_from_dict + from .request_options import RequestOptions + from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +_dynamic_imports: typing.Dict[str, str] = { + "ApiError": ".api_error", + "AsyncClientWrapper": ".client_wrapper", + "AsyncHttpClient": ".http_client", + "AsyncHttpResponse": ".http_response", + "BaseClientWrapper": ".client_wrapper", + "FieldMetadata": ".serialization", + "File": ".file", + "HttpClient": ".http_client", + "HttpResponse": ".http_response", + "IS_PYDANTIC_V2": ".pydantic_utilities", + "RequestOptions": ".request_options", + "SyncClientWrapper": ".client_wrapper", + "UniversalBaseModel": ".pydantic_utilities", + "UniversalRootModel": ".pydantic_utilities", + "convert_and_respect_annotation_metadata": ".serialization", + "convert_file_dict_to_httpx_tuples": ".file", + "encode_query": ".query_encoder", + "jsonable_encoder": ".jsonable_encoder", + "parse_obj_as": ".pydantic_utilities", + "remove_none_from_dict": ".remove_none_from_dict", + "serialize_datetime": ".datetime_utils", + "universal_field_validator": ".pydantic_utilities", + "universal_root_validator": ".pydantic_utilities", + "update_forward_refs": ".pydantic_utilities", + "with_content_type": ".file", +} + + +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__ = [ "ApiError", "AsyncClientWrapper", "AsyncHttpClient", + "AsyncHttpResponse", "BaseClientWrapper", + "FieldMetadata", "File", "HttpClient", + "HttpResponse", + "IS_PYDANTIC_V2", "RequestOptions", "SyncClientWrapper", + "UniversalBaseModel", + "UniversalRootModel", + "convert_and_respect_annotation_metadata", "convert_file_dict_to_httpx_tuples", - "deep_union_pydantic_dicts", "encode_query", "jsonable_encoder", - "pydantic_v1", + "parse_obj_as", "remove_none_from_dict", "serialize_datetime", + "universal_field_validator", + "universal_root_validator", + "update_forward_refs", + "with_content_type", ] diff --git a/langfuse/api/core/api_error.py b/langfuse/api/core/api_error.py index da734b580..6f850a60c 100644 --- a/langfuse/api/core/api_error.py +++ b/langfuse/api/core/api_error.py @@ -1,17 +1,23 @@ # This file was auto-generated by Fern from our API Definition. -import typing +from typing import Any, Dict, Optional class ApiError(Exception): - status_code: typing.Optional[int] - body: typing.Any + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any def __init__( - self, *, status_code: typing.Optional[int] = None, body: typing.Any = None - ): + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + ) -> None: + self.headers = headers self.status_code = status_code self.body = body def __str__(self) -> str: - return f"status_code: {self.status_code}, body: {self.body}" + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}" diff --git a/langfuse/api/core/client_wrapper.py b/langfuse/api/core/client_wrapper.py index 8a053f4a7..22bb6a70b 100644 --- a/langfuse/api/core/client_wrapper.py +++ b/langfuse/api/core/client_wrapper.py @@ -3,7 +3,6 @@ import typing import httpx - from .http_client import AsyncHttpClient, HttpClient @@ -16,6 +15,7 @@ def __init__( x_langfuse_public_key: typing.Optional[str] = None, username: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, password: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, ): @@ -24,11 +24,15 @@ def __init__( self._x_langfuse_public_key = x_langfuse_public_key self._username = username self._password = password + self._headers = headers self._base_url = base_url self._timeout = timeout def get_headers(self) -> typing.Dict[str, str]: - headers: typing.Dict[str, str] = {"X-Fern-Language": "Python"} + headers: typing.Dict[str, str] = { + "X-Fern-Language": "Python", + **(self.get_custom_headers() or {}), + } username = self._get_username() password = self._get_password() if username is not None and password is not None: @@ -53,6 +57,9 @@ def _get_password(self) -> typing.Optional[str]: else: return self._password() + def get_custom_headers(self) -> typing.Optional[typing.Dict[str, str]]: + return self._headers + def get_base_url(self) -> str: return self._base_url @@ -69,6 +76,7 @@ def __init__( x_langfuse_public_key: typing.Optional[str] = None, username: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, password: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, httpx_client: httpx.Client, @@ -79,14 +87,15 @@ def __init__( x_langfuse_public_key=x_langfuse_public_key, username=username, password=password, + headers=headers, base_url=base_url, timeout=timeout, ) self.httpx_client = HttpClient( httpx_client=httpx_client, - base_headers=self.get_headers(), - base_timeout=self.get_timeout(), - base_url=self.get_base_url(), + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, ) @@ -99,8 +108,10 @@ def __init__( x_langfuse_public_key: typing.Optional[str] = None, username: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, password: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, httpx_client: httpx.AsyncClient, ): super().__init__( @@ -109,12 +120,22 @@ def __init__( x_langfuse_public_key=x_langfuse_public_key, username=username, password=password, + headers=headers, base_url=base_url, timeout=timeout, ) + self._async_token = async_token self.httpx_client = AsyncHttpClient( httpx_client=httpx_client, - base_headers=self.get_headers(), - base_timeout=self.get_timeout(), - base_url=self.get_base_url(), + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + async_base_headers=self.async_get_headers, ) + + async def async_get_headers(self) -> typing.Dict[str, str]: + headers = self.get_headers() + if self._async_token is not None: + token = await self._async_token() + headers["Authorization"] = f"Bearer {token}" + return headers diff --git a/langfuse/api/core/enum.py b/langfuse/api/core/enum.py new file mode 100644 index 000000000..a3d17a67b --- /dev/null +++ b/langfuse/api/core/enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +Provides a StrEnum base class that works across Python versions. + +For Python >= 3.11, this re-exports the standard library enum.StrEnum. +For older Python versions, this defines a compatible StrEnum using the +(str, Enum) mixin pattern so that generated SDKs can use a single base +class in all supported Python versions. +""" + +import enum +import sys + +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + + class StrEnum(str, enum.Enum): + pass diff --git a/langfuse/api/core/file.py b/langfuse/api/core/file.py index 6e0f92bfc..3467175cb 100644 --- a/langfuse/api/core/file.py +++ b/langfuse/api/core/file.py @@ -1,30 +1,30 @@ # This file was auto-generated by Fern from our API Definition. -import typing +from typing import IO, Dict, List, Mapping, Optional, Tuple, Union, cast # File typing inspired by the flexibility of types within the httpx library # https://github.com/encode/httpx/blob/master/httpx/_types.py -FileContent = typing.Union[typing.IO[bytes], bytes, str] -File = typing.Union[ +FileContent = Union[IO[bytes], bytes, str] +File = Union[ # file (or bytes) FileContent, # (filename, file (or bytes)) - typing.Tuple[typing.Optional[str], FileContent], + Tuple[Optional[str], FileContent], # (filename, file (or bytes), content_type) - typing.Tuple[typing.Optional[str], FileContent, typing.Optional[str]], + Tuple[Optional[str], FileContent, Optional[str]], # (filename, file (or bytes), content_type, headers) - typing.Tuple[ - typing.Optional[str], + Tuple[ + Optional[str], FileContent, - typing.Optional[str], - typing.Mapping[str, str], + Optional[str], + Mapping[str, str], ], ] def convert_file_dict_to_httpx_tuples( - d: typing.Dict[str, typing.Union[File, typing.List[File]]], -) -> typing.List[typing.Tuple[str, File]]: + d: Dict[str, Union[File, List[File]]], +) -> List[Tuple[str, File]]: """ The format we use is a list of tuples, where the first element is the name of the file and the second is the file object. Typically HTTPX wants @@ -41,3 +41,30 @@ def convert_file_dict_to_httpx_tuples( else: httpx_tuples.append((key, file_like)) return httpx_tuples + + +def with_content_type(*, file: File, default_content_type: str) -> File: + """ + This function resolves to the file's content type, if provided, and defaults + to the default_content_type value if not. + """ + if isinstance(file, tuple): + if len(file) == 2: + filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore + return (filename, content, default_content_type) + elif len(file) == 3: + filename, content, file_content_type = cast( + Tuple[Optional[str], FileContent, Optional[str]], file + ) # type: ignore + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type) + elif len(file) == 4: + filename, content, file_content_type, headers = cast( # type: ignore + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], + file, + ) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type, headers) + else: + raise ValueError(f"Unexpected tuple length: {len(file)}") + return (None, file, default_content_type) diff --git a/langfuse/api/core/force_multipart.py b/langfuse/api/core/force_multipart.py new file mode 100644 index 000000000..5440913fd --- /dev/null +++ b/langfuse/api/core/force_multipart.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict + + +class ForceMultipartDict(Dict[str, Any]): + """ + A dictionary subclass that always evaluates to True in boolean contexts. + + This is used to force multipart/form-data encoding in HTTP requests even when + the dictionary is empty, which would normally evaluate to False. + """ + + def __bool__(self) -> bool: + return True + + +FORCE_MULTIPART = ForceMultipartDict() diff --git a/langfuse/api/core/http_client.py b/langfuse/api/core/http_client.py index 091f71bc1..3025a49ba 100644 --- a/langfuse/api/core/http_client.py +++ b/langfuse/api/core/http_client.py @@ -2,7 +2,6 @@ import asyncio import email.utils -import json import re import time import typing @@ -11,16 +10,17 @@ from random import random import httpx - from .file import File, convert_file_dict_to_httpx_tuples +from .force_multipart import FORCE_MULTIPART from .jsonable_encoder import jsonable_encoder from .query_encoder import encode_query -from .remove_none_from_dict import remove_none_from_dict +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict from .request_options import RequestOptions +from httpx._types import RequestFiles -INITIAL_RETRY_DELAY_SECONDS = 0.5 -MAX_RETRY_DELAY_SECONDS = 10 -MAX_RETRY_DELAY_SECONDS_FROM_HEADER = 30 +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: @@ -64,6 +64,38 @@ def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float return seconds +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + def _retry_timeout(response: httpx.Response, retries: int) -> float: """ Determine the amount of time to wait before retrying a request. @@ -71,24 +103,45 @@ def _retry_timeout(response: httpx.Response, retries: int) -> float: with a jitter to determine the number of seconds to wait. """ - # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + # 1. Check Retry-After header first retry_after = _parse_retry_after(response.headers) - if retry_after is not None and retry_after <= MAX_RETRY_DELAY_SECONDS_FROM_HEADER: - return retry_after + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) - # Apply exponential backoff, capped at MAX_RETRY_DELAY_SECONDS. - retry_delay = min( + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) + + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min( INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS ) - - # Add a randomness / jitter to the retry delay to avoid overwhelming the server with retries. - timeout = retry_delay * (1 - 0.25 * random()) - return timeout if timeout >= 0 else 0 + return _add_symmetric_jitter(backoff) def _should_retry(response: httpx.Response) -> bool: - retriable_400s = [429, 408, 409] - return response.status_code >= 500 or response.status_code in retriable_400s + retryable_400s = [429, 408, 409] + return response.status_code >= 500 or response.status_code in retryable_400s + + +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if ( + data is not None + and isinstance(data, typing.Mapping) + and (request_files or force_multipart) + ): + return remove_none_from_dict(data) + return data def remove_omit_from_dict( @@ -147,7 +200,10 @@ def get_request_body( # If both data and json are None, we send json data in the event extra properties are specified json_body = maybe_filter_request_body(json, request_options, omit) - return json_body, data_body + # If you have an empty JSON body, you should just send None + return ( + json_body if json_body != {} else None + ), data_body if data_body != {} else None class HttpClient: @@ -155,9 +211,9 @@ def __init__( self, *, httpx_client: httpx.Client, - base_timeout: typing.Optional[float], - base_headers: typing.Dict[str, str], - base_url: typing.Optional[str] = None, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, ): self.base_url = base_url self.base_timeout = base_timeout @@ -165,7 +221,10 @@ def __init__( self.httpx_client = httpx_client def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: - base_url = self.base_url if maybe_base_url is None else maybe_base_url + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + if base_url is None: raise ValueError( "A base_url is required to make this request, please provide one and try again." @@ -185,32 +244,74 @@ def request( typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] ] = None, files: typing.Optional[ - typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]] + typing.Union[ + typing.Dict[ + str, typing.Optional[typing.Union[File, typing.List[File]]] + ], + typing.List[typing.Tuple[str, File]], + ] ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: base_url = self.get_base_url(base_url) timeout = ( request_options.get("timeout_in_seconds") if request_options is not None and request_options.get("timeout_in_seconds") is not None - else self.base_timeout + else self.base_timeout() ) json_body, data_body = get_request_body( json=json, data=data, request_options=request_options, omit=omit ) + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples( + remove_omit_from_dict(remove_none_from_dict(files), omit) + ) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + data_body = _maybe_filter_none_from_multipart_data( + data_body, request_files, force_multipart + ) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), headers=jsonable_encoder( remove_none_from_dict( { - **self.base_headers, + **self.base_headers(), **(headers if headers is not None else {}), **( request_options.get("additional_headers", {}) or {} @@ -220,40 +321,19 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get( - "additional_query_parameters", {} - ) - or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=request_files, timeout=timeout, ) max_retries: int = ( - request_options.get("max_retries", 0) if request_options is not None else 0 + request_options.get("max_retries", 2) if request_options is not None else 2 ) if _should_retry(response=response): - if max_retries > retries: + if retries < max_retries: time.sleep(_retry_timeout(response=response, retries=retries)) return self.request( path=path, @@ -285,32 +365,73 @@ def stream( typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] ] = None, files: typing.Optional[ - typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]] + typing.Union[ + typing.Dict[ + str, typing.Optional[typing.Union[File, typing.List[File]]] + ], + typing.List[typing.Tuple[str, File]], + ] ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, ) -> typing.Iterator[httpx.Response]: base_url = self.get_base_url(base_url) timeout = ( request_options.get("timeout_in_seconds") if request_options is not None and request_options.get("timeout_in_seconds") is not None - else self.base_timeout + else self.base_timeout() ) + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples( + remove_omit_from_dict(remove_none_from_dict(files), omit) + ) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + json_body, data_body = get_request_body( json=json, data=data, request_options=request_options, omit=omit ) + data_body = _maybe_filter_none_from_multipart_data( + data_body, request_files, force_multipart + ) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), headers=jsonable_encoder( remove_none_from_dict( { - **self.base_headers, + **self.base_headers(), **(headers if headers is not None else {}), **( request_options.get("additional_headers", {}) @@ -320,31 +441,11 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get( - "additional_query_parameters", {} - ) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=request_files, timeout=timeout, ) as stream: yield stream @@ -355,17 +456,29 @@ def __init__( self, *, httpx_client: httpx.AsyncClient, - base_timeout: typing.Optional[float], - base_headers: typing.Dict[str, str], - base_url: typing.Optional[str] = None, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + async_base_headers: typing.Optional[ + typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]] + ] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers + self.async_base_headers = async_base_headers self.httpx_client = httpx_client + async def _get_headers(self) -> typing.Dict[str, str]: + if self.async_base_headers is not None: + return await self.async_base_headers() + return self.base_headers() + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: - base_url = self.base_url if maybe_base_url is None else maybe_base_url + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + if base_url is None: raise ValueError( "A base_url is required to make this request, please provide one and try again." @@ -385,25 +498,70 @@ async def request( typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] ] = None, files: typing.Optional[ - typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]] + typing.Union[ + typing.Dict[ + str, typing.Optional[typing.Union[File, typing.List[File]]] + ], + typing.List[typing.Tuple[str, File]], + ] ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: base_url = self.get_base_url(base_url) timeout = ( request_options.get("timeout_in_seconds") if request_options is not None and request_options.get("timeout_in_seconds") is not None - else self.base_timeout + else self.base_timeout() ) + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples( + remove_omit_from_dict(remove_none_from_dict(files), omit) + ) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + json_body, data_body = get_request_body( json=json, data=data, request_options=request_options, omit=omit ) + data_body = _maybe_filter_none_from_multipart_data( + data_body, request_files, force_multipart + ) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -411,7 +569,7 @@ async def request( headers=jsonable_encoder( remove_none_from_dict( { - **self.base_headers, + **_headers, **(headers if headers is not None else {}), **( request_options.get("additional_headers", {}) or {} @@ -421,40 +579,19 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get( - "additional_query_parameters", {} - ) - or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=request_files, timeout=timeout, ) max_retries: int = ( - request_options.get("max_retries", 0) if request_options is not None else 0 + request_options.get("max_retries", 2) if request_options is not None else 2 ) if _should_retry(response=response): - if max_retries > retries: + if retries < max_retries: await asyncio.sleep(_retry_timeout(response=response, retries=retries)) return await self.request( path=path, @@ -485,32 +622,76 @@ async def stream( typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] ] = None, files: typing.Optional[ - typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]] + typing.Union[ + typing.Dict[ + str, typing.Optional[typing.Union[File, typing.List[File]]] + ], + typing.List[typing.Tuple[str, File]], + ] ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, ) -> typing.AsyncIterator[httpx.Response]: base_url = self.get_base_url(base_url) timeout = ( request_options.get("timeout_in_seconds") if request_options is not None and request_options.get("timeout_in_seconds") is not None - else self.base_timeout + else self.base_timeout() ) + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples( + remove_omit_from_dict(remove_none_from_dict(files), omit) + ) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + json_body, data_body = get_request_body( json=json, data=data, request_options=request_options, omit=omit ) + data_body = _maybe_filter_none_from_multipart_data( + data_body, request_files, force_multipart + ) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), headers=jsonable_encoder( remove_none_from_dict( { - **self.base_headers, + **_headers, **(headers if headers is not None else {}), **( request_options.get("additional_headers", {}) @@ -520,31 +701,11 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get( - "additional_query_parameters", {} - ) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=request_files, timeout=timeout, ) as stream: yield stream diff --git a/langfuse/api/core/http_response.py b/langfuse/api/core/http_response.py new file mode 100644 index 000000000..2479747e8 --- /dev/null +++ b/langfuse/api/core/http_response.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Dict, Generic, TypeVar + +import httpx + +# Generic to represent the underlying type of the data wrapped by the HTTP response. +T = TypeVar("T") + + +class BaseHttpResponse: + """Minimalist HTTP response wrapper that exposes response headers.""" + + _response: httpx.Response + + def __init__(self, response: httpx.Response): + self._response = response + + @property + def headers(self) -> Dict[str, str]: + return dict(self._response.headers) + + +class HttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + def close(self) -> None: + self._response.close() + + +class AsyncHttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + async def close(self) -> None: + await self._response.aclose() diff --git a/langfuse/api/core/http_sse/__init__.py b/langfuse/api/core/http_sse/__init__.py new file mode 100644 index 000000000..ab0b4995a --- /dev/null +++ b/langfuse/api/core/http_sse/__init__.py @@ -0,0 +1,48 @@ +# 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 ._api import EventSource, aconnect_sse, connect_sse + from ._exceptions import SSEError + from ._models import ServerSentEvent +_dynamic_imports: typing.Dict[str, str] = { + "EventSource": "._api", + "SSEError": "._exceptions", + "ServerSentEvent": "._models", + "aconnect_sse": "._api", + "connect_sse": "._api", +} + + +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__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"] diff --git a/langfuse/api/core/http_sse/_api.py b/langfuse/api/core/http_sse/_api.py new file mode 100644 index 000000000..eb739a22b --- /dev/null +++ b/langfuse/api/core/http_sse/_api.py @@ -0,0 +1,114 @@ +# This file was auto-generated by Fern from our API Definition. + +import re +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast + +import httpx +from ._decoders import SSEDecoder +from ._exceptions import SSEError +from ._models import ServerSentEvent + + +class EventSource: + def __init__(self, response: httpx.Response) -> None: + self._response = response + + def _check_content_type(self) -> None: + content_type = self._response.headers.get("content-type", "").partition(";")[0] + if "text/event-stream" not in content_type: + raise SSEError( + f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}" + ) + + def _get_charset(self) -> str: + """Extract charset from Content-Type header, fallback to UTF-8.""" + content_type = self._response.headers.get("content-type", "") + + # Parse charset parameter using regex + charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE) + if charset_match: + charset = charset_match.group(1).strip("\"'") + # Validate that it's a known encoding + try: + # Test if the charset is valid by trying to encode/decode + "test".encode(charset).decode(charset) + return charset + except (LookupError, UnicodeError): + # If charset is invalid, fall back to UTF-8 + pass + + # Default to UTF-8 if no charset specified or invalid charset + return "utf-8" + + @property + def response(self) -> httpx.Response: + return self._response + + def iter_sse(self) -> Iterator[ServerSentEvent]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + + buffer = "" + for chunk in self._response.iter_bytes(): + # Decode chunk using detected charset + text_chunk = chunk.decode(charset, errors="replace") + buffer += text_chunk + + # Process complete lines + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.rstrip("\r") + sse = decoder.decode(line) + # when we reach a "\n\n" => line = '' + # => decoder will attempt to return an SSE Event + if sse is not None: + yield sse + + # Process any remaining data in buffer + if buffer.strip(): + line = buffer.rstrip("\r") + sse = decoder.decode(line) + if sse is not None: + yield sse + + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: + self._check_content_type() + decoder = SSEDecoder() + lines = cast(AsyncGenerator[str, None], self._response.aiter_lines()) + try: + async for line in lines: + line = line.rstrip("\n") + sse = decoder.decode(line) + if sse is not None: + yield sse + finally: + await lines.aclose() + + +@contextmanager +def connect_sse( + client: httpx.Client, method: str, url: str, **kwargs: Any +) -> Iterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) + + +@asynccontextmanager +async def aconnect_sse( + client: httpx.AsyncClient, + method: str, + url: str, + **kwargs: Any, +) -> AsyncIterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + async with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) diff --git a/langfuse/api/core/http_sse/_decoders.py b/langfuse/api/core/http_sse/_decoders.py new file mode 100644 index 000000000..bdec57b44 --- /dev/null +++ b/langfuse/api/core/http_sse/_decoders.py @@ -0,0 +1,66 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import List, Optional + +from ._models import ServerSentEvent + + +class SSEDecoder: + def __init__(self) -> None: + self._event = "" + self._data: List[str] = [] + self._last_event_id = "" + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if ( + not self._event + and not self._data + and not self._last_event_id + and self._retry is None + ): + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = "" + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/langfuse/api/resources/utils/resources/pagination/__init__.py b/langfuse/api/core/http_sse/_exceptions.py similarity index 51% rename from langfuse/api/resources/utils/resources/pagination/__init__.py rename to langfuse/api/core/http_sse/_exceptions.py index 9bd1e5f71..81605a8a6 100644 --- a/langfuse/api/resources/utils/resources/pagination/__init__.py +++ b/langfuse/api/core/http_sse/_exceptions.py @@ -1,5 +1,7 @@ # This file was auto-generated by Fern from our API Definition. -from .types import MetaResponse +import httpx -__all__ = ["MetaResponse"] + +class SSEError(httpx.TransportError): + pass diff --git a/langfuse/api/core/http_sse/_models.py b/langfuse/api/core/http_sse/_models.py new file mode 100644 index 000000000..1af57f8fd --- /dev/null +++ b/langfuse/api/core/http_sse/_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: Optional[int] = None + + def json(self) -> Any: + """Parse the data field as JSON.""" + return json.loads(self.data) diff --git a/langfuse/api/core/jsonable_encoder.py b/langfuse/api/core/jsonable_encoder.py index 7a05e9190..90f53dfa7 100644 --- a/langfuse/api/core/jsonable_encoder.py +++ b/langfuse/api/core/jsonable_encoder.py @@ -11,35 +11,23 @@ import base64 import dataclasses import datetime as dt -from collections import defaultdict from enum import Enum from pathlib import PurePath from types import GeneratorType -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Callable, Dict, List, Optional, Set, Union +import pydantic from .datetime_utils import serialize_datetime -from .pydantic_utilities import pydantic_v1 +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + encode_by_type, + to_jsonable_with_fallback, +) SetIntStr = Set[Union[int, str]] DictIntStrAny = Dict[Union[int, str], Any] -def generate_encoders_by_class_tuples( - type_encoder_map: Dict[Any, Callable[[Any], Any]], -) -> Dict[Callable[[Any], Any], Tuple[Any, ...]]: - encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict( - tuple - ) - for type_, encoder in type_encoder_map.items(): - encoders_by_class_tuples[encoder] += (type_,) - return encoders_by_class_tuples - - -encoders_by_class_tuples = generate_encoders_by_class_tuples( - pydantic_v1.json.ENCODERS_BY_TYPE -) - - def jsonable_encoder( obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None ) -> Any: @@ -51,16 +39,21 @@ def jsonable_encoder( for encoder_type, encoder_instance in custom_encoder.items(): if isinstance(obj, encoder_type): return encoder_instance(obj) - if isinstance(obj, pydantic_v1.BaseModel): - encoder = getattr(obj.__config__, "json_encoders", {}) + if isinstance(obj, pydantic.BaseModel): + if IS_PYDANTIC_V2: + encoder = getattr(obj.model_config, "json_encoders", {}) # type: ignore # Pydantic v2 + else: + encoder = getattr(obj.__config__, "json_encoders", {}) # type: ignore # Pydantic v1 if custom_encoder: encoder.update(custom_encoder) obj_dict = obj.dict(by_alias=True) if "__root__" in obj_dict: obj_dict = obj_dict["__root__"] + if "root" in obj_dict: + obj_dict = obj_dict["root"] return jsonable_encoder(obj_dict, custom_encoder=encoder) if dataclasses.is_dataclass(obj): - obj_dict = dataclasses.asdict(obj) + obj_dict = dataclasses.asdict(obj) # type: ignore return jsonable_encoder(obj_dict, custom_encoder=custom_encoder) if isinstance(obj, bytes): return base64.b64encode(obj).decode("utf-8") @@ -89,20 +82,21 @@ def jsonable_encoder( encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder)) return encoded_list - if type(obj) in pydantic_v1.json.ENCODERS_BY_TYPE: - return pydantic_v1.json.ENCODERS_BY_TYPE[type(obj)](obj) - for encoder, classes_tuple in encoders_by_class_tuples.items(): - if isinstance(obj, classes_tuple): - return encoder(obj) + def fallback_serializer(o: Any) -> Any: + attempt_encode = encode_by_type(o) + if attempt_encode is not None: + return attempt_encode - try: - data = dict(obj) - except Exception as e: - errors: List[Exception] = [] - errors.append(e) try: - data = vars(obj) + data = dict(o) except Exception as e: + errors: List[Exception] = [] errors.append(e) - raise ValueError(errors) from e - return jsonable_encoder(data, custom_encoder=custom_encoder) + try: + data = vars(o) + except Exception as e: + errors.append(e) + raise ValueError(errors) from e + return jsonable_encoder(data, custom_encoder=custom_encoder) + + return to_jsonable_with_fallback(obj, fallback_serializer) diff --git a/langfuse/api/core/pydantic_utilities.py b/langfuse/api/core/pydantic_utilities.py index a72c1a52f..d2b7b51b6 100644 --- a/langfuse/api/core/pydantic_utilities.py +++ b/langfuse/api/core/pydantic_utilities.py @@ -1,28 +1,310 @@ # This file was auto-generated by Fern from our API Definition. -import typing +# nopycln: file +import datetime as dt +from collections import defaultdict +from typing import ( + Any, + Callable, + ClassVar, + Dict, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, +) import pydantic IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") if IS_PYDANTIC_V2: - import pydantic.v1 as pydantic_v1 # type: ignore # nopycln: import + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union else: - import pydantic as pydantic_v1 # type: ignore # nopycln: import + from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] + from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] + from pydantic.typing import get_args as get_args # type: ignore[no-redef] + from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_union as is_union # type: ignore[no-redef] + +from .datetime_utils import serialize_datetime +from .serialization import convert_and_respect_annotation_metadata +from typing_extensions import TypeAlias + +T = TypeVar("T") +Model = TypeVar("Model", bound=pydantic.BaseModel) + + +def parse_obj_as(type_: Type[T], object_: Any) -> T: + dealiased_object = convert_and_respect_annotation_metadata( + object_=object_, annotation=type_, direction="read" + ) + if IS_PYDANTIC_V2: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + return adapter.validate_python(dealiased_object) + return pydantic.parse_obj_as(type_, dealiased_object) + + +def to_jsonable_with_fallback( + obj: Any, fallback_serializer: Callable[[Any], Any] +) -> Any: + if IS_PYDANTIC_V2: + from pydantic_core import to_jsonable_python + + return to_jsonable_python(obj, fallback=fallback_serializer) + return fallback_serializer(obj) + + +class UniversalBaseModel(pydantic.BaseModel): + if IS_PYDANTIC_V2: + model_config: ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( # type: ignore[typeddict-unknown-key] + # Allow fields beginning with `model_` to be used in the model + protected_namespaces=(), + ) + + @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] + def serialize_model(self) -> Any: # type: ignore[name-defined] + serialized = self.dict() # type: ignore[attr-defined] + data = { + k: serialize_datetime(v) if isinstance(v, dt.datetime) else v + for k, v in serialized.items() + } + return data + + else: + + class Config: + smart_union = True + json_encoders = {dt.datetime: serialize_datetime} + + @classmethod + def model_construct( + cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any + ) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata( + object_=values, annotation=cls, direction="read" + ) + return cls.construct(_fields_set, **dealiased_object) + + @classmethod + def construct( + cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any + ) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata( + object_=values, annotation=cls, direction="read" + ) + if IS_PYDANTIC_V2: + return super().model_construct(_fields_set, **dealiased_object) # type: ignore[misc] + return super().construct(_fields_set, **dealiased_object) + + def json(self, **kwargs: Any) -> str: + kwargs_with_defaults = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + if IS_PYDANTIC_V2: + return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc] + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: Any) -> Dict[str, Any]: + """ + Override the default dict method to `exclude_unset` by default. This function patches + `exclude_unset` to work include fields within non-None default values. + """ + # Note: the logic here is multiplexed given the levers exposed in Pydantic V1 vs V2 + # Pydantic V1's .dict can be extremely slow, so we do not want to call it twice. + # + # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models + # that we have less control over, and this is less intrusive than custom serializers for now. + if IS_PYDANTIC_V2: + kwargs_with_defaults_exclude_unset = { + **kwargs, + "by_alias": True, + "exclude_unset": True, + "exclude_none": False, + } + kwargs_with_defaults_exclude_none = { + **kwargs, + "by_alias": True, + "exclude_none": True, + "exclude_unset": False, + } + dict_dump = deep_union_pydantic_dicts( + super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc] + super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc] + ) + + else: + _fields_set = self.__fields_set__.copy() + + fields = _get_model_fields(self.__class__) + for name, field in fields.items(): + if name not in _fields_set: + default = _get_field_default(field) + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default is not None or ( + "exclude_unset" in kwargs and not kwargs["exclude_unset"] + ): + _fields_set.add(name) + + if default is not None: + self.__fields_set__.add(name) + + kwargs_with_defaults_exclude_unset_include_fields = { + "by_alias": True, + "exclude_unset": True, + "include": _fields_set, + **kwargs, + } + + dict_dump = super().dict( + **kwargs_with_defaults_exclude_unset_include_fields + ) + + return cast( + Dict[str, Any], + convert_and_respect_annotation_metadata( + object_=dict_dump, annotation=self.__class__, direction="write" + ), + ) + + +def _union_list_of_pydantic_dicts( + source: List[Any], destination: List[Any] +) -> List[Any]: + converted_list: List[Any] = [] + for i, item in enumerate(source): + destination_value = destination[i] + if isinstance(item, dict): + converted_list.append(deep_union_pydantic_dicts(item, destination_value)) + elif isinstance(item, list): + converted_list.append( + _union_list_of_pydantic_dicts(item, destination_value) + ) + else: + converted_list.append(item) + return converted_list def deep_union_pydantic_dicts( - source: typing.Dict[str, typing.Any], destination: typing.Dict[str, typing.Any] -) -> typing.Dict[str, typing.Any]: + source: Dict[str, Any], destination: Dict[str, Any] +) -> Dict[str, Any]: for key, value in source.items(): + node = destination.setdefault(key, {}) if isinstance(value, dict): - node = destination.setdefault(key, {}) deep_union_pydantic_dicts(value, node) + # Note: we do not do this same processing for sets given we do not have sets of models + # and given the sets are unordered, the processing of the set and matching objects would + # be non-trivial. + elif isinstance(value, list): + destination[key] = _union_list_of_pydantic_dicts(value, node) else: destination[key] = value return destination -__all__ = ["pydantic_v1"] +if IS_PYDANTIC_V2: + + class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[misc, name-defined, type-arg] + pass + + UniversalRootModel: TypeAlias = V2RootModel # type: ignore[misc] +else: + UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef] + + +def encode_by_type(o: Any) -> Any: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict( + tuple + ) + for type_, encoder in encoders_by_type.items(): + encoders_by_class_tuples[encoder] += (type_,) + + if type(o) in encoders_by_type: + return encoders_by_type[type(o)](o) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(o, classes_tuple): + return encoder(o) + + +def update_forward_refs(model: Type["Model"], **localns: Any) -> None: + if IS_PYDANTIC_V2: + model.model_rebuild(raise_errors=False) # type: ignore[attr-defined] + else: + model.update_forward_refs(**localns) + + +# Mirrors Pydantic's internal typing +AnyCallable = Callable[..., Any] + + +def universal_root_validator( + pre: bool = False, +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] + + return decorator + + +def universal_field_validator( + field_name: str, pre: bool = False +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + return cast( + AnyCallable, + pydantic.field_validator(field_name, mode="before" if pre else "after")( + func + ), + ) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func)) + + return decorator + + +PydanticField = Union[ModelField, pydantic.fields.FieldInfo] + + +def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined] + return cast(Mapping[str, PydanticField], model.__fields__) + + +def _get_field_default(field: PydanticField) -> Any: + try: + value = field.get_default() # type: ignore[union-attr] + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/langfuse/api/core/query_encoder.py b/langfuse/api/core/query_encoder.py index 069633086..03fbf59bd 100644 --- a/langfuse/api/core/query_encoder.py +++ b/langfuse/api/core/query_encoder.py @@ -1,39 +1,60 @@ # This file was auto-generated by Fern from our API Definition. -from collections import ChainMap -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional, Tuple -from .pydantic_utilities import pydantic_v1 +import pydantic # Flattens dicts to be of the form {"key[subkey][subkey2]": value} where value is not a dict def traverse_query_dict( dict_flat: Dict[str, Any], key_prefix: Optional[str] = None -) -> Dict[str, Any]: - result = {} +) -> List[Tuple[str, Any]]: + result = [] for k, v in dict_flat.items(): key = f"{key_prefix}[{k}]" if key_prefix is not None else k if isinstance(v, dict): - result.update(traverse_query_dict(v, key)) + result.extend(traverse_query_dict(v, key)) + elif isinstance(v, list): + for arr_v in v: + if isinstance(arr_v, dict): + result.extend(traverse_query_dict(arr_v, key)) + else: + result.append((key, arr_v)) else: - result[key] = v + result.append((key, v)) return result -def single_query_encoder(query_key: str, query_value: Any) -> Dict[str, Any]: - if isinstance(query_value, pydantic_v1.BaseModel) or isinstance(query_value, dict): - if isinstance(query_value, pydantic_v1.BaseModel): +def single_query_encoder(query_key: str, query_value: Any) -> List[Tuple[str, Any]]: + if isinstance(query_value, pydantic.BaseModel) or isinstance(query_value, dict): + if isinstance(query_value, pydantic.BaseModel): obj_dict = query_value.dict(by_alias=True) else: obj_dict = query_value return traverse_query_dict(obj_dict, query_key) + elif isinstance(query_value, list): + encoded_values: List[Tuple[str, Any]] = [] + for value in query_value: + if isinstance(value, pydantic.BaseModel) or isinstance(value, dict): + if isinstance(value, pydantic.BaseModel): + obj_dict = value.dict(by_alias=True) + elif isinstance(value, dict): + obj_dict = value - return {query_key: query_value} + encoded_values.extend(single_query_encoder(query_key, obj_dict)) + else: + encoded_values.append((query_key, value)) + return encoded_values -def encode_query(query: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: - return ( - dict(ChainMap(*[single_query_encoder(k, v) for k, v in query.items()])) - if query is not None - else None - ) + return [(query_key, query_value)] + + +def encode_query(query: Optional[Dict[str, Any]]) -> Optional[List[Tuple[str, Any]]]: + if query is None: + return None + + encoded_query = [] + for k, v in query.items(): + encoded_query.extend(single_query_encoder(k, v)) + return encoded_query diff --git a/langfuse/api/core/request_options.py b/langfuse/api/core/request_options.py index d0bf0dbce..1b3880443 100644 --- a/langfuse/api/core/request_options.py +++ b/langfuse/api/core/request_options.py @@ -23,6 +23,8 @@ class RequestOptions(typing.TypedDict, total=False): - additional_query_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's query parameters dict - additional_body_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's body parameters dict + + - chunk_size: int. The size, in bytes, to process each chunk of data being streamed back within the response. This equates to leveraging `chunk_size` within `requests` or `httpx`, and is only leveraged for file downloads. """ timeout_in_seconds: NotRequired[int] @@ -30,3 +32,4 @@ class RequestOptions(typing.TypedDict, total=False): additional_headers: NotRequired[typing.Dict[str, typing.Any]] additional_query_parameters: NotRequired[typing.Dict[str, typing.Any]] additional_body_parameters: NotRequired[typing.Dict[str, typing.Any]] + chunk_size: NotRequired[int] diff --git a/langfuse/api/core/serialization.py b/langfuse/api/core/serialization.py new file mode 100644 index 000000000..ad6eb8d7f --- /dev/null +++ b/langfuse/api/core/serialization.py @@ -0,0 +1,282 @@ +# This file was auto-generated by Fern from our API Definition. + +import collections +import inspect +import typing + +import pydantic +import typing_extensions + + +class FieldMetadata: + """ + Metadata class used to annotate fields to provide additional information. + + Example: + class MyDict(TypedDict): + field: typing.Annotated[str, FieldMetadata(alias="field_name")] + + Will serialize: `{"field": "value"}` + To: `{"field_name": "value"}` + """ + + alias: str + + def __init__(self, *, alias: str) -> None: + self.alias = alias + + +def convert_and_respect_annotation_metadata( + *, + object_: typing.Any, + annotation: typing.Any, + inner_type: typing.Optional[typing.Any] = None, + direction: typing.Literal["read", "write"], +) -> typing.Any: + """ + Respect the metadata annotations on a field, such as aliasing. This function effectively + manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for + TypedDicts, which cannot support aliasing out of the box, and can be extended for additional + utilities, such as defaults. + + Parameters + ---------- + object_ : typing.Any + + annotation : type + The type we're looking to apply typing annotations from + + inner_type : typing.Optional[type] + + Returns + ------- + typing.Any + """ + + if object_ is None: + return None + if inner_type is None: + inner_type = annotation + + clean_type = _remove_annotations(inner_type) + # Pydantic models + if ( + inspect.isclass(clean_type) + and issubclass(clean_type, pydantic.BaseModel) + and isinstance(object_, typing.Mapping) + ): + return _convert_mapping(object_, clean_type, direction) + # TypedDicts + if typing_extensions.is_typeddict(clean_type) and isinstance( + object_, typing.Mapping + ): + return _convert_mapping(object_, clean_type, direction) + + if ( + typing_extensions.get_origin(clean_type) == typing.Dict + or typing_extensions.get_origin(clean_type) == dict + or clean_type == typing.Dict + ) and isinstance(object_, typing.Dict): + key_type = typing_extensions.get_args(clean_type)[0] + value_type = typing_extensions.get_args(clean_type)[1] + + return { + key: convert_and_respect_annotation_metadata( + object_=value, + annotation=annotation, + inner_type=value_type, + direction=direction, + ) + for key, value in object_.items() + } + + # If you're iterating on a string, do not bother to coerce it to a sequence. + if not isinstance(object_, str): + if ( + typing_extensions.get_origin(clean_type) == typing.Set + or typing_extensions.get_origin(clean_type) == set + or clean_type == typing.Set + ) and isinstance(object_, typing.Set): + inner_type = typing_extensions.get_args(clean_type)[0] + return { + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + } + elif ( + ( + typing_extensions.get_origin(clean_type) == typing.List + or typing_extensions.get_origin(clean_type) == list + or clean_type == typing.List + ) + and isinstance(object_, typing.List) + ) or ( + ( + typing_extensions.get_origin(clean_type) == typing.Sequence + or typing_extensions.get_origin(clean_type) == collections.abc.Sequence + or clean_type == typing.Sequence + ) + and isinstance(object_, typing.Sequence) + ): + inner_type = typing_extensions.get_args(clean_type)[0] + return [ + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + ] + + if typing_extensions.get_origin(clean_type) == typing.Union: + # We should be able to ~relatively~ safely try to convert keys against all + # member types in the union, the edge case here is if one member aliases a field + # of the same name to a different name from another member + # Or if another member aliases a field of the same name that another member does not. + for member in typing_extensions.get_args(clean_type): + object_ = convert_and_respect_annotation_metadata( + object_=object_, + annotation=annotation, + inner_type=member, + direction=direction, + ) + return object_ + + annotated_type = _get_annotation(annotation) + if annotated_type is None: + return object_ + + # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.) + # Then we can safely call it on the recursive conversion. + return object_ + + +def _convert_mapping( + object_: typing.Mapping[str, object], + expected_type: typing.Any, + direction: typing.Literal["read", "write"], +) -> typing.Mapping[str, object]: + converted_object: typing.Dict[str, object] = {} + try: + annotations = typing_extensions.get_type_hints( + expected_type, include_extras=True + ) + except NameError: + # The TypedDict contains a circular reference, so + # we use the __annotations__ attribute directly. + annotations = getattr(expected_type, "__annotations__", {}) + aliases_to_field_names = _get_alias_to_field_name(annotations) + for key, value in object_.items(): + if direction == "read" and key in aliases_to_field_names: + dealiased_key = aliases_to_field_names.get(key) + if dealiased_key is not None: + type_ = annotations.get(dealiased_key) + else: + type_ = annotations.get(key) + # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map + # + # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias + # then we can just pass the value through as is + if type_ is None: + converted_object[key] = value + elif direction == "read" and key not in aliases_to_field_names: + converted_object[key] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + else: + converted_object[ + _alias_key(key, type_, direction, aliases_to_field_names) + ] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + return converted_object + + +def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return None + + if maybe_annotated_type == typing_extensions.NotRequired: + type_ = typing_extensions.get_args(type_)[0] + maybe_annotated_type = typing_extensions.get_origin(type_) + + if maybe_annotated_type == typing_extensions.Annotated: + return type_ + + return None + + +def _remove_annotations(type_: typing.Any) -> typing.Any: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return type_ + + if maybe_annotated_type == typing_extensions.NotRequired: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + if maybe_annotated_type == typing_extensions.Annotated: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + return type_ + + +def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_alias_to_field_name(annotations) + + +def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_field_to_alias_name(annotations) + + +def _get_alias_to_field_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[maybe_alias] = field + return aliases + + +def _get_field_to_alias_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[field] = maybe_alias + return aliases + + +def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]: + maybe_annotated_type = _get_annotation(type_) + + if maybe_annotated_type is not None: + # The actual annotations are 1 onward, the first is the annotated type + annotations = typing_extensions.get_args(maybe_annotated_type)[1:] + + for annotation in annotations: + if isinstance(annotation, FieldMetadata) and annotation.alias is not None: + return annotation.alias + return None + + +def _alias_key( + key: str, + type_: typing.Any, + direction: typing.Literal["read", "write"], + aliases_to_field_names: typing.Dict[str, str], +) -> str: + if direction == "read": + return aliases_to_field_names.get(key, key) + return _get_alias_from_type(type_=type_) or key diff --git a/langfuse/api/dataset_items/__init__.py b/langfuse/api/dataset_items/__init__.py new file mode 100644 index 000000000..4d009d228 --- /dev/null +++ b/langfuse/api/dataset_items/__init__.py @@ -0,0 +1,52 @@ +# 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 ( + CreateDatasetItemRequest, + DeleteDatasetItemResponse, + PaginatedDatasetItems, + ) +_dynamic_imports: typing.Dict[str, str] = { + "CreateDatasetItemRequest": ".types", + "DeleteDatasetItemResponse": ".types", + "PaginatedDatasetItems": ".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__ = [ + "CreateDatasetItemRequest", + "DeleteDatasetItemResponse", + "PaginatedDatasetItems", +] diff --git a/langfuse/api/dataset_items/client.py b/langfuse/api/dataset_items/client.py new file mode 100644 index 000000000..b21f1a4ee --- /dev/null +++ b/langfuse/api/dataset_items/client.py @@ -0,0 +1,482 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..commons.types.dataset_item import DatasetItem +from ..commons.types.dataset_status import DatasetStatus +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawDatasetItemsClient, RawDatasetItemsClient +from .types.delete_dataset_item_response import DeleteDatasetItemResponse +from .types.paginated_dataset_items import PaginatedDatasetItems + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DatasetItemsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDatasetItemsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDatasetItemsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDatasetItemsClient + """ + return self._raw_client + + def create( + self, + *, + dataset_name: str, + input: typing.Optional[typing.Any] = OMIT, + expected_output: typing.Optional[typing.Any] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + source_trace_id: typing.Optional[str] = OMIT, + source_observation_id: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + status: typing.Optional[DatasetStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DatasetItem: + """ + Create a dataset item + + Parameters + ---------- + dataset_name : str + + input : typing.Optional[typing.Any] + + expected_output : typing.Optional[typing.Any] + + metadata : typing.Optional[typing.Any] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + id : typing.Optional[str] + Dataset items are upserted on their id. Id needs to be unique (project-level) and cannot be reused across datasets. + + status : typing.Optional[DatasetStatus] + Defaults to ACTIVE for newly created items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetItem + + 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.dataset_items.create( + dataset_name="datasetName", + ) + """ + _response = self._raw_client.create( + dataset_name=dataset_name, + input=input, + expected_output=expected_output, + metadata=metadata, + source_trace_id=source_trace_id, + source_observation_id=source_observation_id, + id=id, + status=status, + request_options=request_options, + ) + return _response.data + + def get( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DatasetItem: + """ + Get a dataset item + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetItem + + 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.dataset_items.get( + id="id", + ) + """ + _response = self._raw_client.get(id, request_options=request_options) + return _response.data + + def list( + self, + *, + dataset_name: typing.Optional[str] = None, + source_trace_id: typing.Optional[str] = None, + source_observation_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasetItems: + """ + Get dataset items + + Parameters + ---------- + dataset_name : typing.Optional[str] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasetItems + + 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.dataset_items.list() + """ + _response = self._raw_client.list( + dataset_name=dataset_name, + source_trace_id=source_trace_id, + source_observation_id=source_observation_id, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data + + def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteDatasetItemResponse: + """ + Delete a dataset item and all its run items. This action is irreversible. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetItemResponse + + 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.dataset_items.delete( + id="id", + ) + """ + _response = self._raw_client.delete(id, request_options=request_options) + return _response.data + + +class AsyncDatasetItemsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDatasetItemsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDatasetItemsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDatasetItemsClient + """ + return self._raw_client + + async def create( + self, + *, + dataset_name: str, + input: typing.Optional[typing.Any] = OMIT, + expected_output: typing.Optional[typing.Any] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + source_trace_id: typing.Optional[str] = OMIT, + source_observation_id: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + status: typing.Optional[DatasetStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DatasetItem: + """ + Create a dataset item + + Parameters + ---------- + dataset_name : str + + input : typing.Optional[typing.Any] + + expected_output : typing.Optional[typing.Any] + + metadata : typing.Optional[typing.Any] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + id : typing.Optional[str] + Dataset items are upserted on their id. Id needs to be unique (project-level) and cannot be reused across datasets. + + status : typing.Optional[DatasetStatus] + Defaults to ACTIVE for newly created items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetItem + + 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.dataset_items.create( + dataset_name="datasetName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + dataset_name=dataset_name, + input=input, + expected_output=expected_output, + metadata=metadata, + source_trace_id=source_trace_id, + source_observation_id=source_observation_id, + id=id, + status=status, + request_options=request_options, + ) + return _response.data + + async def get( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DatasetItem: + """ + Get a dataset item + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetItem + + 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.dataset_items.get( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(id, request_options=request_options) + return _response.data + + async def list( + self, + *, + dataset_name: typing.Optional[str] = None, + source_trace_id: typing.Optional[str] = None, + source_observation_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasetItems: + """ + Get dataset items + + Parameters + ---------- + dataset_name : typing.Optional[str] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasetItems + + 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.dataset_items.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + dataset_name=dataset_name, + source_trace_id=source_trace_id, + source_observation_id=source_observation_id, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data + + async def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteDatasetItemResponse: + """ + Delete a dataset item and all its run items. This action is irreversible. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetItemResponse + + 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.dataset_items.delete( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(id, request_options=request_options) + return _response.data diff --git a/langfuse/api/dataset_items/raw_client.py b/langfuse/api/dataset_items/raw_client.py new file mode 100644 index 000000000..1dede7c90 --- /dev/null +++ b/langfuse/api/dataset_items/raw_client.py @@ -0,0 +1,955 @@ +# 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 ..commons.types.dataset_item import DatasetItem +from ..commons.types.dataset_status import DatasetStatus +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 .types.delete_dataset_item_response import DeleteDatasetItemResponse +from .types.paginated_dataset_items import PaginatedDatasetItems + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDatasetItemsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + *, + dataset_name: str, + input: typing.Optional[typing.Any] = OMIT, + expected_output: typing.Optional[typing.Any] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + source_trace_id: typing.Optional[str] = OMIT, + source_observation_id: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + status: typing.Optional[DatasetStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DatasetItem]: + """ + Create a dataset item + + Parameters + ---------- + dataset_name : str + + input : typing.Optional[typing.Any] + + expected_output : typing.Optional[typing.Any] + + metadata : typing.Optional[typing.Any] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + id : typing.Optional[str] + Dataset items are upserted on their id. Id needs to be unique (project-level) and cannot be reused across datasets. + + status : typing.Optional[DatasetStatus] + Defaults to ACTIVE for newly created items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DatasetItem] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/dataset-items", + method="POST", + json={ + "datasetName": dataset_name, + "input": input, + "expectedOutput": expected_output, + "metadata": metadata, + "sourceTraceId": source_trace_id, + "sourceObservationId": source_observation_id, + "id": id, + "status": status, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetItem, + parse_obj_as( + type_=DatasetItem, # 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( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DatasetItem]: + """ + Get a dataset item + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DatasetItem] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/dataset-items/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetItem, + parse_obj_as( + type_=DatasetItem, # 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 list( + self, + *, + dataset_name: typing.Optional[str] = None, + source_trace_id: typing.Optional[str] = None, + source_observation_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedDatasetItems]: + """ + Get dataset items + + Parameters + ---------- + dataset_name : typing.Optional[str] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedDatasetItems] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/dataset-items", + method="GET", + params={ + "datasetName": dataset_name, + "sourceTraceId": source_trace_id, + "sourceObservationId": source_observation_id, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasetItems, + parse_obj_as( + type_=PaginatedDatasetItems, # 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 delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeleteDatasetItemResponse]: + """ + Delete a dataset item and all its run items. This action is irreversible. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteDatasetItemResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/dataset-items/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDatasetItemResponse, + parse_obj_as( + type_=DeleteDatasetItemResponse, # 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 AsyncRawDatasetItemsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + *, + dataset_name: str, + input: typing.Optional[typing.Any] = OMIT, + expected_output: typing.Optional[typing.Any] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + source_trace_id: typing.Optional[str] = OMIT, + source_observation_id: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + status: typing.Optional[DatasetStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DatasetItem]: + """ + Create a dataset item + + Parameters + ---------- + dataset_name : str + + input : typing.Optional[typing.Any] + + expected_output : typing.Optional[typing.Any] + + metadata : typing.Optional[typing.Any] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + id : typing.Optional[str] + Dataset items are upserted on their id. Id needs to be unique (project-level) and cannot be reused across datasets. + + status : typing.Optional[DatasetStatus] + Defaults to ACTIVE for newly created items + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DatasetItem] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/dataset-items", + method="POST", + json={ + "datasetName": dataset_name, + "input": input, + "expectedOutput": expected_output, + "metadata": metadata, + "sourceTraceId": source_trace_id, + "sourceObservationId": source_observation_id, + "id": id, + "status": status, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetItem, + parse_obj_as( + type_=DatasetItem, # 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( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DatasetItem]: + """ + Get a dataset item + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DatasetItem] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/dataset-items/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetItem, + parse_obj_as( + type_=DatasetItem, # 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 list( + self, + *, + dataset_name: typing.Optional[str] = None, + source_trace_id: typing.Optional[str] = None, + source_observation_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedDatasetItems]: + """ + Get dataset items + + Parameters + ---------- + dataset_name : typing.Optional[str] + + source_trace_id : typing.Optional[str] + + source_observation_id : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedDatasetItems] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/dataset-items", + method="GET", + params={ + "datasetName": dataset_name, + "sourceTraceId": source_trace_id, + "sourceObservationId": source_observation_id, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasetItems, + parse_obj_as( + type_=PaginatedDatasetItems, # 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 delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeleteDatasetItemResponse]: + """ + Delete a dataset item and all its run items. This action is irreversible. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteDatasetItemResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/dataset-items/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDatasetItemResponse, + parse_obj_as( + type_=DeleteDatasetItemResponse, # 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/dataset_items/types/__init__.py b/langfuse/api/dataset_items/types/__init__.py new file mode 100644 index 000000000..c7ce59bf4 --- /dev/null +++ b/langfuse/api/dataset_items/types/__init__.py @@ -0,0 +1,50 @@ +# 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_dataset_item_request import CreateDatasetItemRequest + from .delete_dataset_item_response import DeleteDatasetItemResponse + from .paginated_dataset_items import PaginatedDatasetItems +_dynamic_imports: typing.Dict[str, str] = { + "CreateDatasetItemRequest": ".create_dataset_item_request", + "DeleteDatasetItemResponse": ".delete_dataset_item_response", + "PaginatedDatasetItems": ".paginated_dataset_items", +} + + +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__ = [ + "CreateDatasetItemRequest", + "DeleteDatasetItemResponse", + "PaginatedDatasetItems", +] diff --git a/langfuse/api/dataset_items/types/create_dataset_item_request.py b/langfuse/api/dataset_items/types/create_dataset_item_request.py new file mode 100644 index 000000000..b778e42ae --- /dev/null +++ b/langfuse/api/dataset_items/types/create_dataset_item_request.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...commons.types.dataset_status import DatasetStatus +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class CreateDatasetItemRequest(UniversalBaseModel): + dataset_name: typing_extensions.Annotated[str, FieldMetadata(alias="datasetName")] + input: typing.Optional[typing.Any] = None + expected_output: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="expectedOutput") + ] = None + metadata: typing.Optional[typing.Any] = None + source_trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sourceTraceId") + ] = None + source_observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sourceObservationId") + ] = None + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Dataset items are upserted on their id. Id needs to be unique (project-level) and cannot be reused across datasets. + """ + + status: typing.Optional[DatasetStatus] = pydantic.Field(default=None) + """ + Defaults to ACTIVE for newly created items + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/dataset_items/types/delete_dataset_item_response.py b/langfuse/api/dataset_items/types/delete_dataset_item_response.py new file mode 100644 index 000000000..982e4f2dd --- /dev/null +++ b/langfuse/api/dataset_items/types/delete_dataset_item_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class DeleteDatasetItemResponse(UniversalBaseModel): + message: str = pydantic.Field() + """ + Success message after deletion + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/dataset_items/types/paginated_dataset_items.py b/langfuse/api/dataset_items/types/paginated_dataset_items.py new file mode 100644 index 000000000..63a683787 --- /dev/null +++ b/langfuse/api/dataset_items/types/paginated_dataset_items.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.dataset_item import DatasetItem +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class PaginatedDatasetItems(UniversalBaseModel): + data: typing.List[DatasetItem] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/dataset_run_items/__init__.py b/langfuse/api/dataset_run_items/__init__.py new file mode 100644 index 000000000..9ff4e097e --- /dev/null +++ b/langfuse/api/dataset_run_items/__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 CreateDatasetRunItemRequest, PaginatedDatasetRunItems +_dynamic_imports: typing.Dict[str, str] = { + "CreateDatasetRunItemRequest": ".types", + "PaginatedDatasetRunItems": ".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__ = ["CreateDatasetRunItemRequest", "PaginatedDatasetRunItems"] diff --git a/langfuse/api/dataset_run_items/client.py b/langfuse/api/dataset_run_items/client.py new file mode 100644 index 000000000..e033ebbc8 --- /dev/null +++ b/langfuse/api/dataset_run_items/client.py @@ -0,0 +1,306 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..commons.types.dataset_run_item import DatasetRunItem +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawDatasetRunItemsClient, RawDatasetRunItemsClient +from .types.paginated_dataset_run_items import PaginatedDatasetRunItems + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DatasetRunItemsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDatasetRunItemsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDatasetRunItemsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDatasetRunItemsClient + """ + return self._raw_client + + def create( + self, + *, + run_name: str, + dataset_item_id: str, + run_description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + observation_id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DatasetRunItem: + """ + Create a dataset run item + + Parameters + ---------- + run_name : str + + dataset_item_id : str + + run_description : typing.Optional[str] + Description of the run. If run exists, description will be updated. + + metadata : typing.Optional[typing.Any] + Metadata of the dataset run, updates run if run already exists + + observation_id : typing.Optional[str] + + trace_id : typing.Optional[str] + traceId should always be provided. For compatibility with older SDK versions it can also be inferred from the provided observationId. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetRunItem + + 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.dataset_run_items.create( + run_name="runName", + dataset_item_id="datasetItemId", + ) + """ + _response = self._raw_client.create( + run_name=run_name, + dataset_item_id=dataset_item_id, + run_description=run_description, + metadata=metadata, + observation_id=observation_id, + trace_id=trace_id, + request_options=request_options, + ) + return _response.data + + def list( + self, + *, + dataset_id: str, + run_name: str, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasetRunItems: + """ + List dataset run items + + Parameters + ---------- + dataset_id : str + + run_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasetRunItems + + 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.dataset_run_items.list( + dataset_id="datasetId", + run_name="runName", + ) + """ + _response = self._raw_client.list( + dataset_id=dataset_id, + run_name=run_name, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data + + +class AsyncDatasetRunItemsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDatasetRunItemsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDatasetRunItemsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDatasetRunItemsClient + """ + return self._raw_client + + async def create( + self, + *, + run_name: str, + dataset_item_id: str, + run_description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + observation_id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DatasetRunItem: + """ + Create a dataset run item + + Parameters + ---------- + run_name : str + + dataset_item_id : str + + run_description : typing.Optional[str] + Description of the run. If run exists, description will be updated. + + metadata : typing.Optional[typing.Any] + Metadata of the dataset run, updates run if run already exists + + observation_id : typing.Optional[str] + + trace_id : typing.Optional[str] + traceId should always be provided. For compatibility with older SDK versions it can also be inferred from the provided observationId. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetRunItem + + 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.dataset_run_items.create( + run_name="runName", + dataset_item_id="datasetItemId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + run_name=run_name, + dataset_item_id=dataset_item_id, + run_description=run_description, + metadata=metadata, + observation_id=observation_id, + trace_id=trace_id, + request_options=request_options, + ) + return _response.data + + async def list( + self, + *, + dataset_id: str, + run_name: str, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasetRunItems: + """ + List dataset run items + + Parameters + ---------- + dataset_id : str + + run_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasetRunItems + + 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.dataset_run_items.list( + dataset_id="datasetId", + run_name="runName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + dataset_id=dataset_id, + run_name=run_name, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data diff --git a/langfuse/api/dataset_run_items/raw_client.py b/langfuse/api/dataset_run_items/raw_client.py new file mode 100644 index 000000000..17a7e29f0 --- /dev/null +++ b/langfuse/api/dataset_run_items/raw_client.py @@ -0,0 +1,530 @@ +# 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 ..commons.types.dataset_run_item import DatasetRunItem +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.paginated_dataset_run_items import PaginatedDatasetRunItems + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDatasetRunItemsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + *, + run_name: str, + dataset_item_id: str, + run_description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + observation_id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DatasetRunItem]: + """ + Create a dataset run item + + Parameters + ---------- + run_name : str + + dataset_item_id : str + + run_description : typing.Optional[str] + Description of the run. If run exists, description will be updated. + + metadata : typing.Optional[typing.Any] + Metadata of the dataset run, updates run if run already exists + + observation_id : typing.Optional[str] + + trace_id : typing.Optional[str] + traceId should always be provided. For compatibility with older SDK versions it can also be inferred from the provided observationId. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DatasetRunItem] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/dataset-run-items", + method="POST", + json={ + "runName": run_name, + "runDescription": run_description, + "metadata": metadata, + "datasetItemId": dataset_item_id, + "observationId": observation_id, + "traceId": trace_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetRunItem, + parse_obj_as( + type_=DatasetRunItem, # 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 list( + self, + *, + dataset_id: str, + run_name: str, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedDatasetRunItems]: + """ + List dataset run items + + Parameters + ---------- + dataset_id : str + + run_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedDatasetRunItems] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/dataset-run-items", + method="GET", + params={ + "datasetId": dataset_id, + "runName": run_name, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasetRunItems, + parse_obj_as( + type_=PaginatedDatasetRunItems, # 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 AsyncRawDatasetRunItemsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + *, + run_name: str, + dataset_item_id: str, + run_description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + observation_id: typing.Optional[str] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DatasetRunItem]: + """ + Create a dataset run item + + Parameters + ---------- + run_name : str + + dataset_item_id : str + + run_description : typing.Optional[str] + Description of the run. If run exists, description will be updated. + + metadata : typing.Optional[typing.Any] + Metadata of the dataset run, updates run if run already exists + + observation_id : typing.Optional[str] + + trace_id : typing.Optional[str] + traceId should always be provided. For compatibility with older SDK versions it can also be inferred from the provided observationId. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DatasetRunItem] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/dataset-run-items", + method="POST", + json={ + "runName": run_name, + "runDescription": run_description, + "metadata": metadata, + "datasetItemId": dataset_item_id, + "observationId": observation_id, + "traceId": trace_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetRunItem, + parse_obj_as( + type_=DatasetRunItem, # 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 list( + self, + *, + dataset_id: str, + run_name: str, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedDatasetRunItems]: + """ + List dataset run items + + Parameters + ---------- + dataset_id : str + + run_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedDatasetRunItems] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/dataset-run-items", + method="GET", + params={ + "datasetId": dataset_id, + "runName": run_name, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasetRunItems, + parse_obj_as( + type_=PaginatedDatasetRunItems, # 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/dataset_run_items/types/__init__.py b/langfuse/api/dataset_run_items/types/__init__.py new file mode 100644 index 000000000..b520924c0 --- /dev/null +++ b/langfuse/api/dataset_run_items/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_dataset_run_item_request import CreateDatasetRunItemRequest + from .paginated_dataset_run_items import PaginatedDatasetRunItems +_dynamic_imports: typing.Dict[str, str] = { + "CreateDatasetRunItemRequest": ".create_dataset_run_item_request", + "PaginatedDatasetRunItems": ".paginated_dataset_run_items", +} + + +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__ = ["CreateDatasetRunItemRequest", "PaginatedDatasetRunItems"] diff --git a/langfuse/api/dataset_run_items/types/create_dataset_run_item_request.py b/langfuse/api/dataset_run_items/types/create_dataset_run_item_request.py new file mode 100644 index 000000000..cfa3bc1de --- /dev/null +++ b/langfuse/api/dataset_run_items/types/create_dataset_run_item_request.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class CreateDatasetRunItemRequest(UniversalBaseModel): + run_name: typing_extensions.Annotated[str, FieldMetadata(alias="runName")] + run_description: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="runDescription") + ] = pydantic.Field(default=None) + """ + Description of the run. If run exists, description will be updated. + """ + + metadata: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + Metadata of the dataset run, updates run if run already exists + """ + + dataset_item_id: typing_extensions.Annotated[ + str, FieldMetadata(alias="datasetItemId") + ] + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = pydantic.Field(default=None) + """ + traceId should always be provided. For compatibility with older SDK versions it can also be inferred from the provided observationId. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/dataset_run_items/types/paginated_dataset_run_items.py b/langfuse/api/dataset_run_items/types/paginated_dataset_run_items.py new file mode 100644 index 000000000..24f7c1996 --- /dev/null +++ b/langfuse/api/dataset_run_items/types/paginated_dataset_run_items.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.dataset_run_item import DatasetRunItem +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class PaginatedDatasetRunItems(UniversalBaseModel): + data: typing.List[DatasetRunItem] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/datasets/__init__.py b/langfuse/api/datasets/__init__.py new file mode 100644 index 000000000..e9005285e --- /dev/null +++ b/langfuse/api/datasets/__init__.py @@ -0,0 +1,55 @@ +# 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 ( + CreateDatasetRequest, + DeleteDatasetRunResponse, + PaginatedDatasetRuns, + PaginatedDatasets, + ) +_dynamic_imports: typing.Dict[str, str] = { + "CreateDatasetRequest": ".types", + "DeleteDatasetRunResponse": ".types", + "PaginatedDatasetRuns": ".types", + "PaginatedDatasets": ".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__ = [ + "CreateDatasetRequest", + "DeleteDatasetRunResponse", + "PaginatedDatasetRuns", + "PaginatedDatasets", +] diff --git a/langfuse/api/datasets/client.py b/langfuse/api/datasets/client.py new file mode 100644 index 000000000..859ee0fe7 --- /dev/null +++ b/langfuse/api/datasets/client.py @@ -0,0 +1,661 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..commons.types.dataset import Dataset +from ..commons.types.dataset_run_with_items import DatasetRunWithItems +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawDatasetsClient, RawDatasetsClient +from .types.delete_dataset_run_response import DeleteDatasetRunResponse +from .types.paginated_dataset_runs import PaginatedDatasetRuns +from .types.paginated_datasets import PaginatedDatasets + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DatasetsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDatasetsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDatasetsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDatasetsClient + """ + return self._raw_client + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasets: + """ + Get all datasets + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasets + + 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.datasets.list() + """ + _response = self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + def get( + self, + dataset_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dataset: + """ + Get a dataset + + Parameters + ---------- + dataset_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dataset + + 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.datasets.get( + dataset_name="datasetName", + ) + """ + _response = self._raw_client.get(dataset_name, request_options=request_options) + return _response.data + + def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + input_schema: typing.Optional[typing.Any] = OMIT, + expected_output_schema: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dataset: + """ + Create a dataset + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + metadata : typing.Optional[typing.Any] + + input_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item inputs. When set, all new and existing dataset items will be validated against this schema. + + expected_output_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item expected outputs. When set, all new and existing dataset items will be validated against this schema. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dataset + + 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.datasets.create( + name="name", + ) + """ + _response = self._raw_client.create( + name=name, + description=description, + metadata=metadata, + input_schema=input_schema, + expected_output_schema=expected_output_schema, + request_options=request_options, + ) + return _response.data + + def get_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DatasetRunWithItems: + """ + Get a dataset run and its items + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetRunWithItems + + 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.datasets.get_run( + dataset_name="datasetName", + run_name="runName", + ) + """ + _response = self._raw_client.get_run( + dataset_name, run_name, request_options=request_options + ) + return _response.data + + def delete_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDatasetRunResponse: + """ + Delete a dataset run and all its run items. This action is irreversible. + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetRunResponse + + 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.datasets.delete_run( + dataset_name="datasetName", + run_name="runName", + ) + """ + _response = self._raw_client.delete_run( + dataset_name, run_name, request_options=request_options + ) + return _response.data + + def get_runs( + self, + dataset_name: str, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasetRuns: + """ + Get dataset runs + + Parameters + ---------- + dataset_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasetRuns + + 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.datasets.get_runs( + dataset_name="datasetName", + ) + """ + _response = self._raw_client.get_runs( + dataset_name, page=page, limit=limit, request_options=request_options + ) + return _response.data + + +class AsyncDatasetsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDatasetsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDatasetsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDatasetsClient + """ + return self._raw_client + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasets: + """ + Get all datasets + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasets + + 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.datasets.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def get( + self, + dataset_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dataset: + """ + Get a dataset + + Parameters + ---------- + dataset_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dataset + + 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.datasets.get( + dataset_name="datasetName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + dataset_name, request_options=request_options + ) + return _response.data + + async def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + input_schema: typing.Optional[typing.Any] = OMIT, + expected_output_schema: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dataset: + """ + Create a dataset + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + metadata : typing.Optional[typing.Any] + + input_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item inputs. When set, all new and existing dataset items will be validated against this schema. + + expected_output_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item expected outputs. When set, all new and existing dataset items will be validated against this schema. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dataset + + 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.datasets.create( + name="name", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + name=name, + description=description, + metadata=metadata, + input_schema=input_schema, + expected_output_schema=expected_output_schema, + request_options=request_options, + ) + return _response.data + + async def get_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DatasetRunWithItems: + """ + Get a dataset run and its items + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DatasetRunWithItems + + 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.datasets.get_run( + dataset_name="datasetName", + run_name="runName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_run( + dataset_name, run_name, request_options=request_options + ) + return _response.data + + async def delete_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDatasetRunResponse: + """ + Delete a dataset run and all its run items. This action is irreversible. + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDatasetRunResponse + + 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.datasets.delete_run( + dataset_name="datasetName", + run_name="runName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_run( + dataset_name, run_name, request_options=request_options + ) + return _response.data + + async def get_runs( + self, + dataset_name: str, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedDatasetRuns: + """ + Get dataset runs + + Parameters + ---------- + dataset_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedDatasetRuns + + 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.datasets.get_runs( + dataset_name="datasetName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_runs( + dataset_name, page=page, limit=limit, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/datasets/raw_client.py b/langfuse/api/datasets/raw_client.py new file mode 100644 index 000000000..306ad8f76 --- /dev/null +++ b/langfuse/api/datasets/raw_client.py @@ -0,0 +1,1368 @@ +# 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 ..commons.types.dataset import Dataset +from ..commons.types.dataset_run_with_items import DatasetRunWithItems +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 .types.delete_dataset_run_response import DeleteDatasetRunResponse +from .types.paginated_dataset_runs import PaginatedDatasetRuns +from .types.paginated_datasets import PaginatedDatasets + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDatasetsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedDatasets]: + """ + Get all datasets + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedDatasets] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/v2/datasets", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasets, + parse_obj_as( + type_=PaginatedDatasets, # 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( + self, + dataset_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Dataset]: + """ + Get a dataset + + Parameters + ---------- + dataset_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Dataset] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/v2/datasets/{jsonable_encoder(dataset_name)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dataset, + parse_obj_as( + type_=Dataset, # 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 create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + input_schema: typing.Optional[typing.Any] = OMIT, + expected_output_schema: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Dataset]: + """ + Create a dataset + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + metadata : typing.Optional[typing.Any] + + input_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item inputs. When set, all new and existing dataset items will be validated against this schema. + + expected_output_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item expected outputs. When set, all new and existing dataset items will be validated against this schema. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Dataset] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/v2/datasets", + method="POST", + json={ + "name": name, + "description": description, + "metadata": metadata, + "inputSchema": input_schema, + "expectedOutputSchema": expected_output_schema, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dataset, + parse_obj_as( + type_=Dataset, # 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_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DatasetRunWithItems]: + """ + Get a dataset run and its items + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DatasetRunWithItems] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetRunWithItems, + parse_obj_as( + type_=DatasetRunWithItems, # 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 delete_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteDatasetRunResponse]: + """ + Delete a dataset run and all its run items. This action is irreversible. + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteDatasetRunResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDatasetRunResponse, + parse_obj_as( + type_=DeleteDatasetRunResponse, # 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_runs( + self, + dataset_name: str, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedDatasetRuns]: + """ + Get dataset runs + + Parameters + ---------- + dataset_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedDatasetRuns] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasetRuns, + parse_obj_as( + type_=PaginatedDatasetRuns, # 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 AsyncRawDatasetsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedDatasets]: + """ + Get all datasets + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedDatasets] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/v2/datasets", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasets, + parse_obj_as( + type_=PaginatedDatasets, # 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( + self, + dataset_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Dataset]: + """ + Get a dataset + + Parameters + ---------- + dataset_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Dataset] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/v2/datasets/{jsonable_encoder(dataset_name)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dataset, + parse_obj_as( + type_=Dataset, # 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 create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Any] = OMIT, + input_schema: typing.Optional[typing.Any] = OMIT, + expected_output_schema: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Dataset]: + """ + Create a dataset + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + metadata : typing.Optional[typing.Any] + + input_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item inputs. When set, all new and existing dataset items will be validated against this schema. + + expected_output_schema : typing.Optional[typing.Any] + JSON Schema for validating dataset item expected outputs. When set, all new and existing dataset items will be validated against this schema. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Dataset] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/v2/datasets", + method="POST", + json={ + "name": name, + "description": description, + "metadata": metadata, + "inputSchema": input_schema, + "expectedOutputSchema": expected_output_schema, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dataset, + parse_obj_as( + type_=Dataset, # 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_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DatasetRunWithItems]: + """ + Get a dataset run and its items + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DatasetRunWithItems] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DatasetRunWithItems, + parse_obj_as( + type_=DatasetRunWithItems, # 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 delete_run( + self, + dataset_name: str, + run_name: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteDatasetRunResponse]: + """ + Delete a dataset run and all its run items. This action is irreversible. + + Parameters + ---------- + dataset_name : str + + run_name : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteDatasetRunResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDatasetRunResponse, + parse_obj_as( + type_=DeleteDatasetRunResponse, # 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_runs( + self, + dataset_name: str, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedDatasetRuns]: + """ + Get dataset runs + + Parameters + ---------- + dataset_name : str + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedDatasetRuns] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedDatasetRuns, + parse_obj_as( + type_=PaginatedDatasetRuns, # 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/datasets/types/__init__.py b/langfuse/api/datasets/types/__init__.py new file mode 100644 index 000000000..60c58934a --- /dev/null +++ b/langfuse/api/datasets/types/__init__.py @@ -0,0 +1,53 @@ +# 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_dataset_request import CreateDatasetRequest + from .delete_dataset_run_response import DeleteDatasetRunResponse + from .paginated_dataset_runs import PaginatedDatasetRuns + from .paginated_datasets import PaginatedDatasets +_dynamic_imports: typing.Dict[str, str] = { + "CreateDatasetRequest": ".create_dataset_request", + "DeleteDatasetRunResponse": ".delete_dataset_run_response", + "PaginatedDatasetRuns": ".paginated_dataset_runs", + "PaginatedDatasets": ".paginated_datasets", +} + + +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__ = [ + "CreateDatasetRequest", + "DeleteDatasetRunResponse", + "PaginatedDatasetRuns", + "PaginatedDatasets", +] diff --git a/langfuse/api/datasets/types/create_dataset_request.py b/langfuse/api/datasets/types/create_dataset_request.py new file mode 100644 index 000000000..9d9b01089 --- /dev/null +++ b/langfuse/api/datasets/types/create_dataset_request.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class CreateDatasetRequest(UniversalBaseModel): + name: str + description: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + input_schema: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="inputSchema") + ] = pydantic.Field(default=None) + """ + JSON Schema for validating dataset item inputs. When set, all new and existing dataset items will be validated against this schema. + """ + + expected_output_schema: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="expectedOutputSchema") + ] = pydantic.Field(default=None) + """ + JSON Schema for validating dataset item expected outputs. When set, all new and existing dataset items will be validated against this schema. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/datasets/types/delete_dataset_run_response.py b/langfuse/api/datasets/types/delete_dataset_run_response.py new file mode 100644 index 000000000..024433ef6 --- /dev/null +++ b/langfuse/api/datasets/types/delete_dataset_run_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class DeleteDatasetRunResponse(UniversalBaseModel): + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/datasets/types/paginated_dataset_runs.py b/langfuse/api/datasets/types/paginated_dataset_runs.py new file mode 100644 index 000000000..85cd54c55 --- /dev/null +++ b/langfuse/api/datasets/types/paginated_dataset_runs.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.dataset_run import DatasetRun +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class PaginatedDatasetRuns(UniversalBaseModel): + data: typing.List[DatasetRun] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/datasets/types/paginated_datasets.py b/langfuse/api/datasets/types/paginated_datasets.py new file mode 100644 index 000000000..a2e83edf1 --- /dev/null +++ b/langfuse/api/datasets/types/paginated_datasets.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.dataset import Dataset +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class PaginatedDatasets(UniversalBaseModel): + data: typing.List[Dataset] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/health/__init__.py b/langfuse/api/health/__init__.py new file mode 100644 index 000000000..2a101c5f7 --- /dev/null +++ b/langfuse/api/health/__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 .types import HealthResponse + from .errors import ServiceUnavailableError +_dynamic_imports: typing.Dict[str, str] = { + "HealthResponse": ".types", + "ServiceUnavailableError": ".errors", +} + + +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__ = ["HealthResponse", "ServiceUnavailableError"] diff --git a/langfuse/api/health/client.py b/langfuse/api/health/client.py new file mode 100644 index 000000000..7406fef6f --- /dev/null +++ b/langfuse/api/health/client.py @@ -0,0 +1,112 @@ +# 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 AsyncRawHealthClient, RawHealthClient +from .types.health_response import HealthResponse + + +class HealthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawHealthClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawHealthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawHealthClient + """ + return self._raw_client + + def health( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HealthResponse: + """ + Check health of API and database + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HealthResponse + + 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.health.health() + """ + _response = self._raw_client.health(request_options=request_options) + return _response.data + + +class AsyncHealthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawHealthClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawHealthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawHealthClient + """ + return self._raw_client + + async def health( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HealthResponse: + """ + Check health of API and database + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HealthResponse + + 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.health.health() + + + asyncio.run(main()) + """ + _response = await self._raw_client.health(request_options=request_options) + return _response.data diff --git a/langfuse/api/health/errors/__init__.py b/langfuse/api/health/errors/__init__.py new file mode 100644 index 000000000..3567f86b3 --- /dev/null +++ b/langfuse/api/health/errors/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .service_unavailable_error import ServiceUnavailableError +_dynamic_imports: typing.Dict[str, str] = { + "ServiceUnavailableError": ".service_unavailable_error" +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ServiceUnavailableError"] diff --git a/langfuse/api/health/errors/service_unavailable_error.py b/langfuse/api/health/errors/service_unavailable_error.py new file mode 100644 index 000000000..68d5d836e --- /dev/null +++ b/langfuse/api/health/errors/service_unavailable_error.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class ServiceUnavailableError(ApiError): + def __init__(self, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__( + status_code=503, + headers=headers, + ) diff --git a/langfuse/api/health/raw_client.py b/langfuse/api/health/raw_client.py new file mode 100644 index 000000000..afeef1a96 --- /dev/null +++ b/langfuse/api/health/raw_client.py @@ -0,0 +1,227 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..commons.errors.access_denied_error import AccessDeniedError +from ..commons.errors.error import Error +from ..commons.errors.method_not_allowed_error import MethodNotAllowedError +from ..commons.errors.not_found_error import NotFoundError +from ..commons.errors.unauthorized_error import UnauthorizedError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from .errors.service_unavailable_error import ServiceUnavailableError +from .types.health_response import HealthResponse + + +class RawHealthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def health( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[HealthResponse]: + """ + Check health of API and database + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[HealthResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/health", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HealthResponse, + parse_obj_as( + type_=HealthResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 503: + raise ServiceUnavailableError(headers=dict(_response.headers)) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + +class AsyncRawHealthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def health( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[HealthResponse]: + """ + Check health of API and database + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[HealthResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/health", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HealthResponse, + parse_obj_as( + type_=HealthResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 503: + raise ServiceUnavailableError(headers=dict(_response.headers)) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) diff --git a/langfuse/api/health/types/__init__.py b/langfuse/api/health/types/__init__.py new file mode 100644 index 000000000..d4bec6804 --- /dev/null +++ b/langfuse/api/health/types/__init__.py @@ -0,0 +1,40 @@ +# 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 .health_response import HealthResponse +_dynamic_imports: typing.Dict[str, str] = {"HealthResponse": ".health_response"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["HealthResponse"] diff --git a/langfuse/api/health/types/health_response.py b/langfuse/api/health/types/health_response.py new file mode 100644 index 000000000..a1c7a4bea --- /dev/null +++ b/langfuse/api/health/types/health_response.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class HealthResponse(UniversalBaseModel): + """ + Examples + -------- + from langfuse.health import HealthResponse + + HealthResponse( + version="1.25.0", + status="OK", + ) + """ + + version: str = pydantic.Field() + """ + Langfuse server version + """ + + status: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/__init__.py b/langfuse/api/ingestion/__init__.py new file mode 100644 index 000000000..5cd4ba3bd --- /dev/null +++ b/langfuse/api/ingestion/__init__.py @@ -0,0 +1,169 @@ +# 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 ( + BaseEvent, + CreateEventBody, + CreateEventEvent, + CreateGenerationBody, + CreateGenerationEvent, + CreateObservationEvent, + CreateSpanBody, + CreateSpanEvent, + IngestionError, + IngestionEvent, + IngestionEvent_EventCreate, + IngestionEvent_GenerationCreate, + IngestionEvent_GenerationUpdate, + IngestionEvent_ObservationCreate, + IngestionEvent_ObservationUpdate, + IngestionEvent_ScoreCreate, + IngestionEvent_SdkLog, + IngestionEvent_SpanCreate, + IngestionEvent_SpanUpdate, + IngestionEvent_TraceCreate, + IngestionResponse, + IngestionSuccess, + IngestionUsage, + ObservationBody, + ObservationType, + OpenAiCompletionUsageSchema, + OpenAiResponseUsageSchema, + OpenAiUsage, + OptionalObservationBody, + ScoreBody, + ScoreEvent, + SdkLogBody, + SdkLogEvent, + TraceBody, + TraceEvent, + UpdateEventBody, + UpdateGenerationBody, + UpdateGenerationEvent, + UpdateObservationEvent, + UpdateSpanBody, + UpdateSpanEvent, + UsageDetails, + ) +_dynamic_imports: typing.Dict[str, str] = { + "BaseEvent": ".types", + "CreateEventBody": ".types", + "CreateEventEvent": ".types", + "CreateGenerationBody": ".types", + "CreateGenerationEvent": ".types", + "CreateObservationEvent": ".types", + "CreateSpanBody": ".types", + "CreateSpanEvent": ".types", + "IngestionError": ".types", + "IngestionEvent": ".types", + "IngestionEvent_EventCreate": ".types", + "IngestionEvent_GenerationCreate": ".types", + "IngestionEvent_GenerationUpdate": ".types", + "IngestionEvent_ObservationCreate": ".types", + "IngestionEvent_ObservationUpdate": ".types", + "IngestionEvent_ScoreCreate": ".types", + "IngestionEvent_SdkLog": ".types", + "IngestionEvent_SpanCreate": ".types", + "IngestionEvent_SpanUpdate": ".types", + "IngestionEvent_TraceCreate": ".types", + "IngestionResponse": ".types", + "IngestionSuccess": ".types", + "IngestionUsage": ".types", + "ObservationBody": ".types", + "ObservationType": ".types", + "OpenAiCompletionUsageSchema": ".types", + "OpenAiResponseUsageSchema": ".types", + "OpenAiUsage": ".types", + "OptionalObservationBody": ".types", + "ScoreBody": ".types", + "ScoreEvent": ".types", + "SdkLogBody": ".types", + "SdkLogEvent": ".types", + "TraceBody": ".types", + "TraceEvent": ".types", + "UpdateEventBody": ".types", + "UpdateGenerationBody": ".types", + "UpdateGenerationEvent": ".types", + "UpdateObservationEvent": ".types", + "UpdateSpanBody": ".types", + "UpdateSpanEvent": ".types", + "UsageDetails": ".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__ = [ + "BaseEvent", + "CreateEventBody", + "CreateEventEvent", + "CreateGenerationBody", + "CreateGenerationEvent", + "CreateObservationEvent", + "CreateSpanBody", + "CreateSpanEvent", + "IngestionError", + "IngestionEvent", + "IngestionEvent_EventCreate", + "IngestionEvent_GenerationCreate", + "IngestionEvent_GenerationUpdate", + "IngestionEvent_ObservationCreate", + "IngestionEvent_ObservationUpdate", + "IngestionEvent_ScoreCreate", + "IngestionEvent_SdkLog", + "IngestionEvent_SpanCreate", + "IngestionEvent_SpanUpdate", + "IngestionEvent_TraceCreate", + "IngestionResponse", + "IngestionSuccess", + "IngestionUsage", + "ObservationBody", + "ObservationType", + "OpenAiCompletionUsageSchema", + "OpenAiResponseUsageSchema", + "OpenAiUsage", + "OptionalObservationBody", + "ScoreBody", + "ScoreEvent", + "SdkLogBody", + "SdkLogEvent", + "TraceBody", + "TraceEvent", + "UpdateEventBody", + "UpdateGenerationBody", + "UpdateGenerationEvent", + "UpdateObservationEvent", + "UpdateSpanBody", + "UpdateSpanEvent", + "UsageDetails", +] diff --git a/langfuse/api/resources/ingestion/client.py b/langfuse/api/ingestion/client.py similarity index 64% rename from langfuse/api/resources/ingestion/client.py rename to langfuse/api/ingestion/client.py index c009c507b..af1da8396 100644 --- a/langfuse/api/resources/ingestion/client.py +++ b/langfuse/api/ingestion/client.py @@ -1,17 +1,10 @@ # 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.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 ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawIngestionClient, RawIngestionClient from .types.ingestion_event import IngestionEvent from .types.ingestion_response import IngestionResponse @@ -21,7 +14,18 @@ class IngestionClient: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = RawIngestionClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawIngestionClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawIngestionClient + """ + return self._raw_client def batch( self, @@ -66,10 +70,10 @@ def batch( -------- import datetime - from langfuse import IngestionEvent_TraceCreate, TraceBody - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI + from langfuse.ingestion import IngestionEvent_TraceCreate, TraceBody - client = FernLangfuse( + 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", @@ -103,43 +107,26 @@ def batch( ], ) """ - _response = self._client_wrapper.httpx_client.request( - "api/public/ingestion", - method="POST", - json={"batch": batch, "metadata": metadata}, - request_options=request_options, - omit=OMIT, + _response = self._raw_client.batch( + batch=batch, metadata=metadata, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(IngestionResponse, _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) + return _response.data class AsyncIngestionClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = AsyncRawIngestionClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawIngestionClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawIngestionClient + """ + return self._raw_client async def batch( self, @@ -185,10 +172,10 @@ async def batch( import asyncio import datetime - from langfuse import IngestionEvent_TraceCreate, TraceBody - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI + from langfuse.ingestion import IngestionEvent_TraceCreate, TraceBody - client = AsyncFernLangfuse( + 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", @@ -228,35 +215,7 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/ingestion", - method="POST", - json={"batch": batch, "metadata": metadata}, - request_options=request_options, - omit=OMIT, + _response = await self._raw_client.batch( + batch=batch, metadata=metadata, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(IngestionResponse, _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) + return _response.data diff --git a/langfuse/api/ingestion/raw_client.py b/langfuse/api/ingestion/raw_client.py new file mode 100644 index 000000000..cb60a5fba --- /dev/null +++ b/langfuse/api/ingestion/raw_client.py @@ -0,0 +1,293 @@ +# 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 ..core.serialization import convert_and_respect_annotation_metadata +from .types.ingestion_event import IngestionEvent +from .types.ingestion_response import IngestionResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawIngestionClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def batch( + self, + *, + batch: typing.Sequence[IngestionEvent], + metadata: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[IngestionResponse]: + """ + **Legacy endpoint for batch ingestion for Langfuse Observability.** + + -> Please use the OpenTelemetry endpoint (`/api/public/otel/v1/traces`). 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. + Internally, we refer to this as the "event envelope" as it tells us something about the event but not the trace. + We use the event id within this envelope to deduplicate messages to avoid processing the same event twice, i.e. the event id should be unique per request. + The event.body.id is the ID of the actual trace and will be used for updates and will be visible within the Langfuse App. + 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/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. + + Parameters + ---------- + batch : typing.Sequence[IngestionEvent] + Batch of tracing events to be ingested. Discriminated by attribute `type`. + + metadata : typing.Optional[typing.Any] + Optional. Metadata field used by the Langfuse SDKs for debugging. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[IngestionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/ingestion", + method="POST", + json={ + "batch": convert_and_respect_annotation_metadata( + object_=batch, + annotation=typing.Sequence[IngestionEvent], + direction="write", + ), + "metadata": metadata, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + IngestionResponse, + parse_obj_as( + type_=IngestionResponse, # 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 AsyncRawIngestionClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def batch( + self, + *, + batch: typing.Sequence[IngestionEvent], + metadata: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[IngestionResponse]: + """ + **Legacy endpoint for batch ingestion for Langfuse Observability.** + + -> Please use the OpenTelemetry endpoint (`/api/public/otel/v1/traces`). 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. + Internally, we refer to this as the "event envelope" as it tells us something about the event but not the trace. + We use the event id within this envelope to deduplicate messages to avoid processing the same event twice, i.e. the event id should be unique per request. + The event.body.id is the ID of the actual trace and will be used for updates and will be visible within the Langfuse App. + 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/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. + + Parameters + ---------- + batch : typing.Sequence[IngestionEvent] + Batch of tracing events to be ingested. Discriminated by attribute `type`. + + metadata : typing.Optional[typing.Any] + Optional. Metadata field used by the Langfuse SDKs for debugging. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[IngestionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/ingestion", + method="POST", + json={ + "batch": convert_and_respect_annotation_metadata( + object_=batch, + annotation=typing.Sequence[IngestionEvent], + direction="write", + ), + "metadata": metadata, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + IngestionResponse, + parse_obj_as( + type_=IngestionResponse, # 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/ingestion/types/__init__.py b/langfuse/api/ingestion/types/__init__.py new file mode 100644 index 000000000..4addfd9c7 --- /dev/null +++ b/langfuse/api/ingestion/types/__init__.py @@ -0,0 +1,169 @@ +# 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 .base_event import BaseEvent + from .create_event_body import CreateEventBody + from .create_event_event import CreateEventEvent + from .create_generation_body import CreateGenerationBody + from .create_generation_event import CreateGenerationEvent + from .create_observation_event import CreateObservationEvent + from .create_span_body import CreateSpanBody + from .create_span_event import CreateSpanEvent + from .ingestion_error import IngestionError + from .ingestion_event import ( + IngestionEvent, + IngestionEvent_EventCreate, + IngestionEvent_GenerationCreate, + IngestionEvent_GenerationUpdate, + IngestionEvent_ObservationCreate, + IngestionEvent_ObservationUpdate, + IngestionEvent_ScoreCreate, + IngestionEvent_SdkLog, + IngestionEvent_SpanCreate, + IngestionEvent_SpanUpdate, + IngestionEvent_TraceCreate, + ) + from .ingestion_response import IngestionResponse + from .ingestion_success import IngestionSuccess + from .ingestion_usage import IngestionUsage + from .observation_body import ObservationBody + from .observation_type import ObservationType + from .open_ai_completion_usage_schema import OpenAiCompletionUsageSchema + from .open_ai_response_usage_schema import OpenAiResponseUsageSchema + from .open_ai_usage import OpenAiUsage + from .optional_observation_body import OptionalObservationBody + from .score_body import ScoreBody + from .score_event import ScoreEvent + from .sdk_log_body import SdkLogBody + from .sdk_log_event import SdkLogEvent + from .trace_body import TraceBody + from .trace_event import TraceEvent + from .update_event_body import UpdateEventBody + from .update_generation_body import UpdateGenerationBody + from .update_generation_event import UpdateGenerationEvent + from .update_observation_event import UpdateObservationEvent + from .update_span_body import UpdateSpanBody + from .update_span_event import UpdateSpanEvent + from .usage_details import UsageDetails +_dynamic_imports: typing.Dict[str, str] = { + "BaseEvent": ".base_event", + "CreateEventBody": ".create_event_body", + "CreateEventEvent": ".create_event_event", + "CreateGenerationBody": ".create_generation_body", + "CreateGenerationEvent": ".create_generation_event", + "CreateObservationEvent": ".create_observation_event", + "CreateSpanBody": ".create_span_body", + "CreateSpanEvent": ".create_span_event", + "IngestionError": ".ingestion_error", + "IngestionEvent": ".ingestion_event", + "IngestionEvent_EventCreate": ".ingestion_event", + "IngestionEvent_GenerationCreate": ".ingestion_event", + "IngestionEvent_GenerationUpdate": ".ingestion_event", + "IngestionEvent_ObservationCreate": ".ingestion_event", + "IngestionEvent_ObservationUpdate": ".ingestion_event", + "IngestionEvent_ScoreCreate": ".ingestion_event", + "IngestionEvent_SdkLog": ".ingestion_event", + "IngestionEvent_SpanCreate": ".ingestion_event", + "IngestionEvent_SpanUpdate": ".ingestion_event", + "IngestionEvent_TraceCreate": ".ingestion_event", + "IngestionResponse": ".ingestion_response", + "IngestionSuccess": ".ingestion_success", + "IngestionUsage": ".ingestion_usage", + "ObservationBody": ".observation_body", + "ObservationType": ".observation_type", + "OpenAiCompletionUsageSchema": ".open_ai_completion_usage_schema", + "OpenAiResponseUsageSchema": ".open_ai_response_usage_schema", + "OpenAiUsage": ".open_ai_usage", + "OptionalObservationBody": ".optional_observation_body", + "ScoreBody": ".score_body", + "ScoreEvent": ".score_event", + "SdkLogBody": ".sdk_log_body", + "SdkLogEvent": ".sdk_log_event", + "TraceBody": ".trace_body", + "TraceEvent": ".trace_event", + "UpdateEventBody": ".update_event_body", + "UpdateGenerationBody": ".update_generation_body", + "UpdateGenerationEvent": ".update_generation_event", + "UpdateObservationEvent": ".update_observation_event", + "UpdateSpanBody": ".update_span_body", + "UpdateSpanEvent": ".update_span_event", + "UsageDetails": ".usage_details", +} + + +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__ = [ + "BaseEvent", + "CreateEventBody", + "CreateEventEvent", + "CreateGenerationBody", + "CreateGenerationEvent", + "CreateObservationEvent", + "CreateSpanBody", + "CreateSpanEvent", + "IngestionError", + "IngestionEvent", + "IngestionEvent_EventCreate", + "IngestionEvent_GenerationCreate", + "IngestionEvent_GenerationUpdate", + "IngestionEvent_ObservationCreate", + "IngestionEvent_ObservationUpdate", + "IngestionEvent_ScoreCreate", + "IngestionEvent_SdkLog", + "IngestionEvent_SpanCreate", + "IngestionEvent_SpanUpdate", + "IngestionEvent_TraceCreate", + "IngestionResponse", + "IngestionSuccess", + "IngestionUsage", + "ObservationBody", + "ObservationType", + "OpenAiCompletionUsageSchema", + "OpenAiResponseUsageSchema", + "OpenAiUsage", + "OptionalObservationBody", + "ScoreBody", + "ScoreEvent", + "SdkLogBody", + "SdkLogEvent", + "TraceBody", + "TraceEvent", + "UpdateEventBody", + "UpdateGenerationBody", + "UpdateGenerationEvent", + "UpdateObservationEvent", + "UpdateSpanBody", + "UpdateSpanEvent", + "UsageDetails", +] diff --git a/langfuse/api/ingestion/types/base_event.py b/langfuse/api/ingestion/types/base_event.py new file mode 100644 index 000000000..70c6bcfa4 --- /dev/null +++ b/langfuse/api/ingestion/types/base_event.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class BaseEvent(UniversalBaseModel): + id: str = pydantic.Field() + """ + UUID v4 that identifies the event + """ + + timestamp: str = pydantic.Field() + """ + Datetime (ISO 8601) of event creation in client. Should be as close to actual event creation in client as possible, this timestamp will be used for ordering of events in future release. Resolution: milliseconds (required), microseconds (optimal). + """ + + metadata: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + Optional. Metadata field used by the Langfuse SDKs for debugging. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/create_event_body.py b/langfuse/api/ingestion/types/create_event_body.py new file mode 100644 index 000000000..0473d8a0d --- /dev/null +++ b/langfuse/api/ingestion/types/create_event_body.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .optional_observation_body import OptionalObservationBody + + +class CreateEventBody(OptionalObservationBody): + id: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/create_event_event.py b/langfuse/api/ingestion/types/create_event_event.py new file mode 100644 index 000000000..e0cc820e1 --- /dev/null +++ b/langfuse/api/ingestion/types/create_event_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .create_event_body import CreateEventBody + + +class CreateEventEvent(BaseEvent): + body: CreateEventBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/create_generation_body.py b/langfuse/api/ingestion/types/create_generation_body.py new file mode 100644 index 000000000..72cb57116 --- /dev/null +++ b/langfuse/api/ingestion/types/create_generation_body.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...commons.types.map_value import MapValue +from ...core.serialization import FieldMetadata +from .create_span_body import CreateSpanBody +from .ingestion_usage import IngestionUsage +from .usage_details import UsageDetails + + +class CreateGenerationBody(CreateSpanBody): + completion_start_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="completionStartTime") + ] = None + model: typing.Optional[str] = None + model_parameters: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, MapValue]], + FieldMetadata(alias="modelParameters"), + ] = None + usage: typing.Optional[IngestionUsage] = None + usage_details: typing_extensions.Annotated[ + typing.Optional[UsageDetails], FieldMetadata(alias="usageDetails") + ] = None + cost_details: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, float]], FieldMetadata(alias="costDetails") + ] = None + prompt_name: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="promptName") + ] = None + prompt_version: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="promptVersion") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/create_generation_event.py b/langfuse/api/ingestion/types/create_generation_event.py new file mode 100644 index 000000000..d62d6cc41 --- /dev/null +++ b/langfuse/api/ingestion/types/create_generation_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .create_generation_body import CreateGenerationBody + + +class CreateGenerationEvent(BaseEvent): + body: CreateGenerationBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/create_observation_event.py b/langfuse/api/ingestion/types/create_observation_event.py new file mode 100644 index 000000000..06d927f36 --- /dev/null +++ b/langfuse/api/ingestion/types/create_observation_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .observation_body import ObservationBody + + +class CreateObservationEvent(BaseEvent): + body: ObservationBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/create_span_body.py b/langfuse/api/ingestion/types/create_span_body.py new file mode 100644 index 000000000..7a47d9748 --- /dev/null +++ b/langfuse/api/ingestion/types/create_span_body.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .create_event_body import CreateEventBody + + +class CreateSpanBody(CreateEventBody): + end_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="endTime") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/create_span_event.py b/langfuse/api/ingestion/types/create_span_event.py new file mode 100644 index 000000000..6e60cf1fe --- /dev/null +++ b/langfuse/api/ingestion/types/create_span_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .create_span_body import CreateSpanBody + + +class CreateSpanEvent(BaseEvent): + body: CreateSpanBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/ingestion_error.py b/langfuse/api/ingestion/types/ingestion_error.py new file mode 100644 index 000000000..2391d95eb --- /dev/null +++ b/langfuse/api/ingestion/types/ingestion_error.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class IngestionError(UniversalBaseModel): + id: str + status: int + message: typing.Optional[str] = None + error: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/ingestion_event.py b/langfuse/api/ingestion/types/ingestion_event.py new file mode 100644 index 000000000..03202e635 --- /dev/null +++ b/langfuse/api/ingestion/types/ingestion_event.py @@ -0,0 +1,155 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from .create_event_body import CreateEventBody +from .create_generation_body import CreateGenerationBody +from .create_span_body import CreateSpanBody +from .observation_body import ObservationBody +from .score_body import ScoreBody +from .sdk_log_body import SdkLogBody +from .trace_body import TraceBody +from .update_generation_body import UpdateGenerationBody +from .update_span_body import UpdateSpanBody + + +class IngestionEvent_TraceCreate(UniversalBaseModel): + type: typing.Literal["trace-create"] = "trace-create" + body: TraceBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_ScoreCreate(UniversalBaseModel): + type: typing.Literal["score-create"] = "score-create" + body: ScoreBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_SpanCreate(UniversalBaseModel): + type: typing.Literal["span-create"] = "span-create" + body: CreateSpanBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_SpanUpdate(UniversalBaseModel): + type: typing.Literal["span-update"] = "span-update" + body: UpdateSpanBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_GenerationCreate(UniversalBaseModel): + type: typing.Literal["generation-create"] = "generation-create" + body: CreateGenerationBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_GenerationUpdate(UniversalBaseModel): + type: typing.Literal["generation-update"] = "generation-update" + body: UpdateGenerationBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_EventCreate(UniversalBaseModel): + type: typing.Literal["event-create"] = "event-create" + body: CreateEventBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_SdkLog(UniversalBaseModel): + type: typing.Literal["sdk-log"] = "sdk-log" + body: SdkLogBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_ObservationCreate(UniversalBaseModel): + type: typing.Literal["observation-create"] = "observation-create" + body: ObservationBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class IngestionEvent_ObservationUpdate(UniversalBaseModel): + type: typing.Literal["observation-update"] = "observation-update" + body: ObservationBody + id: str + timestamp: str + metadata: typing.Optional[typing.Any] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +IngestionEvent = typing_extensions.Annotated[ + typing.Union[ + IngestionEvent_TraceCreate, + IngestionEvent_ScoreCreate, + IngestionEvent_SpanCreate, + IngestionEvent_SpanUpdate, + IngestionEvent_GenerationCreate, + IngestionEvent_GenerationUpdate, + IngestionEvent_EventCreate, + IngestionEvent_SdkLog, + IngestionEvent_ObservationCreate, + IngestionEvent_ObservationUpdate, + ], + pydantic.Field(discriminator="type"), +] diff --git a/langfuse/api/ingestion/types/ingestion_response.py b/langfuse/api/ingestion/types/ingestion_response.py new file mode 100644 index 000000000..b9781fab5 --- /dev/null +++ b/langfuse/api/ingestion/types/ingestion_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .ingestion_error import IngestionError +from .ingestion_success import IngestionSuccess + + +class IngestionResponse(UniversalBaseModel): + successes: typing.List[IngestionSuccess] + errors: typing.List[IngestionError] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/ingestion_success.py b/langfuse/api/ingestion/types/ingestion_success.py new file mode 100644 index 000000000..e7894797a --- /dev/null +++ b/langfuse/api/ingestion/types/ingestion_success.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class IngestionSuccess(UniversalBaseModel): + id: str + status: int + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/ingestion/types/ingestion_usage.py b/langfuse/api/ingestion/types/ingestion_usage.py similarity index 100% rename from langfuse/api/resources/ingestion/types/ingestion_usage.py rename to langfuse/api/ingestion/types/ingestion_usage.py diff --git a/langfuse/api/ingestion/types/observation_body.py b/langfuse/api/ingestion/types/observation_body.py new file mode 100644 index 000000000..e989a768f --- /dev/null +++ b/langfuse/api/ingestion/types/observation_body.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...commons.types.map_value import MapValue +from ...commons.types.observation_level import ObservationLevel +from ...commons.types.usage import Usage +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .observation_type import ObservationType + + +class ObservationBody(UniversalBaseModel): + id: typing.Optional[str] = None + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + type: ObservationType + name: typing.Optional[str] = None + start_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="startTime") + ] = None + end_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="endTime") + ] = None + completion_start_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="completionStartTime") + ] = None + model: typing.Optional[str] = None + model_parameters: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, MapValue]], + FieldMetadata(alias="modelParameters"), + ] = None + input: typing.Optional[typing.Any] = None + version: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + output: typing.Optional[typing.Any] = None + usage: typing.Optional[Usage] = None + level: typing.Optional[ObservationLevel] = None + status_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="statusMessage") + ] = None + parent_observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="parentObservationId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/ingestion/types/observation_type.py b/langfuse/api/ingestion/types/observation_type.py similarity index 96% rename from langfuse/api/resources/ingestion/types/observation_type.py rename to langfuse/api/ingestion/types/observation_type.py index 2f11300ff..f769e34cf 100644 --- a/langfuse/api/resources/ingestion/types/observation_type.py +++ b/langfuse/api/ingestion/types/observation_type.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class ObservationType(str, enum.Enum): +class ObservationType(enum.StrEnum): SPAN = "SPAN" GENERATION = "GENERATION" EVENT = "EVENT" diff --git a/langfuse/api/ingestion/types/open_ai_completion_usage_schema.py b/langfuse/api/ingestion/types/open_ai_completion_usage_schema.py new file mode 100644 index 000000000..292c51e79 --- /dev/null +++ b/langfuse/api/ingestion/types/open_ai_completion_usage_schema.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class OpenAiCompletionUsageSchema(UniversalBaseModel): + """ + OpenAI Usage schema from (Chat-)Completion APIs + """ + + prompt_tokens: int + completion_tokens: int + total_tokens: int + prompt_tokens_details: typing.Optional[typing.Dict[str, typing.Optional[int]]] = ( + None + ) + completion_tokens_details: typing.Optional[ + typing.Dict[str, typing.Optional[int]] + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/open_ai_response_usage_schema.py b/langfuse/api/ingestion/types/open_ai_response_usage_schema.py new file mode 100644 index 000000000..93cbc7dba --- /dev/null +++ b/langfuse/api/ingestion/types/open_ai_response_usage_schema.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class OpenAiResponseUsageSchema(UniversalBaseModel): + """ + OpenAI Usage schema from Response API + """ + + input_tokens: int + output_tokens: int + total_tokens: int + input_tokens_details: typing.Optional[typing.Dict[str, typing.Optional[int]]] = None + output_tokens_details: typing.Optional[typing.Dict[str, typing.Optional[int]]] = ( + None + ) + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/open_ai_usage.py b/langfuse/api/ingestion/types/open_ai_usage.py new file mode 100644 index 000000000..7c1ab3160 --- /dev/null +++ b/langfuse/api/ingestion/types/open_ai_usage.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class OpenAiUsage(UniversalBaseModel): + """ + Usage interface of OpenAI for improved compatibility. + """ + + prompt_tokens: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="promptTokens") + ] = None + completion_tokens: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="completionTokens") + ] = None + total_tokens: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="totalTokens") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/optional_observation_body.py b/langfuse/api/ingestion/types/optional_observation_body.py new file mode 100644 index 000000000..f2aaf9b6d --- /dev/null +++ b/langfuse/api/ingestion/types/optional_observation_body.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...commons.types.observation_level import ObservationLevel +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class OptionalObservationBody(UniversalBaseModel): + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + name: typing.Optional[str] = None + start_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="startTime") + ] = None + metadata: typing.Optional[typing.Any] = None + input: typing.Optional[typing.Any] = None + output: typing.Optional[typing.Any] = None + level: typing.Optional[ObservationLevel] = None + status_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="statusMessage") + ] = None + parent_observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="parentObservationId") + ] = None + version: typing.Optional[str] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/score_body.py b/langfuse/api/ingestion/types/score_body.py new file mode 100644 index 000000000..c1406558f --- /dev/null +++ b/langfuse/api/ingestion/types/score_body.py @@ -0,0 +1,71 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +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 + + +class ScoreBody(UniversalBaseModel): + """ + Examples + -------- + from langfuse.ingestion import ScoreBody + + ScoreBody( + name="novelty", + value=0.9, + trace_id="cdef-1234-5678-90ab", + ) + """ + + id: typing.Optional[str] = None + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + environment: typing.Optional[str] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = pydantic.Field(default=None) + """ + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + """ + + value: CreateScoreValue = pydantic.Field() + """ + 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) + """ + + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + data_type: typing_extensions.Annotated[ + typing.Optional[ScoreDataType], FieldMetadata(alias="dataType") + ] = pydantic.Field(default=None) + """ + When set, must match the score value's type. If not set, will be inferred from the score value or config + """ + + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = pydantic.Field(default=None) + """ + Reference a score config on a score. When set, the score name must equal the config name and scores must comply with the config's range and data type. For categorical scores, the value must map to a config category. Numeric scores might be constrained by the score config's max and min values + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/score_event.py b/langfuse/api/ingestion/types/score_event.py new file mode 100644 index 000000000..d0d470899 --- /dev/null +++ b/langfuse/api/ingestion/types/score_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .score_body import ScoreBody + + +class ScoreEvent(BaseEvent): + body: ScoreBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/sdk_log_body.py b/langfuse/api/ingestion/types/sdk_log_body.py new file mode 100644 index 000000000..d5b46f118 --- /dev/null +++ b/langfuse/api/ingestion/types/sdk_log_body.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class SdkLogBody(UniversalBaseModel): + log: typing.Any + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/sdk_log_event.py b/langfuse/api/ingestion/types/sdk_log_event.py new file mode 100644 index 000000000..ca303af55 --- /dev/null +++ b/langfuse/api/ingestion/types/sdk_log_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .sdk_log_body import SdkLogBody + + +class SdkLogEvent(BaseEvent): + body: SdkLogBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/trace_body.py b/langfuse/api/ingestion/types/trace_body.py new file mode 100644 index 000000000..7fb2842a0 --- /dev/null +++ b/langfuse/api/ingestion/types/trace_body.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class TraceBody(UniversalBaseModel): + id: typing.Optional[str] = None + timestamp: typing.Optional[dt.datetime] = None + name: typing.Optional[str] = None + user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="userId") + ] = None + input: typing.Optional[typing.Any] = None + output: typing.Optional[typing.Any] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + release: typing.Optional[str] = None + version: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + tags: typing.Optional[typing.List[str]] = None + environment: typing.Optional[str] = None + public: typing.Optional[bool] = pydantic.Field(default=None) + """ + Make trace publicly accessible via url + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/trace_event.py b/langfuse/api/ingestion/types/trace_event.py new file mode 100644 index 000000000..54127597a --- /dev/null +++ b/langfuse/api/ingestion/types/trace_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .trace_body import TraceBody + + +class TraceEvent(BaseEvent): + body: TraceBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/update_event_body.py b/langfuse/api/ingestion/types/update_event_body.py new file mode 100644 index 000000000..055f66f08 --- /dev/null +++ b/langfuse/api/ingestion/types/update_event_body.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .optional_observation_body import OptionalObservationBody + + +class UpdateEventBody(OptionalObservationBody): + id: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/update_generation_body.py b/langfuse/api/ingestion/types/update_generation_body.py new file mode 100644 index 000000000..1d453e759 --- /dev/null +++ b/langfuse/api/ingestion/types/update_generation_body.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...commons.types.map_value import MapValue +from ...core.serialization import FieldMetadata +from .ingestion_usage import IngestionUsage +from .update_span_body import UpdateSpanBody +from .usage_details import UsageDetails + + +class UpdateGenerationBody(UpdateSpanBody): + completion_start_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="completionStartTime") + ] = None + model: typing.Optional[str] = None + model_parameters: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, MapValue]], + FieldMetadata(alias="modelParameters"), + ] = None + usage: typing.Optional[IngestionUsage] = None + prompt_name: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="promptName") + ] = None + usage_details: typing_extensions.Annotated[ + typing.Optional[UsageDetails], FieldMetadata(alias="usageDetails") + ] = None + cost_details: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, float]], FieldMetadata(alias="costDetails") + ] = None + prompt_version: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="promptVersion") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/update_generation_event.py b/langfuse/api/ingestion/types/update_generation_event.py new file mode 100644 index 000000000..e2c7fe284 --- /dev/null +++ b/langfuse/api/ingestion/types/update_generation_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .update_generation_body import UpdateGenerationBody + + +class UpdateGenerationEvent(BaseEvent): + body: UpdateGenerationBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/update_observation_event.py b/langfuse/api/ingestion/types/update_observation_event.py new file mode 100644 index 000000000..5c33e7591 --- /dev/null +++ b/langfuse/api/ingestion/types/update_observation_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .observation_body import ObservationBody + + +class UpdateObservationEvent(BaseEvent): + body: ObservationBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/update_span_body.py b/langfuse/api/ingestion/types/update_span_body.py new file mode 100644 index 000000000..f094b7cdb --- /dev/null +++ b/langfuse/api/ingestion/types/update_span_body.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from .update_event_body import UpdateEventBody + + +class UpdateSpanBody(UpdateEventBody): + end_time: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="endTime") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/ingestion/types/update_span_event.py b/langfuse/api/ingestion/types/update_span_event.py new file mode 100644 index 000000000..20214ac9d --- /dev/null +++ b/langfuse/api/ingestion/types/update_span_event.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_event import BaseEvent +from .update_span_body import UpdateSpanBody + + +class UpdateSpanEvent(BaseEvent): + body: UpdateSpanBody + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/ingestion/types/usage_details.py b/langfuse/api/ingestion/types/usage_details.py similarity index 100% rename from langfuse/api/resources/ingestion/types/usage_details.py rename to langfuse/api/ingestion/types/usage_details.py diff --git a/langfuse/api/llm_connections/__init__.py b/langfuse/api/llm_connections/__init__.py new file mode 100644 index 000000000..aba7157f1 --- /dev/null +++ b/langfuse/api/llm_connections/__init__.py @@ -0,0 +1,55 @@ +# 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 ( + LlmAdapter, + LlmConnection, + PaginatedLlmConnections, + UpsertLlmConnectionRequest, + ) +_dynamic_imports: typing.Dict[str, str] = { + "LlmAdapter": ".types", + "LlmConnection": ".types", + "PaginatedLlmConnections": ".types", + "UpsertLlmConnectionRequest": ".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__ = [ + "LlmAdapter", + "LlmConnection", + "PaginatedLlmConnections", + "UpsertLlmConnectionRequest", +] diff --git a/langfuse/api/llm_connections/client.py b/langfuse/api/llm_connections/client.py new file mode 100644 index 000000000..213e55e9f --- /dev/null +++ b/langfuse/api/llm_connections/client.py @@ -0,0 +1,311 @@ +# 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 AsyncRawLlmConnectionsClient, RawLlmConnectionsClient +from .types.llm_adapter import LlmAdapter +from .types.llm_connection import LlmConnection +from .types.paginated_llm_connections import PaginatedLlmConnections + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class LlmConnectionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawLlmConnectionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawLlmConnectionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawLlmConnectionsClient + """ + return self._raw_client + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedLlmConnections: + """ + Get all LLM connections in a project + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedLlmConnections + + 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.llm_connections.list() + """ + _response = self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + def upsert( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> LlmConnection: + """ + Create or update an LLM connection. The connection is upserted on provider. + + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + LlmConnection + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.llm_connections import LlmAdapter + + 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.llm_connections.upsert( + provider="provider", + adapter=LlmAdapter.ANTHROPIC, + secret_key="secretKey", + ) + """ + _response = self._raw_client.upsert( + provider=provider, + adapter=adapter, + secret_key=secret_key, + base_url=base_url, + custom_models=custom_models, + with_default_models=with_default_models, + extra_headers=extra_headers, + config=config, + request_options=request_options, + ) + return _response.data + + +class AsyncLlmConnectionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawLlmConnectionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawLlmConnectionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawLlmConnectionsClient + """ + return self._raw_client + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedLlmConnections: + """ + Get all LLM connections in a project + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedLlmConnections + + 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.llm_connections.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def upsert( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> LlmConnection: + """ + Create or update an LLM connection. The connection is upserted on provider. + + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + LlmConnection + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.llm_connections import LlmAdapter + + 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.llm_connections.upsert( + provider="provider", + adapter=LlmAdapter.ANTHROPIC, + secret_key="secretKey", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upsert( + provider=provider, + adapter=adapter, + secret_key=secret_key, + base_url=base_url, + custom_models=custom_models, + with_default_models=with_default_models, + extra_headers=extra_headers, + config=config, + request_options=request_options, + ) + return _response.data diff --git a/langfuse/api/llm_connections/raw_client.py b/langfuse/api/llm_connections/raw_client.py new file mode 100644 index 000000000..ef4f87425 --- /dev/null +++ b/langfuse/api/llm_connections/raw_client.py @@ -0,0 +1,541 @@ +# 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.llm_adapter import LlmAdapter +from .types.llm_connection import LlmConnection +from .types.paginated_llm_connections import PaginatedLlmConnections + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawLlmConnectionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedLlmConnections]: + """ + Get all LLM connections in a project + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedLlmConnections] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/llm-connections", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedLlmConnections, + parse_obj_as( + type_=PaginatedLlmConnections, # 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 upsert( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[LlmConnection]: + """ + Create or update an LLM connection. The connection is upserted on provider. + + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[LlmConnection] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/llm-connections", + method="PUT", + json={ + "provider": provider, + "adapter": adapter, + "secretKey": secret_key, + "baseURL": base_url, + "customModels": custom_models, + "withDefaultModels": with_default_models, + "extraHeaders": extra_headers, + "config": config, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + LlmConnection, + parse_obj_as( + type_=LlmConnection, # 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 AsyncRawLlmConnectionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedLlmConnections]: + """ + Get all LLM connections in a project + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedLlmConnections] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/llm-connections", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedLlmConnections, + parse_obj_as( + type_=PaginatedLlmConnections, # 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 upsert( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[LlmConnection]: + """ + Create or update an LLM connection. The connection is upserted on provider. + + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[LlmConnection] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/llm-connections", + method="PUT", + json={ + "provider": provider, + "adapter": adapter, + "secretKey": secret_key, + "baseURL": base_url, + "customModels": custom_models, + "withDefaultModels": with_default_models, + "extraHeaders": extra_headers, + "config": config, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + LlmConnection, + parse_obj_as( + type_=LlmConnection, # 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/llm_connections/types/__init__.py b/langfuse/api/llm_connections/types/__init__.py new file mode 100644 index 000000000..e6ba89200 --- /dev/null +++ b/langfuse/api/llm_connections/types/__init__.py @@ -0,0 +1,53 @@ +# 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 .llm_adapter import LlmAdapter + from .llm_connection import LlmConnection + from .paginated_llm_connections import PaginatedLlmConnections + from .upsert_llm_connection_request import UpsertLlmConnectionRequest +_dynamic_imports: typing.Dict[str, str] = { + "LlmAdapter": ".llm_adapter", + "LlmConnection": ".llm_connection", + "PaginatedLlmConnections": ".paginated_llm_connections", + "UpsertLlmConnectionRequest": ".upsert_llm_connection_request", +} + + +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__ = [ + "LlmAdapter", + "LlmConnection", + "PaginatedLlmConnections", + "UpsertLlmConnectionRequest", +] diff --git a/langfuse/api/resources/llm_connections/types/llm_adapter.py b/langfuse/api/llm_connections/types/llm_adapter.py similarity index 94% rename from langfuse/api/resources/llm_connections/types/llm_adapter.py rename to langfuse/api/llm_connections/types/llm_adapter.py index d03513aeb..08cab5cb9 100644 --- a/langfuse/api/resources/llm_connections/types/llm_adapter.py +++ b/langfuse/api/llm_connections/types/llm_adapter.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class LlmAdapter(str, enum.Enum): +class LlmAdapter(enum.StrEnum): ANTHROPIC = "anthropic" OPEN_AI = "openai" AZURE = "azure" diff --git a/langfuse/api/llm_connections/types/llm_connection.py b/langfuse/api/llm_connections/types/llm_connection.py new file mode 100644 index 000000000..f74ff98c2 --- /dev/null +++ b/langfuse/api/llm_connections/types/llm_connection.py @@ -0,0 +1,77 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class LlmConnection(UniversalBaseModel): + """ + LLM API connection configuration (secrets excluded) + """ + + id: str + provider: str = pydantic.Field() + """ + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + """ + + adapter: str = pydantic.Field() + """ + The adapter used to interface with the LLM + """ + + display_secret_key: typing_extensions.Annotated[ + str, FieldMetadata(alias="displaySecretKey") + ] = pydantic.Field() + """ + Masked version of the secret key for display purposes + """ + + base_url: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="baseURL") + ] = pydantic.Field(default=None) + """ + Custom base URL for the LLM API + """ + + custom_models: typing_extensions.Annotated[ + typing.List[str], FieldMetadata(alias="customModels") + ] = pydantic.Field() + """ + List of custom model names available for this connection + """ + + with_default_models: typing_extensions.Annotated[ + bool, FieldMetadata(alias="withDefaultModels") + ] = pydantic.Field() + """ + Whether to include default models for this adapter + """ + + extra_header_keys: typing_extensions.Annotated[ + typing.List[str], FieldMetadata(alias="extraHeaderKeys") + ] = pydantic.Field() + """ + Keys of extra headers sent with requests (values excluded for security) + """ + + config: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + Adapter-specific configuration. Required for Bedrock (`{"region":"us-east-1"}`), optional for VertexAI (`{"location":"us-central1"}`), not used by other adapters. + """ + + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/llm_connections/types/paginated_llm_connections.py b/langfuse/api/llm_connections/types/paginated_llm_connections.py new file mode 100644 index 000000000..5d8ce52af --- /dev/null +++ b/langfuse/api/llm_connections/types/paginated_llm_connections.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse +from .llm_connection import LlmConnection + + +class PaginatedLlmConnections(UniversalBaseModel): + data: typing.List[LlmConnection] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/llm_connections/types/upsert_llm_connection_request.py b/langfuse/api/llm_connections/types/upsert_llm_connection_request.py new file mode 100644 index 000000000..712362fa1 --- /dev/null +++ b/langfuse/api/llm_connections/types/upsert_llm_connection_request.py @@ -0,0 +1,69 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .llm_adapter import LlmAdapter + + +class UpsertLlmConnectionRequest(UniversalBaseModel): + """ + Request to create or update an LLM connection (upsert) + """ + + provider: str = pydantic.Field() + """ + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + """ + + adapter: LlmAdapter = pydantic.Field() + """ + The adapter used to interface with the LLM + """ + + secret_key: typing_extensions.Annotated[str, FieldMetadata(alias="secretKey")] = ( + pydantic.Field() + ) + """ + Secret key for the LLM API. + """ + + base_url: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="baseURL") + ] = pydantic.Field(default=None) + """ + Custom base URL for the LLM API + """ + + custom_models: typing_extensions.Annotated[ + typing.Optional[typing.List[str]], FieldMetadata(alias="customModels") + ] = pydantic.Field(default=None) + """ + List of custom model names + """ + + with_default_models: typing_extensions.Annotated[ + typing.Optional[bool], FieldMetadata(alias="withDefaultModels") + ] = pydantic.Field(default=None) + """ + Whether to include default models. Default is true. + """ + + extra_headers: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, str]], FieldMetadata(alias="extraHeaders") + ] = pydantic.Field(default=None) + """ + Extra headers to send with requests + """ + + config: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/media/__init__.py b/langfuse/api/media/__init__.py new file mode 100644 index 000000000..85d8f7b4f --- /dev/null +++ b/langfuse/api/media/__init__.py @@ -0,0 +1,58 @@ +# 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 ( + GetMediaResponse, + GetMediaUploadUrlRequest, + GetMediaUploadUrlResponse, + MediaContentType, + PatchMediaBody, + ) +_dynamic_imports: typing.Dict[str, str] = { + "GetMediaResponse": ".types", + "GetMediaUploadUrlRequest": ".types", + "GetMediaUploadUrlResponse": ".types", + "MediaContentType": ".types", + "PatchMediaBody": ".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__ = [ + "GetMediaResponse", + "GetMediaUploadUrlRequest", + "GetMediaUploadUrlResponse", + "MediaContentType", + "PatchMediaBody", +] diff --git a/langfuse/api/media/client.py b/langfuse/api/media/client.py new file mode 100644 index 000000000..b22272b92 --- /dev/null +++ b/langfuse/api/media/client.py @@ -0,0 +1,427 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawMediaClient, RawMediaClient +from .types.get_media_response import GetMediaResponse +from .types.get_media_upload_url_response import GetMediaUploadUrlResponse +from .types.media_content_type import MediaContentType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class MediaClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawMediaClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawMediaClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMediaClient + """ + return self._raw_client + + def get( + self, media_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> GetMediaResponse: + """ + Get a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetMediaResponse + + 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.media.get( + media_id="mediaId", + ) + """ + _response = self._raw_client.get(media_id, request_options=request_options) + return _response.data + + def patch( + self, + media_id: str, + *, + uploaded_at: dt.datetime, + upload_http_status: int, + upload_http_error: typing.Optional[str] = OMIT, + upload_time_ms: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Patch a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + uploaded_at : dt.datetime + The date and time when the media record was uploaded + + upload_http_status : int + The HTTP status code of the upload + + upload_http_error : typing.Optional[str] + The HTTP error message of the upload + + upload_time_ms : typing.Optional[int] + The time in milliseconds it took to upload the media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import datetime + + 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.media.patch( + media_id="mediaId", + uploaded_at=datetime.datetime.fromisoformat( + "2024-01-15 09:30:00+00:00", + ), + upload_http_status=1, + ) + """ + _response = self._raw_client.patch( + media_id, + uploaded_at=uploaded_at, + upload_http_status=upload_http_status, + upload_http_error=upload_http_error, + upload_time_ms=upload_time_ms, + request_options=request_options, + ) + return _response.data + + def get_upload_url( + self, + *, + trace_id: str, + content_type: MediaContentType, + content_length: int, + sha256hash: str, + field: str, + observation_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetMediaUploadUrlResponse: + """ + Get a presigned upload URL for a media record + + Parameters + ---------- + trace_id : str + The trace ID associated with the media record + + content_type : MediaContentType + + content_length : int + The size of the media record in bytes + + sha256hash : str + The SHA-256 hash of the media record + + field : str + The trace / observation field the media record is associated with. This can be one of `input`, `output`, `metadata` + + observation_id : typing.Optional[str] + The observation ID associated with the media record. If the media record is associated directly with a trace, this will be null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetMediaUploadUrlResponse + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.media import MediaContentType + + 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.media.get_upload_url( + trace_id="traceId", + content_type=MediaContentType.IMAGE_PNG, + content_length=1, + sha256hash="sha256Hash", + field="field", + ) + """ + _response = self._raw_client.get_upload_url( + trace_id=trace_id, + content_type=content_type, + content_length=content_length, + sha256hash=sha256hash, + field=field, + observation_id=observation_id, + request_options=request_options, + ) + return _response.data + + +class AsyncMediaClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawMediaClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawMediaClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMediaClient + """ + return self._raw_client + + async def get( + self, media_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> GetMediaResponse: + """ + Get a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetMediaResponse + + 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.media.get( + media_id="mediaId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + media_id, request_options=request_options + ) + return _response.data + + async def patch( + self, + media_id: str, + *, + uploaded_at: dt.datetime, + upload_http_status: int, + upload_http_error: typing.Optional[str] = OMIT, + upload_time_ms: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Patch a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + uploaded_at : dt.datetime + The date and time when the media record was uploaded + + upload_http_status : int + The HTTP status code of the upload + + upload_http_error : typing.Optional[str] + The HTTP error message of the upload + + upload_time_ms : typing.Optional[int] + The time in milliseconds it took to upload the media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + import datetime + + 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.media.patch( + media_id="mediaId", + uploaded_at=datetime.datetime.fromisoformat( + "2024-01-15 09:30:00+00:00", + ), + upload_http_status=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.patch( + media_id, + uploaded_at=uploaded_at, + upload_http_status=upload_http_status, + upload_http_error=upload_http_error, + upload_time_ms=upload_time_ms, + request_options=request_options, + ) + return _response.data + + async def get_upload_url( + self, + *, + trace_id: str, + content_type: MediaContentType, + content_length: int, + sha256hash: str, + field: str, + observation_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetMediaUploadUrlResponse: + """ + Get a presigned upload URL for a media record + + Parameters + ---------- + trace_id : str + The trace ID associated with the media record + + content_type : MediaContentType + + content_length : int + The size of the media record in bytes + + sha256hash : str + The SHA-256 hash of the media record + + field : str + The trace / observation field the media record is associated with. This can be one of `input`, `output`, `metadata` + + observation_id : typing.Optional[str] + The observation ID associated with the media record. If the media record is associated directly with a trace, this will be null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetMediaUploadUrlResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.media import MediaContentType + + 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.media.get_upload_url( + trace_id="traceId", + content_type=MediaContentType.IMAGE_PNG, + content_length=1, + sha256hash="sha256Hash", + field="field", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_upload_url( + trace_id=trace_id, + content_type=content_type, + content_length=content_length, + sha256hash=sha256hash, + field=field, + observation_id=observation_id, + request_options=request_options, + ) + return _response.data diff --git a/langfuse/api/media/raw_client.py b/langfuse/api/media/raw_client.py new file mode 100644 index 000000000..4cc619770 --- /dev/null +++ b/langfuse/api/media/raw_client.py @@ -0,0 +1,739 @@ +# 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 ..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 .types.get_media_response import GetMediaResponse +from .types.get_media_upload_url_response import GetMediaUploadUrlResponse +from .types.media_content_type import MediaContentType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawMediaClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get( + self, media_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[GetMediaResponse]: + """ + Get a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetMediaResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/media/{jsonable_encoder(media_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetMediaResponse, + parse_obj_as( + type_=GetMediaResponse, # 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 patch( + self, + media_id: str, + *, + uploaded_at: dt.datetime, + upload_http_status: int, + upload_http_error: typing.Optional[str] = OMIT, + upload_time_ms: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: + """ + Patch a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + uploaded_at : dt.datetime + The date and time when the media record was uploaded + + upload_http_status : int + The HTTP status code of the upload + + upload_http_error : typing.Optional[str] + The HTTP error message of the upload + + upload_time_ms : typing.Optional[int] + The time in milliseconds it took to upload the media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/media/{jsonable_encoder(media_id)}", + method="PATCH", + json={ + "uploadedAt": uploaded_at, + "uploadHttpStatus": upload_http_status, + "uploadHttpError": upload_http_error, + "uploadTimeMs": upload_time_ms, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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_upload_url( + self, + *, + trace_id: str, + content_type: MediaContentType, + content_length: int, + sha256hash: str, + field: str, + observation_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[GetMediaUploadUrlResponse]: + """ + Get a presigned upload URL for a media record + + Parameters + ---------- + trace_id : str + The trace ID associated with the media record + + content_type : MediaContentType + + content_length : int + The size of the media record in bytes + + sha256hash : str + The SHA-256 hash of the media record + + field : str + The trace / observation field the media record is associated with. This can be one of `input`, `output`, `metadata` + + observation_id : typing.Optional[str] + The observation ID associated with the media record. If the media record is associated directly with a trace, this will be null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetMediaUploadUrlResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/media", + method="POST", + json={ + "traceId": trace_id, + "observationId": observation_id, + "contentType": content_type, + "contentLength": content_length, + "sha256Hash": sha256hash, + "field": field, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetMediaUploadUrlResponse, + parse_obj_as( + type_=GetMediaUploadUrlResponse, # 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 AsyncRawMediaClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get( + self, media_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[GetMediaResponse]: + """ + Get a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetMediaResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/media/{jsonable_encoder(media_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetMediaResponse, + parse_obj_as( + type_=GetMediaResponse, # 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 patch( + self, + media_id: str, + *, + uploaded_at: dt.datetime, + upload_http_status: int, + upload_http_error: typing.Optional[str] = OMIT, + upload_time_ms: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: + """ + Patch a media record + + Parameters + ---------- + media_id : str + The unique langfuse identifier of a media record + + uploaded_at : dt.datetime + The date and time when the media record was uploaded + + upload_http_status : int + The HTTP status code of the upload + + upload_http_error : typing.Optional[str] + The HTTP error message of the upload + + upload_time_ms : typing.Optional[int] + The time in milliseconds it took to upload the media record + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/media/{jsonable_encoder(media_id)}", + method="PATCH", + json={ + "uploadedAt": uploaded_at, + "uploadHttpStatus": upload_http_status, + "uploadHttpError": upload_http_error, + "uploadTimeMs": upload_time_ms, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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_upload_url( + self, + *, + trace_id: str, + content_type: MediaContentType, + content_length: int, + sha256hash: str, + field: str, + observation_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[GetMediaUploadUrlResponse]: + """ + Get a presigned upload URL for a media record + + Parameters + ---------- + trace_id : str + The trace ID associated with the media record + + content_type : MediaContentType + + content_length : int + The size of the media record in bytes + + sha256hash : str + The SHA-256 hash of the media record + + field : str + The trace / observation field the media record is associated with. This can be one of `input`, `output`, `metadata` + + observation_id : typing.Optional[str] + The observation ID associated with the media record. If the media record is associated directly with a trace, this will be null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetMediaUploadUrlResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/media", + method="POST", + json={ + "traceId": trace_id, + "observationId": observation_id, + "contentType": content_type, + "contentLength": content_length, + "sha256Hash": sha256hash, + "field": field, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetMediaUploadUrlResponse, + parse_obj_as( + type_=GetMediaUploadUrlResponse, # 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/media/types/__init__.py b/langfuse/api/media/types/__init__.py new file mode 100644 index 000000000..0fb9a44ed --- /dev/null +++ b/langfuse/api/media/types/__init__.py @@ -0,0 +1,56 @@ +# 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_media_response import GetMediaResponse + from .get_media_upload_url_request import GetMediaUploadUrlRequest + from .get_media_upload_url_response import GetMediaUploadUrlResponse + from .media_content_type import MediaContentType + from .patch_media_body import PatchMediaBody +_dynamic_imports: typing.Dict[str, str] = { + "GetMediaResponse": ".get_media_response", + "GetMediaUploadUrlRequest": ".get_media_upload_url_request", + "GetMediaUploadUrlResponse": ".get_media_upload_url_response", + "MediaContentType": ".media_content_type", + "PatchMediaBody": ".patch_media_body", +} + + +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__ = [ + "GetMediaResponse", + "GetMediaUploadUrlRequest", + "GetMediaUploadUrlResponse", + "MediaContentType", + "PatchMediaBody", +] diff --git a/langfuse/api/media/types/get_media_response.py b/langfuse/api/media/types/get_media_response.py new file mode 100644 index 000000000..fc1f70329 --- /dev/null +++ b/langfuse/api/media/types/get_media_response.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class GetMediaResponse(UniversalBaseModel): + media_id: typing_extensions.Annotated[str, FieldMetadata(alias="mediaId")] = ( + pydantic.Field() + ) + """ + The unique langfuse identifier of a media record + """ + + content_type: typing_extensions.Annotated[ + str, FieldMetadata(alias="contentType") + ] = pydantic.Field() + """ + The MIME type of the media record + """ + + content_length: typing_extensions.Annotated[ + int, FieldMetadata(alias="contentLength") + ] = pydantic.Field() + """ + The size of the media record in bytes + """ + + uploaded_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="uploadedAt") + ] = pydantic.Field() + """ + The date and time when the media record was uploaded + """ + + url: str = pydantic.Field() + """ + The download URL of the media record + """ + + url_expiry: typing_extensions.Annotated[str, FieldMetadata(alias="urlExpiry")] = ( + pydantic.Field() + ) + """ + The expiry date and time of the media record download URL + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/media/types/get_media_upload_url_request.py b/langfuse/api/media/types/get_media_upload_url_request.py new file mode 100644 index 000000000..99f055847 --- /dev/null +++ b/langfuse/api/media/types/get_media_upload_url_request.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .media_content_type import MediaContentType + + +class GetMediaUploadUrlRequest(UniversalBaseModel): + trace_id: typing_extensions.Annotated[str, FieldMetadata(alias="traceId")] = ( + pydantic.Field() + ) + """ + The trace ID associated with the media record + """ + + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = pydantic.Field(default=None) + """ + The observation ID associated with the media record. If the media record is associated directly with a trace, this will be null. + """ + + content_type: typing_extensions.Annotated[ + MediaContentType, FieldMetadata(alias="contentType") + ] + content_length: typing_extensions.Annotated[ + int, FieldMetadata(alias="contentLength") + ] = pydantic.Field() + """ + The size of the media record in bytes + """ + + sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] = ( + pydantic.Field() + ) + """ + The SHA-256 hash of the media record + """ + + field: str = pydantic.Field() + """ + The trace / observation field the media record is associated with. This can be one of `input`, `output`, `metadata` + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/media/types/get_media_upload_url_response.py b/langfuse/api/media/types/get_media_upload_url_response.py new file mode 100644 index 000000000..90c735be3 --- /dev/null +++ b/langfuse/api/media/types/get_media_upload_url_response.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class GetMediaUploadUrlResponse(UniversalBaseModel): + upload_url: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uploadUrl") + ] = pydantic.Field(default=None) + """ + The presigned upload URL. If the asset is already uploaded, this will be null + """ + + media_id: typing_extensions.Annotated[str, FieldMetadata(alias="mediaId")] = ( + pydantic.Field() + ) + """ + The unique langfuse identifier of a media record + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/media/types/media_content_type.py b/langfuse/api/media/types/media_content_type.py similarity index 92% rename from langfuse/api/resources/media/types/media_content_type.py rename to langfuse/api/media/types/media_content_type.py index 6942482bb..9fb5507bc 100644 --- a/langfuse/api/resources/media/types/media_content_type.py +++ b/langfuse/api/media/types/media_content_type.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class MediaContentType(str, enum.Enum): +class MediaContentType(enum.StrEnum): """ The MIME type of the media record """ @@ -22,16 +23,16 @@ class MediaContentType(str, enum.Enum): IMAGE_AVIF = "image/avif" IMAGE_HEIC = "image/heic" AUDIO_MPEG = "audio/mpeg" - AUDIO_MP_3 = "audio/mp3" + AUDIO_MP3 = "audio/mp3" AUDIO_WAV = "audio/wav" AUDIO_OGG = "audio/ogg" AUDIO_OGA = "audio/oga" AUDIO_AAC = "audio/aac" - AUDIO_MP_4 = "audio/mp4" + AUDIO_MP4 = "audio/mp4" AUDIO_FLAC = "audio/flac" AUDIO_OPUS = "audio/opus" AUDIO_WEBM = "audio/webm" - VIDEO_MP_4 = "video/mp4" + VIDEO_MP4 = "video/mp4" VIDEO_WEBM = "video/webm" VIDEO_OGG = "video/ogg" VIDEO_MPEG = "video/mpeg" @@ -68,7 +69,7 @@ class MediaContentType(str, enum.Enum): APPLICATION_PARQUET = "application/vnd.apache.parquet" APPLICATION_GZIP = "application/gzip" APPLICATION_X_TAR = "application/x-tar" - APPLICATION_X_7_Z_COMPRESSED = "application/x-7z-compressed" + APPLICATION_X7Z_COMPRESSED = "application/x-7z-compressed" def visit( self, @@ -83,16 +84,16 @@ def visit( image_avif: typing.Callable[[], T_Result], image_heic: typing.Callable[[], T_Result], audio_mpeg: typing.Callable[[], T_Result], - audio_mp_3: typing.Callable[[], T_Result], + audio_mp3: typing.Callable[[], T_Result], audio_wav: typing.Callable[[], T_Result], audio_ogg: typing.Callable[[], T_Result], audio_oga: typing.Callable[[], T_Result], audio_aac: typing.Callable[[], T_Result], - audio_mp_4: typing.Callable[[], T_Result], + audio_mp4: typing.Callable[[], T_Result], audio_flac: typing.Callable[[], T_Result], audio_opus: typing.Callable[[], T_Result], audio_webm: typing.Callable[[], T_Result], - video_mp_4: typing.Callable[[], T_Result], + video_mp4: typing.Callable[[], T_Result], video_webm: typing.Callable[[], T_Result], video_ogg: typing.Callable[[], T_Result], video_mpeg: typing.Callable[[], T_Result], @@ -123,7 +124,7 @@ def visit( application_parquet: typing.Callable[[], T_Result], application_gzip: typing.Callable[[], T_Result], application_x_tar: typing.Callable[[], T_Result], - application_x_7_z_compressed: typing.Callable[[], T_Result], + application_x7z_compressed: typing.Callable[[], T_Result], ) -> T_Result: if self is MediaContentType.IMAGE_PNG: return image_png() @@ -147,8 +148,8 @@ def visit( return image_heic() if self is MediaContentType.AUDIO_MPEG: return audio_mpeg() - if self is MediaContentType.AUDIO_MP_3: - return audio_mp_3() + if self is MediaContentType.AUDIO_MP3: + return audio_mp3() if self is MediaContentType.AUDIO_WAV: return audio_wav() if self is MediaContentType.AUDIO_OGG: @@ -157,16 +158,16 @@ def visit( return audio_oga() if self is MediaContentType.AUDIO_AAC: return audio_aac() - if self is MediaContentType.AUDIO_MP_4: - return audio_mp_4() + if self is MediaContentType.AUDIO_MP4: + return audio_mp4() if self is MediaContentType.AUDIO_FLAC: return audio_flac() if self is MediaContentType.AUDIO_OPUS: return audio_opus() if self is MediaContentType.AUDIO_WEBM: return audio_webm() - if self is MediaContentType.VIDEO_MP_4: - return video_mp_4() + if self is MediaContentType.VIDEO_MP4: + return video_mp4() if self is MediaContentType.VIDEO_WEBM: return video_webm() if self is MediaContentType.VIDEO_OGG: @@ -227,5 +228,5 @@ def visit( return application_gzip() if self is MediaContentType.APPLICATION_X_TAR: return application_x_tar() - if self is MediaContentType.APPLICATION_X_7_Z_COMPRESSED: - return application_x_7_z_compressed() + if self is MediaContentType.APPLICATION_X7Z_COMPRESSED: + return application_x7z_compressed() diff --git a/langfuse/api/media/types/patch_media_body.py b/langfuse/api/media/types/patch_media_body.py new file mode 100644 index 000000000..e5f93f601 --- /dev/null +++ b/langfuse/api/media/types/patch_media_body.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class PatchMediaBody(UniversalBaseModel): + uploaded_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="uploadedAt") + ] = pydantic.Field() + """ + The date and time when the media record was uploaded + """ + + upload_http_status: typing_extensions.Annotated[ + int, FieldMetadata(alias="uploadHttpStatus") + ] = pydantic.Field() + """ + The HTTP status code of the upload + """ + + upload_http_error: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uploadHttpError") + ] = pydantic.Field(default=None) + """ + The HTTP error message of the upload + """ + + upload_time_ms: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="uploadTimeMs") + ] = pydantic.Field(default=None) + """ + The time in milliseconds it took to upload the media record + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/metrics/__init__.py b/langfuse/api/metrics/__init__.py new file mode 100644 index 000000000..fb47bc976 --- /dev/null +++ b/langfuse/api/metrics/__init__.py @@ -0,0 +1,40 @@ +# 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 MetricsResponse +_dynamic_imports: typing.Dict[str, str] = {"MetricsResponse": ".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__ = ["MetricsResponse"] diff --git a/langfuse/api/resources/metrics/client.py b/langfuse/api/metrics/client.py similarity index 64% rename from langfuse/api/resources/metrics/client.py rename to langfuse/api/metrics/client.py index 471f5182e..7b2cc0136 100644 --- a/langfuse/api/resources/metrics/client.py +++ b/langfuse/api/metrics/client.py @@ -1,23 +1,27 @@ # 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.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 ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawMetricsClient, RawMetricsClient from .types.metrics_response import MetricsResponse class MetricsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = RawMetricsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawMetricsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMetricsClient + """ + return self._raw_client def metrics( self, *, query: str, request_options: typing.Optional[RequestOptions] = None @@ -81,9 +85,9 @@ def metrics( Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -95,42 +99,26 @@ def metrics( query="query", ) """ - _response = self._client_wrapper.httpx_client.request( - "api/public/metrics", - method="GET", - params={"query": query}, - request_options=request_options, + _response = self._raw_client.metrics( + query=query, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MetricsResponse, _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) + return _response.data class AsyncMetricsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = AsyncRawMetricsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawMetricsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMetricsClient + """ + return self._raw_client async def metrics( self, *, query: str, request_options: typing.Optional[RequestOptions] = None @@ -196,9 +184,9 @@ async def metrics( -------- import asyncio - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -216,34 +204,7 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/metrics", - method="GET", - params={"query": query}, - request_options=request_options, + _response = await self._raw_client.metrics( + query=query, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MetricsResponse, _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) + return _response.data diff --git a/langfuse/api/metrics/raw_client.py b/langfuse/api/metrics/raw_client.py new file mode 100644 index 000000000..1ce3c1702 --- /dev/null +++ b/langfuse/api/metrics/raw_client.py @@ -0,0 +1,318 @@ +# 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_response import MetricsResponse + + +class RawMetricsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def metrics( + self, *, query: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[MetricsResponse]: + """ + Get metrics from the Langfuse project using a query object. + + 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[MetricsResponse] + """ + _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( + MetricsResponse, + parse_obj_as( + type_=MetricsResponse, # 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 AsyncRawMetricsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def metrics( + self, *, query: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[MetricsResponse]: + """ + Get metrics from the Langfuse project using a query object. + + 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[MetricsResponse] + """ + _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( + MetricsResponse, + parse_obj_as( + type_=MetricsResponse, # 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/types/__init__.py b/langfuse/api/metrics/types/__init__.py new file mode 100644 index 000000000..308847504 --- /dev/null +++ b/langfuse/api/metrics/types/__init__.py @@ -0,0 +1,40 @@ +# 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 .metrics_response import MetricsResponse +_dynamic_imports: typing.Dict[str, str] = {"MetricsResponse": ".metrics_response"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["MetricsResponse"] diff --git a/langfuse/api/metrics/types/metrics_response.py b/langfuse/api/metrics/types/metrics_response.py new file mode 100644 index 000000000..ffbbfaa59 --- /dev/null +++ b/langfuse/api/metrics/types/metrics_response.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class MetricsResponse(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. + Format varies based on the query parameters. + Histograms will return an array with [lower, upper, height] tuples. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/metrics_v2/__init__.py b/langfuse/api/metrics_v2/__init__.py new file mode 100644 index 000000000..0421785de --- /dev/null +++ b/langfuse/api/metrics_v2/__init__.py @@ -0,0 +1,40 @@ +# 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 MetricsV2Response +_dynamic_imports: typing.Dict[str, str] = {"MetricsV2Response": ".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__ = ["MetricsV2Response"] diff --git a/langfuse/api/metrics_v2/client.py b/langfuse/api/metrics_v2/client.py new file mode 100644 index 000000000..d6c05914c --- /dev/null +++ b/langfuse/api/metrics_v2/client.py @@ -0,0 +1,422 @@ +# 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 new file mode 100644 index 000000000..b79d55713 --- /dev/null +++ b/langfuse/api/metrics_v2/raw_client.py @@ -0,0 +1,530 @@ +# 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/metrics_v2/types/__init__.py b/langfuse/api/metrics_v2/types/__init__.py new file mode 100644 index 000000000..b9510d24f --- /dev/null +++ b/langfuse/api/metrics_v2/types/__init__.py @@ -0,0 +1,40 @@ +# 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 .metrics_v2response import MetricsV2Response +_dynamic_imports: typing.Dict[str, str] = {"MetricsV2Response": ".metrics_v2response"} + + +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__ = ["MetricsV2Response"] diff --git a/langfuse/api/metrics_v2/types/metrics_v2response.py b/langfuse/api/metrics_v2/types/metrics_v2response.py new file mode 100644 index 000000000..461eaf178 --- /dev/null +++ b/langfuse/api/metrics_v2/types/metrics_v2response.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class MetricsV2Response(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. + Format varies based on the query parameters. + Histograms will return an array with [lower, upper, height] tuples. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/models/__init__.py b/langfuse/api/models/__init__.py new file mode 100644 index 000000000..7ebdb7762 --- /dev/null +++ b/langfuse/api/models/__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 CreateModelRequest, PaginatedModels +_dynamic_imports: typing.Dict[str, str] = { + "CreateModelRequest": ".types", + "PaginatedModels": ".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__ = ["CreateModelRequest", "PaginatedModels"] diff --git a/langfuse/api/models/client.py b/langfuse/api/models/client.py new file mode 100644 index 000000000..9f817b8f6 --- /dev/null +++ b/langfuse/api/models/client.py @@ -0,0 +1,523 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..commons.types.model import Model +from ..commons.types.model_usage_unit import ModelUsageUnit +from ..commons.types.pricing_tier_input import PricingTierInput +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawModelsClient, RawModelsClient +from .types.paginated_models import PaginatedModels + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ModelsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawModelsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawModelsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawModelsClient + """ + return self._raw_client + + def create( + self, + *, + model_name: str, + match_pattern: str, + start_date: typing.Optional[dt.datetime] = OMIT, + unit: typing.Optional[ModelUsageUnit] = OMIT, + input_price: typing.Optional[float] = OMIT, + output_price: typing.Optional[float] = OMIT, + total_price: typing.Optional[float] = OMIT, + pricing_tiers: typing.Optional[typing.Sequence[PricingTierInput]] = OMIT, + tokenizer_id: typing.Optional[str] = OMIT, + tokenizer_config: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Model: + """ + Create a model + + Parameters + ---------- + model_name : str + Name of the model definition. If multiple with the same name exist, they are applied in the following order: (1) custom over built-in, (2) newest according to startTime where model.startTime PaginatedModels: + """ + Get all models + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedModels + + 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.models.list() + """ + _response = self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + def get( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Model: + """ + Get a model + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Model + + 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.models.get( + id="id", + ) + """ + _response = self._raw_client.get(id, request_options=request_options) + return _response.data + + def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Delete a model. Cannot delete models managed by Langfuse. You can create your own definition with the same modelName to override the definition though. + + Parameters + ---------- + id : str + + 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.models.delete( + id="id", + ) + """ + _response = self._raw_client.delete(id, request_options=request_options) + return _response.data + + +class AsyncModelsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawModelsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawModelsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawModelsClient + """ + return self._raw_client + + async def create( + self, + *, + model_name: str, + match_pattern: str, + start_date: typing.Optional[dt.datetime] = OMIT, + unit: typing.Optional[ModelUsageUnit] = OMIT, + input_price: typing.Optional[float] = OMIT, + output_price: typing.Optional[float] = OMIT, + total_price: typing.Optional[float] = OMIT, + pricing_tiers: typing.Optional[typing.Sequence[PricingTierInput]] = OMIT, + tokenizer_id: typing.Optional[str] = OMIT, + tokenizer_config: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Model: + """ + Create a model + + Parameters + ---------- + model_name : str + Name of the model definition. If multiple with the same name exist, they are applied in the following order: (1) custom over built-in, (2) newest according to startTime where model.startTime None: + await client.models.create( + model_name="modelName", + match_pattern="matchPattern", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + model_name=model_name, + match_pattern=match_pattern, + start_date=start_date, + unit=unit, + input_price=input_price, + output_price=output_price, + total_price=total_price, + pricing_tiers=pricing_tiers, + tokenizer_id=tokenizer_id, + tokenizer_config=tokenizer_config, + request_options=request_options, + ) + return _response.data + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedModels: + """ + Get all models + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedModels + + 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.models.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def get( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Model: + """ + Get a model + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Model + + 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.models.get( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(id, request_options=request_options) + return _response.data + + async def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Delete a model. Cannot delete models managed by Langfuse. You can create your own definition with the same modelName to override the definition though. + + Parameters + ---------- + id : str + + 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.models.delete( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(id, request_options=request_options) + return _response.data diff --git a/langfuse/api/models/raw_client.py b/langfuse/api/models/raw_client.py new file mode 100644 index 000000000..0fdc72319 --- /dev/null +++ b/langfuse/api/models/raw_client.py @@ -0,0 +1,993 @@ +# 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.model import Model +from ..commons.types.model_usage_unit import ModelUsageUnit +from ..commons.types.pricing_tier_input import PricingTierInput +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.paginated_models import PaginatedModels + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawModelsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + *, + model_name: str, + match_pattern: str, + start_date: typing.Optional[dt.datetime] = OMIT, + unit: typing.Optional[ModelUsageUnit] = OMIT, + input_price: typing.Optional[float] = OMIT, + output_price: typing.Optional[float] = OMIT, + total_price: typing.Optional[float] = OMIT, + pricing_tiers: typing.Optional[typing.Sequence[PricingTierInput]] = OMIT, + tokenizer_id: typing.Optional[str] = OMIT, + tokenizer_config: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Model]: + """ + Create a model + + Parameters + ---------- + model_name : str + Name of the model definition. If multiple with the same name exist, they are applied in the following order: (1) custom over built-in, (2) newest according to startTime where model.startTime HttpResponse[PaginatedModels]: + """ + Get all models + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedModels] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/models", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedModels, + parse_obj_as( + type_=PaginatedModels, # 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( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Model]: + """ + Get a model + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Model] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/models/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Model, + parse_obj_as( + type_=Model, # 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 delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + Delete a model. Cannot delete models managed by Langfuse. You can create your own definition with the same modelName to override the definition though. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/models/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 AsyncRawModelsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + *, + model_name: str, + match_pattern: str, + start_date: typing.Optional[dt.datetime] = OMIT, + unit: typing.Optional[ModelUsageUnit] = OMIT, + input_price: typing.Optional[float] = OMIT, + output_price: typing.Optional[float] = OMIT, + total_price: typing.Optional[float] = OMIT, + pricing_tiers: typing.Optional[typing.Sequence[PricingTierInput]] = OMIT, + tokenizer_id: typing.Optional[str] = OMIT, + tokenizer_config: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Model]: + """ + Create a model + + Parameters + ---------- + model_name : str + Name of the model definition. If multiple with the same name exist, they are applied in the following order: (1) custom over built-in, (2) newest according to startTime where model.startTime AsyncHttpResponse[PaginatedModels]: + """ + Get all models + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedModels] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/models", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedModels, + parse_obj_as( + type_=PaginatedModels, # 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( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Model]: + """ + Get a model + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Model] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/models/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Model, + parse_obj_as( + type_=Model, # 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 delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Delete a model. Cannot delete models managed by Langfuse. You can create your own definition with the same modelName to override the definition though. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/models/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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/models/types/__init__.py b/langfuse/api/models/types/__init__.py new file mode 100644 index 000000000..8b4b651c5 --- /dev/null +++ b/langfuse/api/models/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_model_request import CreateModelRequest + from .paginated_models import PaginatedModels +_dynamic_imports: typing.Dict[str, str] = { + "CreateModelRequest": ".create_model_request", + "PaginatedModels": ".paginated_models", +} + + +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__ = ["CreateModelRequest", "PaginatedModels"] diff --git a/langfuse/api/resources/models/types/create_model_request.py b/langfuse/api/models/types/create_model_request.py similarity index 54% rename from langfuse/api/resources/models/types/create_model_request.py rename to langfuse/api/models/types/create_model_request.py index 3f9f80119..dc19db944 100644 --- a/langfuse/api/resources/models/types/create_model_request.py +++ b/langfuse/api/models/types/create_model_request.py @@ -3,59 +3,66 @@ import datetime as dt import typing -from ....core.datetime_utils import serialize_datetime -from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +import pydantic +import typing_extensions from ...commons.types.model_usage_unit import ModelUsageUnit from ...commons.types.pricing_tier_input import PricingTierInput +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata -class CreateModelRequest(pydantic_v1.BaseModel): - model_name: str = pydantic_v1.Field(alias="modelName") +class CreateModelRequest(UniversalBaseModel): + model_name: typing_extensions.Annotated[str, FieldMetadata(alias="modelName")] = ( + pydantic.Field() + ) """ Name of the model definition. If multiple with the same name exist, they are applied in the following order: (1) custom over built-in, (2) newest according to startTime where model.startTime 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} + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/models/types/paginated_models.py b/langfuse/api/models/types/paginated_models.py new file mode 100644 index 000000000..b4bd803cf --- /dev/null +++ b/langfuse/api/models/types/paginated_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.model import Model +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class PaginatedModels(UniversalBaseModel): + data: typing.List[Model] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/observations/__init__.py b/langfuse/api/observations/__init__.py new file mode 100644 index 000000000..22b445984 --- /dev/null +++ b/langfuse/api/observations/__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 Observations, ObservationsViews +_dynamic_imports: typing.Dict[str, str] = { + "Observations": ".types", + "ObservationsViews": ".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__ = ["Observations", "ObservationsViews"] diff --git a/langfuse/api/resources/observations/client.py b/langfuse/api/observations/client.py similarity index 67% rename from langfuse/api/resources/observations/client.py rename to langfuse/api/observations/client.py index cac4efe4d..0d292a442 100644 --- a/langfuse/api/resources/observations/client.py +++ b/langfuse/api/observations/client.py @@ -2,27 +2,29 @@ import datetime as dt import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.datetime_utils import serialize_datetime -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 ..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 class ObservationsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = RawObservationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawObservationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawObservationsClient + """ + return self._raw_client def get( self, @@ -47,9 +49,9 @@ def get( Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -61,36 +63,10 @@ def get( observation_id="observationId", ) """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/observations/{jsonable_encoder(observation_id)}", - method="GET", - request_options=request_options, + _response = self._raw_client.get( + observation_id, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ObservationsView, _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) + return _response.data def get_many( self, @@ -250,9 +226,9 @@ def get_many( Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -262,60 +238,39 @@ def get_many( ) client.observations.get_many() """ - _response = self._client_wrapper.httpx_client.request( - "api/public/observations", - method="GET", - params={ - "page": page, - "limit": limit, - "name": name, - "userId": user_id, - "type": type, - "traceId": trace_id, - "level": level, - "parentObservationId": parent_observation_id, - "environment": environment, - "fromStartTime": serialize_datetime(from_start_time) - if from_start_time is not None - else None, - "toStartTime": serialize_datetime(to_start_time) - if to_start_time is not None - else None, - "version": version, - "filter": filter, - }, + _response = self._raw_client.get_many( + page=page, + limit=limit, + name=name, + user_id=user_id, + type=type, + trace_id=trace_id, + level=level, + parent_observation_id=parent_observation_id, + environment=environment, + from_start_time=from_start_time, + to_start_time=to_start_time, + version=version, + filter=filter, request_options=request_options, ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ObservationsViews, _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) + return _response.data class AsyncObservationsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = AsyncRawObservationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawObservationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawObservationsClient + """ + return self._raw_client async def get( self, @@ -342,9 +297,9 @@ async def get( -------- import asyncio - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -362,36 +317,10 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/observations/{jsonable_encoder(observation_id)}", - method="GET", - request_options=request_options, + _response = await self._raw_client.get( + observation_id, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ObservationsView, _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) + return _response.data async def get_many( self, @@ -553,9 +482,9 @@ async def get_many( -------- import asyncio - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -571,52 +500,20 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/observations", - method="GET", - params={ - "page": page, - "limit": limit, - "name": name, - "userId": user_id, - "type": type, - "traceId": trace_id, - "level": level, - "parentObservationId": parent_observation_id, - "environment": environment, - "fromStartTime": serialize_datetime(from_start_time) - if from_start_time is not None - else None, - "toStartTime": serialize_datetime(to_start_time) - if to_start_time is not None - else None, - "version": version, - "filter": filter, - }, + _response = await self._raw_client.get_many( + page=page, + limit=limit, + name=name, + user_id=user_id, + type=type, + trace_id=trace_id, + level=level, + parent_observation_id=parent_observation_id, + environment=environment, + from_start_time=from_start_time, + to_start_time=to_start_time, + version=version, + filter=filter, request_options=request_options, ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ObservationsViews, _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) + return _response.data diff --git a/langfuse/api/observations/raw_client.py b/langfuse/api/observations/raw_client.py new file mode 100644 index 000000000..f87c0f044 --- /dev/null +++ b/langfuse/api/observations/raw_client.py @@ -0,0 +1,755 @@ +# 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.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 + + +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, + limit: typing.Optional[int] = None, + name: typing.Optional[str] = None, + user_id: typing.Optional[str] = None, + type: typing.Optional[str] = None, + trace_id: typing.Optional[str] = None, + level: typing.Optional[ObservationLevel] = None, + parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + from_start_time: typing.Optional[dt.datetime] = None, + to_start_time: typing.Optional[dt.datetime] = None, + version: typing.Optional[str] = None, + filter: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObservationsViews]: + """ + Get a list of observations + + 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. + + name : typing.Optional[str] + + user_id : typing.Optional[str] + + type : typing.Optional[str] + + trace_id : typing.Optional[str] + + level : typing.Optional[ObservationLevel] + Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR"). + + parent_observation_id : typing.Optional[str] + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + + from_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time on or after this datetime (ISO 8601). + + to_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time before this datetime (ISO 8601). + + version : typing.Optional[str] + Optional filter to only include observations with a certain version. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Observation Fields + - `id` (string) - Observation ID + - `type` (string) - Observation type (SPAN, GENERATION, EVENT) + - `name` (string) - Observation name + - `traceId` (string) - Associated trace ID + - `startTime` (datetime) - Observation start time + - `endTime` (datetime) - Observation end time + - `environment` (string) - Environment tag + - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) + - `statusMessage` (string) - Status message + - `version` (string) - Version tag + + ### Performance Metrics + - `latency` (number) - Latency in seconds (calculated: end_time - start_time) + - `timeToFirstToken` (number) - Time to first token in seconds + - `tokensPerSecond` (number) - Output tokens per second + + ### Token Usage + - `inputTokens` (number) - Number of input tokens + - `outputTokens` (number) - Number of output tokens + - `totalTokens` (number) - Total tokens (alias: `tokens`) + + ### Cost Metrics + - `inputCost` (number) - Input cost in USD + - `outputCost` (number) - Output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Model Information + - `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 + [ + { + "type": "string", + "column": "type", + "operator": "=", + "value": "GENERATION" + }, + { + "type": "number", + "column": "latency", + "operator": ">=", + "value": 2.5 + }, + { + "type": "stringObject", + "column": "metadata", + "key": "environment", + "operator": "=", + "value": "production" + } + ] + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObservationsViews] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/observations", + method="GET", + params={ + "page": page, + "limit": limit, + "name": name, + "userId": user_id, + "type": type, + "traceId": trace_id, + "level": level, + "parentObservationId": parent_observation_id, + "environment": environment, + "fromStartTime": serialize_datetime(from_start_time) + if from_start_time is not None + else None, + "toStartTime": serialize_datetime(to_start_time) + if to_start_time is not None + else None, + "version": version, + "filter": filter, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationsViews, + parse_obj_as( + type_=ObservationsViews, # 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 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, + limit: typing.Optional[int] = None, + name: typing.Optional[str] = None, + user_id: typing.Optional[str] = None, + type: typing.Optional[str] = None, + trace_id: typing.Optional[str] = None, + level: typing.Optional[ObservationLevel] = None, + parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + from_start_time: typing.Optional[dt.datetime] = None, + to_start_time: typing.Optional[dt.datetime] = None, + version: typing.Optional[str] = None, + filter: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObservationsViews]: + """ + Get a list of observations + + 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. + + name : typing.Optional[str] + + user_id : typing.Optional[str] + + type : typing.Optional[str] + + trace_id : typing.Optional[str] + + level : typing.Optional[ObservationLevel] + Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR"). + + parent_observation_id : typing.Optional[str] + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + + from_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time on or after this datetime (ISO 8601). + + to_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time before this datetime (ISO 8601). + + version : typing.Optional[str] + Optional filter to only include observations with a certain version. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Observation Fields + - `id` (string) - Observation ID + - `type` (string) - Observation type (SPAN, GENERATION, EVENT) + - `name` (string) - Observation name + - `traceId` (string) - Associated trace ID + - `startTime` (datetime) - Observation start time + - `endTime` (datetime) - Observation end time + - `environment` (string) - Environment tag + - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) + - `statusMessage` (string) - Status message + - `version` (string) - Version tag + + ### Performance Metrics + - `latency` (number) - Latency in seconds (calculated: end_time - start_time) + - `timeToFirstToken` (number) - Time to first token in seconds + - `tokensPerSecond` (number) - Output tokens per second + + ### Token Usage + - `inputTokens` (number) - Number of input tokens + - `outputTokens` (number) - Number of output tokens + - `totalTokens` (number) - Total tokens (alias: `tokens`) + + ### Cost Metrics + - `inputCost` (number) - Input cost in USD + - `outputCost` (number) - Output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Model Information + - `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 + [ + { + "type": "string", + "column": "type", + "operator": "=", + "value": "GENERATION" + }, + { + "type": "number", + "column": "latency", + "operator": ">=", + "value": 2.5 + }, + { + "type": "stringObject", + "column": "metadata", + "key": "environment", + "operator": "=", + "value": "production" + } + ] + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObservationsViews] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/observations", + method="GET", + params={ + "page": page, + "limit": limit, + "name": name, + "userId": user_id, + "type": type, + "traceId": trace_id, + "level": level, + "parentObservationId": parent_observation_id, + "environment": environment, + "fromStartTime": serialize_datetime(from_start_time) + if from_start_time is not None + else None, + "toStartTime": serialize_datetime(to_start_time) + if to_start_time is not None + else None, + "version": version, + "filter": filter, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationsViews, + parse_obj_as( + type_=ObservationsViews, # 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/types/__init__.py b/langfuse/api/observations/types/__init__.py new file mode 100644 index 000000000..247b674a1 --- /dev/null +++ b/langfuse/api/observations/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 .observations import Observations + from .observations_views import ObservationsViews +_dynamic_imports: typing.Dict[str, str] = { + "Observations": ".observations", + "ObservationsViews": ".observations_views", +} + + +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__ = ["Observations", "ObservationsViews"] diff --git a/langfuse/api/observations/types/observations.py b/langfuse/api/observations/types/observations.py new file mode 100644 index 000000000..61a47cdc5 --- /dev/null +++ b/langfuse/api/observations/types/observations.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.observation import Observation +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class Observations(UniversalBaseModel): + data: typing.List[Observation] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/observations/types/observations_views.py b/langfuse/api/observations/types/observations_views.py new file mode 100644 index 000000000..ee682ed39 --- /dev/null +++ b/langfuse/api/observations/types/observations_views.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +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 + + +class ObservationsViews(UniversalBaseModel): + data: typing.List[ObservationsView] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/observations_v2/__init__.py b/langfuse/api/observations_v2/__init__.py new file mode 100644 index 000000000..66816e540 --- /dev/null +++ b/langfuse/api/observations_v2/__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 ObservationsV2Meta, ObservationsV2Response +_dynamic_imports: typing.Dict[str, str] = { + "ObservationsV2Meta": ".types", + "ObservationsV2Response": ".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__ = ["ObservationsV2Meta", "ObservationsV2Response"] diff --git a/langfuse/api/observations_v2/client.py b/langfuse/api/observations_v2/client.py new file mode 100644 index 000000000..5594c3dc5 --- /dev/null +++ b/langfuse/api/observations_v2/client.py @@ -0,0 +1,504 @@ +# This file was auto-generated by Fern from our API Definition. + +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 + + +class ObservationsV2Client: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawObservationsV2Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawObservationsV2Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawObservationsV2Client + """ + return self._raw_client + + def get_many( + self, + *, + fields: 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, + trace_id: typing.Optional[str] = None, + level: typing.Optional[ObservationLevel] = None, + parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + from_start_time: typing.Optional[dt.datetime] = None, + to_start_time: typing.Optional[dt.datetime] = None, + version: typing.Optional[str] = None, + filter: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ObservationsV2Response: + """ + 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 + - `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 + ---------- + 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" + + 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] + Set to `true` to parse input/output fields as JSON, or `false` to return raw strings. + Defaults to `false` if not provided. + + 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] + + level : typing.Optional[ObservationLevel] + Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR"). + + parent_observation_id : typing.Optional[str] + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + + from_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time on or after this datetime (ISO 8601). + + to_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time before this datetime (ISO 8601). + + version : typing.Optional[str] + Optional filter to only include observations with a certain version. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Observation Fields + - `id` (string) - Observation ID + - `type` (string) - Observation type (SPAN, GENERATION, EVENT) + - `name` (string) - Observation name + - `traceId` (string) - Associated trace ID + - `startTime` (datetime) - Observation start time + - `endTime` (datetime) - Observation end time + - `environment` (string) - Environment tag + - `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) + - `timeToFirstToken` (number) - Time to first token in seconds + - `tokensPerSecond` (number) - Output tokens per second + + ### Token Usage + - `inputTokens` (number) - Number of input tokens + - `outputTokens` (number) - Number of output tokens + - `totalTokens` (number) - Total tokens (alias: `tokens`) + + ### Cost Metrics + - `inputCost` (number) - Input cost in USD + - `outputCost` (number) - Output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Model Information + - `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. + + ## Filter Examples + ```json + [ + { + "type": "string", + "column": "type", + "operator": "=", + "value": "GENERATION" + }, + { + "type": "number", + "column": "latency", + "operator": ">=", + "value": 2.5 + }, + { + "type": "stringObject", + "column": "metadata", + "key": "environment", + "operator": "=", + "value": "production" + } + ] + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationsV2Response + + 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_v2.get_many() + """ + _response = self._raw_client.get_many( + fields=fields, + limit=limit, + cursor=cursor, + parse_io_as_json=parse_io_as_json, + name=name, + user_id=user_id, + type=type, + trace_id=trace_id, + level=level, + parent_observation_id=parent_observation_id, + environment=environment, + from_start_time=from_start_time, + to_start_time=to_start_time, + version=version, + filter=filter, + request_options=request_options, + ) + return _response.data + + +class AsyncObservationsV2Client: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawObservationsV2Client(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawObservationsV2Client: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawObservationsV2Client + """ + return self._raw_client + + async def get_many( + self, + *, + fields: 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, + trace_id: typing.Optional[str] = None, + level: typing.Optional[ObservationLevel] = None, + parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + from_start_time: typing.Optional[dt.datetime] = None, + to_start_time: typing.Optional[dt.datetime] = None, + version: typing.Optional[str] = None, + filter: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ObservationsV2Response: + """ + 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 + - `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 + ---------- + 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" + + 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] + Set to `true` to parse input/output fields as JSON, or `false` to return raw strings. + Defaults to `false` if not provided. + + 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] + + level : typing.Optional[ObservationLevel] + Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR"). + + parent_observation_id : typing.Optional[str] + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + + from_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time on or after this datetime (ISO 8601). + + to_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time before this datetime (ISO 8601). + + version : typing.Optional[str] + Optional filter to only include observations with a certain version. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Observation Fields + - `id` (string) - Observation ID + - `type` (string) - Observation type (SPAN, GENERATION, EVENT) + - `name` (string) - Observation name + - `traceId` (string) - Associated trace ID + - `startTime` (datetime) - Observation start time + - `endTime` (datetime) - Observation end time + - `environment` (string) - Environment tag + - `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) + - `timeToFirstToken` (number) - Time to first token in seconds + - `tokensPerSecond` (number) - Output tokens per second + + ### Token Usage + - `inputTokens` (number) - Number of input tokens + - `outputTokens` (number) - Number of output tokens + - `totalTokens` (number) - Total tokens (alias: `tokens`) + + ### Cost Metrics + - `inputCost` (number) - Input cost in USD + - `outputCost` (number) - Output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Model Information + - `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. + + ## Filter Examples + ```json + [ + { + "type": "string", + "column": "type", + "operator": "=", + "value": "GENERATION" + }, + { + "type": "number", + "column": "latency", + "operator": ">=", + "value": 2.5 + }, + { + "type": "stringObject", + "column": "metadata", + "key": "environment", + "operator": "=", + "value": "production" + } + ] + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationsV2Response + + 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_v2.get_many() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_many( + fields=fields, + limit=limit, + cursor=cursor, + parse_io_as_json=parse_io_as_json, + name=name, + user_id=user_id, + type=type, + trace_id=trace_id, + level=level, + parent_observation_id=parent_observation_id, + environment=environment, + from_start_time=from_start_time, + to_start_time=to_start_time, + version=version, + filter=filter, + request_options=request_options, + ) + return _response.data diff --git a/langfuse/api/observations_v2/raw_client.py b/langfuse/api/observations_v2/raw_client.py new file mode 100644 index 000000000..d80bc948f --- /dev/null +++ b/langfuse/api/observations_v2/raw_client.py @@ -0,0 +1,623 @@ +# 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.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: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_many( + self, + *, + fields: 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, + trace_id: typing.Optional[str] = None, + level: typing.Optional[ObservationLevel] = None, + parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + from_start_time: typing.Optional[dt.datetime] = None, + to_start_time: typing.Optional[dt.datetime] = None, + version: typing.Optional[str] = None, + filter: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObservationsV2Response]: + """ + 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 + - `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 + ---------- + 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" + + 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] + Set to `true` to parse input/output fields as JSON, or `false` to return raw strings. + Defaults to `false` if not provided. + + 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] + + level : typing.Optional[ObservationLevel] + Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR"). + + parent_observation_id : typing.Optional[str] + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + + from_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time on or after this datetime (ISO 8601). + + to_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time before this datetime (ISO 8601). + + version : typing.Optional[str] + Optional filter to only include observations with a certain version. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Observation Fields + - `id` (string) - Observation ID + - `type` (string) - Observation type (SPAN, GENERATION, EVENT) + - `name` (string) - Observation name + - `traceId` (string) - Associated trace ID + - `startTime` (datetime) - Observation start time + - `endTime` (datetime) - Observation end time + - `environment` (string) - Environment tag + - `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) + - `timeToFirstToken` (number) - Time to first token in seconds + - `tokensPerSecond` (number) - Output tokens per second + + ### Token Usage + - `inputTokens` (number) - Number of input tokens + - `outputTokens` (number) - Number of output tokens + - `totalTokens` (number) - Total tokens (alias: `tokens`) + + ### Cost Metrics + - `inputCost` (number) - Input cost in USD + - `outputCost` (number) - Output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Model Information + - `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. + + ## Filter Examples + ```json + [ + { + "type": "string", + "column": "type", + "operator": "=", + "value": "GENERATION" + }, + { + "type": "number", + "column": "latency", + "operator": ">=", + "value": 2.5 + }, + { + "type": "stringObject", + "column": "metadata", + "key": "environment", + "operator": "=", + "value": "production" + } + ] + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObservationsV2Response] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/v2/observations", + method="GET", + params={ + "fields": fields, + "limit": limit, + "cursor": cursor, + "parseIoAsJson": parse_io_as_json, + "name": name, + "userId": user_id, + "type": type, + "traceId": trace_id, + "level": level, + "parentObservationId": parent_observation_id, + "environment": environment, + "fromStartTime": serialize_datetime(from_start_time) + if from_start_time is not None + else None, + "toStartTime": serialize_datetime(to_start_time) + if to_start_time is not None + else None, + "version": version, + "filter": filter, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationsV2Response, + parse_obj_as( + type_=ObservationsV2Response, # 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 AsyncRawObservationsV2Client: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_many( + self, + *, + fields: 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, + trace_id: typing.Optional[str] = None, + level: typing.Optional[ObservationLevel] = None, + parent_observation_id: typing.Optional[str] = None, + environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + from_start_time: typing.Optional[dt.datetime] = None, + to_start_time: typing.Optional[dt.datetime] = None, + version: typing.Optional[str] = None, + filter: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObservationsV2Response]: + """ + 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 + - `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 + ---------- + 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" + + 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] + Set to `true` to parse input/output fields as JSON, or `false` to return raw strings. + Defaults to `false` if not provided. + + 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] + + level : typing.Optional[ObservationLevel] + Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR"). + + parent_observation_id : typing.Optional[str] + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for observations where the environment is one of the provided values. + + from_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time on or after this datetime (ISO 8601). + + to_start_time : typing.Optional[dt.datetime] + Retrieve only observations with a start_time before this datetime (ISO 8601). + + version : typing.Optional[str] + Optional filter to only include observations with a certain version. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Observation Fields + - `id` (string) - Observation ID + - `type` (string) - Observation type (SPAN, GENERATION, EVENT) + - `name` (string) - Observation name + - `traceId` (string) - Associated trace ID + - `startTime` (datetime) - Observation start time + - `endTime` (datetime) - Observation end time + - `environment` (string) - Environment tag + - `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) + - `timeToFirstToken` (number) - Time to first token in seconds + - `tokensPerSecond` (number) - Output tokens per second + + ### Token Usage + - `inputTokens` (number) - Number of input tokens + - `outputTokens` (number) - Number of output tokens + - `totalTokens` (number) - Total tokens (alias: `tokens`) + + ### Cost Metrics + - `inputCost` (number) - Input cost in USD + - `outputCost` (number) - Output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Model Information + - `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. + + ## Filter Examples + ```json + [ + { + "type": "string", + "column": "type", + "operator": "=", + "value": "GENERATION" + }, + { + "type": "number", + "column": "latency", + "operator": ">=", + "value": 2.5 + }, + { + "type": "stringObject", + "column": "metadata", + "key": "environment", + "operator": "=", + "value": "production" + } + ] + ``` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObservationsV2Response] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/v2/observations", + method="GET", + params={ + "fields": fields, + "limit": limit, + "cursor": cursor, + "parseIoAsJson": parse_io_as_json, + "name": name, + "userId": user_id, + "type": type, + "traceId": trace_id, + "level": level, + "parentObservationId": parent_observation_id, + "environment": environment, + "fromStartTime": serialize_datetime(from_start_time) + if from_start_time is not None + else None, + "toStartTime": serialize_datetime(to_start_time) + if to_start_time is not None + else None, + "version": version, + "filter": filter, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationsV2Response, + parse_obj_as( + type_=ObservationsV2Response, # 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_v2/types/__init__.py b/langfuse/api/observations_v2/types/__init__.py new file mode 100644 index 000000000..6e132aba6 --- /dev/null +++ b/langfuse/api/observations_v2/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 .observations_v2meta import ObservationsV2Meta + from .observations_v2response import ObservationsV2Response +_dynamic_imports: typing.Dict[str, str] = { + "ObservationsV2Meta": ".observations_v2meta", + "ObservationsV2Response": ".observations_v2response", +} + + +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__ = ["ObservationsV2Meta", "ObservationsV2Response"] diff --git a/langfuse/api/observations_v2/types/observations_v2meta.py b/langfuse/api/observations_v2/types/observations_v2meta.py new file mode 100644 index 000000000..8f86a6512 --- /dev/null +++ b/langfuse/api/observations_v2/types/observations_v2meta.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ObservationsV2Meta(UniversalBaseModel): + """ + Metadata for cursor-based pagination + """ + + cursor: typing.Optional[str] = pydantic.Field(default=None) + """ + Base64-encoded cursor to use for retrieving the next page. If not present, there are no more results. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/observations_v2/types/observations_v2response.py b/langfuse/api/observations_v2/types/observations_v2response.py new file mode 100644 index 000000000..e833e1918 --- /dev/null +++ b/langfuse/api/observations_v2/types/observations_v2response.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .observations_v2meta import ObservationsV2Meta + + +class ObservationsV2Response(UniversalBaseModel): + """ + Response containing observations with field-group-based filtering and cursor-based pagination. + + The `data` array contains observation objects with only the requested field groups included. + Use the `cursor` in `meta` to retrieve the next page of results. + """ + + data: typing.List[typing.Dict[str, typing.Any]] = pydantic.Field() + """ + Array of observation objects. Fields included depend on the `fields` parameter in the request. + """ + + meta: ObservationsV2Meta + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/__init__.py b/langfuse/api/opentelemetry/__init__.py new file mode 100644 index 000000000..30caa3796 --- /dev/null +++ b/langfuse/api/opentelemetry/__init__.py @@ -0,0 +1,67 @@ +# 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 ( + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, + OtelTraceResponse, + ) +_dynamic_imports: typing.Dict[str, str] = { + "OtelAttribute": ".types", + "OtelAttributeValue": ".types", + "OtelResource": ".types", + "OtelResourceSpan": ".types", + "OtelScope": ".types", + "OtelScopeSpan": ".types", + "OtelSpan": ".types", + "OtelTraceResponse": ".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__ = [ + "OtelAttribute", + "OtelAttributeValue", + "OtelResource", + "OtelResourceSpan", + "OtelScope", + "OtelScopeSpan", + "OtelSpan", + "OtelTraceResponse", +] diff --git a/langfuse/api/resources/opentelemetry/client.py b/langfuse/api/opentelemetry/client.py similarity index 69% rename from langfuse/api/resources/opentelemetry/client.py rename to langfuse/api/opentelemetry/client.py index de17949d4..13177e5e6 100644 --- a/langfuse/api/resources/opentelemetry/client.py +++ b/langfuse/api/opentelemetry/client.py @@ -1,17 +1,10 @@ # 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.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 ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawOpentelemetryClient, RawOpentelemetryClient from .types.otel_resource_span import OtelResourceSpan from .types.otel_trace_response import OtelTraceResponse @@ -21,7 +14,18 @@ class OpentelemetryClient: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = RawOpentelemetryClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawOpentelemetryClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawOpentelemetryClient + """ + return self._raw_client def export_traces( self, @@ -61,7 +65,8 @@ def export_traces( Examples -------- - from langfuse import ( + from langfuse import LangfuseAPI + from langfuse.opentelemetry import ( OtelAttribute, OtelAttributeValue, OtelResource, @@ -70,9 +75,8 @@ def export_traces( OtelScopeSpan, OtelSpan, ) - from langfuse.client import FernLangfuse - client = FernLangfuse( + 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", @@ -130,43 +134,26 @@ def export_traces( ], ) """ - _response = self._client_wrapper.httpx_client.request( - "api/public/otel/v1/traces", - method="POST", - json={"resourceSpans": resource_spans}, - request_options=request_options, - omit=OMIT, + _response = self._raw_client.export_traces( + resource_spans=resource_spans, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(OtelTraceResponse, _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) + return _response.data class AsyncOpentelemetryClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = AsyncRawOpentelemetryClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawOpentelemetryClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawOpentelemetryClient + """ + return self._raw_client async def export_traces( self, @@ -208,7 +195,8 @@ async def export_traces( -------- import asyncio - from langfuse import ( + from langfuse import AsyncLangfuseAPI + from langfuse.opentelemetry import ( OtelAttribute, OtelAttributeValue, OtelResource, @@ -217,9 +205,8 @@ async def export_traces( OtelScopeSpan, OtelSpan, ) - from langfuse.client import AsyncFernLangfuse - client = AsyncFernLangfuse( + 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", @@ -283,35 +270,7 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/otel/v1/traces", - method="POST", - json={"resourceSpans": resource_spans}, - request_options=request_options, - omit=OMIT, + _response = await self._raw_client.export_traces( + resource_spans=resource_spans, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(OtelTraceResponse, _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) + return _response.data diff --git a/langfuse/api/opentelemetry/raw_client.py b/langfuse/api/opentelemetry/raw_client.py new file mode 100644 index 000000000..6b68f909b --- /dev/null +++ b/langfuse/api/opentelemetry/raw_client.py @@ -0,0 +1,291 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..commons.errors.access_denied_error import AccessDeniedError +from ..commons.errors.error import Error +from ..commons.errors.method_not_allowed_error import MethodNotAllowedError +from ..commons.errors.not_found_error import NotFoundError +from ..commons.errors.unauthorized_error import UnauthorizedError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from .types.otel_resource_span import OtelResourceSpan +from .types.otel_trace_response import OtelTraceResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawOpentelemetryClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def export_traces( + self, + *, + resource_spans: typing.Sequence[OtelResourceSpan], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[OtelTraceResponse]: + """ + **OpenTelemetry Traces Ingestion Endpoint** + + This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + + **Supported Formats:** + - Binary Protobuf: `Content-Type: application/x-protobuf` + - JSON Protobuf: `Content-Type: application/json` + - Supports gzip compression via `Content-Encoding: gzip` header + + **Specification Compliance:** + - Conforms to [OTLP/HTTP Trace Export](https://opentelemetry.io/docs/specs/otlp/#otlphttp) + - Implements `ExportTraceServiceRequest` message format + + **Documentation:** + - Integration guide: https://langfuse.com/integrations/native/opentelemetry + - Data model: https://langfuse.com/docs/observability/data-model + + Parameters + ---------- + resource_spans : typing.Sequence[OtelResourceSpan] + Array of resource spans containing trace data as defined in the OTLP specification + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[OtelTraceResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/otel/v1/traces", + method="POST", + json={ + "resourceSpans": convert_and_respect_annotation_metadata( + object_=resource_spans, + annotation=typing.Sequence[OtelResourceSpan], + direction="write", + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + OtelTraceResponse, + parse_obj_as( + type_=OtelTraceResponse, # 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 AsyncRawOpentelemetryClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def export_traces( + self, + *, + resource_spans: typing.Sequence[OtelResourceSpan], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[OtelTraceResponse]: + """ + **OpenTelemetry Traces Ingestion Endpoint** + + This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + + **Supported Formats:** + - Binary Protobuf: `Content-Type: application/x-protobuf` + - JSON Protobuf: `Content-Type: application/json` + - Supports gzip compression via `Content-Encoding: gzip` header + + **Specification Compliance:** + - Conforms to [OTLP/HTTP Trace Export](https://opentelemetry.io/docs/specs/otlp/#otlphttp) + - Implements `ExportTraceServiceRequest` message format + + **Documentation:** + - Integration guide: https://langfuse.com/integrations/native/opentelemetry + - Data model: https://langfuse.com/docs/observability/data-model + + Parameters + ---------- + resource_spans : typing.Sequence[OtelResourceSpan] + Array of resource spans containing trace data as defined in the OTLP specification + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[OtelTraceResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/otel/v1/traces", + method="POST", + json={ + "resourceSpans": convert_and_respect_annotation_metadata( + object_=resource_spans, + annotation=typing.Sequence[OtelResourceSpan], + direction="write", + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + OtelTraceResponse, + parse_obj_as( + type_=OtelTraceResponse, # 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/opentelemetry/types/__init__.py b/langfuse/api/opentelemetry/types/__init__.py new file mode 100644 index 000000000..ad2fd4899 --- /dev/null +++ b/langfuse/api/opentelemetry/types/__init__.py @@ -0,0 +1,65 @@ +# 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 .otel_attribute import OtelAttribute + from .otel_attribute_value import OtelAttributeValue + from .otel_resource import OtelResource + from .otel_resource_span import OtelResourceSpan + from .otel_scope import OtelScope + from .otel_scope_span import OtelScopeSpan + from .otel_span import OtelSpan + from .otel_trace_response import OtelTraceResponse +_dynamic_imports: typing.Dict[str, str] = { + "OtelAttribute": ".otel_attribute", + "OtelAttributeValue": ".otel_attribute_value", + "OtelResource": ".otel_resource", + "OtelResourceSpan": ".otel_resource_span", + "OtelScope": ".otel_scope", + "OtelScopeSpan": ".otel_scope_span", + "OtelSpan": ".otel_span", + "OtelTraceResponse": ".otel_trace_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "OtelAttribute", + "OtelAttributeValue", + "OtelResource", + "OtelResourceSpan", + "OtelScope", + "OtelScopeSpan", + "OtelSpan", + "OtelTraceResponse", +] diff --git a/langfuse/api/opentelemetry/types/otel_attribute.py b/langfuse/api/opentelemetry/types/otel_attribute.py new file mode 100644 index 000000000..479a46551 --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_attribute.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .otel_attribute_value import OtelAttributeValue + + +class OtelAttribute(UniversalBaseModel): + """ + Key-value attribute pair for resources, scopes, or spans + """ + + key: typing.Optional[str] = pydantic.Field(default=None) + """ + Attribute key (e.g., "service.name", "langfuse.observation.type") + """ + + value: typing.Optional[OtelAttributeValue] = pydantic.Field(default=None) + """ + Attribute value + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/types/otel_attribute_value.py b/langfuse/api/opentelemetry/types/otel_attribute_value.py new file mode 100644 index 000000000..c381dd28f --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_attribute_value.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class OtelAttributeValue(UniversalBaseModel): + """ + Attribute value wrapper supporting different value types + """ + + string_value: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="stringValue") + ] = pydantic.Field(default=None) + """ + String value + """ + + int_value: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="intValue") + ] = pydantic.Field(default=None) + """ + Integer value + """ + + double_value: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="doubleValue") + ] = pydantic.Field(default=None) + """ + Double value + """ + + bool_value: typing_extensions.Annotated[ + typing.Optional[bool], FieldMetadata(alias="boolValue") + ] = pydantic.Field(default=None) + """ + Boolean value + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/types/otel_resource.py b/langfuse/api/opentelemetry/types/otel_resource.py new file mode 100644 index 000000000..c8f1bf2b0 --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_resource.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .otel_attribute import OtelAttribute + + +class OtelResource(UniversalBaseModel): + """ + Resource attributes identifying the source of telemetry + """ + + attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic.Field( + default=None + ) + """ + Resource attributes like service.name, service.version, etc. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/types/otel_resource_span.py b/langfuse/api/opentelemetry/types/otel_resource_span.py new file mode 100644 index 000000000..d26404cd3 --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_resource_span.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .otel_resource import OtelResource +from .otel_scope_span import OtelScopeSpan + + +class OtelResourceSpan(UniversalBaseModel): + """ + Represents a collection of spans from a single resource as per OTLP specification + """ + + resource: typing.Optional[OtelResource] = pydantic.Field(default=None) + """ + Resource information + """ + + scope_spans: typing_extensions.Annotated[ + typing.Optional[typing.List[OtelScopeSpan]], FieldMetadata(alias="scopeSpans") + ] = pydantic.Field(default=None) + """ + Array of scope spans + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/types/otel_scope.py b/langfuse/api/opentelemetry/types/otel_scope.py new file mode 100644 index 000000000..a705fc2ec --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_scope.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .otel_attribute import OtelAttribute + + +class OtelScope(UniversalBaseModel): + """ + Instrumentation scope information + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Instrumentation scope name + """ + + version: typing.Optional[str] = pydantic.Field(default=None) + """ + Instrumentation scope version + """ + + attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic.Field( + default=None + ) + """ + Additional scope attributes + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/types/otel_scope_span.py b/langfuse/api/opentelemetry/types/otel_scope_span.py new file mode 100644 index 000000000..9736454b5 --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_scope_span.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .otel_scope import OtelScope +from .otel_span import OtelSpan + + +class OtelScopeSpan(UniversalBaseModel): + """ + Collection of spans from a single instrumentation scope + """ + + scope: typing.Optional[OtelScope] = pydantic.Field(default=None) + """ + Instrumentation scope information + """ + + spans: typing.Optional[typing.List[OtelSpan]] = pydantic.Field(default=None) + """ + Array of spans + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/types/otel_span.py b/langfuse/api/opentelemetry/types/otel_span.py new file mode 100644 index 000000000..f6ef51a36 --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_span.py @@ -0,0 +1,76 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .otel_attribute import OtelAttribute + + +class OtelSpan(UniversalBaseModel): + """ + Individual span representing a unit of work or operation + """ + + trace_id: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="traceId") + ] = pydantic.Field(default=None) + """ + Trace ID (16 bytes, hex-encoded string in JSON or Buffer in binary) + """ + + span_id: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="spanId") + ] = pydantic.Field(default=None) + """ + Span ID (8 bytes, hex-encoded string in JSON or Buffer in binary) + """ + + parent_span_id: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="parentSpanId") + ] = pydantic.Field(default=None) + """ + Parent span ID if this is a child span + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Span name describing the operation + """ + + kind: typing.Optional[int] = pydantic.Field(default=None) + """ + Span kind (1=INTERNAL, 2=SERVER, 3=CLIENT, 4=PRODUCER, 5=CONSUMER) + """ + + start_time_unix_nano: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="startTimeUnixNano") + ] = pydantic.Field(default=None) + """ + Start time in nanoseconds since Unix epoch + """ + + end_time_unix_nano: typing_extensions.Annotated[ + typing.Optional[typing.Any], FieldMetadata(alias="endTimeUnixNano") + ] = pydantic.Field(default=None) + """ + End time in nanoseconds since Unix epoch + """ + + attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic.Field( + default=None + ) + """ + Span attributes including Langfuse-specific attributes (langfuse.observation.*) + """ + + status: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + Span status object + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/opentelemetry/types/otel_trace_response.py b/langfuse/api/opentelemetry/types/otel_trace_response.py new file mode 100644 index 000000000..02386b088 --- /dev/null +++ b/langfuse/api/opentelemetry/types/otel_trace_response.py @@ -0,0 +1,16 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class OtelTraceResponse(UniversalBaseModel): + """ + Response from trace export request. Empty object indicates success. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/__init__.py b/langfuse/api/organizations/__init__.py new file mode 100644 index 000000000..469d10a42 --- /dev/null +++ b/langfuse/api/organizations/__init__.py @@ -0,0 +1,73 @@ +# 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 ( + DeleteMembershipRequest, + MembershipDeletionResponse, + MembershipRequest, + MembershipResponse, + MembershipRole, + MembershipsResponse, + OrganizationApiKey, + OrganizationApiKeysResponse, + OrganizationProject, + OrganizationProjectsResponse, + ) +_dynamic_imports: typing.Dict[str, str] = { + "DeleteMembershipRequest": ".types", + "MembershipDeletionResponse": ".types", + "MembershipRequest": ".types", + "MembershipResponse": ".types", + "MembershipRole": ".types", + "MembershipsResponse": ".types", + "OrganizationApiKey": ".types", + "OrganizationApiKeysResponse": ".types", + "OrganizationProject": ".types", + "OrganizationProjectsResponse": ".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__ = [ + "DeleteMembershipRequest", + "MembershipDeletionResponse", + "MembershipRequest", + "MembershipResponse", + "MembershipRole", + "MembershipsResponse", + "OrganizationApiKey", + "OrganizationApiKeysResponse", + "OrganizationProject", + "OrganizationProjectsResponse", +] diff --git a/langfuse/api/organizations/client.py b/langfuse/api/organizations/client.py new file mode 100644 index 000000000..085c14c5f --- /dev/null +++ b/langfuse/api/organizations/client.py @@ -0,0 +1,756 @@ +# 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 AsyncRawOrganizationsClient, RawOrganizationsClient +from .types.membership_deletion_response import MembershipDeletionResponse +from .types.membership_response import MembershipResponse +from .types.membership_role import MembershipRole +from .types.memberships_response import MembershipsResponse +from .types.organization_api_keys_response import OrganizationApiKeysResponse +from .types.organization_projects_response import OrganizationProjectsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class OrganizationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawOrganizationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawOrganizationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawOrganizationsClient + """ + return self._raw_client + + def get_organization_memberships( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> MembershipsResponse: + """ + Get all memberships for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipsResponse + + 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.organizations.get_organization_memberships() + """ + _response = self._raw_client.get_organization_memberships( + request_options=request_options + ) + return _response.data + + def update_organization_membership( + self, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipResponse: + """ + Create or update a membership for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipResponse + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.organizations import MembershipRole + + 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.organizations.update_organization_membership( + user_id="userId", + role=MembershipRole.OWNER, + ) + """ + _response = self._raw_client.update_organization_membership( + user_id=user_id, role=role, request_options=request_options + ) + return _response.data + + def delete_organization_membership( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> MembershipDeletionResponse: + """ + Delete a membership from the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + 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.organizations.delete_organization_membership( + user_id="userId", + ) + """ + _response = self._raw_client.delete_organization_membership( + user_id=user_id, request_options=request_options + ) + return _response.data + + def get_project_memberships( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipsResponse: + """ + Get all memberships for a specific project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipsResponse + + 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.organizations.get_project_memberships( + project_id="projectId", + ) + """ + _response = self._raw_client.get_project_memberships( + project_id, request_options=request_options + ) + return _response.data + + def update_project_membership( + self, + project_id: str, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipResponse: + """ + Create or update a membership for a specific project (requires organization-scoped API key). The user must already be a member of the organization. + + Parameters + ---------- + project_id : str + + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipResponse + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.organizations import MembershipRole + + 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.organizations.update_project_membership( + project_id="projectId", + user_id="userId", + role=MembershipRole.OWNER, + ) + """ + _response = self._raw_client.update_project_membership( + project_id, user_id=user_id, role=role, request_options=request_options + ) + return _response.data + + def delete_project_membership( + self, + project_id: str, + *, + user_id: str, + 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 + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + 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.organizations.delete_project_membership( + project_id="projectId", + user_id="userId", + ) + """ + _response = self._raw_client.delete_project_membership( + project_id, user_id=user_id, request_options=request_options + ) + return _response.data + + def get_organization_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> OrganizationProjectsResponse: + """ + Get all projects for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + OrganizationProjectsResponse + + 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.organizations.get_organization_projects() + """ + _response = self._raw_client.get_organization_projects( + request_options=request_options + ) + return _response.data + + def get_organization_api_keys( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> OrganizationApiKeysResponse: + """ + Get all API keys for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + OrganizationApiKeysResponse + + 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.organizations.get_organization_api_keys() + """ + _response = self._raw_client.get_organization_api_keys( + request_options=request_options + ) + return _response.data + + +class AsyncOrganizationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawOrganizationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawOrganizationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawOrganizationsClient + """ + return self._raw_client + + async def get_organization_memberships( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> MembershipsResponse: + """ + Get all memberships for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipsResponse + + 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.organizations.get_organization_memberships() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_organization_memberships( + request_options=request_options + ) + return _response.data + + async def update_organization_membership( + self, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipResponse: + """ + Create or update a membership for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.organizations import MembershipRole + + 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.organizations.update_organization_membership( + user_id="userId", + role=MembershipRole.OWNER, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_organization_membership( + user_id=user_id, role=role, request_options=request_options + ) + return _response.data + + async def delete_organization_membership( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> MembershipDeletionResponse: + """ + Delete a membership from the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + 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.organizations.delete_organization_membership( + user_id="userId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_organization_membership( + user_id=user_id, request_options=request_options + ) + return _response.data + + async def get_project_memberships( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipsResponse: + """ + Get all memberships for a specific project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipsResponse + + 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.organizations.get_project_memberships( + project_id="projectId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_project_memberships( + project_id, request_options=request_options + ) + return _response.data + + async def update_project_membership( + self, + project_id: str, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> MembershipResponse: + """ + Create or update a membership for a specific project (requires organization-scoped API key). The user must already be a member of the organization. + + Parameters + ---------- + project_id : str + + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.organizations import MembershipRole + + 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.organizations.update_project_membership( + project_id="projectId", + user_id="userId", + role=MembershipRole.OWNER, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_project_membership( + project_id, user_id=user_id, role=role, request_options=request_options + ) + return _response.data + + async def delete_project_membership( + self, + project_id: str, + *, + user_id: str, + 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 + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MembershipDeletionResponse + + 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.organizations.delete_project_membership( + project_id="projectId", + user_id="userId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_project_membership( + project_id, user_id=user_id, request_options=request_options + ) + return _response.data + + async def get_organization_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> OrganizationProjectsResponse: + """ + Get all projects for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + OrganizationProjectsResponse + + 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.organizations.get_organization_projects() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_organization_projects( + request_options=request_options + ) + return _response.data + + async def get_organization_api_keys( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> OrganizationApiKeysResponse: + """ + Get all API keys for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + OrganizationApiKeysResponse + + 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.organizations.get_organization_api_keys() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_organization_api_keys( + request_options=request_options + ) + return _response.data diff --git a/langfuse/api/organizations/raw_client.py b/langfuse/api/organizations/raw_client.py new file mode 100644 index 000000000..89596e8bf --- /dev/null +++ b/langfuse/api/organizations/raw_client.py @@ -0,0 +1,1707 @@ +# 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.jsonable_encoder import jsonable_encoder +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from .types.membership_deletion_response import MembershipDeletionResponse +from .types.membership_response import MembershipResponse +from .types.membership_role import MembershipRole +from .types.memberships_response import MembershipsResponse +from .types.organization_api_keys_response import OrganizationApiKeysResponse +from .types.organization_projects_response import OrganizationProjectsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawOrganizationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_organization_memberships( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[MembershipsResponse]: + """ + Get all memberships for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MembershipsResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipsResponse, + parse_obj_as( + type_=MembershipsResponse, # 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 update_organization_membership( + self, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[MembershipResponse]: + """ + Create or update a membership for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MembershipResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="PUT", + json={ + "userId": user_id, + "role": role, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipResponse, + parse_obj_as( + type_=MembershipResponse, # 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 delete_organization_membership( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[MembershipDeletionResponse]: + """ + Delete a membership from the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MembershipDeletionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="DELETE", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipDeletionResponse, + parse_obj_as( + type_=MembershipDeletionResponse, # 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_project_memberships( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[MembershipsResponse]: + """ + Get all memberships for a specific project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MembershipsResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipsResponse, + parse_obj_as( + type_=MembershipsResponse, # 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 update_project_membership( + self, + project_id: str, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[MembershipResponse]: + """ + Create or update a membership for a specific project (requires organization-scoped API key). The user must already be a member of the organization. + + Parameters + ---------- + project_id : str + + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MembershipResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="PUT", + json={ + "userId": user_id, + "role": role, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipResponse, + parse_obj_as( + type_=MembershipResponse, # 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 delete_project_membership( + self, + project_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[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 + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MembershipDeletionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="DELETE", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipDeletionResponse, + parse_obj_as( + type_=MembershipDeletionResponse, # 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_organization_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[OrganizationProjectsResponse]: + """ + Get all projects for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[OrganizationProjectsResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + OrganizationProjectsResponse, + parse_obj_as( + type_=OrganizationProjectsResponse, # 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_organization_api_keys( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[OrganizationApiKeysResponse]: + """ + Get all API keys for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[OrganizationApiKeysResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/apiKeys", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + OrganizationApiKeysResponse, + parse_obj_as( + type_=OrganizationApiKeysResponse, # 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 AsyncRawOrganizationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_organization_memberships( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[MembershipsResponse]: + """ + Get all memberships for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MembershipsResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipsResponse, + parse_obj_as( + type_=MembershipsResponse, # 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 update_organization_membership( + self, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[MembershipResponse]: + """ + Create or update a membership for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MembershipResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="PUT", + json={ + "userId": user_id, + "role": role, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipResponse, + parse_obj_as( + type_=MembershipResponse, # 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 delete_organization_membership( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[MembershipDeletionResponse]: + """ + Delete a membership from the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MembershipDeletionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/organizations/memberships", + method="DELETE", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipDeletionResponse, + parse_obj_as( + type_=MembershipDeletionResponse, # 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_project_memberships( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[MembershipsResponse]: + """ + Get all memberships for a specific project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MembershipsResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipsResponse, + parse_obj_as( + type_=MembershipsResponse, # 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 update_project_membership( + self, + project_id: str, + *, + user_id: str, + role: MembershipRole, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[MembershipResponse]: + """ + Create or update a membership for a specific project (requires organization-scoped API key). The user must already be a member of the organization. + + Parameters + ---------- + project_id : str + + user_id : str + + role : MembershipRole + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MembershipResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="PUT", + json={ + "userId": user_id, + "role": role, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipResponse, + parse_obj_as( + type_=MembershipResponse, # 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 delete_project_membership( + self, + project_id: str, + *, + user_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[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 + + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MembershipDeletionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/memberships", + method="DELETE", + json={ + "userId": user_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MembershipDeletionResponse, + parse_obj_as( + type_=MembershipDeletionResponse, # 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_organization_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[OrganizationProjectsResponse]: + """ + Get all projects for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[OrganizationProjectsResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/organizations/projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + OrganizationProjectsResponse, + parse_obj_as( + type_=OrganizationProjectsResponse, # 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_organization_api_keys( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[OrganizationApiKeysResponse]: + """ + Get all API keys for the organization associated with the API key (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[OrganizationApiKeysResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/organizations/apiKeys", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + OrganizationApiKeysResponse, + parse_obj_as( + type_=OrganizationApiKeysResponse, # 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/organizations/types/__init__.py b/langfuse/api/organizations/types/__init__.py new file mode 100644 index 000000000..2ac6cb3e7 --- /dev/null +++ b/langfuse/api/organizations/types/__init__.py @@ -0,0 +1,71 @@ +# 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 .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 + from .memberships_response import MembershipsResponse + from .organization_api_key import OrganizationApiKey + from .organization_api_keys_response import OrganizationApiKeysResponse + from .organization_project import OrganizationProject + from .organization_projects_response import OrganizationProjectsResponse +_dynamic_imports: typing.Dict[str, str] = { + "DeleteMembershipRequest": ".delete_membership_request", + "MembershipDeletionResponse": ".membership_deletion_response", + "MembershipRequest": ".membership_request", + "MembershipResponse": ".membership_response", + "MembershipRole": ".membership_role", + "MembershipsResponse": ".memberships_response", + "OrganizationApiKey": ".organization_api_key", + "OrganizationApiKeysResponse": ".organization_api_keys_response", + "OrganizationProject": ".organization_project", + "OrganizationProjectsResponse": ".organization_projects_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "DeleteMembershipRequest", + "MembershipDeletionResponse", + "MembershipRequest", + "MembershipResponse", + "MembershipRole", + "MembershipsResponse", + "OrganizationApiKey", + "OrganizationApiKeysResponse", + "OrganizationProject", + "OrganizationProjectsResponse", +] diff --git a/langfuse/api/organizations/types/delete_membership_request.py b/langfuse/api/organizations/types/delete_membership_request.py new file mode 100644 index 000000000..a48c85283 --- /dev/null +++ b/langfuse/api/organizations/types/delete_membership_request.py @@ -0,0 +1,16 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class DeleteMembershipRequest(UniversalBaseModel): + user_id: typing_extensions.Annotated[str, FieldMetadata(alias="userId")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/types/membership_deletion_response.py b/langfuse/api/organizations/types/membership_deletion_response.py new file mode 100644 index 000000000..b1c0c3940 --- /dev/null +++ b/langfuse/api/organizations/types/membership_deletion_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class MembershipDeletionResponse(UniversalBaseModel): + message: str + user_id: typing_extensions.Annotated[str, FieldMetadata(alias="userId")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/types/membership_request.py b/langfuse/api/organizations/types/membership_request.py new file mode 100644 index 000000000..c1edbebdd --- /dev/null +++ b/langfuse/api/organizations/types/membership_request.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .membership_role import MembershipRole + + +class MembershipRequest(UniversalBaseModel): + user_id: typing_extensions.Annotated[str, FieldMetadata(alias="userId")] + role: MembershipRole + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/types/membership_response.py b/langfuse/api/organizations/types/membership_response.py new file mode 100644 index 000000000..24074c370 --- /dev/null +++ b/langfuse/api/organizations/types/membership_response.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .membership_role import MembershipRole + + +class MembershipResponse(UniversalBaseModel): + user_id: typing_extensions.Annotated[str, FieldMetadata(alias="userId")] + role: MembershipRole + email: str + name: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/organizations/types/membership_role.py b/langfuse/api/organizations/types/membership_role.py similarity index 92% rename from langfuse/api/resources/organizations/types/membership_role.py rename to langfuse/api/organizations/types/membership_role.py index 1721cc0ed..fa84eec3a 100644 --- a/langfuse/api/resources/organizations/types/membership_role.py +++ b/langfuse/api/organizations/types/membership_role.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class MembershipRole(str, enum.Enum): +class MembershipRole(enum.StrEnum): OWNER = "OWNER" ADMIN = "ADMIN" MEMBER = "MEMBER" diff --git a/langfuse/api/organizations/types/memberships_response.py b/langfuse/api/organizations/types/memberships_response.py new file mode 100644 index 000000000..f45dc9942 --- /dev/null +++ b/langfuse/api/organizations/types/memberships_response.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .membership_response import MembershipResponse + + +class MembershipsResponse(UniversalBaseModel): + memberships: typing.List[MembershipResponse] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/types/organization_api_key.py b/langfuse/api/organizations/types/organization_api_key.py new file mode 100644 index 000000000..572015ab3 --- /dev/null +++ b/langfuse/api/organizations/types/organization_api_key.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class OrganizationApiKey(UniversalBaseModel): + id: str + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + expires_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="expiresAt") + ] = None + last_used_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="lastUsedAt") + ] = None + note: typing.Optional[str] = None + public_key: typing_extensions.Annotated[str, FieldMetadata(alias="publicKey")] + display_secret_key: typing_extensions.Annotated[ + str, FieldMetadata(alias="displaySecretKey") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/types/organization_api_keys_response.py b/langfuse/api/organizations/types/organization_api_keys_response.py new file mode 100644 index 000000000..f0ca789da --- /dev/null +++ b/langfuse/api/organizations/types/organization_api_keys_response.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .organization_api_key import OrganizationApiKey + + +class OrganizationApiKeysResponse(UniversalBaseModel): + api_keys: typing_extensions.Annotated[ + typing.List[OrganizationApiKey], FieldMetadata(alias="apiKeys") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/types/organization_project.py b/langfuse/api/organizations/types/organization_project.py new file mode 100644 index 000000000..9df0fe961 --- /dev/null +++ b/langfuse/api/organizations/types/organization_project.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class OrganizationProject(UniversalBaseModel): + id: str + name: str + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/organizations/types/organization_projects_response.py b/langfuse/api/organizations/types/organization_projects_response.py new file mode 100644 index 000000000..e70925ae0 --- /dev/null +++ b/langfuse/api/organizations/types/organization_projects_response.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .organization_project import OrganizationProject + + +class OrganizationProjectsResponse(UniversalBaseModel): + projects: typing.List[OrganizationProject] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/projects/__init__.py b/langfuse/api/projects/__init__.py new file mode 100644 index 000000000..6cb87b5e2 --- /dev/null +++ b/langfuse/api/projects/__init__.py @@ -0,0 +1,64 @@ +# 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 ( + ApiKeyDeletionResponse, + ApiKeyList, + ApiKeyResponse, + ApiKeySummary, + Project, + ProjectDeletionResponse, + Projects, + ) +_dynamic_imports: typing.Dict[str, str] = { + "ApiKeyDeletionResponse": ".types", + "ApiKeyList": ".types", + "ApiKeyResponse": ".types", + "ApiKeySummary": ".types", + "Project": ".types", + "ProjectDeletionResponse": ".types", + "Projects": ".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__ = [ + "ApiKeyDeletionResponse", + "ApiKeyList", + "ApiKeyResponse", + "ApiKeySummary", + "Project", + "ProjectDeletionResponse", + "Projects", +] diff --git a/langfuse/api/projects/client.py b/langfuse/api/projects/client.py new file mode 100644 index 000000000..238c29c1d --- /dev/null +++ b/langfuse/api/projects/client.py @@ -0,0 +1,756 @@ +# 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 AsyncRawProjectsClient, RawProjectsClient +from .types.api_key_deletion_response import ApiKeyDeletionResponse +from .types.api_key_list import ApiKeyList +from .types.api_key_response import ApiKeyResponse +from .types.project import Project +from .types.project_deletion_response import ProjectDeletionResponse +from .types.projects import Projects + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ProjectsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawProjectsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawProjectsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawProjectsClient + """ + return self._raw_client + + def get( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> Projects: + """ + Get Project associated with API key (requires project-scoped API key). You can use GET /api/public/organizations/projects to get all projects with an organization-scoped key. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Projects + + 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.projects.get() + """ + _response = self._raw_client.get(request_options=request_options) + return _response.data + + def create( + self, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: + """ + Create a new project (requires organization-scoped API key) + + Parameters + ---------- + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + + 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.projects.create( + name="name", + retention=1, + ) + """ + _response = self._raw_client.create( + name=name, + retention=retention, + metadata=metadata, + request_options=request_options, + ) + return _response.data + + def update( + self, + project_id: str, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: + """ + Update a project by ID (requires organization-scoped API key). + + Parameters + ---------- + project_id : str + + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + + 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.projects.update( + project_id="projectId", + name="name", + retention=1, + ) + """ + _response = self._raw_client.update( + project_id, + name=name, + retention=retention, + metadata=metadata, + request_options=request_options, + ) + return _response.data + + def delete( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ProjectDeletionResponse: + """ + Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously. + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ProjectDeletionResponse + + 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.projects.delete( + project_id="projectId", + ) + """ + _response = self._raw_client.delete(project_id, request_options=request_options) + return _response.data + + def get_api_keys( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiKeyList: + """ + Get all API keys for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiKeyList + + 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.projects.get_api_keys( + project_id="projectId", + ) + """ + _response = self._raw_client.get_api_keys( + project_id, request_options=request_options + ) + return _response.data + + def create_api_key( + self, + project_id: str, + *, + note: typing.Optional[str] = OMIT, + public_key: typing.Optional[str] = OMIT, + secret_key: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiKeyResponse: + """ + Create a new API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + note : typing.Optional[str] + Optional note for the API key + + public_key : typing.Optional[str] + Optional predefined public key. Must start with 'pk-lf-'. If provided, secretKey must also be provided. + + secret_key : typing.Optional[str] + Optional predefined secret key. Must start with 'sk-lf-'. If provided, publicKey must also be provided. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiKeyResponse + + 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.projects.create_api_key( + project_id="projectId", + ) + """ + _response = self._raw_client.create_api_key( + project_id, + note=note, + public_key=public_key, + secret_key=secret_key, + request_options=request_options, + ) + return _response.data + + def delete_api_key( + self, + project_id: str, + api_key_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiKeyDeletionResponse: + """ + Delete an API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + api_key_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiKeyDeletionResponse + + 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.projects.delete_api_key( + project_id="projectId", + api_key_id="apiKeyId", + ) + """ + _response = self._raw_client.delete_api_key( + project_id, api_key_id, request_options=request_options + ) + return _response.data + + +class AsyncProjectsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawProjectsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawProjectsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawProjectsClient + """ + return self._raw_client + + async def get( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> Projects: + """ + Get Project associated with API key (requires project-scoped API key). You can use GET /api/public/organizations/projects to get all projects with an organization-scoped key. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Projects + + 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.projects.get() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(request_options=request_options) + return _response.data + + async def create( + self, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: + """ + Create a new project (requires organization-scoped API key) + + Parameters + ---------- + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + + 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.projects.create( + name="name", + retention=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + name=name, + retention=retention, + metadata=metadata, + request_options=request_options, + ) + return _response.data + + async def update( + self, + project_id: str, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: + """ + Update a project by ID (requires organization-scoped API key). + + Parameters + ---------- + project_id : str + + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + + 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.projects.update( + project_id="projectId", + name="name", + retention=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + project_id, + name=name, + retention=retention, + metadata=metadata, + request_options=request_options, + ) + return _response.data + + async def delete( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ProjectDeletionResponse: + """ + Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously. + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ProjectDeletionResponse + + 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.projects.delete( + project_id="projectId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete( + project_id, request_options=request_options + ) + return _response.data + + async def get_api_keys( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiKeyList: + """ + Get all API keys for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiKeyList + + 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.projects.get_api_keys( + project_id="projectId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_api_keys( + project_id, request_options=request_options + ) + return _response.data + + async def create_api_key( + self, + project_id: str, + *, + note: typing.Optional[str] = OMIT, + public_key: typing.Optional[str] = OMIT, + secret_key: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiKeyResponse: + """ + Create a new API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + note : typing.Optional[str] + Optional note for the API key + + public_key : typing.Optional[str] + Optional predefined public key. Must start with 'pk-lf-'. If provided, secretKey must also be provided. + + secret_key : typing.Optional[str] + Optional predefined secret key. Must start with 'sk-lf-'. If provided, publicKey must also be provided. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiKeyResponse + + 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.projects.create_api_key( + project_id="projectId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_api_key( + project_id, + note=note, + public_key=public_key, + secret_key=secret_key, + request_options=request_options, + ) + return _response.data + + async def delete_api_key( + self, + project_id: str, + api_key_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> ApiKeyDeletionResponse: + """ + Delete an API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + api_key_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ApiKeyDeletionResponse + + 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.projects.delete_api_key( + project_id="projectId", + api_key_id="apiKeyId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_api_key( + project_id, api_key_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/projects/raw_client.py b/langfuse/api/projects/raw_client.py new file mode 100644 index 000000000..a5c2218e6 --- /dev/null +++ b/langfuse/api/projects/raw_client.py @@ -0,0 +1,1571 @@ +# 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.jsonable_encoder import jsonable_encoder +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from .types.api_key_deletion_response import ApiKeyDeletionResponse +from .types.api_key_list import ApiKeyList +from .types.api_key_response import ApiKeyResponse +from .types.project import Project +from .types.project_deletion_response import ProjectDeletionResponse +from .types.projects import Projects + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawProjectsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Projects]: + """ + Get Project associated with API key (requires project-scoped API key). You can use GET /api/public/organizations/projects to get all projects with an organization-scoped key. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Projects] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Projects, + parse_obj_as( + type_=Projects, # 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 create( + self, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Project]: + """ + Create a new project (requires organization-scoped API key) + + Parameters + ---------- + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Project] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/projects", + method="POST", + json={ + "name": name, + "metadata": metadata, + "retention": retention, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # 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 update( + self, + project_id: str, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Project]: + """ + Update a project by ID (requires organization-scoped API key). + + Parameters + ---------- + project_id : str + + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Project] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}", + method="PUT", + json={ + "name": name, + "metadata": metadata, + "retention": retention, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # 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 delete( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ProjectDeletionResponse]: + """ + Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously. + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ProjectDeletionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ProjectDeletionResponse, + parse_obj_as( + type_=ProjectDeletionResponse, # 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_api_keys( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ApiKeyList]: + """ + Get all API keys for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ApiKeyList] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiKeyList, + parse_obj_as( + type_=ApiKeyList, # 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 create_api_key( + self, + project_id: str, + *, + note: typing.Optional[str] = OMIT, + public_key: typing.Optional[str] = OMIT, + secret_key: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ApiKeyResponse]: + """ + Create a new API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + note : typing.Optional[str] + Optional note for the API key + + public_key : typing.Optional[str] + Optional predefined public key. Must start with 'pk-lf-'. If provided, secretKey must also be provided. + + secret_key : typing.Optional[str] + Optional predefined secret key. Must start with 'sk-lf-'. If provided, publicKey must also be provided. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ApiKeyResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", + method="POST", + json={ + "note": note, + "publicKey": public_key, + "secretKey": secret_key, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiKeyResponse, + parse_obj_as( + type_=ApiKeyResponse, # 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 delete_api_key( + self, + project_id: str, + api_key_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ApiKeyDeletionResponse]: + """ + Delete an API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + api_key_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ApiKeyDeletionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys/{jsonable_encoder(api_key_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiKeyDeletionResponse, + parse_obj_as( + type_=ApiKeyDeletionResponse, # 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 AsyncRawProjectsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Projects]: + """ + Get Project associated with API key (requires project-scoped API key). You can use GET /api/public/organizations/projects to get all projects with an organization-scoped key. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Projects] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Projects, + parse_obj_as( + type_=Projects, # 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 create( + self, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Project]: + """ + Create a new project (requires organization-scoped API key) + + Parameters + ---------- + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Project] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/projects", + method="POST", + json={ + "name": name, + "metadata": metadata, + "retention": retention, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # 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 update( + self, + project_id: str, + *, + name: str, + retention: int, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Project]: + """ + Update a project by ID (requires organization-scoped API key). + + Parameters + ---------- + project_id : str + + name : str + + retention : int + Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + Optional metadata for the project + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Project] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}", + method="PUT", + json={ + "name": name, + "metadata": metadata, + "retention": retention, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # 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 delete( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ProjectDeletionResponse]: + """ + Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously. + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ProjectDeletionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ProjectDeletionResponse, + parse_obj_as( + type_=ProjectDeletionResponse, # 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_api_keys( + self, + project_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ApiKeyList]: + """ + Get all API keys for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ApiKeyList] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiKeyList, + parse_obj_as( + type_=ApiKeyList, # 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 create_api_key( + self, + project_id: str, + *, + note: typing.Optional[str] = OMIT, + public_key: typing.Optional[str] = OMIT, + secret_key: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ApiKeyResponse]: + """ + Create a new API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + note : typing.Optional[str] + Optional note for the API key + + public_key : typing.Optional[str] + Optional predefined public key. Must start with 'pk-lf-'. If provided, secretKey must also be provided. + + secret_key : typing.Optional[str] + Optional predefined secret key. Must start with 'sk-lf-'. If provided, publicKey must also be provided. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ApiKeyResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", + method="POST", + json={ + "note": note, + "publicKey": public_key, + "secretKey": secret_key, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiKeyResponse, + parse_obj_as( + type_=ApiKeyResponse, # 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 delete_api_key( + self, + project_id: str, + api_key_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ApiKeyDeletionResponse]: + """ + Delete an API key for a project (requires organization-scoped API key) + + Parameters + ---------- + project_id : str + + api_key_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ApiKeyDeletionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys/{jsonable_encoder(api_key_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ApiKeyDeletionResponse, + parse_obj_as( + type_=ApiKeyDeletionResponse, # 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/projects/types/__init__.py b/langfuse/api/projects/types/__init__.py new file mode 100644 index 000000000..e336c1ebb --- /dev/null +++ b/langfuse/api/projects/types/__init__.py @@ -0,0 +1,62 @@ +# 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 .api_key_deletion_response import ApiKeyDeletionResponse + from .api_key_list import ApiKeyList + from .api_key_response import ApiKeyResponse + from .api_key_summary import ApiKeySummary + from .project import Project + from .project_deletion_response import ProjectDeletionResponse + from .projects import Projects +_dynamic_imports: typing.Dict[str, str] = { + "ApiKeyDeletionResponse": ".api_key_deletion_response", + "ApiKeyList": ".api_key_list", + "ApiKeyResponse": ".api_key_response", + "ApiKeySummary": ".api_key_summary", + "Project": ".project", + "ProjectDeletionResponse": ".project_deletion_response", + "Projects": ".projects", +} + + +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__ = [ + "ApiKeyDeletionResponse", + "ApiKeyList", + "ApiKeyResponse", + "ApiKeySummary", + "Project", + "ProjectDeletionResponse", + "Projects", +] diff --git a/langfuse/api/projects/types/api_key_deletion_response.py b/langfuse/api/projects/types/api_key_deletion_response.py new file mode 100644 index 000000000..fd6a69448 --- /dev/null +++ b/langfuse/api/projects/types/api_key_deletion_response.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ApiKeyDeletionResponse(UniversalBaseModel): + """ + Response for API key deletion + """ + + success: bool + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/projects/types/api_key_list.py b/langfuse/api/projects/types/api_key_list.py new file mode 100644 index 000000000..bf38d9be1 --- /dev/null +++ b/langfuse/api/projects/types/api_key_list.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .api_key_summary import ApiKeySummary + + +class ApiKeyList(UniversalBaseModel): + """ + List of API keys for a project + """ + + api_keys: typing_extensions.Annotated[ + typing.List[ApiKeySummary], FieldMetadata(alias="apiKeys") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/projects/types/api_key_response.py b/langfuse/api/projects/types/api_key_response.py new file mode 100644 index 000000000..06ad54610 --- /dev/null +++ b/langfuse/api/projects/types/api_key_response.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class ApiKeyResponse(UniversalBaseModel): + """ + Response for API key creation + """ + + id: str + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + public_key: typing_extensions.Annotated[str, FieldMetadata(alias="publicKey")] + secret_key: typing_extensions.Annotated[str, FieldMetadata(alias="secretKey")] + display_secret_key: typing_extensions.Annotated[ + str, FieldMetadata(alias="displaySecretKey") + ] + note: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/projects/types/api_key_summary.py b/langfuse/api/projects/types/api_key_summary.py new file mode 100644 index 000000000..68d21421b --- /dev/null +++ b/langfuse/api/projects/types/api_key_summary.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class ApiKeySummary(UniversalBaseModel): + """ + Summary of an API key + """ + + id: str + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + expires_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="expiresAt") + ] = None + last_used_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="lastUsedAt") + ] = None + note: typing.Optional[str] = None + public_key: typing_extensions.Annotated[str, FieldMetadata(alias="publicKey")] + display_secret_key: typing_extensions.Annotated[ + str, FieldMetadata(alias="displaySecretKey") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/projects/types/project.py b/langfuse/api/projects/types/project.py new file mode 100644 index 000000000..4f46b7546 --- /dev/null +++ b/langfuse/api/projects/types/project.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class Project(UniversalBaseModel): + id: str + name: str + metadata: typing.Dict[str, typing.Any] = pydantic.Field() + """ + Metadata for the project + """ + + retention_days: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="retentionDays") + ] = pydantic.Field(default=None) + """ + Number of days to retain data. Null or 0 means no retention. Omitted if no retention is configured. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/projects/types/project_deletion_response.py b/langfuse/api/projects/types/project_deletion_response.py new file mode 100644 index 000000000..e3d471c78 --- /dev/null +++ b/langfuse/api/projects/types/project_deletion_response.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ProjectDeletionResponse(UniversalBaseModel): + success: bool + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/projects/types/projects.py b/langfuse/api/projects/types/projects.py new file mode 100644 index 000000000..5771c0051 --- /dev/null +++ b/langfuse/api/projects/types/projects.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .project import Project + + +class Projects(UniversalBaseModel): + data: typing.List[Project] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/prompt_version/__init__.py b/langfuse/api/prompt_version/__init__.py similarity index 76% rename from langfuse/api/resources/prompt_version/__init__.py rename to langfuse/api/prompt_version/__init__.py index f3ea2659b..5cde0202d 100644 --- a/langfuse/api/resources/prompt_version/__init__.py +++ b/langfuse/api/prompt_version/__init__.py @@ -1,2 +1,4 @@ # This file was auto-generated by Fern from our API Definition. +# isort: skip_file + diff --git a/langfuse/api/prompt_version/client.py b/langfuse/api/prompt_version/client.py new file mode 100644 index 000000000..f212447a4 --- /dev/null +++ b/langfuse/api/prompt_version/client.py @@ -0,0 +1,157 @@ +# 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 ..prompts.types.prompt import Prompt +from .raw_client import AsyncRawPromptVersionClient, RawPromptVersionClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class PromptVersionClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawPromptVersionClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawPromptVersionClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawPromptVersionClient + """ + return self._raw_client + + def update( + self, + name: str, + version: int, + *, + new_labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> Prompt: + """ + Update labels for a specific prompt version + + Parameters + ---------- + name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : int + Version of the prompt to update + + new_labels : typing.Sequence[str] + New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Prompt + + 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.prompt_version.update( + name="name", + version=1, + new_labels=["newLabels", "newLabels"], + ) + """ + _response = self._raw_client.update( + name, version, new_labels=new_labels, request_options=request_options + ) + return _response.data + + +class AsyncPromptVersionClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawPromptVersionClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawPromptVersionClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawPromptVersionClient + """ + return self._raw_client + + async def update( + self, + name: str, + version: int, + *, + new_labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> Prompt: + """ + Update labels for a specific prompt version + + Parameters + ---------- + name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : int + Version of the prompt to update + + new_labels : typing.Sequence[str] + New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Prompt + + 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.prompt_version.update( + name="name", + version=1, + new_labels=["newLabels", "newLabels"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + name, version, new_labels=new_labels, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/prompt_version/raw_client.py b/langfuse/api/prompt_version/raw_client.py new file mode 100644 index 000000000..7d1e8d3f2 --- /dev/null +++ b/langfuse/api/prompt_version/raw_client.py @@ -0,0 +1,264 @@ +# 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.jsonable_encoder import jsonable_encoder +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from ..prompts.types.prompt import Prompt + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawPromptVersionClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def update( + self, + name: str, + version: int, + *, + new_labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Prompt]: + """ + Update labels for a specific prompt version + + Parameters + ---------- + name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : int + Version of the prompt to update + + new_labels : typing.Sequence[str] + New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Prompt] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/v2/prompts/{jsonable_encoder(name)}/versions/{jsonable_encoder(version)}", + method="PATCH", + json={ + "newLabels": new_labels, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Prompt, + parse_obj_as( + type_=Prompt, # 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 AsyncRawPromptVersionClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def update( + self, + name: str, + version: int, + *, + new_labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Prompt]: + """ + Update labels for a specific prompt version + + Parameters + ---------- + name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : int + Version of the prompt to update + + new_labels : typing.Sequence[str] + New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Prompt] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/v2/prompts/{jsonable_encoder(name)}/versions/{jsonable_encoder(version)}", + method="PATCH", + json={ + "newLabels": new_labels, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Prompt, + parse_obj_as( + type_=Prompt, # 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/prompts/__init__.py b/langfuse/api/prompts/__init__.py new file mode 100644 index 000000000..27e0a999d --- /dev/null +++ b/langfuse/api/prompts/__init__.py @@ -0,0 +1,94 @@ +# 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 ( + BasePrompt, + ChatMessage, + ChatMessageWithPlaceholders, + ChatPrompt, + CreateChatPromptRequest, + CreateChatPromptType, + CreatePromptRequest, + CreateTextPromptRequest, + CreateTextPromptType, + PlaceholderMessage, + Prompt, + PromptMeta, + PromptMetaListResponse, + PromptType, + Prompt_Chat, + Prompt_Text, + TextPrompt, + ) +_dynamic_imports: typing.Dict[str, str] = { + "BasePrompt": ".types", + "ChatMessage": ".types", + "ChatMessageWithPlaceholders": ".types", + "ChatPrompt": ".types", + "CreateChatPromptRequest": ".types", + "CreateChatPromptType": ".types", + "CreatePromptRequest": ".types", + "CreateTextPromptRequest": ".types", + "CreateTextPromptType": ".types", + "PlaceholderMessage": ".types", + "Prompt": ".types", + "PromptMeta": ".types", + "PromptMetaListResponse": ".types", + "PromptType": ".types", + "Prompt_Chat": ".types", + "Prompt_Text": ".types", + "TextPrompt": ".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__ = [ + "BasePrompt", + "ChatMessage", + "ChatMessageWithPlaceholders", + "ChatPrompt", + "CreateChatPromptRequest", + "CreateChatPromptType", + "CreatePromptRequest", + "CreateTextPromptRequest", + "CreateTextPromptType", + "PlaceholderMessage", + "Prompt", + "PromptMeta", + "PromptMetaListResponse", + "PromptType", + "Prompt_Chat", + "Prompt_Text", + "TextPrompt", +] diff --git a/langfuse/api/prompts/client.py b/langfuse/api/prompts/client.py new file mode 100644 index 000000000..fc6203787 --- /dev/null +++ b/langfuse/api/prompts/client.py @@ -0,0 +1,534 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawPromptsClient, RawPromptsClient +from .types.create_prompt_request import CreatePromptRequest +from .types.prompt import Prompt +from .types.prompt_meta_list_response import PromptMetaListResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class PromptsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawPromptsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawPromptsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawPromptsClient + """ + return self._raw_client + + def get( + self, + prompt_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Prompt: + """ + Get a prompt + + Parameters + ---------- + prompt_name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : typing.Optional[int] + Version of the prompt to be retrieved. + + label : typing.Optional[str] + Label of the prompt to be retrieved. Defaults to "production" if no label or version is set. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Prompt + + 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.prompts.get( + prompt_name="promptName", + ) + """ + _response = self._raw_client.get( + prompt_name, version=version, label=label, request_options=request_options + ) + return _response.data + + def list( + self, + *, + name: typing.Optional[str] = None, + label: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PromptMetaListResponse: + """ + Get a list of prompt names with versions and labels + + Parameters + ---------- + name : typing.Optional[str] + + label : typing.Optional[str] + + tag : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + from_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated on or after a certain datetime (ISO 8601) + + to_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PromptMetaListResponse + + 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.prompts.list() + """ + _response = self._raw_client.list( + name=name, + label=label, + tag=tag, + page=page, + limit=limit, + from_updated_at=from_updated_at, + to_updated_at=to_updated_at, + request_options=request_options, + ) + return _response.data + + def create( + self, + *, + request: CreatePromptRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> Prompt: + """ + Create a new version for the prompt with the given `name` + + Parameters + ---------- + request : CreatePromptRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Prompt + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.prompts import ( + ChatMessage, + CreateChatPromptRequest, + CreateChatPromptType, + ) + + 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.prompts.create( + request=CreateChatPromptRequest( + name="name", + prompt=[ + ChatMessage( + role="role", + content="content", + ), + ChatMessage( + role="role", + content="content", + ), + ], + type=CreateChatPromptType.CHAT, + ), + ) + """ + _response = self._raw_client.create( + request=request, request_options=request_options + ) + return _response.data + + def delete( + self, + prompt_name: str, + *, + label: typing.Optional[str] = None, + version: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Delete prompt versions. If neither version nor label is specified, all versions of the prompt are deleted. + + Parameters + ---------- + prompt_name : str + The name of the prompt + + label : typing.Optional[str] + Optional label to filter deletion. If specified, deletes all prompt versions that have this label. + + version : typing.Optional[int] + Optional version to filter deletion. If specified, deletes only this specific version of the prompt. + + 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.prompts.delete( + prompt_name="promptName", + ) + """ + _response = self._raw_client.delete( + prompt_name, label=label, version=version, request_options=request_options + ) + return _response.data + + +class AsyncPromptsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawPromptsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawPromptsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawPromptsClient + """ + return self._raw_client + + async def get( + self, + prompt_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Prompt: + """ + Get a prompt + + Parameters + ---------- + prompt_name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : typing.Optional[int] + Version of the prompt to be retrieved. + + label : typing.Optional[str] + Label of the prompt to be retrieved. Defaults to "production" if no label or version is set. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Prompt + + 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.prompts.get( + prompt_name="promptName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + prompt_name, version=version, label=label, request_options=request_options + ) + return _response.data + + async def list( + self, + *, + name: typing.Optional[str] = None, + label: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PromptMetaListResponse: + """ + Get a list of prompt names with versions and labels + + Parameters + ---------- + name : typing.Optional[str] + + label : typing.Optional[str] + + tag : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + from_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated on or after a certain datetime (ISO 8601) + + to_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PromptMetaListResponse + + 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.prompts.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + name=name, + label=label, + tag=tag, + page=page, + limit=limit, + from_updated_at=from_updated_at, + to_updated_at=to_updated_at, + request_options=request_options, + ) + return _response.data + + async def create( + self, + *, + request: CreatePromptRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> Prompt: + """ + Create a new version for the prompt with the given `name` + + Parameters + ---------- + request : CreatePromptRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Prompt + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.prompts import ( + ChatMessage, + CreateChatPromptRequest, + CreateChatPromptType, + ) + + 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.prompts.create( + request=CreateChatPromptRequest( + name="name", + prompt=[ + ChatMessage( + role="role", + content="content", + ), + ChatMessage( + role="role", + content="content", + ), + ], + type=CreateChatPromptType.CHAT, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + request=request, request_options=request_options + ) + return _response.data + + async def delete( + self, + prompt_name: str, + *, + label: typing.Optional[str] = None, + version: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Delete prompt versions. If neither version nor label is specified, all versions of the prompt are deleted. + + Parameters + ---------- + prompt_name : str + The name of the prompt + + label : typing.Optional[str] + Optional label to filter deletion. If specified, deletes all prompt versions that have this label. + + version : typing.Optional[int] + Optional version to filter deletion. If specified, deletes only this specific version of the prompt. + + 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.prompts.delete( + prompt_name="promptName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete( + prompt_name, label=label, version=version, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/prompts/raw_client.py b/langfuse/api/prompts/raw_client.py new file mode 100644 index 000000000..81b108968 --- /dev/null +++ b/langfuse/api/prompts/raw_client.py @@ -0,0 +1,977 @@ +# 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 ..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_prompt_request import CreatePromptRequest +from .types.prompt import Prompt +from .types.prompt_meta_list_response import PromptMetaListResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawPromptsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get( + self, + prompt_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Prompt]: + """ + Get a prompt + + Parameters + ---------- + prompt_name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : typing.Optional[int] + Version of the prompt to be retrieved. + + label : typing.Optional[str] + Label of the prompt to be retrieved. Defaults to "production" if no label or version is set. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Prompt] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", + method="GET", + params={ + "version": version, + "label": label, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Prompt, + parse_obj_as( + type_=Prompt, # 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 list( + self, + *, + name: typing.Optional[str] = None, + label: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PromptMetaListResponse]: + """ + Get a list of prompt names with versions and labels + + Parameters + ---------- + name : typing.Optional[str] + + label : typing.Optional[str] + + tag : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + from_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated on or after a certain datetime (ISO 8601) + + to_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PromptMetaListResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/v2/prompts", + method="GET", + params={ + "name": name, + "label": label, + "tag": tag, + "page": page, + "limit": limit, + "fromUpdatedAt": serialize_datetime(from_updated_at) + if from_updated_at is not None + else None, + "toUpdatedAt": serialize_datetime(to_updated_at) + if to_updated_at is not None + else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PromptMetaListResponse, + parse_obj_as( + type_=PromptMetaListResponse, # 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 create( + self, + *, + request: CreatePromptRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Prompt]: + """ + Create a new version for the prompt with the given `name` + + Parameters + ---------- + request : CreatePromptRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Prompt] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/v2/prompts", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreatePromptRequest, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Prompt, + parse_obj_as( + type_=Prompt, # 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 delete( + self, + prompt_name: str, + *, + label: typing.Optional[str] = None, + version: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: + """ + Delete prompt versions. If neither version nor label is specified, all versions of the prompt are deleted. + + Parameters + ---------- + prompt_name : str + The name of the prompt + + label : typing.Optional[str] + Optional label to filter deletion. If specified, deletes all prompt versions that have this label. + + version : typing.Optional[int] + Optional version to filter deletion. If specified, deletes only this specific version of the prompt. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", + method="DELETE", + params={ + "label": label, + "version": version, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 AsyncRawPromptsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get( + self, + prompt_name: str, + *, + version: typing.Optional[int] = None, + label: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Prompt]: + """ + Get a prompt + + Parameters + ---------- + prompt_name : str + The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), + the folder path must be URL encoded. + + version : typing.Optional[int] + Version of the prompt to be retrieved. + + label : typing.Optional[str] + Label of the prompt to be retrieved. Defaults to "production" if no label or version is set. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Prompt] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", + method="GET", + params={ + "version": version, + "label": label, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Prompt, + parse_obj_as( + type_=Prompt, # 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 list( + self, + *, + name: typing.Optional[str] = None, + label: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PromptMetaListResponse]: + """ + Get a list of prompt names with versions and labels + + Parameters + ---------- + name : typing.Optional[str] + + label : typing.Optional[str] + + tag : typing.Optional[str] + + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + from_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated on or after a certain datetime (ISO 8601) + + to_updated_at : typing.Optional[dt.datetime] + Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PromptMetaListResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/v2/prompts", + method="GET", + params={ + "name": name, + "label": label, + "tag": tag, + "page": page, + "limit": limit, + "fromUpdatedAt": serialize_datetime(from_updated_at) + if from_updated_at is not None + else None, + "toUpdatedAt": serialize_datetime(to_updated_at) + if to_updated_at is not None + else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PromptMetaListResponse, + parse_obj_as( + type_=PromptMetaListResponse, # 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 create( + self, + *, + request: CreatePromptRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Prompt]: + """ + Create a new version for the prompt with the given `name` + + Parameters + ---------- + request : CreatePromptRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Prompt] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/v2/prompts", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreatePromptRequest, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Prompt, + parse_obj_as( + type_=Prompt, # 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 delete( + self, + prompt_name: str, + *, + label: typing.Optional[str] = None, + version: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: + """ + Delete prompt versions. If neither version nor label is specified, all versions of the prompt are deleted. + + Parameters + ---------- + prompt_name : str + The name of the prompt + + label : typing.Optional[str] + Optional label to filter deletion. If specified, deletes all prompt versions that have this label. + + version : typing.Optional[int] + Optional version to filter deletion. If specified, deletes only this specific version of the prompt. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", + method="DELETE", + params={ + "label": label, + "version": version, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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/prompts/types/__init__.py b/langfuse/api/prompts/types/__init__.py new file mode 100644 index 000000000..6522163be --- /dev/null +++ b/langfuse/api/prompts/types/__init__.py @@ -0,0 +1,90 @@ +# 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 .base_prompt import BasePrompt + from .chat_message import ChatMessage + from .chat_message_with_placeholders import ChatMessageWithPlaceholders + from .chat_prompt import ChatPrompt + from .create_chat_prompt_request import CreateChatPromptRequest + from .create_chat_prompt_type import CreateChatPromptType + from .create_prompt_request import CreatePromptRequest + from .create_text_prompt_request import CreateTextPromptRequest + from .create_text_prompt_type import CreateTextPromptType + from .placeholder_message import PlaceholderMessage + from .prompt import Prompt, Prompt_Chat, Prompt_Text + from .prompt_meta import PromptMeta + from .prompt_meta_list_response import PromptMetaListResponse + from .prompt_type import PromptType + from .text_prompt import TextPrompt +_dynamic_imports: typing.Dict[str, str] = { + "BasePrompt": ".base_prompt", + "ChatMessage": ".chat_message", + "ChatMessageWithPlaceholders": ".chat_message_with_placeholders", + "ChatPrompt": ".chat_prompt", + "CreateChatPromptRequest": ".create_chat_prompt_request", + "CreateChatPromptType": ".create_chat_prompt_type", + "CreatePromptRequest": ".create_prompt_request", + "CreateTextPromptRequest": ".create_text_prompt_request", + "CreateTextPromptType": ".create_text_prompt_type", + "PlaceholderMessage": ".placeholder_message", + "Prompt": ".prompt", + "PromptMeta": ".prompt_meta", + "PromptMetaListResponse": ".prompt_meta_list_response", + "PromptType": ".prompt_type", + "Prompt_Chat": ".prompt", + "Prompt_Text": ".prompt", + "TextPrompt": ".text_prompt", +} + + +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__ = [ + "BasePrompt", + "ChatMessage", + "ChatMessageWithPlaceholders", + "ChatPrompt", + "CreateChatPromptRequest", + "CreateChatPromptType", + "CreatePromptRequest", + "CreateTextPromptRequest", + "CreateTextPromptType", + "PlaceholderMessage", + "Prompt", + "PromptMeta", + "PromptMetaListResponse", + "PromptType", + "Prompt_Chat", + "Prompt_Text", + "TextPrompt", +] diff --git a/langfuse/api/prompts/types/base_prompt.py b/langfuse/api/prompts/types/base_prompt.py new file mode 100644 index 000000000..bd9461600 --- /dev/null +++ b/langfuse/api/prompts/types/base_prompt.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class BasePrompt(UniversalBaseModel): + name: str + version: int + config: typing.Any + labels: typing.List[str] = pydantic.Field() + """ + List of deployment labels of this prompt version. + """ + + tags: typing.List[str] = pydantic.Field() + """ + List of tags. Used to filter via UI and API. The same across versions of a prompt. + """ + + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = pydantic.Field(default=None) + """ + Commit message for this prompt version. + """ + + resolution_graph: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, typing.Any]], + FieldMetadata(alias="resolutionGraph"), + ] = pydantic.Field(default=None) + """ + The dependency resolution graph for the current prompt. Null if prompt has no dependencies. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/prompts/types/chat_message.py b/langfuse/api/prompts/types/chat_message.py new file mode 100644 index 000000000..3d29e7140 --- /dev/null +++ b/langfuse/api/prompts/types/chat_message.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ChatMessage(UniversalBaseModel): + role: str + content: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/prompts/types/chat_message_with_placeholders.py b/langfuse/api/prompts/types/chat_message_with_placeholders.py new file mode 100644 index 000000000..e077ca144 --- /dev/null +++ b/langfuse/api/prompts/types/chat_message_with_placeholders.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .chat_message import ChatMessage +from .placeholder_message import PlaceholderMessage + +ChatMessageWithPlaceholders = typing.Union[ChatMessage, PlaceholderMessage] diff --git a/langfuse/api/prompts/types/chat_prompt.py b/langfuse/api/prompts/types/chat_prompt.py new file mode 100644 index 000000000..ce347537f --- /dev/null +++ b/langfuse/api/prompts/types/chat_prompt.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_prompt import BasePrompt +from .chat_message_with_placeholders import ChatMessageWithPlaceholders + + +class ChatPrompt(BasePrompt): + prompt: typing.List[ChatMessageWithPlaceholders] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/prompts/types/create_chat_prompt_request.py b/langfuse/api/prompts/types/create_chat_prompt_request.py new file mode 100644 index 000000000..0fe1de0c1 --- /dev/null +++ b/langfuse/api/prompts/types/create_chat_prompt_request.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .chat_message_with_placeholders import ChatMessageWithPlaceholders +from .create_chat_prompt_type import CreateChatPromptType + + +class CreateChatPromptRequest(UniversalBaseModel): + name: str + prompt: typing.List[ChatMessageWithPlaceholders] + config: typing.Optional[typing.Any] = None + type: CreateChatPromptType + labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + List of deployment labels of this prompt version. + """ + + tags: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + List of tags to apply to all versions of this prompt. + """ + + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = pydantic.Field(default=None) + """ + Commit message for this prompt version. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/prompts/types/create_chat_prompt_type.py b/langfuse/api/prompts/types/create_chat_prompt_type.py new file mode 100644 index 000000000..12f6748a4 --- /dev/null +++ b/langfuse/api/prompts/types/create_chat_prompt_type.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core import enum + +T_Result = typing.TypeVar("T_Result") + + +class CreateChatPromptType(enum.StrEnum): + CHAT = "chat" + + def visit(self, chat: typing.Callable[[], T_Result]) -> T_Result: + if self is CreateChatPromptType.CHAT: + return chat() diff --git a/langfuse/api/prompts/types/create_prompt_request.py b/langfuse/api/prompts/types/create_prompt_request.py new file mode 100644 index 000000000..13d75e7e1 --- /dev/null +++ b/langfuse/api/prompts/types/create_prompt_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .create_chat_prompt_request import CreateChatPromptRequest +from .create_text_prompt_request import CreateTextPromptRequest + +CreatePromptRequest = typing.Union[CreateChatPromptRequest, CreateTextPromptRequest] diff --git a/langfuse/api/prompts/types/create_text_prompt_request.py b/langfuse/api/prompts/types/create_text_prompt_request.py new file mode 100644 index 000000000..be87a7dde --- /dev/null +++ b/langfuse/api/prompts/types/create_text_prompt_request.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .create_text_prompt_type import CreateTextPromptType + + +class CreateTextPromptRequest(UniversalBaseModel): + name: str + prompt: str + config: typing.Optional[typing.Any] = None + type: typing.Optional[CreateTextPromptType] = None + labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + List of deployment labels of this prompt version. + """ + + tags: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + List of tags to apply to all versions of this prompt. + """ + + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = pydantic.Field(default=None) + """ + Commit message for this prompt version. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/prompts/types/create_text_prompt_type.py b/langfuse/api/prompts/types/create_text_prompt_type.py new file mode 100644 index 000000000..825fcee0d --- /dev/null +++ b/langfuse/api/prompts/types/create_text_prompt_type.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core import enum + +T_Result = typing.TypeVar("T_Result") + + +class CreateTextPromptType(enum.StrEnum): + TEXT = "text" + + def visit(self, text: typing.Callable[[], T_Result]) -> T_Result: + if self is CreateTextPromptType.TEXT: + return text() diff --git a/langfuse/api/prompts/types/placeholder_message.py b/langfuse/api/prompts/types/placeholder_message.py new file mode 100644 index 000000000..a75a58bd3 --- /dev/null +++ b/langfuse/api/prompts/types/placeholder_message.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class PlaceholderMessage(UniversalBaseModel): + name: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/prompts/types/prompt.py b/langfuse/api/prompts/types/prompt.py new file mode 100644 index 000000000..813e5992f --- /dev/null +++ b/langfuse/api/prompts/types/prompt.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .chat_message_with_placeholders import ChatMessageWithPlaceholders + + +class Prompt_Chat(UniversalBaseModel): + type: typing.Literal["chat"] = "chat" + prompt: typing.List[ChatMessageWithPlaceholders] + name: str + version: int + config: typing.Any + labels: typing.List[str] + tags: typing.List[str] + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = None + resolution_graph: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, typing.Any]], + FieldMetadata(alias="resolutionGraph"), + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class Prompt_Text(UniversalBaseModel): + type: typing.Literal["text"] = "text" + prompt: str + name: str + version: int + config: typing.Any + labels: typing.List[str] + tags: typing.List[str] + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = None + resolution_graph: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, typing.Any]], + FieldMetadata(alias="resolutionGraph"), + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +Prompt = typing_extensions.Annotated[ + typing.Union[Prompt_Chat, Prompt_Text], pydantic.Field(discriminator="type") +] diff --git a/langfuse/api/prompts/types/prompt_meta.py b/langfuse/api/prompts/types/prompt_meta.py new file mode 100644 index 000000000..974a50252 --- /dev/null +++ b/langfuse/api/prompts/types/prompt_meta.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .prompt_type import PromptType + + +class PromptMeta(UniversalBaseModel): + name: str + type: PromptType = pydantic.Field() + """ + Indicates whether the prompt is a text or chat prompt. + """ + + versions: typing.List[int] + labels: typing.List[str] + tags: typing.List[str] + last_updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="lastUpdatedAt") + ] + last_config: typing_extensions.Annotated[ + typing.Any, FieldMetadata(alias="lastConfig") + ] = pydantic.Field() + """ + Config object of the most recent prompt version that matches the filters (if any are provided) + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/prompts/types/prompt_meta_list_response.py b/langfuse/api/prompts/types/prompt_meta_list_response.py new file mode 100644 index 000000000..22043d008 --- /dev/null +++ b/langfuse/api/prompts/types/prompt_meta_list_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse +from .prompt_meta import PromptMeta + + +class PromptMetaListResponse(UniversalBaseModel): + data: typing.List[PromptMeta] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/resources/prompts/types/prompt_type.py b/langfuse/api/prompts/types/prompt_type.py similarity index 87% rename from langfuse/api/resources/prompts/types/prompt_type.py rename to langfuse/api/prompts/types/prompt_type.py index 958d544a6..7d8230db2 100644 --- a/langfuse/api/resources/prompts/types/prompt_type.py +++ b/langfuse/api/prompts/types/prompt_type.py @@ -1,12 +1,13 @@ # This file was auto-generated by Fern from our API Definition. -import enum import typing +from ...core import enum + T_Result = typing.TypeVar("T_Result") -class PromptType(str, enum.Enum): +class PromptType(enum.StrEnum): CHAT = "chat" TEXT = "text" diff --git a/langfuse/api/prompts/types/text_prompt.py b/langfuse/api/prompts/types/text_prompt.py new file mode 100644 index 000000000..fbc53c2f5 --- /dev/null +++ b/langfuse/api/prompts/types/text_prompt.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from .base_prompt import BasePrompt + + +class TextPrompt(BasePrompt): + prompt: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/reference.md b/langfuse/api/reference.md deleted file mode 100644 index 66c008bb7..000000000 --- a/langfuse/api/reference.md +++ /dev/null @@ -1,7553 +0,0 @@ -# Reference -## AnnotationQueues -
client.annotation_queues.list_queues(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all annotation queues -
-
-
-
- -#### 🔌 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.annotation_queues.list_queues() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.create_queue(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create an annotation queue -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateAnnotationQueueRequest -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.annotation_queues.create_queue( - request=CreateAnnotationQueueRequest( - name="name", - score_config_ids=["scoreConfigIds", "scoreConfigIds"], - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateAnnotationQueueRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.get_queue(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get an annotation queue by ID -
-
-
-
- -#### 🔌 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.annotation_queues.get_queue( - queue_id="queueId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.list_queue_items(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get items for a specific annotation queue -
-
-
-
- -#### 🔌 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.annotation_queues.list_queue_items( - queue_id="queueId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**status:** `typing.Optional[AnnotationQueueStatus]` — Filter by status - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.get_queue_item(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a specific item from an annotation queue -
-
-
-
- -#### 🔌 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.annotation_queues.get_queue_item( - queue_id="queueId", - item_id="itemId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**item_id:** `str` — The unique identifier of the annotation queue item - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.create_queue_item(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Add an item to an annotation queue -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import AnnotationQueueObjectType, CreateAnnotationQueueItemRequest -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.annotation_queues.create_queue_item( - queue_id="queueId", - request=CreateAnnotationQueueItemRequest( - object_id="objectId", - object_type=AnnotationQueueObjectType.TRACE, - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**request:** `CreateAnnotationQueueItemRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.update_queue_item(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Update an annotation queue item -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import UpdateAnnotationQueueItemRequest -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.annotation_queues.update_queue_item( - queue_id="queueId", - item_id="itemId", - request=UpdateAnnotationQueueItemRequest(), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**item_id:** `str` — The unique identifier of the annotation queue item - -
-
- -
-
- -**request:** `UpdateAnnotationQueueItemRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.delete_queue_item(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Remove an item from an annotation queue -
-
-
-
- -#### 🔌 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.annotation_queues.delete_queue_item( - queue_id="queueId", - item_id="itemId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**item_id:** `str` — The unique identifier of the annotation queue item - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.create_queue_assignment(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create an assignment for a user to an annotation queue -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import AnnotationQueueAssignmentRequest -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.annotation_queues.create_queue_assignment( - queue_id="queueId", - request=AnnotationQueueAssignmentRequest( - user_id="userId", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**request:** `AnnotationQueueAssignmentRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.annotation_queues.delete_queue_assignment(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete an assignment for a user to an annotation queue -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import AnnotationQueueAssignmentRequest -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.annotation_queues.delete_queue_assignment( - queue_id="queueId", - request=AnnotationQueueAssignmentRequest( - user_id="userId", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**queue_id:** `str` — The unique identifier of the annotation queue - -
-
- -
-
- -**request:** `AnnotationQueueAssignmentRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## 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. - -
-
-
-
- - -
-
-
- -## Comments -
client.comments.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a comment. Comments may be attached to different object types (trace, observation, session, prompt). -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateCommentRequest -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.comments.create( - request=CreateCommentRequest( - project_id="projectId", - object_type="objectType", - object_id="objectId", - content="content", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateCommentRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.comments.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all comments -
-
-
-
- -#### 🔌 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.comments.get() - -``` -
-
-
-
- -#### ⚙️ 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 - -
-
- -
-
- -**object_type:** `typing.Optional[str]` — Filter comments by object type (trace, observation, session, prompt). - -
-
- -
-
- -**object_id:** `typing.Optional[str]` — Filter comments by object id. If objectType is not provided, an error will be thrown. - -
-
- -
-
- -**author_user_id:** `typing.Optional[str]` — Filter comments by author user id. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.comments.get_by_id(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a comment by id -
-
-
-
- -#### 🔌 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.comments.get_by_id( - comment_id="commentId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**comment_id:** `str` — The unique langfuse identifier of a comment - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## DatasetItems -
client.dataset_items.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a dataset item -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateDatasetItemRequest -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.dataset_items.create( - request=CreateDatasetItemRequest( - dataset_name="datasetName", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateDatasetItemRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.dataset_items.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a dataset item -
-
-
-
- -#### 🔌 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.dataset_items.get( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.dataset_items.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get dataset items -
-
-
-
- -#### 🔌 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.dataset_items.list() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**dataset_name:** `typing.Optional[str]` - -
-
- -
-
- -**source_trace_id:** `typing.Optional[str]` - -
-
- -
-
- -**source_observation_id:** `typing.Optional[str]` - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.dataset_items.delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete a dataset item and all its run items. This action is irreversible. -
-
-
-
- -#### 🔌 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.dataset_items.delete( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## DatasetRunItems -
client.dataset_run_items.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a dataset run item -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateDatasetRunItemRequest -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.dataset_run_items.create( - request=CreateDatasetRunItemRequest( - run_name="runName", - dataset_item_id="datasetItemId", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateDatasetRunItemRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.dataset_run_items.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -List dataset run items -
-
-
-
- -#### 🔌 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.dataset_run_items.list( - dataset_id="datasetId", - run_name="runName", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**dataset_id:** `str` - -
-
- -
-
- -**run_name:** `str` - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Datasets -
client.datasets.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all datasets -
-
-
-
- -#### 🔌 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.datasets.list() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.datasets.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a dataset -
-
-
-
- -#### 🔌 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.datasets.get( - dataset_name="datasetName", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**dataset_name:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.datasets.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a dataset -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateDatasetRequest -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.datasets.create( - request=CreateDatasetRequest( - name="name", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateDatasetRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.datasets.get_run(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a dataset run and its items -
-
-
-
- -#### 🔌 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.datasets.get_run( - dataset_name="datasetName", - run_name="runName", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**dataset_name:** `str` - -
-
- -
-
- -**run_name:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.datasets.delete_run(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete a dataset run and all its run items. This action is irreversible. -
-
-
-
- -#### 🔌 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.datasets.delete_run( - dataset_name="datasetName", - run_name="runName", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**dataset_name:** `str` - -
-
- -
-
- -**run_name:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.datasets.get_runs(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get dataset runs -
-
-
-
- -#### 🔌 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.datasets.get_runs( - dataset_name="datasetName", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**dataset_name:** `str` - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Health -
client.health.health() -
-
- -#### 📝 Description - -
-
- -
-
- -Check health of API and database -
-
-
-
- -#### 🔌 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.health.health() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Ingestion -
client.ingestion.batch(...) -
-
- -#### 📝 Description - -
-
- -
-
- -**Legacy endpoint for batch ingestion for Langfuse Observability.** - --> Please use the OpenTelemetry endpoint (`/api/public/otel/v1/traces`). 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. -Internally, we refer to this as the "event envelope" as it tells us something about the event but not the trace. -We use the event id within this envelope to deduplicate messages to avoid processing the same event twice, i.e. the event id should be unique per request. -The event.body.id is the ID of the actual trace and will be used for updates and will be visible within the Langfuse App. -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/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. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import IngestionEvent_ScoreCreate, ScoreBody -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.ingestion.batch( - batch=[ - IngestionEvent_ScoreCreate( - id="abcdef-1234-5678-90ab", - timestamp="2022-01-01T00:00:00.000Z", - body=ScoreBody( - id="abcdef-1234-5678-90ab", - trace_id="1234-5678-90ab-cdef", - name="My Score", - value=0.9, - environment="default", - ), - ) - ], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**batch:** `typing.Sequence[IngestionEvent]` — Batch of tracing events to be ingested. Discriminated by attribute `type`. - -
-
- -
-
- -**metadata:** `typing.Optional[typing.Any]` — Optional. Metadata field used by the Langfuse SDKs for debugging. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## LlmConnections -
client.llm_connections.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all LLM connections in a project -
-
-
-
- -#### 🔌 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.llm_connections.list() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.llm_connections.upsert(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create or update an LLM connection. The connection is upserted on provider. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import LlmAdapter, UpsertLlmConnectionRequest -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.llm_connections.upsert( - request=UpsertLlmConnectionRequest( - provider="provider", - adapter=LlmAdapter.ANTHROPIC, - secret_key="secretKey", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `UpsertLlmConnectionRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Media -
client.media.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a media record -
-
-
-
- -#### 🔌 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.media.get( - media_id="mediaId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**media_id:** `str` — The unique langfuse identifier of a media record - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.media.patch(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Patch a media record -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -import datetime - -from langfuse import PatchMediaBody -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.media.patch( - media_id="mediaId", - request=PatchMediaBody( - uploaded_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - upload_http_status=1, - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**media_id:** `str` — The unique langfuse identifier of a media record - -
-
- -
-
- -**request:** `PatchMediaBody` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.media.get_upload_url(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a presigned upload URL for a media record -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import GetMediaUploadUrlRequest, MediaContentType -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.media.get_upload_url( - request=GetMediaUploadUrlRequest( - trace_id="traceId", - content_type=MediaContentType.IMAGE_PNG, - content_length=1, - sha_256_hash="sha256Hash", - field="field", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `GetMediaUploadUrlRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Metrics -
client.metrics.metrics(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get metrics from the Langfuse project using a query object. - -For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api). -
-
-
-
- -#### 🔌 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.metrics.metrics( - query="query", -) - -``` -
-
-
-
- -#### ⚙️ 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. - -
-
-
-
- - -
-
-
- -## Models -
client.models.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a model -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateModelRequest -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.models.create( - request=CreateModelRequest( - model_name="modelName", - match_pattern="matchPattern", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateModelRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.models.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all models -
-
-
-
- -#### 🔌 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.models.list() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.models.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a model -
-
-
-
- -#### 🔌 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.models.get( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.models.delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete a model. Cannot delete models managed by Langfuse. You can create your own definition with the same modelName to override the definition though. -
-
-
-
- -#### 🔌 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.models.delete( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Observations -
client.observations.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a observation -
-
-
-
- -#### 🔌 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.observations.get( - observation_id="observationId", -) - -``` -
-
-
-
- -#### ⚙️ 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. - -
-
-
-
- - -
-
-
- -
client.observations.get_many(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a list of observations -
-
-
-
- -#### 🔌 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.observations.get_many() - -``` -
-
-
-
- -#### ⚙️ 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. - -
-
- -
-
- -**name:** `typing.Optional[str]` - -
-
- -
-
- -**user_id:** `typing.Optional[str]` - -
-
- -
-
- -**type:** `typing.Optional[str]` - -
-
- -
-
- -**trace_id:** `typing.Optional[str]` - -
-
- -
-
- -**level:** `typing.Optional[ObservationLevel]` — Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR"). - -
-
- -
-
- -**parent_observation_id:** `typing.Optional[str]` - -
-
- -
-
- -**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for observations where the environment is one of the provided values. - -
-
- -
-
- -**from_start_time:** `typing.Optional[dt.datetime]` — Retrieve only observations with a start_time on or after this datetime (ISO 8601). - -
-
- -
-
- -**to_start_time:** `typing.Optional[dt.datetime]` — Retrieve only observations with a start_time before this datetime (ISO 8601). - -
-
- -
-
- -**version:** `typing.Optional[str]` — Optional filter to only include observations with a certain version. - -
-
- -
-
- -**filter:** `typing.Optional[str]` - -JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...). - -## Filter Structure -Each filter condition has the following structure: -```json -[ - { - "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "column": string, // Required. Column to filter on (see available columns below) - "operator": string, // Required. Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - categoryOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" - // - numberObject: "=", ">", "<", ">=", "<=" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Required (except for null type). Value to compare against. Type depends on filter type - "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata - } -] -``` - -## Available Columns - -### Core Observation Fields -- `id` (string) - Observation ID -- `type` (string) - Observation type (SPAN, GENERATION, EVENT) -- `name` (string) - Observation name -- `traceId` (string) - Associated trace ID -- `startTime` (datetime) - Observation start time -- `endTime` (datetime) - Observation end time -- `environment` (string) - Environment tag -- `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR) -- `statusMessage` (string) - Status message -- `version` (string) - Version tag - -### Performance Metrics -- `latency` (number) - Latency in seconds (calculated: end_time - start_time) -- `timeToFirstToken` (number) - Time to first token in seconds -- `tokensPerSecond` (number) - Output tokens per second - -### Token Usage -- `inputTokens` (number) - Number of input tokens -- `outputTokens` (number) - Number of output tokens -- `totalTokens` (number) - Total tokens (alias: `tokens`) - -### Cost Metrics -- `inputCost` (number) - Input cost in USD -- `outputCost` (number) - Output cost in USD -- `totalCost` (number) - Total cost in USD - -### Model Information -- `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 -[ - { - "type": "string", - "column": "type", - "operator": "=", - "value": "GENERATION" - }, - { - "type": "number", - "column": "latency", - "operator": ">=", - "value": 2.5 - }, - { - "type": "stringObject", - "column": "metadata", - "key": "environment", - "operator": "=", - "value": "production" - } -] -``` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Opentelemetry -
client.opentelemetry.export_traces(...) -
-
- -#### 📝 Description - -
-
- -
-
- -**OpenTelemetry Traces Ingestion Endpoint** - -This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. - -**Supported Formats:** -- Binary Protobuf: `Content-Type: application/x-protobuf` -- JSON Protobuf: `Content-Type: application/json` -- Supports gzip compression via `Content-Encoding: gzip` header - -**Specification Compliance:** -- Conforms to [OTLP/HTTP Trace Export](https://opentelemetry.io/docs/specs/otlp/#otlphttp) -- Implements `ExportTraceServiceRequest` message format - -**Documentation:** -- Integration guide: https://langfuse.com/integrations/native/opentelemetry -- Data model: https://langfuse.com/docs/observability/data-model -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import ( - OtelAttribute, - OtelAttributeValue, - OtelResource, - OtelResourceSpan, - OtelScope, - OtelScopeSpan, - OtelSpan, -) -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.opentelemetry.export_traces( - resource_spans=[ - OtelResourceSpan( - resource=OtelResource( - attributes=[ - OtelAttribute( - key="service.name", - value=OtelAttributeValue( - string_value="my-service", - ), - ), - OtelAttribute( - key="service.version", - value=OtelAttributeValue( - string_value="1.0.0", - ), - ), - ], - ), - scope_spans=[ - OtelScopeSpan( - scope=OtelScope( - name="langfuse-sdk", - version="2.60.3", - ), - spans=[ - OtelSpan( - trace_id="0123456789abcdef0123456789abcdef", - span_id="0123456789abcdef", - name="my-operation", - kind=1, - start_time_unix_nano="1747872000000000000", - end_time_unix_nano="1747872001000000000", - attributes=[ - OtelAttribute( - key="langfuse.observation.type", - value=OtelAttributeValue( - string_value="generation", - ), - ) - ], - status={}, - ) - ], - ) - ], - ) - ], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**resource_spans:** `typing.Sequence[OtelResourceSpan]` — Array of resource spans containing trace data as defined in the OTLP specification - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Organizations -
client.organizations.get_organization_memberships() -
-
- -#### 📝 Description - -
-
- -
-
- -Get all memberships for the organization associated with the API key (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.organizations.get_organization_memberships() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.organizations.update_organization_membership(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create or update a membership for the organization associated with the API key (requires organization-scoped API key) -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import MembershipRequest, MembershipRole -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.update_organization_membership( - request=MembershipRequest( - user_id="userId", - role=MembershipRole.OWNER, - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `MembershipRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
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. - -
-
-
-
- - -
-
-
- -
client.organizations.get_project_memberships(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all memberships for a specific project (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.organizations.get_project_memberships( - project_id="projectId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**project_id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.organizations.update_project_membership(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create or update a membership for a specific project (requires organization-scoped API key). The user must already be a member of the organization. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import MembershipRequest, MembershipRole -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.update_project_membership( - project_id="projectId", - request=MembershipRequest( - user_id="userId", - role=MembershipRole.OWNER, - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**project_id:** `str` - -
-
- -
-
- -**request:** `MembershipRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
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. - -
-
-
-
- - -
-
-
- -
client.organizations.get_organization_projects() -
-
- -#### 📝 Description - -
-
- -
-
- -Get all projects for the organization associated with the API key (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.organizations.get_organization_projects() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.organizations.get_organization_api_keys() -
-
- -#### 📝 Description - -
-
- -
-
- -Get all API keys for the organization associated with the API key (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.organizations.get_organization_api_keys() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Projects -
client.projects.get() -
-
- -#### 📝 Description - -
-
- -
-
- -Get Project associated with API key (requires project-scoped API key). You can use GET /api/public/organizations/projects to get all projects with an organization-scoped 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.projects.get() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.projects.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a new project (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.projects.create( - name="name", - retention=1, -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**name:** `str` - -
-
- -
-
- -**retention:** `int` — Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. - -
-
- -
-
- -**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]` — Optional metadata for the project - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.projects.update(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Update a project 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.projects.update( - project_id="projectId", - name="name", - retention=1, -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**project_id:** `str` - -
-
- -
-
- -**name:** `str` - -
-
- -
-
- -**retention:** `int` — Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. - -
-
- -
-
- -**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]` — Optional metadata for the project - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.projects.delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously. -
-
-
-
- -#### 🔌 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.projects.delete( - project_id="projectId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**project_id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.projects.get_api_keys(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all API keys for a project (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.projects.get_api_keys( - project_id="projectId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**project_id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.projects.create_api_key(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a new API key for a project (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.projects.create_api_key( - project_id="projectId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**project_id:** `str` - -
-
- -
-
- -**note:** `typing.Optional[str]` — Optional note for the API key - -
-
- -
-
- -**public_key:** `typing.Optional[str]` — Optional predefined public key. Must start with 'pk-lf-'. If provided, secretKey must also be provided. - -
-
- -
-
- -**secret_key:** `typing.Optional[str]` — Optional predefined secret key. Must start with 'sk-lf-'. If provided, publicKey must also be provided. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.projects.delete_api_key(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete an API key for a project (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.projects.delete_api_key( - project_id="projectId", - api_key_id="apiKeyId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**project_id:** `str` - -
-
- -
-
- -**api_key_id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## PromptVersion -
client.prompt_version.update(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Update labels for a specific prompt version -
-
-
-
- -#### 🔌 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.prompt_version.update( - name="name", - version=1, - new_labels=["newLabels", "newLabels"], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**name:** `str` - -The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), -the folder path must be URL encoded. - -
-
- -
-
- -**version:** `int` — Version of the prompt to update - -
-
- -
-
- -**new_labels:** `typing.Sequence[str]` — New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Prompts -
client.prompts.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a prompt -
-
-
-
- -#### 🔌 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.prompts.get( - prompt_name="promptName", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**prompt_name:** `str` - -The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), -the folder path must be URL encoded. - -
-
- -
-
- -**version:** `typing.Optional[int]` — Version of the prompt to be retrieved. - -
-
- -
-
- -**label:** `typing.Optional[str]` — Label of the prompt to be retrieved. Defaults to "production" if no label or version is set. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.prompts.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a list of prompt names with versions and labels -
-
-
-
- -#### 🔌 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.prompts.list() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**name:** `typing.Optional[str]` - -
-
- -
-
- -**label:** `typing.Optional[str]` - -
-
- -
-
- -**tag:** `typing.Optional[str]` - -
-
- -
-
- -**page:** `typing.Optional[int]` — page number, starts at 1 - -
-
- -
-
- -**limit:** `typing.Optional[int]` — limit of items per page - -
-
- -
-
- -**from_updated_at:** `typing.Optional[dt.datetime]` — Optional filter to only include prompt versions created/updated on or after a certain datetime (ISO 8601) - -
-
- -
-
- -**to_updated_at:** `typing.Optional[dt.datetime]` — Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.prompts.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a new version for the prompt with the given `name` -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import ( - ChatMessageWithPlaceholders_Chatmessage, - CreatePromptRequest_Chat, -) -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.prompts.create( - request=CreatePromptRequest_Chat( - name="name", - prompt=[ - ChatMessageWithPlaceholders_Chatmessage( - role="role", - content="content", - ), - ChatMessageWithPlaceholders_Chatmessage( - role="role", - content="content", - ), - ], - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreatePromptRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.prompts.delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete prompt versions. If neither version nor label is specified, all versions of the prompt are deleted. -
-
-
-
- -#### 🔌 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.prompts.delete( - prompt_name="promptName", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**prompt_name:** `str` — The name of the prompt - -
-
- -
-
- -**label:** `typing.Optional[str]` — Optional label to filter deletion. If specified, deletes all prompt versions that have this label. - -
-
- -
-
- -**version:** `typing.Optional[int]` — Optional version to filter deletion. If specified, deletes only this specific version of the prompt. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Scim -
client.scim.get_service_provider_config() -
-
- -#### 📝 Description - -
-
- -
-
- -Get SCIM Service Provider Configuration (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.scim.get_service_provider_config() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.scim.get_resource_types() -
-
- -#### 📝 Description - -
-
- -
-
- -Get SCIM Resource Types (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.scim.get_resource_types() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.scim.get_schemas() -
-
- -#### 📝 Description - -
-
- -
-
- -Get SCIM Schemas (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.scim.get_schemas() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.scim.list_users(...) -
-
- -#### 📝 Description - -
-
- -
-
- -List users in 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.scim.list_users() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**filter:** `typing.Optional[str]` — Filter expression (e.g. userName eq "value") - -
-
- -
-
- -**start_index:** `typing.Optional[int]` — 1-based index of the first result to return (default 1) - -
-
- -
-
- -**count:** `typing.Optional[int]` — Maximum number of results to return (default 100) - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.scim.create_user(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a new user in the organization (requires organization-scoped API key) -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import ScimName -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.scim.create_user( - user_name="userName", - name=ScimName(), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**user_name:** `str` — User's email address (required) - -
-
- -
-
- -**name:** `ScimName` — User's name information - -
-
- -
-
- -**emails:** `typing.Optional[typing.Sequence[ScimEmail]]` — User's email addresses - -
-
- -
-
- -**active:** `typing.Optional[bool]` — Whether the user is active - -
-
- -
-
- -**password:** `typing.Optional[str]` — Initial password for the user - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.scim.get_user(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a specific user 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.scim.get_user( - user_id="userId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**user_id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.scim.delete_user(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Remove a user from the organization (requires organization-scoped API key). Note that this only removes the user from the organization but does not delete the user entity itself. -
-
-
-
- -#### 🔌 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.scim.delete_user( - user_id="userId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**user_id:** `str` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## ScoreConfigs -
client.score_configs.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a score configuration (config). Score configs are used to define the structure of scores -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateScoreConfigRequest, ScoreDataType -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.score_configs.create( - request=CreateScoreConfigRequest( - name="name", - data_type=ScoreDataType.NUMERIC, - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateScoreConfigRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.score_configs.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get all score configs -
-
-
-
- -#### 🔌 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.score_configs.get() - -``` -
-
-
-
- -#### ⚙️ 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 - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.score_configs.get_by_id(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a score config -
-
-
-
- -#### 🔌 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.score_configs.get_by_id( - config_id="configId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**config_id:** `str` — The unique langfuse identifier of a score config - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.score_configs.update(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Update a score config -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import UpdateScoreConfigRequest -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.score_configs.update( - config_id="configId", - request=UpdateScoreConfigRequest(), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**config_id:** `str` — The unique langfuse identifier of a score config - -
-
- -
-
- -**request:** `UpdateScoreConfigRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## ScoreV2 -
client.score_v_2.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a list of scores (supports both trace and session scores) -
-
-
-
- -#### 🔌 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.score_v_2.get() - -``` -
-
-
-
- -#### ⚙️ 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. - -
-
- -
-
- -**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. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.score_v_2.get_by_id(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a score (supports both trace and session scores) -
-
-
-
- -#### 🔌 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.score_v_2.get_by_id( - score_id="scoreId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**score_id:** `str` — The unique langfuse identifier of a score - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Score -
client.score.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Create a score (supports both trace and session scores) -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from langfuse import CreateScoreRequest -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.score.create( - request=CreateScoreRequest( - name="name", - value=1.1, - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `CreateScoreRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.score.delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete a score (supports both trace and session scores) -
-
-
-
- -#### 🔌 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.score.delete( - score_id="scoreId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**score_id:** `str` — The unique langfuse identifier of a score - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Sessions -
client.sessions.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get sessions -
-
-
-
- -#### 🔌 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.sessions.list() - -``` -
-
-
-
- -#### ⚙️ 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. - -
-
- -
-
- -**from_timestamp:** `typing.Optional[dt.datetime]` — Optional filter to only include sessions created on or after a certain datetime (ISO 8601) - -
-
- -
-
- -**to_timestamp:** `typing.Optional[dt.datetime]` — Optional filter to only include sessions created before a certain datetime (ISO 8601) - -
-
- -
-
- -**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for sessions where the environment is one of the provided values. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.sessions.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=` -
-
-
-
- -#### 🔌 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.sessions.get( - session_id="sessionId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**session_id:** `str` — The unique id of a session - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Trace -
client.trace.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get a specific trace -
-
-
-
- -#### 🔌 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.trace.get( - trace_id="traceId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**trace_id:** `str` — The unique langfuse identifier of a trace - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.trace.delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete a specific trace -
-
-
-
- -#### 🔌 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.trace.delete( - trace_id="traceId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**trace_id:** `str` — The unique langfuse identifier of the trace to delete - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.trace.list(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Get list of traces -
-
-
-
- -#### 🔌 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.trace.list() - -``` -
-
-
-
- -#### ⚙️ 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]` - -
-
- -
-
- -**name:** `typing.Optional[str]` - -
-
- -
-
- -**session_id:** `typing.Optional[str]` - -
-
- -
-
- -**from_timestamp:** `typing.Optional[dt.datetime]` — Optional filter to only include traces with a trace.timestamp on or after a certain datetime (ISO 8601) - -
-
- -
-
- -**to_timestamp:** `typing.Optional[dt.datetime]` — Optional filter to only include traces with a trace.timestamp before a certain datetime (ISO 8601) - -
-
- -
-
- -**order_by:** `typing.Optional[str]` — Format of the string [field].[asc/desc]. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: timestamp.asc - -
-
- -
-
- -**tags:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Only traces that include all of these tags will be returned. - -
-
- -
-
- -**version:** `typing.Optional[str]` — Optional filter to only include traces with a certain version. - -
-
- -
-
- -**release:** `typing.Optional[str]` — Optional filter to only include traces with a certain release. - -
-
- -
-
- -**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — 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: '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'. - -
-
- -
-
- -**filter:** `typing.Optional[str]` - -JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, sessionId, tags, version, release, environment, fromTimestamp, toTimestamp). - -## Filter Structure -Each filter condition has the following structure: -```json -[ - { - "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" - "column": string, // Required. Column to filter on (see available columns below) - "operator": string, // Required. Operator based on type: - // - datetime: ">", "<", ">=", "<=" - // - string: "=", "contains", "does not contain", "starts with", "ends with" - // - stringOptions: "any of", "none of" - // - categoryOptions: "any of", "none of" - // - arrayOptions: "any of", "none of", "all of" - // - number: "=", ">", "<", ">=", "<=" - // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" - // - numberObject: "=", ">", "<", ">=", "<=" - // - boolean: "=", "<>" - // - null: "is null", "is not null" - "value": any, // Required (except for null type). Value to compare against. Type depends on filter type - "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata - } -] -``` - -## Available Columns - -### Core Trace Fields -- `id` (string) - Trace ID -- `name` (string) - Trace name -- `timestamp` (datetime) - Trace timestamp -- `userId` (string) - User ID -- `sessionId` (string) - Session ID -- `environment` (string) - Environment tag -- `version` (string) - Version tag -- `release` (string) - Release tag -- `tags` (arrayOptions) - Array of tags -- `bookmarked` (boolean) - Bookmark status - -### Structured Data -- `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. - -### Aggregated Metrics (from observations) -These metrics are aggregated from all observations within the trace: -- `latency` (number) - Latency in seconds (time from first observation start to last observation end) -- `inputTokens` (number) - Total input tokens across all observations -- `outputTokens` (number) - Total output tokens across all observations -- `totalTokens` (number) - Total tokens (alias: `tokens`) -- `inputCost` (number) - Total input cost in USD -- `outputCost` (number) - Total output cost in USD -- `totalCost` (number) - Total cost in USD - -### Observation Level Aggregations -These fields aggregate observation levels within the trace: -- `level` (string) - Highest severity level (ERROR > WARNING > DEFAULT > DEBUG) -- `warningCount` (number) - Count of WARNING level observations -- `errorCount` (number) - Count of ERROR level observations -- `defaultCount` (number) - Count of DEFAULT level observations -- `debugCount` (number) - Count of DEBUG level observations - -### Scores (requires join with scores table) -- `scores_avg` (number) - Average of numeric scores (alias: `scores`) -- `score_categories` (categoryOptions) - Categorical score values - -## Filter Examples -```json -[ - { - "type": "datetime", - "column": "timestamp", - "operator": ">=", - "value": "2024-01-01T00:00:00Z" - }, - { - "type": "string", - "column": "userId", - "operator": "=", - "value": "user-123" - }, - { - "type": "number", - "column": "totalCost", - "operator": ">=", - "value": 0.01 - }, - { - "type": "arrayOptions", - "column": "tags", - "operator": "all of", - "value": ["production", "critical"] - }, - { - "type": "stringObject", - "column": "metadata", - "key": "customer_tier", - "operator": "=", - "value": "enterprise" - } -] -``` - -## Performance Notes -- Filtering on `userId`, `sessionId`, or `metadata` may enable skip indexes for better query performance -- Score filters require a join with the scores table and may impact query performance - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.trace.delete_multiple(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete multiple traces -
-
-
-
- -#### 🔌 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.trace.delete_multiple( - trace_ids=["traceIds", "traceIds"], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**trace_ids:** `typing.Sequence[str]` — List of trace IDs to delete - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- diff --git a/langfuse/api/resources/__init__.py b/langfuse/api/resources/__init__.py deleted file mode 100644 index d91362a28..000000000 --- a/langfuse/api/resources/__init__.py +++ /dev/null @@ -1,522 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from . import ( - annotation_queues, - blob_storage_integrations, - comments, - commons, - dataset_items, - dataset_run_items, - datasets, - health, - ingestion, - llm_connections, - media, - metrics, - models, - observations, - opentelemetry, - organizations, - projects, - prompt_version, - prompts, - scim, - score, - score_configs, - score_v_2, - sessions, - trace, - utils, -) -from .annotation_queues import ( - AnnotationQueue, - AnnotationQueueAssignmentRequest, - AnnotationQueueItem, - AnnotationQueueObjectType, - AnnotationQueueStatus, - CreateAnnotationQueueAssignmentResponse, - CreateAnnotationQueueItemRequest, - CreateAnnotationQueueRequest, - DeleteAnnotationQueueAssignmentResponse, - DeleteAnnotationQueueItemResponse, - PaginatedAnnotationQueueItems, - 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, - BaseScore, - BaseScoreV1, - BooleanScore, - BooleanScoreV1, - CategoricalScore, - CategoricalScoreV1, - Comment, - CommentObjectType, - ConfigCategory, - CreateScoreValue, - Dataset, - DatasetItem, - DatasetRun, - DatasetRunItem, - DatasetRunWithItems, - DatasetStatus, - Error, - MapValue, - MethodNotAllowedError, - Model, - ModelPrice, - ModelUsageUnit, - NotFoundError, - NumericScore, - NumericScoreV1, - Observation, - ObservationLevel, - ObservationsView, - PricingTier, - PricingTierCondition, - PricingTierInput, - PricingTierOperator, - Score, - ScoreConfig, - ScoreDataType, - ScoreSource, - ScoreV1, - ScoreV1_Boolean, - ScoreV1_Categorical, - ScoreV1_Numeric, - Score_Boolean, - Score_Categorical, - Score_Numeric, - Session, - SessionWithTraces, - Trace, - TraceWithDetails, - TraceWithFullDetails, - UnauthorizedError, - Usage, -) -from .dataset_items import ( - CreateDatasetItemRequest, - DeleteDatasetItemResponse, - PaginatedDatasetItems, -) -from .dataset_run_items import CreateDatasetRunItemRequest, PaginatedDatasetRunItems -from .datasets import ( - CreateDatasetRequest, - DeleteDatasetRunResponse, - PaginatedDatasetRuns, - PaginatedDatasets, -) -from .health import HealthResponse, ServiceUnavailableError -from .ingestion import ( - BaseEvent, - CreateEventBody, - CreateEventEvent, - CreateGenerationBody, - CreateGenerationEvent, - CreateObservationEvent, - CreateSpanBody, - CreateSpanEvent, - IngestionError, - IngestionEvent, - IngestionEvent_EventCreate, - IngestionEvent_GenerationCreate, - IngestionEvent_GenerationUpdate, - IngestionEvent_ObservationCreate, - IngestionEvent_ObservationUpdate, - IngestionEvent_ScoreCreate, - IngestionEvent_SdkLog, - IngestionEvent_SpanCreate, - IngestionEvent_SpanUpdate, - IngestionEvent_TraceCreate, - IngestionResponse, - IngestionSuccess, - IngestionUsage, - ObservationBody, - ObservationType, - OpenAiCompletionUsageSchema, - OpenAiResponseUsageSchema, - OpenAiUsage, - OptionalObservationBody, - ScoreBody, - ScoreEvent, - SdkLogBody, - SdkLogEvent, - TraceBody, - TraceEvent, - UpdateEventBody, - UpdateGenerationBody, - UpdateGenerationEvent, - UpdateObservationEvent, - UpdateSpanBody, - UpdateSpanEvent, - UsageDetails, -) -from .llm_connections import ( - LlmAdapter, - LlmConnection, - PaginatedLlmConnections, - UpsertLlmConnectionRequest, -) -from .media import ( - GetMediaResponse, - GetMediaUploadUrlRequest, - GetMediaUploadUrlResponse, - MediaContentType, - PatchMediaBody, -) -from .metrics import MetricsResponse -from .models import CreateModelRequest, PaginatedModels -from .observations import Observations, ObservationsViews -from .opentelemetry import ( - OtelAttribute, - OtelAttributeValue, - OtelResource, - OtelResourceSpan, - OtelScope, - OtelScopeSpan, - OtelSpan, - OtelTraceResponse, -) -from .organizations import ( - DeleteMembershipRequest, - MembershipDeletionResponse, - MembershipRequest, - MembershipResponse, - MembershipRole, - MembershipsResponse, - OrganizationApiKey, - OrganizationApiKeysResponse, - OrganizationProject, - OrganizationProjectsResponse, -) -from .projects import ( - ApiKeyDeletionResponse, - ApiKeyList, - ApiKeyResponse, - ApiKeySummary, - Project, - ProjectDeletionResponse, - Projects, -) -from .prompts import ( - BasePrompt, - ChatMessage, - ChatMessageWithPlaceholders, - ChatMessageWithPlaceholders_Chatmessage, - ChatMessageWithPlaceholders_Placeholder, - ChatPrompt, - CreateChatPromptRequest, - CreatePromptRequest, - CreatePromptRequest_Chat, - CreatePromptRequest_Text, - CreateTextPromptRequest, - PlaceholderMessage, - Prompt, - PromptMeta, - PromptMetaListResponse, - PromptType, - Prompt_Chat, - Prompt_Text, - TextPrompt, -) -from .scim import ( - AuthenticationScheme, - BulkConfig, - EmptyResponse, - FilterConfig, - ResourceMeta, - ResourceType, - ResourceTypesResponse, - SchemaExtension, - SchemaResource, - SchemasResponse, - ScimEmail, - ScimFeatureSupport, - ScimName, - ScimUser, - ScimUsersListResponse, - ServiceProviderConfig, - UserMeta, -) -from .score import CreateScoreRequest, CreateScoreResponse -from .score_configs import ( - CreateScoreConfigRequest, - ScoreConfigs, - UpdateScoreConfigRequest, -) -from .score_v_2 import ( - GetScoresResponse, - GetScoresResponseData, - GetScoresResponseDataBoolean, - GetScoresResponseDataCategorical, - GetScoresResponseDataNumeric, - GetScoresResponseData_Boolean, - GetScoresResponseData_Categorical, - GetScoresResponseData_Numeric, - GetScoresResponseTraceData, -) -from .sessions import PaginatedSessions -from .trace import DeleteTraceResponse, Sort, Traces - -__all__ = [ - "AccessDeniedError", - "AnnotationQueue", - "AnnotationQueueAssignmentRequest", - "AnnotationQueueItem", - "AnnotationQueueObjectType", - "AnnotationQueueStatus", - "ApiKeyDeletionResponse", - "ApiKeyList", - "ApiKeyResponse", - "ApiKeySummary", - "AuthenticationScheme", - "BaseEvent", - "BasePrompt", - "BaseScore", - "BaseScoreV1", - "BlobStorageExportFrequency", - "BlobStorageExportMode", - "BlobStorageIntegrationDeletionResponse", - "BlobStorageIntegrationFileType", - "BlobStorageIntegrationResponse", - "BlobStorageIntegrationType", - "BlobStorageIntegrationsResponse", - "BooleanScore", - "BooleanScoreV1", - "BulkConfig", - "CategoricalScore", - "CategoricalScoreV1", - "ChatMessage", - "ChatMessageWithPlaceholders", - "ChatMessageWithPlaceholders_Chatmessage", - "ChatMessageWithPlaceholders_Placeholder", - "ChatPrompt", - "Comment", - "CommentObjectType", - "ConfigCategory", - "CreateAnnotationQueueAssignmentResponse", - "CreateAnnotationQueueItemRequest", - "CreateAnnotationQueueRequest", - "CreateBlobStorageIntegrationRequest", - "CreateChatPromptRequest", - "CreateCommentRequest", - "CreateCommentResponse", - "CreateDatasetItemRequest", - "CreateDatasetRequest", - "CreateDatasetRunItemRequest", - "CreateEventBody", - "CreateEventEvent", - "CreateGenerationBody", - "CreateGenerationEvent", - "CreateModelRequest", - "CreateObservationEvent", - "CreatePromptRequest", - "CreatePromptRequest_Chat", - "CreatePromptRequest_Text", - "CreateScoreConfigRequest", - "CreateScoreRequest", - "CreateScoreResponse", - "CreateScoreValue", - "CreateSpanBody", - "CreateSpanEvent", - "CreateTextPromptRequest", - "Dataset", - "DatasetItem", - "DatasetRun", - "DatasetRunItem", - "DatasetRunWithItems", - "DatasetStatus", - "DeleteAnnotationQueueAssignmentResponse", - "DeleteAnnotationQueueItemResponse", - "DeleteDatasetItemResponse", - "DeleteDatasetRunResponse", - "DeleteMembershipRequest", - "DeleteTraceResponse", - "EmptyResponse", - "Error", - "FilterConfig", - "GetCommentsResponse", - "GetMediaResponse", - "GetMediaUploadUrlRequest", - "GetMediaUploadUrlResponse", - "GetScoresResponse", - "GetScoresResponseData", - "GetScoresResponseDataBoolean", - "GetScoresResponseDataCategorical", - "GetScoresResponseDataNumeric", - "GetScoresResponseData_Boolean", - "GetScoresResponseData_Categorical", - "GetScoresResponseData_Numeric", - "GetScoresResponseTraceData", - "HealthResponse", - "IngestionError", - "IngestionEvent", - "IngestionEvent_EventCreate", - "IngestionEvent_GenerationCreate", - "IngestionEvent_GenerationUpdate", - "IngestionEvent_ObservationCreate", - "IngestionEvent_ObservationUpdate", - "IngestionEvent_ScoreCreate", - "IngestionEvent_SdkLog", - "IngestionEvent_SpanCreate", - "IngestionEvent_SpanUpdate", - "IngestionEvent_TraceCreate", - "IngestionResponse", - "IngestionSuccess", - "IngestionUsage", - "LlmAdapter", - "LlmConnection", - "MapValue", - "MediaContentType", - "MembershipDeletionResponse", - "MembershipRequest", - "MembershipResponse", - "MembershipRole", - "MembershipsResponse", - "MethodNotAllowedError", - "MetricsResponse", - "Model", - "ModelPrice", - "ModelUsageUnit", - "NotFoundError", - "NumericScore", - "NumericScoreV1", - "Observation", - "ObservationBody", - "ObservationLevel", - "ObservationType", - "Observations", - "ObservationsView", - "ObservationsViews", - "OpenAiCompletionUsageSchema", - "OpenAiResponseUsageSchema", - "OpenAiUsage", - "OptionalObservationBody", - "OrganizationApiKey", - "OrganizationApiKeysResponse", - "OrganizationProject", - "OrganizationProjectsResponse", - "OtelAttribute", - "OtelAttributeValue", - "OtelResource", - "OtelResourceSpan", - "OtelScope", - "OtelScopeSpan", - "OtelSpan", - "OtelTraceResponse", - "PaginatedAnnotationQueueItems", - "PaginatedAnnotationQueues", - "PaginatedDatasetItems", - "PaginatedDatasetRunItems", - "PaginatedDatasetRuns", - "PaginatedDatasets", - "PaginatedLlmConnections", - "PaginatedModels", - "PaginatedSessions", - "PatchMediaBody", - "PlaceholderMessage", - "PricingTier", - "PricingTierCondition", - "PricingTierInput", - "PricingTierOperator", - "Project", - "ProjectDeletionResponse", - "Projects", - "Prompt", - "PromptMeta", - "PromptMetaListResponse", - "PromptType", - "Prompt_Chat", - "Prompt_Text", - "ResourceMeta", - "ResourceType", - "ResourceTypesResponse", - "SchemaExtension", - "SchemaResource", - "SchemasResponse", - "ScimEmail", - "ScimFeatureSupport", - "ScimName", - "ScimUser", - "ScimUsersListResponse", - "Score", - "ScoreBody", - "ScoreConfig", - "ScoreConfigs", - "ScoreDataType", - "ScoreEvent", - "ScoreSource", - "ScoreV1", - "ScoreV1_Boolean", - "ScoreV1_Categorical", - "ScoreV1_Numeric", - "Score_Boolean", - "Score_Categorical", - "Score_Numeric", - "SdkLogBody", - "SdkLogEvent", - "ServiceProviderConfig", - "ServiceUnavailableError", - "Session", - "SessionWithTraces", - "Sort", - "TextPrompt", - "Trace", - "TraceBody", - "TraceEvent", - "TraceWithDetails", - "TraceWithFullDetails", - "Traces", - "UnauthorizedError", - "UpdateAnnotationQueueItemRequest", - "UpdateEventBody", - "UpdateGenerationBody", - "UpdateGenerationEvent", - "UpdateObservationEvent", - "UpdateScoreConfigRequest", - "UpdateSpanBody", - "UpdateSpanEvent", - "UpsertLlmConnectionRequest", - "Usage", - "UsageDetails", - "UserMeta", - "annotation_queues", - "blob_storage_integrations", - "comments", - "commons", - "dataset_items", - "dataset_run_items", - "datasets", - "health", - "ingestion", - "llm_connections", - "media", - "metrics", - "models", - "observations", - "opentelemetry", - "organizations", - "projects", - "prompt_version", - "prompts", - "scim", - "score", - "score_configs", - "score_v_2", - "sessions", - "trace", - "utils", -] diff --git a/langfuse/api/resources/annotation_queues/__init__.py b/langfuse/api/resources/annotation_queues/__init__.py deleted file mode 100644 index eed891727..000000000 --- a/langfuse/api/resources/annotation_queues/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - AnnotationQueue, - AnnotationQueueAssignmentRequest, - AnnotationQueueItem, - AnnotationQueueObjectType, - AnnotationQueueStatus, - CreateAnnotationQueueAssignmentResponse, - CreateAnnotationQueueItemRequest, - CreateAnnotationQueueRequest, - DeleteAnnotationQueueAssignmentResponse, - DeleteAnnotationQueueItemResponse, - PaginatedAnnotationQueueItems, - PaginatedAnnotationQueues, - UpdateAnnotationQueueItemRequest, -) - -__all__ = [ - "AnnotationQueue", - "AnnotationQueueAssignmentRequest", - "AnnotationQueueItem", - "AnnotationQueueObjectType", - "AnnotationQueueStatus", - "CreateAnnotationQueueAssignmentResponse", - "CreateAnnotationQueueItemRequest", - "CreateAnnotationQueueRequest", - "DeleteAnnotationQueueAssignmentResponse", - "DeleteAnnotationQueueItemResponse", - "PaginatedAnnotationQueueItems", - "PaginatedAnnotationQueues", - "UpdateAnnotationQueueItemRequest", -] diff --git a/langfuse/api/resources/annotation_queues/client.py b/langfuse/api/resources/annotation_queues/client.py deleted file mode 100644 index 97c7c2216..000000000 --- a/langfuse/api/resources/annotation_queues/client.py +++ /dev/null @@ -1,1642 +0,0 @@ -# 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.annotation_queue import AnnotationQueue -from .types.annotation_queue_assignment_request import AnnotationQueueAssignmentRequest -from .types.annotation_queue_item import AnnotationQueueItem -from .types.annotation_queue_status import AnnotationQueueStatus -from .types.create_annotation_queue_assignment_response import ( - CreateAnnotationQueueAssignmentResponse, -) -from .types.create_annotation_queue_item_request import CreateAnnotationQueueItemRequest -from .types.create_annotation_queue_request import CreateAnnotationQueueRequest -from .types.delete_annotation_queue_assignment_response import ( - DeleteAnnotationQueueAssignmentResponse, -) -from .types.delete_annotation_queue_item_response import ( - DeleteAnnotationQueueItemResponse, -) -from .types.paginated_annotation_queue_items import PaginatedAnnotationQueueItems -from .types.paginated_annotation_queues import PaginatedAnnotationQueues -from .types.update_annotation_queue_item_request import UpdateAnnotationQueueItemRequest - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class AnnotationQueuesClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list_queues( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedAnnotationQueues: - """ - Get all annotation queues - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedAnnotationQueues - - 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.annotation_queues.list_queues() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/annotation-queues", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedAnnotationQueues, _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 create_queue( - self, - *, - request: CreateAnnotationQueueRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueue: - """ - Create an annotation queue - - Parameters - ---------- - request : CreateAnnotationQueueRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueue - - Examples - -------- - from langfuse import CreateAnnotationQueueRequest - 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.annotation_queues.create_queue( - request=CreateAnnotationQueueRequest( - name="name", - score_config_ids=["scoreConfigIds", "scoreConfigIds"], - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/annotation-queues", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueue, _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_queue( - self, queue_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AnnotationQueue: - """ - Get an annotation queue by ID - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueue - - 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.annotation_queues.get_queue( - queue_id="queueId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueue, _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 list_queue_items( - self, - queue_id: str, - *, - status: typing.Optional[AnnotationQueueStatus] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedAnnotationQueueItems: - """ - Get items for a specific annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - status : typing.Optional[AnnotationQueueStatus] - Filter by status - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedAnnotationQueueItems - - 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.annotation_queues.list_queue_items( - queue_id="queueId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", - method="GET", - params={"status": status, "page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedAnnotationQueueItems, _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_queue_item( - self, - queue_id: str, - item_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueueItem: - """ - Get a specific item from an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - item_id : str - The unique identifier of the annotation queue item - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueueItem - - 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.annotation_queues.get_queue_item( - queue_id="queueId", - item_id="itemId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueueItem, _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 create_queue_item( - self, - queue_id: str, - *, - request: CreateAnnotationQueueItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueueItem: - """ - Add an item to an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request : CreateAnnotationQueueItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueueItem - - Examples - -------- - from langfuse import AnnotationQueueObjectType, CreateAnnotationQueueItemRequest - 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.annotation_queues.create_queue_item( - queue_id="queueId", - request=CreateAnnotationQueueItemRequest( - object_id="objectId", - object_type=AnnotationQueueObjectType.TRACE, - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueueItem, _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 update_queue_item( - self, - queue_id: str, - item_id: str, - *, - request: UpdateAnnotationQueueItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueueItem: - """ - Update an annotation queue item - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - item_id : str - The unique identifier of the annotation queue item - - request : UpdateAnnotationQueueItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueueItem - - Examples - -------- - from langfuse import UpdateAnnotationQueueItemRequest - 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.annotation_queues.update_queue_item( - queue_id="queueId", - item_id="itemId", - request=UpdateAnnotationQueueItemRequest(), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", - method="PATCH", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueueItem, _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_queue_item( - self, - queue_id: str, - item_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteAnnotationQueueItemResponse: - """ - Remove an item from an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - item_id : str - The unique identifier of the annotation queue item - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteAnnotationQueueItemResponse - - 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.annotation_queues.delete_queue_item( - queue_id="queueId", - item_id="itemId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteAnnotationQueueItemResponse, _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 create_queue_assignment( - self, - queue_id: str, - *, - request: AnnotationQueueAssignmentRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CreateAnnotationQueueAssignmentResponse: - """ - Create an assignment for a user to an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request : AnnotationQueueAssignmentRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CreateAnnotationQueueAssignmentResponse - - Examples - -------- - from langfuse import AnnotationQueueAssignmentRequest - 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.annotation_queues.create_queue_assignment( - queue_id="queueId", - request=AnnotationQueueAssignmentRequest( - user_id="userId", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - CreateAnnotationQueueAssignmentResponse, _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_queue_assignment( - self, - queue_id: str, - *, - request: AnnotationQueueAssignmentRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteAnnotationQueueAssignmentResponse: - """ - Delete an assignment for a user to an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request : AnnotationQueueAssignmentRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteAnnotationQueueAssignmentResponse - - Examples - -------- - from langfuse import AnnotationQueueAssignmentRequest - 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.annotation_queues.delete_queue_assignment( - queue_id="queueId", - request=AnnotationQueueAssignmentRequest( - user_id="userId", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", - method="DELETE", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteAnnotationQueueAssignmentResponse, _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 AsyncAnnotationQueuesClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list_queues( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedAnnotationQueues: - """ - Get all annotation queues - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedAnnotationQueues - - 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.annotation_queues.list_queues() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/annotation-queues", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedAnnotationQueues, _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 create_queue( - self, - *, - request: CreateAnnotationQueueRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueue: - """ - Create an annotation queue - - Parameters - ---------- - request : CreateAnnotationQueueRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueue - - Examples - -------- - import asyncio - - from langfuse import CreateAnnotationQueueRequest - 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.annotation_queues.create_queue( - request=CreateAnnotationQueueRequest( - name="name", - score_config_ids=["scoreConfigIds", "scoreConfigIds"], - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/annotation-queues", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueue, _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_queue( - self, queue_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AnnotationQueue: - """ - Get an annotation queue by ID - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueue - - 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.annotation_queues.get_queue( - queue_id="queueId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueue, _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 list_queue_items( - self, - queue_id: str, - *, - status: typing.Optional[AnnotationQueueStatus] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedAnnotationQueueItems: - """ - Get items for a specific annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - status : typing.Optional[AnnotationQueueStatus] - Filter by status - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedAnnotationQueueItems - - 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.annotation_queues.list_queue_items( - queue_id="queueId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", - method="GET", - params={"status": status, "page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedAnnotationQueueItems, _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_queue_item( - self, - queue_id: str, - item_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueueItem: - """ - Get a specific item from an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - item_id : str - The unique identifier of the annotation queue item - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueueItem - - 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.annotation_queues.get_queue_item( - queue_id="queueId", - item_id="itemId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueueItem, _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 create_queue_item( - self, - queue_id: str, - *, - request: CreateAnnotationQueueItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueueItem: - """ - Add an item to an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request : CreateAnnotationQueueItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueueItem - - Examples - -------- - import asyncio - - from langfuse import AnnotationQueueObjectType, CreateAnnotationQueueItemRequest - 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.annotation_queues.create_queue_item( - queue_id="queueId", - request=CreateAnnotationQueueItemRequest( - object_id="objectId", - object_type=AnnotationQueueObjectType.TRACE, - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueueItem, _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 update_queue_item( - self, - queue_id: str, - item_id: str, - *, - request: UpdateAnnotationQueueItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> AnnotationQueueItem: - """ - Update an annotation queue item - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - item_id : str - The unique identifier of the annotation queue item - - request : UpdateAnnotationQueueItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AnnotationQueueItem - - Examples - -------- - import asyncio - - from langfuse import UpdateAnnotationQueueItemRequest - 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.annotation_queues.update_queue_item( - queue_id="queueId", - item_id="itemId", - request=UpdateAnnotationQueueItemRequest(), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", - method="PATCH", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AnnotationQueueItem, _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_queue_item( - self, - queue_id: str, - item_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteAnnotationQueueItemResponse: - """ - Remove an item from an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - item_id : str - The unique identifier of the annotation queue item - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteAnnotationQueueItemResponse - - 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.annotation_queues.delete_queue_item( - queue_id="queueId", - item_id="itemId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/items/{jsonable_encoder(item_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteAnnotationQueueItemResponse, _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 create_queue_assignment( - self, - queue_id: str, - *, - request: AnnotationQueueAssignmentRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CreateAnnotationQueueAssignmentResponse: - """ - Create an assignment for a user to an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request : AnnotationQueueAssignmentRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CreateAnnotationQueueAssignmentResponse - - Examples - -------- - import asyncio - - from langfuse import AnnotationQueueAssignmentRequest - 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.annotation_queues.create_queue_assignment( - queue_id="queueId", - request=AnnotationQueueAssignmentRequest( - user_id="userId", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - CreateAnnotationQueueAssignmentResponse, _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_queue_assignment( - self, - queue_id: str, - *, - request: AnnotationQueueAssignmentRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteAnnotationQueueAssignmentResponse: - """ - Delete an assignment for a user to an annotation queue - - Parameters - ---------- - queue_id : str - The unique identifier of the annotation queue - - request : AnnotationQueueAssignmentRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteAnnotationQueueAssignmentResponse - - Examples - -------- - import asyncio - - from langfuse import AnnotationQueueAssignmentRequest - 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.annotation_queues.delete_queue_assignment( - queue_id="queueId", - request=AnnotationQueueAssignmentRequest( - user_id="userId", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/annotation-queues/{jsonable_encoder(queue_id)}/assignments", - method="DELETE", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteAnnotationQueueAssignmentResponse, _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/annotation_queues/types/__init__.py b/langfuse/api/resources/annotation_queues/types/__init__.py deleted file mode 100644 index 9f9ce37dd..000000000 --- a/langfuse/api/resources/annotation_queues/types/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .annotation_queue import AnnotationQueue -from .annotation_queue_assignment_request import AnnotationQueueAssignmentRequest -from .annotation_queue_item import AnnotationQueueItem -from .annotation_queue_object_type import AnnotationQueueObjectType -from .annotation_queue_status import AnnotationQueueStatus -from .create_annotation_queue_assignment_response import ( - CreateAnnotationQueueAssignmentResponse, -) -from .create_annotation_queue_item_request import CreateAnnotationQueueItemRequest -from .create_annotation_queue_request import CreateAnnotationQueueRequest -from .delete_annotation_queue_assignment_response import ( - DeleteAnnotationQueueAssignmentResponse, -) -from .delete_annotation_queue_item_response import DeleteAnnotationQueueItemResponse -from .paginated_annotation_queue_items import PaginatedAnnotationQueueItems -from .paginated_annotation_queues import PaginatedAnnotationQueues -from .update_annotation_queue_item_request import UpdateAnnotationQueueItemRequest - -__all__ = [ - "AnnotationQueue", - "AnnotationQueueAssignmentRequest", - "AnnotationQueueItem", - "AnnotationQueueObjectType", - "AnnotationQueueStatus", - "CreateAnnotationQueueAssignmentResponse", - "CreateAnnotationQueueItemRequest", - "CreateAnnotationQueueRequest", - "DeleteAnnotationQueueAssignmentResponse", - "DeleteAnnotationQueueItemResponse", - "PaginatedAnnotationQueueItems", - "PaginatedAnnotationQueues", - "UpdateAnnotationQueueItemRequest", -] diff --git a/langfuse/api/resources/annotation_queues/types/annotation_queue.py b/langfuse/api/resources/annotation_queues/types/annotation_queue.py deleted file mode 100644 index c4cc23282..000000000 --- a/langfuse/api/resources/annotation_queues/types/annotation_queue.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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 AnnotationQueue(pydantic_v1.BaseModel): - id: str - name: str - description: typing.Optional[str] = None - score_config_ids: typing.List[str] = pydantic_v1.Field(alias="scoreConfigIds") - 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/annotation_queues/types/annotation_queue_assignment_request.py b/langfuse/api/resources/annotation_queues/types/annotation_queue_assignment_request.py deleted file mode 100644 index aa3980438..000000000 --- a/langfuse/api/resources/annotation_queues/types/annotation_queue_assignment_request.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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 AnnotationQueueAssignmentRequest(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/annotation_queues/types/annotation_queue_item.py b/langfuse/api/resources/annotation_queues/types/annotation_queue_item.py deleted file mode 100644 index e88829a1f..000000000 --- a/langfuse/api/resources/annotation_queues/types/annotation_queue_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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 .annotation_queue_object_type import AnnotationQueueObjectType -from .annotation_queue_status import AnnotationQueueStatus - - -class AnnotationQueueItem(pydantic_v1.BaseModel): - id: str - queue_id: str = pydantic_v1.Field(alias="queueId") - object_id: str = pydantic_v1.Field(alias="objectId") - object_type: AnnotationQueueObjectType = pydantic_v1.Field(alias="objectType") - status: AnnotationQueueStatus - completed_at: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="completedAt", 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/annotation_queues/types/create_annotation_queue_assignment_response.py b/langfuse/api/resources/annotation_queues/types/create_annotation_queue_assignment_response.py deleted file mode 100644 index ae6a46862..000000000 --- a/langfuse/api/resources/annotation_queues/types/create_annotation_queue_assignment_response.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 CreateAnnotationQueueAssignmentResponse(pydantic_v1.BaseModel): - user_id: str = pydantic_v1.Field(alias="userId") - queue_id: str = pydantic_v1.Field(alias="queueId") - project_id: str = pydantic_v1.Field(alias="projectId") - - 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/annotation_queues/types/create_annotation_queue_item_request.py b/langfuse/api/resources/annotation_queues/types/create_annotation_queue_item_request.py deleted file mode 100644 index cbf257f29..000000000 --- a/langfuse/api/resources/annotation_queues/types/create_annotation_queue_item_request.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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 .annotation_queue_object_type import AnnotationQueueObjectType -from .annotation_queue_status import AnnotationQueueStatus - - -class CreateAnnotationQueueItemRequest(pydantic_v1.BaseModel): - object_id: str = pydantic_v1.Field(alias="objectId") - object_type: AnnotationQueueObjectType = pydantic_v1.Field(alias="objectType") - status: typing.Optional[AnnotationQueueStatus] = pydantic_v1.Field(default=None) - """ - Defaults to PENDING for new queue items - """ - - 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/annotation_queues/types/create_annotation_queue_request.py b/langfuse/api/resources/annotation_queues/types/create_annotation_queue_request.py deleted file mode 100644 index 7f793cea2..000000000 --- a/langfuse/api/resources/annotation_queues/types/create_annotation_queue_request.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 CreateAnnotationQueueRequest(pydantic_v1.BaseModel): - name: str - description: typing.Optional[str] = None - score_config_ids: typing.List[str] = pydantic_v1.Field(alias="scoreConfigIds") - - 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/annotation_queues/types/delete_annotation_queue_assignment_response.py b/langfuse/api/resources/annotation_queues/types/delete_annotation_queue_assignment_response.py deleted file mode 100644 index e348d546c..000000000 --- a/langfuse/api/resources/annotation_queues/types/delete_annotation_queue_assignment_response.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 DeleteAnnotationQueueAssignmentResponse(pydantic_v1.BaseModel): - success: bool - - 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/annotation_queues/types/delete_annotation_queue_item_response.py b/langfuse/api/resources/annotation_queues/types/delete_annotation_queue_item_response.py deleted file mode 100644 index a412c85b7..000000000 --- a/langfuse/api/resources/annotation_queues/types/delete_annotation_queue_item_response.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 DeleteAnnotationQueueItemResponse(pydantic_v1.BaseModel): - success: bool - 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/annotation_queues/types/paginated_annotation_queue_items.py b/langfuse/api/resources/annotation_queues/types/paginated_annotation_queue_items.py deleted file mode 100644 index 587188d89..000000000 --- a/langfuse/api/resources/annotation_queues/types/paginated_annotation_queue_items.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...utils.resources.pagination.types.meta_response import MetaResponse -from .annotation_queue_item import AnnotationQueueItem - - -class PaginatedAnnotationQueueItems(pydantic_v1.BaseModel): - data: typing.List[AnnotationQueueItem] - meta: MetaResponse - - 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/annotation_queues/types/paginated_annotation_queues.py b/langfuse/api/resources/annotation_queues/types/paginated_annotation_queues.py deleted file mode 100644 index aba338414..000000000 --- a/langfuse/api/resources/annotation_queues/types/paginated_annotation_queues.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...utils.resources.pagination.types.meta_response import MetaResponse -from .annotation_queue import AnnotationQueue - - -class PaginatedAnnotationQueues(pydantic_v1.BaseModel): - data: typing.List[AnnotationQueue] - meta: MetaResponse - - 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/annotation_queues/types/update_annotation_queue_item_request.py b/langfuse/api/resources/annotation_queues/types/update_annotation_queue_item_request.py deleted file mode 100644 index 3b1c130fe..000000000 --- a/langfuse/api/resources/annotation_queues/types/update_annotation_queue_item_request.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 .annotation_queue_status import AnnotationQueueStatus - - -class UpdateAnnotationQueueItemRequest(pydantic_v1.BaseModel): - status: typing.Optional[AnnotationQueueStatus] = None - - 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/__init__.py b/langfuse/api/resources/blob_storage_integrations/__init__.py deleted file mode 100644 index a635fba57..000000000 --- a/langfuse/api/resources/blob_storage_integrations/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 deleted file mode 100644 index 73aec4fa4..000000000 --- a/langfuse/api/resources/blob_storage_integrations/client.py +++ /dev/null @@ -1,492 +0,0 @@ -# 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 deleted file mode 100644 index 621196c11..000000000 --- a/langfuse/api/resources/blob_storage_integrations/types/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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_integration_deletion_response.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_deletion_response.py deleted file mode 100644 index 4305cff2f..000000000 --- a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_deletion_response.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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_response.py b/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_response.py deleted file mode 100644 index e308e8113..000000000 --- a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_response.py +++ /dev/null @@ -1,75 +0,0 @@ -# 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 deleted file mode 100644 index 38bacbf85..000000000 --- a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integration_type.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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 deleted file mode 100644 index c6231a23e..000000000 --- a/langfuse/api/resources/blob_storage_integrations/types/blob_storage_integrations_response.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 deleted file mode 100644 index 31b5779c6..000000000 --- a/langfuse/api/resources/blob_storage_integrations/types/create_blob_storage_integration_request.py +++ /dev/null @@ -1,108 +0,0 @@ -# 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/comments/__init__.py b/langfuse/api/resources/comments/__init__.py deleted file mode 100644 index e40c8546f..000000000 --- a/langfuse/api/resources/comments/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import CreateCommentRequest, CreateCommentResponse, GetCommentsResponse - -__all__ = ["CreateCommentRequest", "CreateCommentResponse", "GetCommentsResponse"] diff --git a/langfuse/api/resources/comments/client.py b/langfuse/api/resources/comments/client.py deleted file mode 100644 index 9c78ca23f..000000000 --- a/langfuse/api/resources/comments/client.py +++ /dev/null @@ -1,520 +0,0 @@ -# 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 ..commons.types.comment import Comment -from .types.create_comment_request import CreateCommentRequest -from .types.create_comment_response import CreateCommentResponse -from .types.get_comments_response import GetCommentsResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class CommentsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def create( - self, - *, - request: CreateCommentRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CreateCommentResponse: - """ - Create a comment. Comments may be attached to different object types (trace, observation, session, prompt). - - Parameters - ---------- - request : CreateCommentRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CreateCommentResponse - - Examples - -------- - from langfuse import CreateCommentRequest - 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.comments.create( - request=CreateCommentRequest( - project_id="projectId", - object_type="objectType", - object_id="objectId", - content="content", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/comments", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CreateCommentResponse, _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( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - object_type: typing.Optional[str] = None, - object_id: typing.Optional[str] = None, - author_user_id: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> GetCommentsResponse: - """ - Get all comments - - 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 - - object_type : typing.Optional[str] - Filter comments by object type (trace, observation, session, prompt). - - object_id : typing.Optional[str] - Filter comments by object id. If objectType is not provided, an error will be thrown. - - author_user_id : typing.Optional[str] - Filter comments by author user id. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetCommentsResponse - - 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.comments.get() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/comments", - method="GET", - params={ - "page": page, - "limit": limit, - "objectType": object_type, - "objectId": object_id, - "authorUserId": author_user_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(GetCommentsResponse, _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_by_id( - self, - comment_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> Comment: - """ - Get a comment by id - - Parameters - ---------- - comment_id : str - The unique langfuse identifier of a comment - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Comment - - 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.comments.get_by_id( - comment_id="commentId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/comments/{jsonable_encoder(comment_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Comment, _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 AsyncCommentsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def create( - self, - *, - request: CreateCommentRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CreateCommentResponse: - """ - Create a comment. Comments may be attached to different object types (trace, observation, session, prompt). - - Parameters - ---------- - request : CreateCommentRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CreateCommentResponse - - Examples - -------- - import asyncio - - from langfuse import CreateCommentRequest - 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.comments.create( - request=CreateCommentRequest( - project_id="projectId", - object_type="objectType", - object_id="objectId", - content="content", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/comments", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CreateCommentResponse, _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( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - object_type: typing.Optional[str] = None, - object_id: typing.Optional[str] = None, - author_user_id: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> GetCommentsResponse: - """ - Get all comments - - 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 - - object_type : typing.Optional[str] - Filter comments by object type (trace, observation, session, prompt). - - object_id : typing.Optional[str] - Filter comments by object id. If objectType is not provided, an error will be thrown. - - author_user_id : typing.Optional[str] - Filter comments by author user id. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetCommentsResponse - - 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.comments.get() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/comments", - method="GET", - params={ - "page": page, - "limit": limit, - "objectType": object_type, - "objectId": object_id, - "authorUserId": author_user_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(GetCommentsResponse, _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_by_id( - self, - comment_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> Comment: - """ - Get a comment by id - - Parameters - ---------- - comment_id : str - The unique langfuse identifier of a comment - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Comment - - 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.comments.get_by_id( - comment_id="commentId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/comments/{jsonable_encoder(comment_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Comment, _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/comments/types/__init__.py b/langfuse/api/resources/comments/types/__init__.py deleted file mode 100644 index 13dc1d8d9..000000000 --- a/langfuse/api/resources/comments/types/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .create_comment_request import CreateCommentRequest -from .create_comment_response import CreateCommentResponse -from .get_comments_response import GetCommentsResponse - -__all__ = ["CreateCommentRequest", "CreateCommentResponse", "GetCommentsResponse"] diff --git a/langfuse/api/resources/comments/types/create_comment_request.py b/langfuse/api/resources/comments/types/create_comment_request.py deleted file mode 100644 index 3c35c64e2..000000000 --- a/langfuse/api/resources/comments/types/create_comment_request.py +++ /dev/null @@ -1,69 +0,0 @@ -# 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 CreateCommentRequest(pydantic_v1.BaseModel): - project_id: str = pydantic_v1.Field(alias="projectId") - """ - The id of the project to attach the comment to. - """ - - object_type: str = pydantic_v1.Field(alias="objectType") - """ - The type of the object to attach the comment to (trace, observation, session, prompt). - """ - - object_id: str = pydantic_v1.Field(alias="objectId") - """ - The id of the object to attach the comment to. If this does not reference a valid existing object, an error will be thrown. - """ - - content: str = pydantic_v1.Field() - """ - The content of the comment. May include markdown. Currently limited to 5000 characters. - """ - - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - """ - The id of the user who created the comment. - """ - - 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/comments/types/create_comment_response.py b/langfuse/api/resources/comments/types/create_comment_response.py deleted file mode 100644 index d7708f798..000000000 --- a/langfuse/api/resources/comments/types/create_comment_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 CreateCommentResponse(pydantic_v1.BaseModel): - id: str = pydantic_v1.Field() - """ - The id of the created object in Langfuse - """ - - 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/comments/types/get_comments_response.py b/langfuse/api/resources/comments/types/get_comments_response.py deleted file mode 100644 index 66a8b9527..000000000 --- a/langfuse/api/resources/comments/types/get_comments_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.comment import Comment -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class GetCommentsResponse(pydantic_v1.BaseModel): - data: typing.List[Comment] - meta: MetaResponse - - 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/commons/__init__.py b/langfuse/api/resources/commons/__init__.py deleted file mode 100644 index 77097ba49..000000000 --- a/langfuse/api/resources/commons/__init__.py +++ /dev/null @@ -1,111 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - BaseScore, - BaseScoreV1, - BooleanScore, - BooleanScoreV1, - CategoricalScore, - CategoricalScoreV1, - Comment, - CommentObjectType, - ConfigCategory, - CreateScoreValue, - Dataset, - DatasetItem, - DatasetRun, - DatasetRunItem, - DatasetRunWithItems, - DatasetStatus, - MapValue, - Model, - ModelPrice, - ModelUsageUnit, - NumericScore, - NumericScoreV1, - Observation, - ObservationLevel, - ObservationsView, - PricingTier, - PricingTierCondition, - PricingTierInput, - PricingTierOperator, - Score, - ScoreConfig, - ScoreDataType, - ScoreSource, - ScoreV1, - ScoreV1_Boolean, - ScoreV1_Categorical, - ScoreV1_Numeric, - Score_Boolean, - Score_Categorical, - Score_Numeric, - Session, - SessionWithTraces, - Trace, - TraceWithDetails, - TraceWithFullDetails, - Usage, -) -from .errors import ( - AccessDeniedError, - Error, - MethodNotAllowedError, - NotFoundError, - UnauthorizedError, -) - -__all__ = [ - "AccessDeniedError", - "BaseScore", - "BaseScoreV1", - "BooleanScore", - "BooleanScoreV1", - "CategoricalScore", - "CategoricalScoreV1", - "Comment", - "CommentObjectType", - "ConfigCategory", - "CreateScoreValue", - "Dataset", - "DatasetItem", - "DatasetRun", - "DatasetRunItem", - "DatasetRunWithItems", - "DatasetStatus", - "Error", - "MapValue", - "MethodNotAllowedError", - "Model", - "ModelPrice", - "ModelUsageUnit", - "NotFoundError", - "NumericScore", - "NumericScoreV1", - "Observation", - "ObservationLevel", - "ObservationsView", - "PricingTier", - "PricingTierCondition", - "PricingTierInput", - "PricingTierOperator", - "Score", - "ScoreConfig", - "ScoreDataType", - "ScoreSource", - "ScoreV1", - "ScoreV1_Boolean", - "ScoreV1_Categorical", - "ScoreV1_Numeric", - "Score_Boolean", - "Score_Categorical", - "Score_Numeric", - "Session", - "SessionWithTraces", - "Trace", - "TraceWithDetails", - "TraceWithFullDetails", - "UnauthorizedError", - "Usage", -] diff --git a/langfuse/api/resources/commons/errors/__init__.py b/langfuse/api/resources/commons/errors/__init__.py deleted file mode 100644 index 0aef2f92f..000000000 --- a/langfuse/api/resources/commons/errors/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .access_denied_error import AccessDeniedError -from .error import Error -from .method_not_allowed_error import MethodNotAllowedError -from .not_found_error import NotFoundError -from .unauthorized_error import UnauthorizedError - -__all__ = [ - "AccessDeniedError", - "Error", - "MethodNotAllowedError", - "NotFoundError", - "UnauthorizedError", -] diff --git a/langfuse/api/resources/commons/errors/access_denied_error.py b/langfuse/api/resources/commons/errors/access_denied_error.py deleted file mode 100644 index 9114ba9ac..000000000 --- a/langfuse/api/resources/commons/errors/access_denied_error.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ....core.api_error import ApiError - - -class AccessDeniedError(ApiError): - def __init__(self, body: typing.Any): - super().__init__(status_code=403, body=body) diff --git a/langfuse/api/resources/commons/errors/error.py b/langfuse/api/resources/commons/errors/error.py deleted file mode 100644 index 06020120c..000000000 --- a/langfuse/api/resources/commons/errors/error.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ....core.api_error import ApiError - - -class Error(ApiError): - def __init__(self, body: typing.Any): - super().__init__(status_code=400, body=body) diff --git a/langfuse/api/resources/commons/errors/method_not_allowed_error.py b/langfuse/api/resources/commons/errors/method_not_allowed_error.py deleted file mode 100644 index 32731a5c7..000000000 --- a/langfuse/api/resources/commons/errors/method_not_allowed_error.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ....core.api_error import ApiError - - -class MethodNotAllowedError(ApiError): - def __init__(self, body: typing.Any): - super().__init__(status_code=405, body=body) diff --git a/langfuse/api/resources/commons/errors/not_found_error.py b/langfuse/api/resources/commons/errors/not_found_error.py deleted file mode 100644 index 564ffca2c..000000000 --- a/langfuse/api/resources/commons/errors/not_found_error.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ....core.api_error import ApiError - - -class NotFoundError(ApiError): - def __init__(self, body: typing.Any): - super().__init__(status_code=404, body=body) diff --git a/langfuse/api/resources/commons/errors/unauthorized_error.py b/langfuse/api/resources/commons/errors/unauthorized_error.py deleted file mode 100644 index 2997f54f6..000000000 --- a/langfuse/api/resources/commons/errors/unauthorized_error.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ....core.api_error import ApiError - - -class UnauthorizedError(ApiError): - def __init__(self, body: typing.Any): - super().__init__(status_code=401, body=body) diff --git a/langfuse/api/resources/commons/types/__init__.py b/langfuse/api/resources/commons/types/__init__.py deleted file mode 100644 index ee7f5714b..000000000 --- a/langfuse/api/resources/commons/types/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .base_score import BaseScore -from .base_score_v_1 import BaseScoreV1 -from .boolean_score import BooleanScore -from .boolean_score_v_1 import BooleanScoreV1 -from .categorical_score import CategoricalScore -from .categorical_score_v_1 import CategoricalScoreV1 -from .comment import Comment -from .comment_object_type import CommentObjectType -from .config_category import ConfigCategory -from .create_score_value import CreateScoreValue -from .dataset import Dataset -from .dataset_item import DatasetItem -from .dataset_run import DatasetRun -from .dataset_run_item import DatasetRunItem -from .dataset_run_with_items import DatasetRunWithItems -from .dataset_status import DatasetStatus -from .map_value import MapValue -from .model import Model -from .model_price import ModelPrice -from .model_usage_unit import ModelUsageUnit -from .numeric_score import NumericScore -from .numeric_score_v_1 import NumericScoreV1 -from .observation import Observation -from .observation_level import ObservationLevel -from .observations_view import ObservationsView -from .pricing_tier import PricingTier -from .pricing_tier_condition import PricingTierCondition -from .pricing_tier_input import PricingTierInput -from .pricing_tier_operator import PricingTierOperator -from .score import Score, Score_Boolean, Score_Categorical, Score_Numeric -from .score_config import ScoreConfig -from .score_data_type import ScoreDataType -from .score_source import ScoreSource -from .score_v_1 import ScoreV1, ScoreV1_Boolean, ScoreV1_Categorical, ScoreV1_Numeric -from .session import Session -from .session_with_traces import SessionWithTraces -from .trace import Trace -from .trace_with_details import TraceWithDetails -from .trace_with_full_details import TraceWithFullDetails -from .usage import Usage - -__all__ = [ - "BaseScore", - "BaseScoreV1", - "BooleanScore", - "BooleanScoreV1", - "CategoricalScore", - "CategoricalScoreV1", - "Comment", - "CommentObjectType", - "ConfigCategory", - "CreateScoreValue", - "Dataset", - "DatasetItem", - "DatasetRun", - "DatasetRunItem", - "DatasetRunWithItems", - "DatasetStatus", - "MapValue", - "Model", - "ModelPrice", - "ModelUsageUnit", - "NumericScore", - "NumericScoreV1", - "Observation", - "ObservationLevel", - "ObservationsView", - "PricingTier", - "PricingTierCondition", - "PricingTierInput", - "PricingTierOperator", - "Score", - "ScoreConfig", - "ScoreDataType", - "ScoreSource", - "ScoreV1", - "ScoreV1_Boolean", - "ScoreV1_Categorical", - "ScoreV1_Numeric", - "Score_Boolean", - "Score_Categorical", - "Score_Numeric", - "Session", - "SessionWithTraces", - "Trace", - "TraceWithDetails", - "TraceWithFullDetails", - "Usage", -] diff --git a/langfuse/api/resources/commons/types/base_score.py b/langfuse/api/resources/commons/types/base_score.py deleted file mode 100644 index dd5449c83..000000000 --- a/langfuse/api/resources/commons/types/base_score.py +++ /dev/null @@ -1,79 +0,0 @@ -# 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 .score_source import ScoreSource - - -class BaseScore(pydantic_v1.BaseModel): - id: str - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - source: ScoreSource - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - """ - Reference a score config on a score. When set, config and score name must be equal and value must comply to optionally defined numerical range - """ - - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - """ - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. - """ - - environment: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The environment from which this score originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. - """ - - 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/commons/types/base_score_v_1.py b/langfuse/api/resources/commons/types/base_score_v_1.py deleted file mode 100644 index 478dcc6e6..000000000 --- a/langfuse/api/resources/commons/types/base_score_v_1.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 .score_source import ScoreSource - - -class BaseScoreV1(pydantic_v1.BaseModel): - id: str - trace_id: str = pydantic_v1.Field(alias="traceId") - name: str - source: ScoreSource - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - """ - Reference a score config on a score. When set, config and score name must be equal and value must comply to optionally defined numerical range - """ - - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - """ - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. - """ - - environment: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The environment from which this score originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. - """ - - 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/commons/types/boolean_score.py b/langfuse/api/resources/commons/types/boolean_score.py deleted file mode 100644 index d838b7db9..000000000 --- a/langfuse/api/resources/commons/types/boolean_score.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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 .base_score import BaseScore - - -class BooleanScore(BaseScore): - value: float = pydantic_v1.Field() - """ - The numeric value of the score. Equals 1 for "True" and 0 for "False" - """ - - string_value: str = pydantic_v1.Field(alias="stringValue") - """ - The string representation of the score value. Is inferred from the numeric value and equals "True" or "False" - """ - - 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/commons/types/boolean_score_v_1.py b/langfuse/api/resources/commons/types/boolean_score_v_1.py deleted file mode 100644 index 9f8e8935f..000000000 --- a/langfuse/api/resources/commons/types/boolean_score_v_1.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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 .base_score_v_1 import BaseScoreV1 - - -class BooleanScoreV1(BaseScoreV1): - value: float = pydantic_v1.Field() - """ - The numeric value of the score. Equals 1 for "True" and 0 for "False" - """ - - string_value: str = pydantic_v1.Field(alias="stringValue") - """ - The string representation of the score value. Is inferred from the numeric value and equals "True" or "False" - """ - - 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/commons/types/categorical_score.py b/langfuse/api/resources/commons/types/categorical_score.py deleted file mode 100644 index 363ed03ff..000000000 --- a/langfuse/api/resources/commons/types/categorical_score.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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 .base_score import BaseScore - - -class CategoricalScore(BaseScore): - value: float = pydantic_v1.Field() - """ - Represents the numeric category mapping of the stringValue. If no config is linked, defaults to 0. - """ - - string_value: str = pydantic_v1.Field(alias="stringValue") - """ - The string representation of the score value. If no config is linked, can be any string. Otherwise, must map to a config category - """ - - 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/commons/types/categorical_score_v_1.py b/langfuse/api/resources/commons/types/categorical_score_v_1.py deleted file mode 100644 index 2aa42d586..000000000 --- a/langfuse/api/resources/commons/types/categorical_score_v_1.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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 .base_score_v_1 import BaseScoreV1 - - -class CategoricalScoreV1(BaseScoreV1): - value: float = pydantic_v1.Field() - """ - Represents the numeric category mapping of the stringValue. If no config is linked, defaults to 0. - """ - - string_value: str = pydantic_v1.Field(alias="stringValue") - """ - The string representation of the score value. If no config is linked, can be any string. Otherwise, must map to a config category - """ - - 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/commons/types/comment.py b/langfuse/api/resources/commons/types/comment.py deleted file mode 100644 index 4d8b1916a..000000000 --- a/langfuse/api/resources/commons/types/comment.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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 .comment_object_type import CommentObjectType - - -class Comment(pydantic_v1.BaseModel): - id: str - project_id: str = pydantic_v1.Field(alias="projectId") - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - object_type: CommentObjectType = pydantic_v1.Field(alias="objectType") - object_id: str = pydantic_v1.Field(alias="objectId") - content: str - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - - 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/commons/types/config_category.py b/langfuse/api/resources/commons/types/config_category.py deleted file mode 100644 index b1cbde9f2..000000000 --- a/langfuse/api/resources/commons/types/config_category.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 ConfigCategory(pydantic_v1.BaseModel): - value: float - label: 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/commons/types/dataset.py b/langfuse/api/resources/commons/types/dataset.py deleted file mode 100644 index 116bff135..000000000 --- a/langfuse/api/resources/commons/types/dataset.py +++ /dev/null @@ -1,64 +0,0 @@ -# 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 Dataset(pydantic_v1.BaseModel): - id: str - name: str - description: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - input_schema: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="inputSchema", default=None - ) - """ - JSON Schema for validating dataset item inputs - """ - - expected_output_schema: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="expectedOutputSchema", default=None - ) - """ - JSON Schema for validating dataset item expected outputs - """ - - project_id: str = pydantic_v1.Field(alias="projectId") - 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/commons/types/dataset_item.py b/langfuse/api/resources/commons/types/dataset_item.py deleted file mode 100644 index dd5f85e78..000000000 --- a/langfuse/api/resources/commons/types/dataset_item.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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 .dataset_status import DatasetStatus - - -class DatasetItem(pydantic_v1.BaseModel): - id: str - status: DatasetStatus - input: typing.Optional[typing.Any] = None - expected_output: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="expectedOutput", default=None - ) - metadata: typing.Optional[typing.Any] = None - source_trace_id: typing.Optional[str] = pydantic_v1.Field( - alias="sourceTraceId", default=None - ) - source_observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="sourceObservationId", default=None - ) - dataset_id: str = pydantic_v1.Field(alias="datasetId") - dataset_name: str = pydantic_v1.Field(alias="datasetName") - 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/commons/types/dataset_run.py b/langfuse/api/resources/commons/types/dataset_run.py deleted file mode 100644 index 74b1a2ac8..000000000 --- a/langfuse/api/resources/commons/types/dataset_run.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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 DatasetRun(pydantic_v1.BaseModel): - id: str = pydantic_v1.Field() - """ - Unique identifier of the dataset run - """ - - name: str = pydantic_v1.Field() - """ - Name of the dataset run - """ - - description: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Description of the run - """ - - metadata: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - Metadata of the dataset run - """ - - dataset_id: str = pydantic_v1.Field(alias="datasetId") - """ - Id of the associated dataset - """ - - dataset_name: str = pydantic_v1.Field(alias="datasetName") - """ - Name of the associated dataset - """ - - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - """ - The date and time when the dataset run was created - """ - - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - """ - The date and time when the dataset run was last updated - """ - - 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/commons/types/dataset_run_item.py b/langfuse/api/resources/commons/types/dataset_run_item.py deleted file mode 100644 index f1b3af163..000000000 --- a/langfuse/api/resources/commons/types/dataset_run_item.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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 DatasetRunItem(pydantic_v1.BaseModel): - id: str - dataset_run_id: str = pydantic_v1.Field(alias="datasetRunId") - dataset_run_name: str = pydantic_v1.Field(alias="datasetRunName") - dataset_item_id: str = pydantic_v1.Field(alias="datasetItemId") - trace_id: str = pydantic_v1.Field(alias="traceId") - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", 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/commons/types/dataset_run_with_items.py b/langfuse/api/resources/commons/types/dataset_run_with_items.py deleted file mode 100644 index 647d2c553..000000000 --- a/langfuse/api/resources/commons/types/dataset_run_with_items.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 .dataset_run import DatasetRun -from .dataset_run_item import DatasetRunItem - - -class DatasetRunWithItems(DatasetRun): - dataset_run_items: typing.List[DatasetRunItem] = pydantic_v1.Field( - alias="datasetRunItems" - ) - - 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/commons/types/model_price.py b/langfuse/api/resources/commons/types/model_price.py deleted file mode 100644 index 8882004e7..000000000 --- a/langfuse/api/resources/commons/types/model_price.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 ModelPrice(pydantic_v1.BaseModel): - price: float - - 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/commons/types/numeric_score.py b/langfuse/api/resources/commons/types/numeric_score.py deleted file mode 100644 index d7f860cd5..000000000 --- a/langfuse/api/resources/commons/types/numeric_score.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 .base_score import BaseScore - - -class NumericScore(BaseScore): - value: float = pydantic_v1.Field() - """ - The numeric value of the score - """ - - 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/commons/types/numeric_score_v_1.py b/langfuse/api/resources/commons/types/numeric_score_v_1.py deleted file mode 100644 index 773d84b46..000000000 --- a/langfuse/api/resources/commons/types/numeric_score_v_1.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 .base_score_v_1 import BaseScoreV1 - - -class NumericScoreV1(BaseScoreV1): - value: float = pydantic_v1.Field() - """ - The numeric value of the score - """ - - 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/commons/types/observation.py b/langfuse/api/resources/commons/types/observation.py deleted file mode 100644 index b821476f9..000000000 --- a/langfuse/api/resources/commons/types/observation.py +++ /dev/null @@ -1,164 +0,0 @@ -# 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 .map_value import MapValue -from .observation_level import ObservationLevel -from .usage import Usage - - -class Observation(pydantic_v1.BaseModel): - id: str = pydantic_v1.Field() - """ - The unique identifier of the observation - """ - - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - """ - The trace ID associated with the observation - """ - - type: str = pydantic_v1.Field() - """ - The type of the observation - """ - - name: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The name of the observation - """ - - start_time: dt.datetime = pydantic_v1.Field(alias="startTime") - """ - The start time of the observation - """ - - end_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="endTime", default=None - ) - """ - The end time of the observation. - """ - - completion_start_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="completionStartTime", default=None - ) - """ - The completion start time of the observation - """ - - model: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The model used for the observation - """ - - model_parameters: typing.Optional[typing.Dict[str, MapValue]] = pydantic_v1.Field( - alias="modelParameters", default=None - ) - """ - The parameters of the model used for the observation - """ - - input: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - The input data of the observation - """ - - version: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The version of the observation - """ - - metadata: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - Additional metadata of the observation - """ - - output: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - The output data of the observation - """ - - usage: typing.Optional[Usage] = pydantic_v1.Field(default=None) - """ - (Deprecated. Use usageDetails and costDetails instead.) The usage data of the observation - """ - - level: ObservationLevel = pydantic_v1.Field() - """ - The level of the observation - """ - - status_message: typing.Optional[str] = pydantic_v1.Field( - alias="statusMessage", default=None - ) - """ - The status message of the observation - """ - - parent_observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="parentObservationId", default=None - ) - """ - The parent observation ID - """ - - prompt_id: typing.Optional[str] = pydantic_v1.Field(alias="promptId", default=None) - """ - The prompt ID associated with the observation - """ - - usage_details: typing.Optional[typing.Dict[str, int]] = pydantic_v1.Field( - alias="usageDetails", default=None - ) - """ - The usage details of the observation. Key is the name of the usage metric, value is the number of units consumed. The total key is the sum of all (non-total) usage metrics or the total value ingested. - """ - - cost_details: typing.Optional[typing.Dict[str, float]] = pydantic_v1.Field( - alias="costDetails", default=None - ) - """ - The cost details of the observation. Key is the name of the cost metric, value is the cost in USD. The total key is the sum of all (non-total) cost metrics or the total value ingested. - """ - - environment: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The environment from which this observation originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. - """ - - 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/commons/types/observations_view.py b/langfuse/api/resources/commons/types/observations_view.py deleted file mode 100644 index e011fa32b..000000000 --- a/langfuse/api/resources/commons/types/observations_view.py +++ /dev/null @@ -1,116 +0,0 @@ -# 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 .observation import Observation - - -class ObservationsView(Observation): - prompt_name: typing.Optional[str] = pydantic_v1.Field( - alias="promptName", default=None - ) - """ - The name of the prompt associated with the observation - """ - - prompt_version: typing.Optional[int] = pydantic_v1.Field( - alias="promptVersion", default=None - ) - """ - The version of the prompt associated with the observation - """ - - model_id: typing.Optional[str] = pydantic_v1.Field(alias="modelId", default=None) - """ - The unique identifier of the model - """ - - input_price: typing.Optional[float] = pydantic_v1.Field( - alias="inputPrice", default=None - ) - """ - The price of the input in USD - """ - - output_price: typing.Optional[float] = pydantic_v1.Field( - alias="outputPrice", default=None - ) - """ - The price of the output in USD. - """ - - total_price: typing.Optional[float] = pydantic_v1.Field( - alias="totalPrice", default=None - ) - """ - The total price in USD. - """ - - calculated_input_cost: typing.Optional[float] = pydantic_v1.Field( - alias="calculatedInputCost", default=None - ) - """ - (Deprecated. Use usageDetails and costDetails instead.) The calculated cost of the input in USD - """ - - calculated_output_cost: typing.Optional[float] = pydantic_v1.Field( - alias="calculatedOutputCost", default=None - ) - """ - (Deprecated. Use usageDetails and costDetails instead.) The calculated cost of the output in USD - """ - - calculated_total_cost: typing.Optional[float] = pydantic_v1.Field( - alias="calculatedTotalCost", default=None - ) - """ - (Deprecated. Use usageDetails and costDetails instead.) The calculated total cost in USD - """ - - latency: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - The latency in seconds. - """ - - time_to_first_token: typing.Optional[float] = pydantic_v1.Field( - alias="timeToFirstToken", default=None - ) - """ - The time to the first token in seconds - """ - - 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/commons/types/score.py b/langfuse/api/resources/commons/types/score.py deleted file mode 100644 index f0b866067..000000000 --- a/langfuse/api/resources/commons/types/score.py +++ /dev/null @@ -1,207 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -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 .score_source import ScoreSource - - -class Score_Numeric(pydantic_v1.BaseModel): - value: float - id: str - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - source: ScoreSource - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["NUMERIC"] = pydantic_v1.Field( - alias="dataType", default="NUMERIC" - ) - - 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} - - -class Score_Categorical(pydantic_v1.BaseModel): - value: float - string_value: str = pydantic_v1.Field(alias="stringValue") - id: str - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - source: ScoreSource - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["CATEGORICAL"] = pydantic_v1.Field( - alias="dataType", default="CATEGORICAL" - ) - - 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} - - -class Score_Boolean(pydantic_v1.BaseModel): - value: float - string_value: str = pydantic_v1.Field(alias="stringValue") - id: str - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - source: ScoreSource - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["BOOLEAN"] = pydantic_v1.Field( - alias="dataType", default="BOOLEAN" - ) - - 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} - - -Score = typing.Union[Score_Numeric, Score_Categorical, Score_Boolean] diff --git a/langfuse/api/resources/commons/types/score_config.py b/langfuse/api/resources/commons/types/score_config.py deleted file mode 100644 index 4a7b30e0e..000000000 --- a/langfuse/api/resources/commons/types/score_config.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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 .config_category import ConfigCategory -from .score_data_type import ScoreDataType - - -class ScoreConfig(pydantic_v1.BaseModel): - """ - Configuration for a score - """ - - id: str - name: str - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - project_id: str = pydantic_v1.Field(alias="projectId") - data_type: ScoreDataType = pydantic_v1.Field(alias="dataType") - is_archived: bool = pydantic_v1.Field(alias="isArchived") - """ - Whether the score config is archived. Defaults to false - """ - - min_value: typing.Optional[float] = pydantic_v1.Field( - alias="minValue", default=None - ) - """ - Sets minimum value for numerical scores. If not set, the minimum value defaults to -∞ - """ - - max_value: typing.Optional[float] = pydantic_v1.Field( - alias="maxValue", default=None - ) - """ - Sets maximum value for numerical scores. If not set, the maximum value defaults to +∞ - """ - - categories: typing.Optional[typing.List[ConfigCategory]] = pydantic_v1.Field( - default=None - ) - """ - Configures custom categories for categorical scores - """ - - description: typing.Optional[str] = None - - 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/commons/types/score_v_1.py b/langfuse/api/resources/commons/types/score_v_1.py deleted file mode 100644 index 191e0d96f..000000000 --- a/langfuse/api/resources/commons/types/score_v_1.py +++ /dev/null @@ -1,189 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -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 .score_source import ScoreSource - - -class ScoreV1_Numeric(pydantic_v1.BaseModel): - value: float - id: str - trace_id: str = pydantic_v1.Field(alias="traceId") - name: str - source: ScoreSource - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["NUMERIC"] = pydantic_v1.Field( - alias="dataType", default="NUMERIC" - ) - - 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} - - -class ScoreV1_Categorical(pydantic_v1.BaseModel): - value: float - string_value: str = pydantic_v1.Field(alias="stringValue") - id: str - trace_id: str = pydantic_v1.Field(alias="traceId") - name: str - source: ScoreSource - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["CATEGORICAL"] = pydantic_v1.Field( - alias="dataType", default="CATEGORICAL" - ) - - 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} - - -class ScoreV1_Boolean(pydantic_v1.BaseModel): - value: float - string_value: str = pydantic_v1.Field(alias="stringValue") - id: str - trace_id: str = pydantic_v1.Field(alias="traceId") - name: str - source: ScoreSource - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["BOOLEAN"] = pydantic_v1.Field( - alias="dataType", default="BOOLEAN" - ) - - 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} - - -ScoreV1 = typing.Union[ScoreV1_Numeric, ScoreV1_Categorical, ScoreV1_Boolean] diff --git a/langfuse/api/resources/commons/types/session.py b/langfuse/api/resources/commons/types/session.py deleted file mode 100644 index 46a0a6b96..000000000 --- a/langfuse/api/resources/commons/types/session.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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 Session(pydantic_v1.BaseModel): - id: str - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - project_id: str = pydantic_v1.Field(alias="projectId") - environment: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The environment from which this session originated. - """ - - 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/commons/types/session_with_traces.py b/langfuse/api/resources/commons/types/session_with_traces.py deleted file mode 100644 index b5465daa9..000000000 --- a/langfuse/api/resources/commons/types/session_with_traces.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .session import Session -from .trace import Trace - - -class SessionWithTraces(Session): - traces: typing.List[Trace] - - 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/commons/types/trace.py b/langfuse/api/resources/commons/types/trace.py deleted file mode 100644 index d977ed3d7..000000000 --- a/langfuse/api/resources/commons/types/trace.py +++ /dev/null @@ -1,109 +0,0 @@ -# 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 Trace(pydantic_v1.BaseModel): - id: str = pydantic_v1.Field() - """ - The unique identifier of a trace - """ - - timestamp: dt.datetime = pydantic_v1.Field() - """ - The timestamp when the trace was created - """ - - name: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The name of the trace - """ - - input: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - The input data of the trace. Can be any JSON. - """ - - output: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - The output data of the trace. Can be any JSON. - """ - - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - """ - The session identifier associated with the trace - """ - - release: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The release version of the application when the trace was created - """ - - version: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The version of the trace - """ - - user_id: typing.Optional[str] = pydantic_v1.Field(alias="userId", default=None) - """ - The user identifier associated with the trace - """ - - metadata: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - The metadata associated with the trace. Can be any JSON. - """ - - tags: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - The tags associated with the trace. Can be an array of strings or null. - """ - - public: typing.Optional[bool] = pydantic_v1.Field(default=None) - """ - Public traces are accessible via url without login - """ - - environment: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The environment from which this trace originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. - """ - - 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/commons/types/trace_with_details.py b/langfuse/api/resources/commons/types/trace_with_details.py deleted file mode 100644 index 5ffe6f218..000000000 --- a/langfuse/api/resources/commons/types/trace_with_details.py +++ /dev/null @@ -1,68 +0,0 @@ -# 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 .trace import Trace - - -class TraceWithDetails(Trace): - html_path: str = pydantic_v1.Field(alias="htmlPath") - """ - Path of trace in Langfuse UI - """ - - latency: float = pydantic_v1.Field() - """ - Latency of trace in seconds - """ - - total_cost: float = pydantic_v1.Field(alias="totalCost") - """ - Cost of trace in USD - """ - - observations: typing.List[str] = pydantic_v1.Field() - """ - List of observation ids - """ - - scores: typing.List[str] = pydantic_v1.Field() - """ - List of score ids - """ - - 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/commons/types/trace_with_full_details.py b/langfuse/api/resources/commons/types/trace_with_full_details.py deleted file mode 100644 index 2c6a99402..000000000 --- a/langfuse/api/resources/commons/types/trace_with_full_details.py +++ /dev/null @@ -1,70 +0,0 @@ -# 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 .observations_view import ObservationsView -from .score_v_1 import ScoreV1 -from .trace import Trace - - -class TraceWithFullDetails(Trace): - html_path: str = pydantic_v1.Field(alias="htmlPath") - """ - Path of trace in Langfuse UI - """ - - latency: float = pydantic_v1.Field() - """ - Latency of trace in seconds - """ - - total_cost: float = pydantic_v1.Field(alias="totalCost") - """ - Cost of trace in USD - """ - - observations: typing.List[ObservationsView] = pydantic_v1.Field() - """ - List of observations - """ - - scores: typing.List[ScoreV1] = pydantic_v1.Field() - """ - List of scores - """ - - 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/commons/types/usage.py b/langfuse/api/resources/commons/types/usage.py deleted file mode 100644 index c38330494..000000000 --- a/langfuse/api/resources/commons/types/usage.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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 .model_usage_unit import ModelUsageUnit - - -class Usage(pydantic_v1.BaseModel): - """ - (Deprecated. Use usageDetails and costDetails instead.) Standard interface for usage and cost - """ - - input: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - Number of input units (e.g. tokens) - """ - - output: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - Number of output units (e.g. tokens) - """ - - total: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - Defaults to input+output if not set - """ - - unit: typing.Optional[ModelUsageUnit] = None - input_cost: typing.Optional[float] = pydantic_v1.Field( - alias="inputCost", default=None - ) - """ - USD input cost - """ - - output_cost: typing.Optional[float] = pydantic_v1.Field( - alias="outputCost", default=None - ) - """ - USD output cost - """ - - total_cost: typing.Optional[float] = pydantic_v1.Field( - alias="totalCost", default=None - ) - """ - USD total cost, defaults to input+output - """ - - 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/dataset_items/__init__.py b/langfuse/api/resources/dataset_items/__init__.py deleted file mode 100644 index 06d2ae527..000000000 --- a/langfuse/api/resources/dataset_items/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - CreateDatasetItemRequest, - DeleteDatasetItemResponse, - PaginatedDatasetItems, -) - -__all__ = [ - "CreateDatasetItemRequest", - "DeleteDatasetItemResponse", - "PaginatedDatasetItems", -] diff --git a/langfuse/api/resources/dataset_items/client.py b/langfuse/api/resources/dataset_items/client.py deleted file mode 100644 index 8ece3a790..000000000 --- a/langfuse/api/resources/dataset_items/client.py +++ /dev/null @@ -1,640 +0,0 @@ -# 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 ..commons.types.dataset_item import DatasetItem -from .types.create_dataset_item_request import CreateDatasetItemRequest -from .types.delete_dataset_item_response import DeleteDatasetItemResponse -from .types.paginated_dataset_items import PaginatedDatasetItems - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class DatasetItemsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def create( - self, - *, - request: CreateDatasetItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> DatasetItem: - """ - Create a dataset item - - Parameters - ---------- - request : CreateDatasetItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetItem - - Examples - -------- - from langfuse import CreateDatasetItemRequest - 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.dataset_items.create( - request=CreateDatasetItemRequest( - dataset_name="datasetName", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/dataset-items", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetItem, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> DatasetItem: - """ - Get a dataset item - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetItem - - 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.dataset_items.get( - id="id", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/dataset-items/{jsonable_encoder(id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetItem, _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 list( - self, - *, - dataset_name: typing.Optional[str] = None, - source_trace_id: typing.Optional[str] = None, - source_observation_id: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasetItems: - """ - Get dataset items - - Parameters - ---------- - dataset_name : typing.Optional[str] - - source_trace_id : typing.Optional[str] - - source_observation_id : typing.Optional[str] - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasetItems - - 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.dataset_items.list() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/dataset-items", - method="GET", - params={ - "datasetName": dataset_name, - "sourceTraceId": source_trace_id, - "sourceObservationId": source_observation_id, - "page": page, - "limit": limit, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedDatasetItems, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> DeleteDatasetItemResponse: - """ - Delete a dataset item and all its run items. This action is irreversible. - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteDatasetItemResponse - - 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.dataset_items.delete( - id="id", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/dataset-items/{jsonable_encoder(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteDatasetItemResponse, _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 AsyncDatasetItemsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def create( - self, - *, - request: CreateDatasetItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> DatasetItem: - """ - Create a dataset item - - Parameters - ---------- - request : CreateDatasetItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetItem - - Examples - -------- - import asyncio - - from langfuse import CreateDatasetItemRequest - 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.dataset_items.create( - request=CreateDatasetItemRequest( - dataset_name="datasetName", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/dataset-items", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetItem, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> DatasetItem: - """ - Get a dataset item - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetItem - - 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.dataset_items.get( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/dataset-items/{jsonable_encoder(id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetItem, _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 list( - self, - *, - dataset_name: typing.Optional[str] = None, - source_trace_id: typing.Optional[str] = None, - source_observation_id: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasetItems: - """ - Get dataset items - - Parameters - ---------- - dataset_name : typing.Optional[str] - - source_trace_id : typing.Optional[str] - - source_observation_id : typing.Optional[str] - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasetItems - - 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.dataset_items.list() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/dataset-items", - method="GET", - params={ - "datasetName": dataset_name, - "sourceTraceId": source_trace_id, - "sourceObservationId": source_observation_id, - "page": page, - "limit": limit, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedDatasetItems, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> DeleteDatasetItemResponse: - """ - Delete a dataset item and all its run items. This action is irreversible. - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteDatasetItemResponse - - 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.dataset_items.delete( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/dataset-items/{jsonable_encoder(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteDatasetItemResponse, _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/dataset_items/types/__init__.py b/langfuse/api/resources/dataset_items/types/__init__.py deleted file mode 100644 index 214adce0e..000000000 --- a/langfuse/api/resources/dataset_items/types/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .create_dataset_item_request import CreateDatasetItemRequest -from .delete_dataset_item_response import DeleteDatasetItemResponse -from .paginated_dataset_items import PaginatedDatasetItems - -__all__ = [ - "CreateDatasetItemRequest", - "DeleteDatasetItemResponse", - "PaginatedDatasetItems", -] diff --git a/langfuse/api/resources/dataset_items/types/create_dataset_item_request.py b/langfuse/api/resources/dataset_items/types/create_dataset_item_request.py deleted file mode 100644 index 111f6819a..000000000 --- a/langfuse/api/resources/dataset_items/types/create_dataset_item_request.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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 ...commons.types.dataset_status import DatasetStatus - - -class CreateDatasetItemRequest(pydantic_v1.BaseModel): - dataset_name: str = pydantic_v1.Field(alias="datasetName") - input: typing.Optional[typing.Any] = None - expected_output: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="expectedOutput", default=None - ) - metadata: typing.Optional[typing.Any] = None - source_trace_id: typing.Optional[str] = pydantic_v1.Field( - alias="sourceTraceId", default=None - ) - source_observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="sourceObservationId", default=None - ) - id: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Dataset items are upserted on their id. Id needs to be unique (project-level) and cannot be reused across datasets. - """ - - status: typing.Optional[DatasetStatus] = pydantic_v1.Field(default=None) - """ - Defaults to ACTIVE for newly created items - """ - - 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/dataset_items/types/delete_dataset_item_response.py b/langfuse/api/resources/dataset_items/types/delete_dataset_item_response.py deleted file mode 100644 index 4d700ff75..000000000 --- a/langfuse/api/resources/dataset_items/types/delete_dataset_item_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 DeleteDatasetItemResponse(pydantic_v1.BaseModel): - message: str = pydantic_v1.Field() - """ - Success message after deletion - """ - - 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/dataset_items/types/paginated_dataset_items.py b/langfuse/api/resources/dataset_items/types/paginated_dataset_items.py deleted file mode 100644 index 8592ba80f..000000000 --- a/langfuse/api/resources/dataset_items/types/paginated_dataset_items.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.dataset_item import DatasetItem -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class PaginatedDatasetItems(pydantic_v1.BaseModel): - data: typing.List[DatasetItem] - meta: MetaResponse - - 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/dataset_run_items/__init__.py b/langfuse/api/resources/dataset_run_items/__init__.py deleted file mode 100644 index d522a3129..000000000 --- a/langfuse/api/resources/dataset_run_items/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import CreateDatasetRunItemRequest, PaginatedDatasetRunItems - -__all__ = ["CreateDatasetRunItemRequest", "PaginatedDatasetRunItems"] diff --git a/langfuse/api/resources/dataset_run_items/client.py b/langfuse/api/resources/dataset_run_items/client.py deleted file mode 100644 index 3664fde96..000000000 --- a/langfuse/api/resources/dataset_run_items/client.py +++ /dev/null @@ -1,366 +0,0 @@ -# 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.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 ..commons.types.dataset_run_item import DatasetRunItem -from .types.create_dataset_run_item_request import CreateDatasetRunItemRequest -from .types.paginated_dataset_run_items import PaginatedDatasetRunItems - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class DatasetRunItemsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def create( - self, - *, - request: CreateDatasetRunItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> DatasetRunItem: - """ - Create a dataset run item - - Parameters - ---------- - request : CreateDatasetRunItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetRunItem - - Examples - -------- - from langfuse import CreateDatasetRunItemRequest - 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.dataset_run_items.create( - request=CreateDatasetRunItemRequest( - run_name="runName", - dataset_item_id="datasetItemId", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/dataset-run-items", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetRunItem, _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 list( - self, - *, - dataset_id: str, - run_name: str, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasetRunItems: - """ - List dataset run items - - Parameters - ---------- - dataset_id : str - - run_name : str - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasetRunItems - - 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.dataset_run_items.list( - dataset_id="datasetId", - run_name="runName", - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/dataset-run-items", - method="GET", - params={ - "datasetId": dataset_id, - "runName": run_name, - "page": page, - "limit": limit, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedDatasetRunItems, _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 AsyncDatasetRunItemsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def create( - self, - *, - request: CreateDatasetRunItemRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> DatasetRunItem: - """ - Create a dataset run item - - Parameters - ---------- - request : CreateDatasetRunItemRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetRunItem - - Examples - -------- - import asyncio - - from langfuse import CreateDatasetRunItemRequest - 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.dataset_run_items.create( - request=CreateDatasetRunItemRequest( - run_name="runName", - dataset_item_id="datasetItemId", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/dataset-run-items", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetRunItem, _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 list( - self, - *, - dataset_id: str, - run_name: str, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasetRunItems: - """ - List dataset run items - - Parameters - ---------- - dataset_id : str - - run_name : str - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasetRunItems - - 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.dataset_run_items.list( - dataset_id="datasetId", - run_name="runName", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/dataset-run-items", - method="GET", - params={ - "datasetId": dataset_id, - "runName": run_name, - "page": page, - "limit": limit, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedDatasetRunItems, _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/dataset_run_items/types/__init__.py b/langfuse/api/resources/dataset_run_items/types/__init__.py deleted file mode 100644 index e48e72c27..000000000 --- a/langfuse/api/resources/dataset_run_items/types/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .create_dataset_run_item_request import CreateDatasetRunItemRequest -from .paginated_dataset_run_items import PaginatedDatasetRunItems - -__all__ = ["CreateDatasetRunItemRequest", "PaginatedDatasetRunItems"] diff --git a/langfuse/api/resources/dataset_run_items/types/create_dataset_run_item_request.py b/langfuse/api/resources/dataset_run_items/types/create_dataset_run_item_request.py deleted file mode 100644 index 0a643b835..000000000 --- a/langfuse/api/resources/dataset_run_items/types/create_dataset_run_item_request.py +++ /dev/null @@ -1,64 +0,0 @@ -# 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 CreateDatasetRunItemRequest(pydantic_v1.BaseModel): - run_name: str = pydantic_v1.Field(alias="runName") - run_description: typing.Optional[str] = pydantic_v1.Field( - alias="runDescription", default=None - ) - """ - Description of the run. If run exists, description will be updated. - """ - - metadata: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - Metadata of the dataset run, updates run if run already exists - """ - - dataset_item_id: str = pydantic_v1.Field(alias="datasetItemId") - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - """ - traceId should always be provided. For compatibility with older SDK versions it can also be inferred from the provided observationId. - """ - - 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/dataset_run_items/types/paginated_dataset_run_items.py b/langfuse/api/resources/dataset_run_items/types/paginated_dataset_run_items.py deleted file mode 100644 index c1611bae0..000000000 --- a/langfuse/api/resources/dataset_run_items/types/paginated_dataset_run_items.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.dataset_run_item import DatasetRunItem -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class PaginatedDatasetRunItems(pydantic_v1.BaseModel): - data: typing.List[DatasetRunItem] - meta: MetaResponse - - 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/datasets/__init__.py b/langfuse/api/resources/datasets/__init__.py deleted file mode 100644 index dd30a359d..000000000 --- a/langfuse/api/resources/datasets/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - CreateDatasetRequest, - DeleteDatasetRunResponse, - PaginatedDatasetRuns, - PaginatedDatasets, -) - -__all__ = [ - "CreateDatasetRequest", - "DeleteDatasetRunResponse", - "PaginatedDatasetRuns", - "PaginatedDatasets", -] diff --git a/langfuse/api/resources/datasets/client.py b/langfuse/api/resources/datasets/client.py deleted file mode 100644 index aff7293a0..000000000 --- a/langfuse/api/resources/datasets/client.py +++ /dev/null @@ -1,942 +0,0 @@ -# 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 ..commons.types.dataset import Dataset -from ..commons.types.dataset_run_with_items import DatasetRunWithItems -from .types.create_dataset_request import CreateDatasetRequest -from .types.delete_dataset_run_response import DeleteDatasetRunResponse -from .types.paginated_dataset_runs import PaginatedDatasetRuns -from .types.paginated_datasets import PaginatedDatasets - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class DatasetsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasets: - """ - Get all datasets - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasets - - 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.datasets.list() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/v2/datasets", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedDatasets, _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( - self, - dataset_name: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> Dataset: - """ - Get a dataset - - Parameters - ---------- - dataset_name : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Dataset - - 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.datasets.get( - dataset_name="datasetName", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/v2/datasets/{jsonable_encoder(dataset_name)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Dataset, _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 create( - self, - *, - request: CreateDatasetRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> Dataset: - """ - Create a dataset - - Parameters - ---------- - request : CreateDatasetRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Dataset - - Examples - -------- - from langfuse import CreateDatasetRequest - 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.datasets.create( - request=CreateDatasetRequest( - name="name", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/v2/datasets", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Dataset, _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_run( - self, - dataset_name: str, - run_name: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DatasetRunWithItems: - """ - Get a dataset run and its items - - Parameters - ---------- - dataset_name : str - - run_name : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetRunWithItems - - 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.datasets.get_run( - dataset_name="datasetName", - run_name="runName", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetRunWithItems, _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_run( - self, - dataset_name: str, - run_name: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteDatasetRunResponse: - """ - Delete a dataset run and all its run items. This action is irreversible. - - Parameters - ---------- - dataset_name : str - - run_name : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteDatasetRunResponse - - 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.datasets.delete_run( - dataset_name="datasetName", - run_name="runName", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteDatasetRunResponse, _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_runs( - self, - dataset_name: str, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasetRuns: - """ - Get dataset runs - - Parameters - ---------- - dataset_name : str - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasetRuns - - 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.datasets.get_runs( - dataset_name="datasetName", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedDatasetRuns, _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 AsyncDatasetsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasets: - """ - Get all datasets - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasets - - 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.datasets.list() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/v2/datasets", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedDatasets, _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( - self, - dataset_name: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> Dataset: - """ - Get a dataset - - Parameters - ---------- - dataset_name : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Dataset - - 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.datasets.get( - dataset_name="datasetName", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/v2/datasets/{jsonable_encoder(dataset_name)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Dataset, _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 create( - self, - *, - request: CreateDatasetRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> Dataset: - """ - Create a dataset - - Parameters - ---------- - request : CreateDatasetRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Dataset - - Examples - -------- - import asyncio - - from langfuse import CreateDatasetRequest - 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.datasets.create( - request=CreateDatasetRequest( - name="name", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/v2/datasets", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Dataset, _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_run( - self, - dataset_name: str, - run_name: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DatasetRunWithItems: - """ - Get a dataset run and its items - - Parameters - ---------- - dataset_name : str - - run_name : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DatasetRunWithItems - - 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.datasets.get_run( - dataset_name="datasetName", - run_name="runName", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DatasetRunWithItems, _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_run( - self, - dataset_name: str, - run_name: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteDatasetRunResponse: - """ - Delete a dataset run and all its run items. This action is irreversible. - - Parameters - ---------- - dataset_name : str - - run_name : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteDatasetRunResponse - - 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.datasets.delete_run( - dataset_name="datasetName", - run_name="runName", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs/{jsonable_encoder(run_name)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - DeleteDatasetRunResponse, _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_runs( - self, - dataset_name: str, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedDatasetRuns: - """ - Get dataset runs - - Parameters - ---------- - dataset_name : str - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedDatasetRuns - - 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.datasets.get_runs( - dataset_name="datasetName", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/datasets/{jsonable_encoder(dataset_name)}/runs", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedDatasetRuns, _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/datasets/types/__init__.py b/langfuse/api/resources/datasets/types/__init__.py deleted file mode 100644 index f3304a59f..000000000 --- a/langfuse/api/resources/datasets/types/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .create_dataset_request import CreateDatasetRequest -from .delete_dataset_run_response import DeleteDatasetRunResponse -from .paginated_dataset_runs import PaginatedDatasetRuns -from .paginated_datasets import PaginatedDatasets - -__all__ = [ - "CreateDatasetRequest", - "DeleteDatasetRunResponse", - "PaginatedDatasetRuns", - "PaginatedDatasets", -] diff --git a/langfuse/api/resources/datasets/types/create_dataset_request.py b/langfuse/api/resources/datasets/types/create_dataset_request.py deleted file mode 100644 index 228527909..000000000 --- a/langfuse/api/resources/datasets/types/create_dataset_request.py +++ /dev/null @@ -1,59 +0,0 @@ -# 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 CreateDatasetRequest(pydantic_v1.BaseModel): - name: str - description: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - input_schema: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="inputSchema", default=None - ) - """ - JSON Schema for validating dataset item inputs. When set, all new and existing dataset items will be validated against this schema. - """ - - expected_output_schema: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="expectedOutputSchema", default=None - ) - """ - JSON Schema for validating dataset item expected outputs. When set, all new and existing dataset items will be validated against this schema. - """ - - 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/datasets/types/delete_dataset_run_response.py b/langfuse/api/resources/datasets/types/delete_dataset_run_response.py deleted file mode 100644 index cf52eca14..000000000 --- a/langfuse/api/resources/datasets/types/delete_dataset_run_response.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 DeleteDatasetRunResponse(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/datasets/types/paginated_dataset_runs.py b/langfuse/api/resources/datasets/types/paginated_dataset_runs.py deleted file mode 100644 index 86f2f0a73..000000000 --- a/langfuse/api/resources/datasets/types/paginated_dataset_runs.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.dataset_run import DatasetRun -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class PaginatedDatasetRuns(pydantic_v1.BaseModel): - data: typing.List[DatasetRun] - meta: MetaResponse - - 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/datasets/types/paginated_datasets.py b/langfuse/api/resources/datasets/types/paginated_datasets.py deleted file mode 100644 index c2d436bf4..000000000 --- a/langfuse/api/resources/datasets/types/paginated_datasets.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.dataset import Dataset -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class PaginatedDatasets(pydantic_v1.BaseModel): - data: typing.List[Dataset] - meta: MetaResponse - - 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/health/__init__.py b/langfuse/api/resources/health/__init__.py deleted file mode 100644 index f468cdffb..000000000 --- a/langfuse/api/resources/health/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import HealthResponse -from .errors import ServiceUnavailableError - -__all__ = ["HealthResponse", "ServiceUnavailableError"] diff --git a/langfuse/api/resources/health/client.py b/langfuse/api/resources/health/client.py deleted file mode 100644 index 029be7a0c..000000000 --- a/langfuse/api/resources/health/client.py +++ /dev/null @@ -1,154 +0,0 @@ -# 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.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 .errors.service_unavailable_error import ServiceUnavailableError -from .types.health_response import HealthResponse - - -class HealthClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def health( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> HealthResponse: - """ - Check health of API and database - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HealthResponse - - 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.health.health() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/health", method="GET", request_options=request_options - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(HealthResponse, _response.json()) # type: ignore - if _response.status_code == 503: - raise ServiceUnavailableError() - 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 AsyncHealthClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def health( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> HealthResponse: - """ - Check health of API and database - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HealthResponse - - 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.health.health() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/health", method="GET", request_options=request_options - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(HealthResponse, _response.json()) # type: ignore - if _response.status_code == 503: - raise ServiceUnavailableError() - 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/health/errors/__init__.py b/langfuse/api/resources/health/errors/__init__.py deleted file mode 100644 index 46bb3fedd..000000000 --- a/langfuse/api/resources/health/errors/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .service_unavailable_error import ServiceUnavailableError - -__all__ = ["ServiceUnavailableError"] diff --git a/langfuse/api/resources/health/errors/service_unavailable_error.py b/langfuse/api/resources/health/errors/service_unavailable_error.py deleted file mode 100644 index acfd8fbf3..000000000 --- a/langfuse/api/resources/health/errors/service_unavailable_error.py +++ /dev/null @@ -1,8 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from ....core.api_error import ApiError - - -class ServiceUnavailableError(ApiError): - def __init__(self) -> None: - super().__init__(status_code=503) diff --git a/langfuse/api/resources/health/types/__init__.py b/langfuse/api/resources/health/types/__init__.py deleted file mode 100644 index 5fb7ec574..000000000 --- a/langfuse/api/resources/health/types/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .health_response import HealthResponse - -__all__ = ["HealthResponse"] diff --git a/langfuse/api/resources/health/types/health_response.py b/langfuse/api/resources/health/types/health_response.py deleted file mode 100644 index 633da67a8..000000000 --- a/langfuse/api/resources/health/types/health_response.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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 HealthResponse(pydantic_v1.BaseModel): - """ - Examples - -------- - from langfuse import HealthResponse - - HealthResponse( - version="1.25.0", - status="OK", - ) - """ - - version: str = pydantic_v1.Field() - """ - Langfuse server version - """ - - status: 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/ingestion/__init__.py b/langfuse/api/resources/ingestion/__init__.py deleted file mode 100644 index 9e072dc17..000000000 --- a/langfuse/api/resources/ingestion/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - BaseEvent, - CreateEventBody, - CreateEventEvent, - CreateGenerationBody, - CreateGenerationEvent, - CreateObservationEvent, - CreateSpanBody, - CreateSpanEvent, - IngestionError, - IngestionEvent, - IngestionEvent_EventCreate, - IngestionEvent_GenerationCreate, - IngestionEvent_GenerationUpdate, - IngestionEvent_ObservationCreate, - IngestionEvent_ObservationUpdate, - IngestionEvent_ScoreCreate, - IngestionEvent_SdkLog, - IngestionEvent_SpanCreate, - IngestionEvent_SpanUpdate, - IngestionEvent_TraceCreate, - IngestionResponse, - IngestionSuccess, - IngestionUsage, - ObservationBody, - ObservationType, - OpenAiCompletionUsageSchema, - OpenAiResponseUsageSchema, - OpenAiUsage, - OptionalObservationBody, - ScoreBody, - ScoreEvent, - SdkLogBody, - SdkLogEvent, - TraceBody, - TraceEvent, - UpdateEventBody, - UpdateGenerationBody, - UpdateGenerationEvent, - UpdateObservationEvent, - UpdateSpanBody, - UpdateSpanEvent, - UsageDetails, -) - -__all__ = [ - "BaseEvent", - "CreateEventBody", - "CreateEventEvent", - "CreateGenerationBody", - "CreateGenerationEvent", - "CreateObservationEvent", - "CreateSpanBody", - "CreateSpanEvent", - "IngestionError", - "IngestionEvent", - "IngestionEvent_EventCreate", - "IngestionEvent_GenerationCreate", - "IngestionEvent_GenerationUpdate", - "IngestionEvent_ObservationCreate", - "IngestionEvent_ObservationUpdate", - "IngestionEvent_ScoreCreate", - "IngestionEvent_SdkLog", - "IngestionEvent_SpanCreate", - "IngestionEvent_SpanUpdate", - "IngestionEvent_TraceCreate", - "IngestionResponse", - "IngestionSuccess", - "IngestionUsage", - "ObservationBody", - "ObservationType", - "OpenAiCompletionUsageSchema", - "OpenAiResponseUsageSchema", - "OpenAiUsage", - "OptionalObservationBody", - "ScoreBody", - "ScoreEvent", - "SdkLogBody", - "SdkLogEvent", - "TraceBody", - "TraceEvent", - "UpdateEventBody", - "UpdateGenerationBody", - "UpdateGenerationEvent", - "UpdateObservationEvent", - "UpdateSpanBody", - "UpdateSpanEvent", - "UsageDetails", -] diff --git a/langfuse/api/resources/ingestion/types/__init__.py b/langfuse/api/resources/ingestion/types/__init__.py deleted file mode 100644 index a3490e4dc..000000000 --- a/langfuse/api/resources/ingestion/types/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .base_event import BaseEvent -from .create_event_body import CreateEventBody -from .create_event_event import CreateEventEvent -from .create_generation_body import CreateGenerationBody -from .create_generation_event import CreateGenerationEvent -from .create_observation_event import CreateObservationEvent -from .create_span_body import CreateSpanBody -from .create_span_event import CreateSpanEvent -from .ingestion_error import IngestionError -from .ingestion_event import ( - IngestionEvent, - IngestionEvent_EventCreate, - IngestionEvent_GenerationCreate, - IngestionEvent_GenerationUpdate, - IngestionEvent_ObservationCreate, - IngestionEvent_ObservationUpdate, - IngestionEvent_ScoreCreate, - IngestionEvent_SdkLog, - IngestionEvent_SpanCreate, - IngestionEvent_SpanUpdate, - IngestionEvent_TraceCreate, -) -from .ingestion_response import IngestionResponse -from .ingestion_success import IngestionSuccess -from .ingestion_usage import IngestionUsage -from .observation_body import ObservationBody -from .observation_type import ObservationType -from .open_ai_completion_usage_schema import OpenAiCompletionUsageSchema -from .open_ai_response_usage_schema import OpenAiResponseUsageSchema -from .open_ai_usage import OpenAiUsage -from .optional_observation_body import OptionalObservationBody -from .score_body import ScoreBody -from .score_event import ScoreEvent -from .sdk_log_body import SdkLogBody -from .sdk_log_event import SdkLogEvent -from .trace_body import TraceBody -from .trace_event import TraceEvent -from .update_event_body import UpdateEventBody -from .update_generation_body import UpdateGenerationBody -from .update_generation_event import UpdateGenerationEvent -from .update_observation_event import UpdateObservationEvent -from .update_span_body import UpdateSpanBody -from .update_span_event import UpdateSpanEvent -from .usage_details import UsageDetails - -__all__ = [ - "BaseEvent", - "CreateEventBody", - "CreateEventEvent", - "CreateGenerationBody", - "CreateGenerationEvent", - "CreateObservationEvent", - "CreateSpanBody", - "CreateSpanEvent", - "IngestionError", - "IngestionEvent", - "IngestionEvent_EventCreate", - "IngestionEvent_GenerationCreate", - "IngestionEvent_GenerationUpdate", - "IngestionEvent_ObservationCreate", - "IngestionEvent_ObservationUpdate", - "IngestionEvent_ScoreCreate", - "IngestionEvent_SdkLog", - "IngestionEvent_SpanCreate", - "IngestionEvent_SpanUpdate", - "IngestionEvent_TraceCreate", - "IngestionResponse", - "IngestionSuccess", - "IngestionUsage", - "ObservationBody", - "ObservationType", - "OpenAiCompletionUsageSchema", - "OpenAiResponseUsageSchema", - "OpenAiUsage", - "OptionalObservationBody", - "ScoreBody", - "ScoreEvent", - "SdkLogBody", - "SdkLogEvent", - "TraceBody", - "TraceEvent", - "UpdateEventBody", - "UpdateGenerationBody", - "UpdateGenerationEvent", - "UpdateObservationEvent", - "UpdateSpanBody", - "UpdateSpanEvent", - "UsageDetails", -] diff --git a/langfuse/api/resources/ingestion/types/base_event.py b/langfuse/api/resources/ingestion/types/base_event.py deleted file mode 100644 index dec8a52e7..000000000 --- a/langfuse/api/resources/ingestion/types/base_event.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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 BaseEvent(pydantic_v1.BaseModel): - id: str = pydantic_v1.Field() - """ - UUID v4 that identifies the event - """ - - timestamp: str = pydantic_v1.Field() - """ - Datetime (ISO 8601) of event creation in client. Should be as close to actual event creation in client as possible, this timestamp will be used for ordering of events in future release. Resolution: milliseconds (required), microseconds (optimal). - """ - - metadata: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - Optional. Metadata field used by the Langfuse SDKs for debugging. - """ - - 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/ingestion/types/create_event_body.py b/langfuse/api/resources/ingestion/types/create_event_body.py deleted file mode 100644 index afe8677f3..000000000 --- a/langfuse/api/resources/ingestion/types/create_event_body.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 .optional_observation_body import OptionalObservationBody - - -class CreateEventBody(OptionalObservationBody): - id: typing.Optional[str] = None - - 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/types/create_event_event.py b/langfuse/api/resources/ingestion/types/create_event_event.py deleted file mode 100644 index 0c3cce040..000000000 --- a/langfuse/api/resources/ingestion/types/create_event_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .create_event_body import CreateEventBody - - -class CreateEventEvent(BaseEvent): - body: CreateEventBody - - 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/types/create_generation_body.py b/langfuse/api/resources/ingestion/types/create_generation_body.py deleted file mode 100644 index 428b58607..000000000 --- a/langfuse/api/resources/ingestion/types/create_generation_body.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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 ...commons.types.map_value import MapValue -from .create_span_body import CreateSpanBody -from .ingestion_usage import IngestionUsage -from .usage_details import UsageDetails - - -class CreateGenerationBody(CreateSpanBody): - completion_start_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="completionStartTime", default=None - ) - model: typing.Optional[str] = None - model_parameters: typing.Optional[typing.Dict[str, MapValue]] = pydantic_v1.Field( - alias="modelParameters", default=None - ) - usage: typing.Optional[IngestionUsage] = None - usage_details: typing.Optional[UsageDetails] = pydantic_v1.Field( - alias="usageDetails", default=None - ) - cost_details: typing.Optional[typing.Dict[str, float]] = pydantic_v1.Field( - alias="costDetails", default=None - ) - prompt_name: typing.Optional[str] = pydantic_v1.Field( - alias="promptName", default=None - ) - prompt_version: typing.Optional[int] = pydantic_v1.Field( - alias="promptVersion", default=None - ) - - 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/types/create_generation_event.py b/langfuse/api/resources/ingestion/types/create_generation_event.py deleted file mode 100644 index cb7b484dd..000000000 --- a/langfuse/api/resources/ingestion/types/create_generation_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .create_generation_body import CreateGenerationBody - - -class CreateGenerationEvent(BaseEvent): - body: CreateGenerationBody - - 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/types/create_observation_event.py b/langfuse/api/resources/ingestion/types/create_observation_event.py deleted file mode 100644 index adfefc793..000000000 --- a/langfuse/api/resources/ingestion/types/create_observation_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .observation_body import ObservationBody - - -class CreateObservationEvent(BaseEvent): - body: ObservationBody - - 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/types/create_span_body.py b/langfuse/api/resources/ingestion/types/create_span_body.py deleted file mode 100644 index c31fde567..000000000 --- a/langfuse/api/resources/ingestion/types/create_span_body.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 .create_event_body import CreateEventBody - - -class CreateSpanBody(CreateEventBody): - end_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="endTime", default=None - ) - - 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/types/create_span_event.py b/langfuse/api/resources/ingestion/types/create_span_event.py deleted file mode 100644 index 7a8e8154c..000000000 --- a/langfuse/api/resources/ingestion/types/create_span_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .create_span_body import CreateSpanBody - - -class CreateSpanEvent(BaseEvent): - body: CreateSpanBody - - 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/types/ingestion_error.py b/langfuse/api/resources/ingestion/types/ingestion_error.py deleted file mode 100644 index b9028ce1d..000000000 --- a/langfuse/api/resources/ingestion/types/ingestion_error.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 IngestionError(pydantic_v1.BaseModel): - id: str - status: int - message: typing.Optional[str] = None - error: typing.Optional[typing.Any] = None - - 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/ingestion/types/ingestion_event.py b/langfuse/api/resources/ingestion/types/ingestion_event.py deleted file mode 100644 index e083c9354..000000000 --- a/langfuse/api/resources/ingestion/types/ingestion_event.py +++ /dev/null @@ -1,422 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -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 .create_event_body import CreateEventBody -from .create_generation_body import CreateGenerationBody -from .create_span_body import CreateSpanBody -from .observation_body import ObservationBody -from .score_body import ScoreBody -from .sdk_log_body import SdkLogBody -from .trace_body import TraceBody -from .update_generation_body import UpdateGenerationBody -from .update_span_body import UpdateSpanBody - - -class IngestionEvent_TraceCreate(pydantic_v1.BaseModel): - body: TraceBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["trace-create"] = "trace-create" - - 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} - - -class IngestionEvent_ScoreCreate(pydantic_v1.BaseModel): - body: ScoreBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["score-create"] = "score-create" - - 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} - - -class IngestionEvent_SpanCreate(pydantic_v1.BaseModel): - body: CreateSpanBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["span-create"] = "span-create" - - 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} - - -class IngestionEvent_SpanUpdate(pydantic_v1.BaseModel): - body: UpdateSpanBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["span-update"] = "span-update" - - 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} - - -class IngestionEvent_GenerationCreate(pydantic_v1.BaseModel): - body: CreateGenerationBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["generation-create"] = "generation-create" - - 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} - - -class IngestionEvent_GenerationUpdate(pydantic_v1.BaseModel): - body: UpdateGenerationBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["generation-update"] = "generation-update" - - 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} - - -class IngestionEvent_EventCreate(pydantic_v1.BaseModel): - body: CreateEventBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["event-create"] = "event-create" - - 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} - - -class IngestionEvent_SdkLog(pydantic_v1.BaseModel): - body: SdkLogBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["sdk-log"] = "sdk-log" - - 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} - - -class IngestionEvent_ObservationCreate(pydantic_v1.BaseModel): - body: ObservationBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["observation-create"] = "observation-create" - - 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} - - -class IngestionEvent_ObservationUpdate(pydantic_v1.BaseModel): - body: ObservationBody - id: str - timestamp: str - metadata: typing.Optional[typing.Any] = None - type: typing.Literal["observation-update"] = "observation-update" - - 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} - - -IngestionEvent = typing.Union[ - IngestionEvent_TraceCreate, - IngestionEvent_ScoreCreate, - IngestionEvent_SpanCreate, - IngestionEvent_SpanUpdate, - IngestionEvent_GenerationCreate, - IngestionEvent_GenerationUpdate, - IngestionEvent_EventCreate, - IngestionEvent_SdkLog, - IngestionEvent_ObservationCreate, - IngestionEvent_ObservationUpdate, -] diff --git a/langfuse/api/resources/ingestion/types/ingestion_response.py b/langfuse/api/resources/ingestion/types/ingestion_response.py deleted file mode 100644 index b4e66349c..000000000 --- a/langfuse/api/resources/ingestion/types/ingestion_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 .ingestion_error import IngestionError -from .ingestion_success import IngestionSuccess - - -class IngestionResponse(pydantic_v1.BaseModel): - successes: typing.List[IngestionSuccess] - errors: typing.List[IngestionError] - - 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/ingestion/types/ingestion_success.py b/langfuse/api/resources/ingestion/types/ingestion_success.py deleted file mode 100644 index 481e64752..000000000 --- a/langfuse/api/resources/ingestion/types/ingestion_success.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 IngestionSuccess(pydantic_v1.BaseModel): - id: str - status: int - - 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/ingestion/types/observation_body.py b/langfuse/api/resources/ingestion/types/observation_body.py deleted file mode 100644 index d191a1f12..000000000 --- a/langfuse/api/resources/ingestion/types/observation_body.py +++ /dev/null @@ -1,77 +0,0 @@ -# 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 ...commons.types.map_value import MapValue -from ...commons.types.observation_level import ObservationLevel -from ...commons.types.usage import Usage -from .observation_type import ObservationType - - -class ObservationBody(pydantic_v1.BaseModel): - id: typing.Optional[str] = None - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - type: ObservationType - name: typing.Optional[str] = None - start_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="startTime", default=None - ) - end_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="endTime", default=None - ) - completion_start_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="completionStartTime", default=None - ) - model: typing.Optional[str] = None - model_parameters: typing.Optional[typing.Dict[str, MapValue]] = pydantic_v1.Field( - alias="modelParameters", default=None - ) - input: typing.Optional[typing.Any] = None - version: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - output: typing.Optional[typing.Any] = None - usage: typing.Optional[Usage] = None - level: typing.Optional[ObservationLevel] = None - status_message: typing.Optional[str] = pydantic_v1.Field( - alias="statusMessage", default=None - ) - parent_observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="parentObservationId", default=None - ) - environment: typing.Optional[str] = None - - 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/types/open_ai_completion_usage_schema.py b/langfuse/api/resources/ingestion/types/open_ai_completion_usage_schema.py deleted file mode 100644 index 368a7da03..000000000 --- a/langfuse/api/resources/ingestion/types/open_ai_completion_usage_schema.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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 OpenAiCompletionUsageSchema(pydantic_v1.BaseModel): - """ - OpenAI Usage schema from (Chat-)Completion APIs - """ - - prompt_tokens: int - completion_tokens: int - total_tokens: int - prompt_tokens_details: typing.Optional[typing.Dict[str, typing.Optional[int]]] = ( - None - ) - completion_tokens_details: typing.Optional[ - typing.Dict[str, typing.Optional[int]] - ] = None - - 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/ingestion/types/open_ai_response_usage_schema.py b/langfuse/api/resources/ingestion/types/open_ai_response_usage_schema.py deleted file mode 100644 index 0c68e6a7d..000000000 --- a/langfuse/api/resources/ingestion/types/open_ai_response_usage_schema.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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 OpenAiResponseUsageSchema(pydantic_v1.BaseModel): - """ - OpenAI Usage schema from Response API - """ - - input_tokens: int - output_tokens: int - total_tokens: int - input_tokens_details: typing.Optional[typing.Dict[str, typing.Optional[int]]] = None - output_tokens_details: typing.Optional[typing.Dict[str, typing.Optional[int]]] = ( - None - ) - - 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/ingestion/types/open_ai_usage.py b/langfuse/api/resources/ingestion/types/open_ai_usage.py deleted file mode 100644 index 86e7ebd82..000000000 --- a/langfuse/api/resources/ingestion/types/open_ai_usage.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 OpenAiUsage(pydantic_v1.BaseModel): - """ - Usage interface of OpenAI for improved compatibility. - """ - - prompt_tokens: typing.Optional[int] = pydantic_v1.Field( - alias="promptTokens", default=None - ) - completion_tokens: typing.Optional[int] = pydantic_v1.Field( - alias="completionTokens", default=None - ) - total_tokens: typing.Optional[int] = pydantic_v1.Field( - alias="totalTokens", default=None - ) - - 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/types/optional_observation_body.py b/langfuse/api/resources/ingestion/types/optional_observation_body.py deleted file mode 100644 index 7302d30f9..000000000 --- a/langfuse/api/resources/ingestion/types/optional_observation_body.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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 ...commons.types.observation_level import ObservationLevel - - -class OptionalObservationBody(pydantic_v1.BaseModel): - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - name: typing.Optional[str] = None - start_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="startTime", default=None - ) - metadata: typing.Optional[typing.Any] = None - input: typing.Optional[typing.Any] = None - output: typing.Optional[typing.Any] = None - level: typing.Optional[ObservationLevel] = None - status_message: typing.Optional[str] = pydantic_v1.Field( - alias="statusMessage", default=None - ) - parent_observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="parentObservationId", default=None - ) - version: typing.Optional[str] = None - environment: typing.Optional[str] = None - - 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/types/score_body.py b/langfuse/api/resources/ingestion/types/score_body.py deleted file mode 100644 index 549046564..000000000 --- a/langfuse/api/resources/ingestion/types/score_body.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 ...commons.types.create_score_value import CreateScoreValue -from ...commons.types.score_data_type import ScoreDataType - - -class ScoreBody(pydantic_v1.BaseModel): - """ - Examples - -------- - from langfuse import ScoreBody - - ScoreBody( - name="novelty", - value=0.9, - trace_id="cdef-1234-5678-90ab", - ) - """ - - id: typing.Optional[str] = None - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - environment: typing.Optional[str] = None - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - """ - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. - """ - - value: CreateScoreValue = pydantic_v1.Field() - """ - 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) - """ - - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - data_type: typing.Optional[ScoreDataType] = pydantic_v1.Field( - alias="dataType", default=None - ) - """ - When set, must match the score value's type. If not set, will be inferred from the score value or config - """ - - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - """ - Reference a score config on a score. When set, the score name must equal the config name and scores must comply with the config's range and data type. For categorical scores, the value must map to a config category. Numeric scores might be constrained by the score config's max and min values - """ - - 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/types/score_event.py b/langfuse/api/resources/ingestion/types/score_event.py deleted file mode 100644 index ea05aedef..000000000 --- a/langfuse/api/resources/ingestion/types/score_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .score_body import ScoreBody - - -class ScoreEvent(BaseEvent): - body: ScoreBody - - 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/types/sdk_log_body.py b/langfuse/api/resources/ingestion/types/sdk_log_body.py deleted file mode 100644 index df8972860..000000000 --- a/langfuse/api/resources/ingestion/types/sdk_log_body.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 SdkLogBody(pydantic_v1.BaseModel): - log: typing.Any - - 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/ingestion/types/sdk_log_event.py b/langfuse/api/resources/ingestion/types/sdk_log_event.py deleted file mode 100644 index d7ad87de8..000000000 --- a/langfuse/api/resources/ingestion/types/sdk_log_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .sdk_log_body import SdkLogBody - - -class SdkLogEvent(BaseEvent): - body: SdkLogBody - - 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/types/trace_body.py b/langfuse/api/resources/ingestion/types/trace_body.py deleted file mode 100644 index 3f5550435..000000000 --- a/langfuse/api/resources/ingestion/types/trace_body.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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 TraceBody(pydantic_v1.BaseModel): - id: typing.Optional[str] = None - timestamp: typing.Optional[dt.datetime] = None - name: typing.Optional[str] = None - user_id: typing.Optional[str] = pydantic_v1.Field(alias="userId", default=None) - input: typing.Optional[typing.Any] = None - output: typing.Optional[typing.Any] = None - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - release: typing.Optional[str] = None - version: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - tags: typing.Optional[typing.List[str]] = None - environment: typing.Optional[str] = None - public: typing.Optional[bool] = pydantic_v1.Field(default=None) - """ - Make trace publicly accessible via url - """ - - 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/types/trace_event.py b/langfuse/api/resources/ingestion/types/trace_event.py deleted file mode 100644 index b84ddd615..000000000 --- a/langfuse/api/resources/ingestion/types/trace_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .trace_body import TraceBody - - -class TraceEvent(BaseEvent): - body: TraceBody - - 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/types/update_event_body.py b/langfuse/api/resources/ingestion/types/update_event_body.py deleted file mode 100644 index 35bbb359b..000000000 --- a/langfuse/api/resources/ingestion/types/update_event_body.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 .optional_observation_body import OptionalObservationBody - - -class UpdateEventBody(OptionalObservationBody): - id: 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 - 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/types/update_generation_body.py b/langfuse/api/resources/ingestion/types/update_generation_body.py deleted file mode 100644 index 2058543af..000000000 --- a/langfuse/api/resources/ingestion/types/update_generation_body.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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 ...commons.types.map_value import MapValue -from .ingestion_usage import IngestionUsage -from .update_span_body import UpdateSpanBody -from .usage_details import UsageDetails - - -class UpdateGenerationBody(UpdateSpanBody): - completion_start_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="completionStartTime", default=None - ) - model: typing.Optional[str] = None - model_parameters: typing.Optional[typing.Dict[str, MapValue]] = pydantic_v1.Field( - alias="modelParameters", default=None - ) - usage: typing.Optional[IngestionUsage] = None - prompt_name: typing.Optional[str] = pydantic_v1.Field( - alias="promptName", default=None - ) - usage_details: typing.Optional[UsageDetails] = pydantic_v1.Field( - alias="usageDetails", default=None - ) - cost_details: typing.Optional[typing.Dict[str, float]] = pydantic_v1.Field( - alias="costDetails", default=None - ) - prompt_version: typing.Optional[int] = pydantic_v1.Field( - alias="promptVersion", default=None - ) - - 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/types/update_generation_event.py b/langfuse/api/resources/ingestion/types/update_generation_event.py deleted file mode 100644 index da8f6a9fa..000000000 --- a/langfuse/api/resources/ingestion/types/update_generation_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .update_generation_body import UpdateGenerationBody - - -class UpdateGenerationEvent(BaseEvent): - body: UpdateGenerationBody - - 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/types/update_observation_event.py b/langfuse/api/resources/ingestion/types/update_observation_event.py deleted file mode 100644 index 9d7af357f..000000000 --- a/langfuse/api/resources/ingestion/types/update_observation_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .observation_body import ObservationBody - - -class UpdateObservationEvent(BaseEvent): - body: ObservationBody - - 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/types/update_span_body.py b/langfuse/api/resources/ingestion/types/update_span_body.py deleted file mode 100644 index e3484879b..000000000 --- a/langfuse/api/resources/ingestion/types/update_span_body.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 .update_event_body import UpdateEventBody - - -class UpdateSpanBody(UpdateEventBody): - end_time: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="endTime", default=None - ) - - 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/types/update_span_event.py b/langfuse/api/resources/ingestion/types/update_span_event.py deleted file mode 100644 index ec7d83b15..000000000 --- a/langfuse/api/resources/ingestion/types/update_span_event.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_event import BaseEvent -from .update_span_body import UpdateSpanBody - - -class UpdateSpanEvent(BaseEvent): - body: UpdateSpanBody - - 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/llm_connections/__init__.py b/langfuse/api/resources/llm_connections/__init__.py deleted file mode 100644 index 3cf778f1b..000000000 --- a/langfuse/api/resources/llm_connections/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - LlmAdapter, - LlmConnection, - PaginatedLlmConnections, - UpsertLlmConnectionRequest, -) - -__all__ = [ - "LlmAdapter", - "LlmConnection", - "PaginatedLlmConnections", - "UpsertLlmConnectionRequest", -] diff --git a/langfuse/api/resources/llm_connections/client.py b/langfuse/api/resources/llm_connections/client.py deleted file mode 100644 index 4497598c5..000000000 --- a/langfuse/api/resources/llm_connections/client.py +++ /dev/null @@ -1,340 +0,0 @@ -# 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.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.llm_connection import LlmConnection -from .types.paginated_llm_connections import PaginatedLlmConnections -from .types.upsert_llm_connection_request import UpsertLlmConnectionRequest - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class LlmConnectionsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedLlmConnections: - """ - Get all LLM connections in a project - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedLlmConnections - - 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.llm_connections.list() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/llm-connections", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedLlmConnections, _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( - self, - *, - request: UpsertLlmConnectionRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> LlmConnection: - """ - Create or update an LLM connection. The connection is upserted on provider. - - Parameters - ---------- - request : UpsertLlmConnectionRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - LlmConnection - - Examples - -------- - from langfuse import LlmAdapter, UpsertLlmConnectionRequest - 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.llm_connections.upsert( - request=UpsertLlmConnectionRequest( - provider="provider", - adapter=LlmAdapter.ANTHROPIC, - secret_key="secretKey", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/llm-connections", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(LlmConnection, _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 AsyncLlmConnectionsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedLlmConnections: - """ - Get all LLM connections in a project - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedLlmConnections - - 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.llm_connections.list() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/llm-connections", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PaginatedLlmConnections, _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( - self, - *, - request: UpsertLlmConnectionRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> LlmConnection: - """ - Create or update an LLM connection. The connection is upserted on provider. - - Parameters - ---------- - request : UpsertLlmConnectionRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - LlmConnection - - Examples - -------- - import asyncio - - from langfuse import LlmAdapter, UpsertLlmConnectionRequest - 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.llm_connections.upsert( - request=UpsertLlmConnectionRequest( - provider="provider", - adapter=LlmAdapter.ANTHROPIC, - secret_key="secretKey", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/llm-connections", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(LlmConnection, _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/llm_connections/types/__init__.py b/langfuse/api/resources/llm_connections/types/__init__.py deleted file mode 100644 index b490e6e27..000000000 --- a/langfuse/api/resources/llm_connections/types/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .llm_adapter import LlmAdapter -from .llm_connection import LlmConnection -from .paginated_llm_connections import PaginatedLlmConnections -from .upsert_llm_connection_request import UpsertLlmConnectionRequest - -__all__ = [ - "LlmAdapter", - "LlmConnection", - "PaginatedLlmConnections", - "UpsertLlmConnectionRequest", -] diff --git a/langfuse/api/resources/llm_connections/types/llm_connection.py b/langfuse/api/resources/llm_connections/types/llm_connection.py deleted file mode 100644 index 6ded1459a..000000000 --- a/langfuse/api/resources/llm_connections/types/llm_connection.py +++ /dev/null @@ -1,92 +0,0 @@ -# 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 LlmConnection(pydantic_v1.BaseModel): - """ - LLM API connection configuration (secrets excluded) - """ - - id: str - provider: str = pydantic_v1.Field() - """ - Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. - """ - - adapter: str = pydantic_v1.Field() - """ - The adapter used to interface with the LLM - """ - - display_secret_key: str = pydantic_v1.Field(alias="displaySecretKey") - """ - Masked version of the secret key for display purposes - """ - - base_url: typing.Optional[str] = pydantic_v1.Field(alias="baseURL", default=None) - """ - Custom base URL for the LLM API - """ - - custom_models: typing.List[str] = pydantic_v1.Field(alias="customModels") - """ - List of custom model names available for this connection - """ - - with_default_models: bool = pydantic_v1.Field(alias="withDefaultModels") - """ - Whether to include default models for this adapter - """ - - extra_header_keys: typing.List[str] = pydantic_v1.Field(alias="extraHeaderKeys") - """ - Keys of extra headers sent with requests (values excluded for security) - """ - - config: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( - default=None - ) - """ - Adapter-specific configuration. Required for Bedrock (`{"region":"us-east-1"}`), optional for VertexAI (`{"location":"us-central1"}`), not used by other adapters. - """ - - 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/llm_connections/types/paginated_llm_connections.py b/langfuse/api/resources/llm_connections/types/paginated_llm_connections.py deleted file mode 100644 index 986dbb0bb..000000000 --- a/langfuse/api/resources/llm_connections/types/paginated_llm_connections.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...utils.resources.pagination.types.meta_response import MetaResponse -from .llm_connection import LlmConnection - - -class PaginatedLlmConnections(pydantic_v1.BaseModel): - data: typing.List[LlmConnection] - meta: MetaResponse - - 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/llm_connections/types/upsert_llm_connection_request.py b/langfuse/api/resources/llm_connections/types/upsert_llm_connection_request.py deleted file mode 100644 index 7490f25b7..000000000 --- a/langfuse/api/resources/llm_connections/types/upsert_llm_connection_request.py +++ /dev/null @@ -1,95 +0,0 @@ -# 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 .llm_adapter import LlmAdapter - - -class UpsertLlmConnectionRequest(pydantic_v1.BaseModel): - """ - Request to create or update an LLM connection (upsert) - """ - - provider: str = pydantic_v1.Field() - """ - Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. - """ - - adapter: LlmAdapter = pydantic_v1.Field() - """ - The adapter used to interface with the LLM - """ - - secret_key: str = pydantic_v1.Field(alias="secretKey") - """ - Secret key for the LLM API. - """ - - base_url: typing.Optional[str] = pydantic_v1.Field(alias="baseURL", default=None) - """ - Custom base URL for the LLM API - """ - - custom_models: typing.Optional[typing.List[str]] = pydantic_v1.Field( - alias="customModels", default=None - ) - """ - List of custom model names - """ - - with_default_models: typing.Optional[bool] = pydantic_v1.Field( - alias="withDefaultModels", default=None - ) - """ - Whether to include default models. Default is true. - """ - - extra_headers: typing.Optional[typing.Dict[str, str]] = pydantic_v1.Field( - alias="extraHeaders", default=None - ) - """ - Extra headers to send with requests - """ - - config: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( - default=None - ) - """ - Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. - """ - - 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/media/__init__.py b/langfuse/api/resources/media/__init__.py deleted file mode 100644 index f337d7a04..000000000 --- a/langfuse/api/resources/media/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - GetMediaResponse, - GetMediaUploadUrlRequest, - GetMediaUploadUrlResponse, - MediaContentType, - PatchMediaBody, -) - -__all__ = [ - "GetMediaResponse", - "GetMediaUploadUrlRequest", - "GetMediaUploadUrlResponse", - "MediaContentType", - "PatchMediaBody", -] diff --git a/langfuse/api/resources/media/client.py b/langfuse/api/resources/media/client.py deleted file mode 100644 index bb8e4b149..000000000 --- a/langfuse/api/resources/media/client.py +++ /dev/null @@ -1,505 +0,0 @@ -# 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.get_media_response import GetMediaResponse -from .types.get_media_upload_url_request import GetMediaUploadUrlRequest -from .types.get_media_upload_url_response import GetMediaUploadUrlResponse -from .types.patch_media_body import PatchMediaBody - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class MediaClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get( - self, media_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> GetMediaResponse: - """ - Get a media record - - Parameters - ---------- - media_id : str - The unique langfuse identifier of a media record - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetMediaResponse - - 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.media.get( - media_id="mediaId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/media/{jsonable_encoder(media_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(GetMediaResponse, _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 patch( - self, - media_id: str, - *, - request: PatchMediaBody, - request_options: typing.Optional[RequestOptions] = None, - ) -> None: - """ - Patch a media record - - Parameters - ---------- - media_id : str - The unique langfuse identifier of a media record - - request : PatchMediaBody - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - Examples - -------- - import datetime - - from langfuse import PatchMediaBody - 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.media.patch( - media_id="mediaId", - request=PatchMediaBody( - uploaded_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - upload_http_status=1, - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/media/{jsonable_encoder(media_id)}", - method="PATCH", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return - 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_upload_url( - self, - *, - request: GetMediaUploadUrlRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> GetMediaUploadUrlResponse: - """ - Get a presigned upload URL for a media record - - Parameters - ---------- - request : GetMediaUploadUrlRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetMediaUploadUrlResponse - - Examples - -------- - from langfuse import GetMediaUploadUrlRequest, MediaContentType - 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.media.get_upload_url( - request=GetMediaUploadUrlRequest( - trace_id="traceId", - content_type=MediaContentType.IMAGE_PNG, - content_length=1, - sha_256_hash="sha256Hash", - field="field", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/media", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - GetMediaUploadUrlResponse, _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 AsyncMediaClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( - self, media_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> GetMediaResponse: - """ - Get a media record - - Parameters - ---------- - media_id : str - The unique langfuse identifier of a media record - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetMediaResponse - - 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.media.get( - media_id="mediaId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/media/{jsonable_encoder(media_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(GetMediaResponse, _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 patch( - self, - media_id: str, - *, - request: PatchMediaBody, - request_options: typing.Optional[RequestOptions] = None, - ) -> None: - """ - Patch a media record - - Parameters - ---------- - media_id : str - The unique langfuse identifier of a media record - - request : PatchMediaBody - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - Examples - -------- - import asyncio - import datetime - - from langfuse import PatchMediaBody - 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.media.patch( - media_id="mediaId", - request=PatchMediaBody( - uploaded_at=datetime.datetime.fromisoformat( - "2024-01-15 09:30:00+00:00", - ), - upload_http_status=1, - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/media/{jsonable_encoder(media_id)}", - method="PATCH", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return - 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_upload_url( - self, - *, - request: GetMediaUploadUrlRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> GetMediaUploadUrlResponse: - """ - Get a presigned upload URL for a media record - - Parameters - ---------- - request : GetMediaUploadUrlRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - GetMediaUploadUrlResponse - - Examples - -------- - import asyncio - - from langfuse import GetMediaUploadUrlRequest, MediaContentType - 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.media.get_upload_url( - request=GetMediaUploadUrlRequest( - trace_id="traceId", - content_type=MediaContentType.IMAGE_PNG, - content_length=1, - sha_256_hash="sha256Hash", - field="field", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/media", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - GetMediaUploadUrlResponse, _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/media/types/__init__.py b/langfuse/api/resources/media/types/__init__.py deleted file mode 100644 index 20af676d8..000000000 --- a/langfuse/api/resources/media/types/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .get_media_response import GetMediaResponse -from .get_media_upload_url_request import GetMediaUploadUrlRequest -from .get_media_upload_url_response import GetMediaUploadUrlResponse -from .media_content_type import MediaContentType -from .patch_media_body import PatchMediaBody - -__all__ = [ - "GetMediaResponse", - "GetMediaUploadUrlRequest", - "GetMediaUploadUrlResponse", - "MediaContentType", - "PatchMediaBody", -] diff --git a/langfuse/api/resources/media/types/get_media_response.py b/langfuse/api/resources/media/types/get_media_response.py deleted file mode 100644 index fa5368872..000000000 --- a/langfuse/api/resources/media/types/get_media_response.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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 GetMediaResponse(pydantic_v1.BaseModel): - media_id: str = pydantic_v1.Field(alias="mediaId") - """ - The unique langfuse identifier of a media record - """ - - content_type: str = pydantic_v1.Field(alias="contentType") - """ - The MIME type of the media record - """ - - content_length: int = pydantic_v1.Field(alias="contentLength") - """ - The size of the media record in bytes - """ - - uploaded_at: dt.datetime = pydantic_v1.Field(alias="uploadedAt") - """ - The date and time when the media record was uploaded - """ - - url: str = pydantic_v1.Field() - """ - The download URL of the media record - """ - - url_expiry: str = pydantic_v1.Field(alias="urlExpiry") - """ - The expiry date and time of the media record download URL - """ - - 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/media/types/get_media_upload_url_request.py b/langfuse/api/resources/media/types/get_media_upload_url_request.py deleted file mode 100644 index d0cde59fe..000000000 --- a/langfuse/api/resources/media/types/get_media_upload_url_request.py +++ /dev/null @@ -1,71 +0,0 @@ -# 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 .media_content_type import MediaContentType - - -class GetMediaUploadUrlRequest(pydantic_v1.BaseModel): - trace_id: str = pydantic_v1.Field(alias="traceId") - """ - The trace ID associated with the media record - """ - - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - """ - The observation ID associated with the media record. If the media record is associated directly with a trace, this will be null. - """ - - content_type: MediaContentType = pydantic_v1.Field(alias="contentType") - content_length: int = pydantic_v1.Field(alias="contentLength") - """ - The size of the media record in bytes - """ - - sha_256_hash: str = pydantic_v1.Field(alias="sha256Hash") - """ - The SHA-256 hash of the media record - """ - - field: str = pydantic_v1.Field() - """ - The trace / observation field the media record is associated with. This can be one of `input`, `output`, `metadata` - """ - - 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/media/types/get_media_upload_url_response.py b/langfuse/api/resources/media/types/get_media_upload_url_response.py deleted file mode 100644 index fadc76c01..000000000 --- a/langfuse/api/resources/media/types/get_media_upload_url_response.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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 GetMediaUploadUrlResponse(pydantic_v1.BaseModel): - upload_url: typing.Optional[str] = pydantic_v1.Field( - alias="uploadUrl", default=None - ) - """ - The presigned upload URL. If the asset is already uploaded, this will be null - """ - - media_id: str = pydantic_v1.Field(alias="mediaId") - """ - The unique langfuse identifier of a media record - """ - - 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/media/types/patch_media_body.py b/langfuse/api/resources/media/types/patch_media_body.py deleted file mode 100644 index 49f0c3432..000000000 --- a/langfuse/api/resources/media/types/patch_media_body.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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 PatchMediaBody(pydantic_v1.BaseModel): - uploaded_at: dt.datetime = pydantic_v1.Field(alias="uploadedAt") - """ - The date and time when the media record was uploaded - """ - - upload_http_status: int = pydantic_v1.Field(alias="uploadHttpStatus") - """ - The HTTP status code of the upload - """ - - upload_http_error: typing.Optional[str] = pydantic_v1.Field( - alias="uploadHttpError", default=None - ) - """ - The HTTP error message of the upload - """ - - upload_time_ms: typing.Optional[int] = pydantic_v1.Field( - alias="uploadTimeMs", default=None - ) - """ - The time in milliseconds it took to upload the media record - """ - - 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/metrics/__init__.py b/langfuse/api/resources/metrics/__init__.py deleted file mode 100644 index 90e510b5f..000000000 --- a/langfuse/api/resources/metrics/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import MetricsResponse - -__all__ = ["MetricsResponse"] diff --git a/langfuse/api/resources/metrics/types/__init__.py b/langfuse/api/resources/metrics/types/__init__.py deleted file mode 100644 index 7bf03027d..000000000 --- a/langfuse/api/resources/metrics/types/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .metrics_response import MetricsResponse - -__all__ = ["MetricsResponse"] diff --git a/langfuse/api/resources/metrics/types/metrics_response.py b/langfuse/api/resources/metrics/types/metrics_response.py deleted file mode 100644 index af0121c84..000000000 --- a/langfuse/api/resources/metrics/types/metrics_response.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 MetricsResponse(pydantic_v1.BaseModel): - data: typing.List[typing.Dict[str, typing.Any]] = pydantic_v1.Field() - """ - The metrics data. Each item in the list contains the metric values and dimensions requested in the query. - Format varies based on the query parameters. - Histograms will return an array with [lower, upper, height] tuples. - """ - - 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/models/__init__.py b/langfuse/api/resources/models/__init__.py deleted file mode 100644 index a41fff3e5..000000000 --- a/langfuse/api/resources/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import CreateModelRequest, PaginatedModels - -__all__ = ["CreateModelRequest", "PaginatedModels"] diff --git a/langfuse/api/resources/models/client.py b/langfuse/api/resources/models/client.py deleted file mode 100644 index 4f4b727fa..000000000 --- a/langfuse/api/resources/models/client.py +++ /dev/null @@ -1,607 +0,0 @@ -# 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 ..commons.types.model import Model -from .types.create_model_request import CreateModelRequest -from .types.paginated_models import PaginatedModels - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class ModelsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def create( - self, - *, - request: CreateModelRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> Model: - """ - Create a model - - Parameters - ---------- - request : CreateModelRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Model - - Examples - -------- - from langfuse import CreateModelRequest - 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.models.create( - request=CreateModelRequest( - model_name="modelName", - match_pattern="matchPattern", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/models", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Model, _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 list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedModels: - """ - Get all models - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedModels - - 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.models.list() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/models", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedModels, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> Model: - """ - Get a model - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Model - - 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.models.get( - id="id", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/models/{jsonable_encoder(id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Model, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> None: - """ - Delete a model. Cannot delete models managed by Langfuse. You can create your own definition with the same modelName to override the definition though. - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - 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.models.delete( - id="id", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/models/{jsonable_encoder(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return - 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 AsyncModelsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def create( - self, - *, - request: CreateModelRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> Model: - """ - Create a model - - Parameters - ---------- - request : CreateModelRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Model - - Examples - -------- - import asyncio - - from langfuse import CreateModelRequest - 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.models.create( - request=CreateModelRequest( - model_name="modelName", - match_pattern="matchPattern", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/models", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Model, _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 list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedModels: - """ - Get all models - - Parameters - ---------- - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedModels - - 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.models.list() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/models", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedModels, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> Model: - """ - Get a model - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Model - - 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.models.get( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/models/{jsonable_encoder(id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Model, _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( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> None: - """ - Delete a model. Cannot delete models managed by Langfuse. You can create your own definition with the same modelName to override the definition though. - - Parameters - ---------- - id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - 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.models.delete( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/models/{jsonable_encoder(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return - 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/models/types/__init__.py b/langfuse/api/resources/models/types/__init__.py deleted file mode 100644 index 94285af35..000000000 --- a/langfuse/api/resources/models/types/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .create_model_request import CreateModelRequest -from .paginated_models import PaginatedModels - -__all__ = ["CreateModelRequest", "PaginatedModels"] diff --git a/langfuse/api/resources/models/types/paginated_models.py b/langfuse/api/resources/models/types/paginated_models.py deleted file mode 100644 index 3469a1fe6..000000000 --- a/langfuse/api/resources/models/types/paginated_models.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.model import Model -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class PaginatedModels(pydantic_v1.BaseModel): - data: typing.List[Model] - meta: MetaResponse - - 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/observations/__init__.py b/langfuse/api/resources/observations/__init__.py deleted file mode 100644 index 95fd7c721..000000000 --- a/langfuse/api/resources/observations/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import Observations, ObservationsViews - -__all__ = ["Observations", "ObservationsViews"] diff --git a/langfuse/api/resources/observations/types/__init__.py b/langfuse/api/resources/observations/types/__init__.py deleted file mode 100644 index 60f9d4e01..000000000 --- a/langfuse/api/resources/observations/types/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .observations import Observations -from .observations_views import ObservationsViews - -__all__ = ["Observations", "ObservationsViews"] diff --git a/langfuse/api/resources/observations/types/observations.py b/langfuse/api/resources/observations/types/observations.py deleted file mode 100644 index 1534dc87e..000000000 --- a/langfuse/api/resources/observations/types/observations.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.observation import Observation -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class Observations(pydantic_v1.BaseModel): - data: typing.List[Observation] - meta: MetaResponse - - 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/observations/types/observations_views.py b/langfuse/api/resources/observations/types/observations_views.py deleted file mode 100644 index ed86b7d1e..000000000 --- a/langfuse/api/resources/observations/types/observations_views.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.observations_view import ObservationsView -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class ObservationsViews(pydantic_v1.BaseModel): - data: typing.List[ObservationsView] - meta: MetaResponse - - 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/opentelemetry/__init__.py b/langfuse/api/resources/opentelemetry/__init__.py deleted file mode 100644 index bada2052f..000000000 --- a/langfuse/api/resources/opentelemetry/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - OtelAttribute, - OtelAttributeValue, - OtelResource, - OtelResourceSpan, - OtelScope, - OtelScopeSpan, - OtelSpan, - OtelTraceResponse, -) - -__all__ = [ - "OtelAttribute", - "OtelAttributeValue", - "OtelResource", - "OtelResourceSpan", - "OtelScope", - "OtelScopeSpan", - "OtelSpan", - "OtelTraceResponse", -] diff --git a/langfuse/api/resources/opentelemetry/types/__init__.py b/langfuse/api/resources/opentelemetry/types/__init__.py deleted file mode 100644 index 4ca603db6..000000000 --- a/langfuse/api/resources/opentelemetry/types/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .otel_attribute import OtelAttribute -from .otel_attribute_value import OtelAttributeValue -from .otel_resource import OtelResource -from .otel_resource_span import OtelResourceSpan -from .otel_scope import OtelScope -from .otel_scope_span import OtelScopeSpan -from .otel_span import OtelSpan -from .otel_trace_response import OtelTraceResponse - -__all__ = [ - "OtelAttribute", - "OtelAttributeValue", - "OtelResource", - "OtelResourceSpan", - "OtelScope", - "OtelScopeSpan", - "OtelSpan", - "OtelTraceResponse", -] diff --git a/langfuse/api/resources/opentelemetry/types/otel_attribute.py b/langfuse/api/resources/opentelemetry/types/otel_attribute.py deleted file mode 100644 index 91b9e2b70..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_attribute.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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 .otel_attribute_value import OtelAttributeValue - - -class OtelAttribute(pydantic_v1.BaseModel): - """ - Key-value attribute pair for resources, scopes, or spans - """ - - key: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Attribute key (e.g., "service.name", "langfuse.observation.type") - """ - - value: typing.Optional[OtelAttributeValue] = pydantic_v1.Field(default=None) - """ - Attribute value - """ - - 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/opentelemetry/types/otel_attribute_value.py b/langfuse/api/resources/opentelemetry/types/otel_attribute_value.py deleted file mode 100644 index 51f026495..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_attribute_value.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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 OtelAttributeValue(pydantic_v1.BaseModel): - """ - Attribute value wrapper supporting different value types - """ - - string_value: typing.Optional[str] = pydantic_v1.Field( - alias="stringValue", default=None - ) - """ - String value - """ - - int_value: typing.Optional[int] = pydantic_v1.Field(alias="intValue", default=None) - """ - Integer value - """ - - double_value: typing.Optional[float] = pydantic_v1.Field( - alias="doubleValue", default=None - ) - """ - Double value - """ - - bool_value: typing.Optional[bool] = pydantic_v1.Field( - alias="boolValue", default=None - ) - """ - Boolean value - """ - - 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/opentelemetry/types/otel_resource.py b/langfuse/api/resources/opentelemetry/types/otel_resource.py deleted file mode 100644 index 0d76d5a15..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_resource.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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 .otel_attribute import OtelAttribute - - -class OtelResource(pydantic_v1.BaseModel): - """ - Resource attributes identifying the source of telemetry - """ - - attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic_v1.Field( - default=None - ) - """ - Resource attributes like service.name, service.version, etc. - """ - - 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/opentelemetry/types/otel_resource_span.py b/langfuse/api/resources/opentelemetry/types/otel_resource_span.py deleted file mode 100644 index e270ba7d8..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_resource_span.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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 .otel_resource import OtelResource -from .otel_scope_span import OtelScopeSpan - - -class OtelResourceSpan(pydantic_v1.BaseModel): - """ - Represents a collection of spans from a single resource as per OTLP specification - """ - - resource: typing.Optional[OtelResource] = pydantic_v1.Field(default=None) - """ - Resource information - """ - - scope_spans: typing.Optional[typing.List[OtelScopeSpan]] = pydantic_v1.Field( - alias="scopeSpans", default=None - ) - """ - Array of scope spans - """ - - 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/opentelemetry/types/otel_scope.py b/langfuse/api/resources/opentelemetry/types/otel_scope.py deleted file mode 100644 index 71e9b75b8..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_scope.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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 .otel_attribute import OtelAttribute - - -class OtelScope(pydantic_v1.BaseModel): - """ - Instrumentation scope information - """ - - name: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Instrumentation scope name - """ - - version: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Instrumentation scope version - """ - - attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic_v1.Field( - default=None - ) - """ - Additional scope attributes - """ - - 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/opentelemetry/types/otel_scope_span.py b/langfuse/api/resources/opentelemetry/types/otel_scope_span.py deleted file mode 100644 index 854951a60..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_scope_span.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 .otel_scope import OtelScope -from .otel_span import OtelSpan - - -class OtelScopeSpan(pydantic_v1.BaseModel): - """ - Collection of spans from a single instrumentation scope - """ - - scope: typing.Optional[OtelScope] = pydantic_v1.Field(default=None) - """ - Instrumentation scope information - """ - - spans: typing.Optional[typing.List[OtelSpan]] = pydantic_v1.Field(default=None) - """ - Array of spans - """ - - 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/opentelemetry/types/otel_span.py b/langfuse/api/resources/opentelemetry/types/otel_span.py deleted file mode 100644 index 08b7be7fb..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_span.py +++ /dev/null @@ -1,104 +0,0 @@ -# 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 .otel_attribute import OtelAttribute - - -class OtelSpan(pydantic_v1.BaseModel): - """ - Individual span representing a unit of work or operation - """ - - trace_id: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="traceId", default=None - ) - """ - Trace ID (16 bytes, hex-encoded string in JSON or Buffer in binary) - """ - - span_id: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="spanId", default=None - ) - """ - Span ID (8 bytes, hex-encoded string in JSON or Buffer in binary) - """ - - parent_span_id: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="parentSpanId", default=None - ) - """ - Parent span ID if this is a child span - """ - - name: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Span name describing the operation - """ - - kind: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - Span kind (1=INTERNAL, 2=SERVER, 3=CLIENT, 4=PRODUCER, 5=CONSUMER) - """ - - start_time_unix_nano: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="startTimeUnixNano", default=None - ) - """ - Start time in nanoseconds since Unix epoch - """ - - end_time_unix_nano: typing.Optional[typing.Any] = pydantic_v1.Field( - alias="endTimeUnixNano", default=None - ) - """ - End time in nanoseconds since Unix epoch - """ - - attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic_v1.Field( - default=None - ) - """ - Span attributes including Langfuse-specific attributes (langfuse.observation.*) - """ - - status: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) - """ - Span status object - """ - - 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/opentelemetry/types/otel_trace_response.py b/langfuse/api/resources/opentelemetry/types/otel_trace_response.py deleted file mode 100644 index ef9897f06..000000000 --- a/langfuse/api/resources/opentelemetry/types/otel_trace_response.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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 OtelTraceResponse(pydantic_v1.BaseModel): - """ - Response from trace export request. Empty object indicates success. - """ - - 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/organizations/__init__.py b/langfuse/api/resources/organizations/__init__.py deleted file mode 100644 index 36249ae36..000000000 --- a/langfuse/api/resources/organizations/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - DeleteMembershipRequest, - MembershipDeletionResponse, - MembershipRequest, - MembershipResponse, - MembershipRole, - MembershipsResponse, - OrganizationApiKey, - OrganizationApiKeysResponse, - OrganizationProject, - OrganizationProjectsResponse, -) - -__all__ = [ - "DeleteMembershipRequest", - "MembershipDeletionResponse", - "MembershipRequest", - "MembershipResponse", - "MembershipRole", - "MembershipsResponse", - "OrganizationApiKey", - "OrganizationApiKeysResponse", - "OrganizationProject", - "OrganizationProjectsResponse", -] diff --git a/langfuse/api/resources/organizations/client.py b/langfuse/api/resources/organizations/client.py deleted file mode 100644 index 1e7bcd117..000000000 --- a/langfuse/api/resources/organizations/client.py +++ /dev/null @@ -1,1205 +0,0 @@ -# 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.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 -from .types.organization_api_keys_response import OrganizationApiKeysResponse -from .types.organization_projects_response import OrganizationProjectsResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class OrganizationsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get_organization_memberships( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> MembershipsResponse: - """ - Get all memberships for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipsResponse - - 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.organizations.get_organization_memberships() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/organizations/memberships", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipsResponse, _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 update_organization_membership( - self, - *, - request: MembershipRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> MembershipResponse: - """ - Create or update a membership for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request : MembershipRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipResponse - - Examples - -------- - from langfuse import MembershipRequest, MembershipRole - 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.update_organization_membership( - request=MembershipRequest( - user_id="userId", - role=MembershipRole.OWNER, - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/organizations/memberships", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipResponse, _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_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, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> MembershipsResponse: - """ - Get all memberships for a specific project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipsResponse - - 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.organizations.get_project_memberships( - project_id="projectId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/memberships", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipsResponse, _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 update_project_membership( - self, - project_id: str, - *, - request: MembershipRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> MembershipResponse: - """ - Create or update a membership for a specific project (requires organization-scoped API key). The user must already be a member of the organization. - - Parameters - ---------- - project_id : str - - request : MembershipRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipResponse - - Examples - -------- - from langfuse import MembershipRequest, MembershipRole - 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.update_project_membership( - project_id="projectId", - request=MembershipRequest( - user_id="userId", - role=MembershipRole.OWNER, - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/memberships", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipResponse, _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_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: - """ - Get all projects for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - OrganizationProjectsResponse - - 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.organizations.get_organization_projects() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/organizations/projects", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - OrganizationProjectsResponse, _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_api_keys( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> OrganizationApiKeysResponse: - """ - Get all API keys for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - OrganizationApiKeysResponse - - 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.organizations.get_organization_api_keys() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/organizations/apiKeys", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - OrganizationApiKeysResponse, _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 AsyncOrganizationsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get_organization_memberships( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> MembershipsResponse: - """ - Get all memberships for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipsResponse - - 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.organizations.get_organization_memberships() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/organizations/memberships", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipsResponse, _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 update_organization_membership( - self, - *, - request: MembershipRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> MembershipResponse: - """ - Create or update a membership for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request : MembershipRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipResponse - - Examples - -------- - import asyncio - - from langfuse import MembershipRequest, MembershipRole - 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.update_organization_membership( - request=MembershipRequest( - user_id="userId", - role=MembershipRole.OWNER, - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/organizations/memberships", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipResponse, _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_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, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> MembershipsResponse: - """ - Get all memberships for a specific project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipsResponse - - 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.organizations.get_project_memberships( - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/memberships", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipsResponse, _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 update_project_membership( - self, - project_id: str, - *, - request: MembershipRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> MembershipResponse: - """ - Create or update a membership for a specific project (requires organization-scoped API key). The user must already be a member of the organization. - - Parameters - ---------- - project_id : str - - request : MembershipRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - MembershipResponse - - Examples - -------- - import asyncio - - from langfuse import MembershipRequest, MembershipRole - 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.update_project_membership( - project_id="projectId", - request=MembershipRequest( - user_id="userId", - role=MembershipRole.OWNER, - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/memberships", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(MembershipResponse, _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_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: - """ - Get all projects for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - OrganizationProjectsResponse - - 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.organizations.get_organization_projects() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/organizations/projects", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - OrganizationProjectsResponse, _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_api_keys( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> OrganizationApiKeysResponse: - """ - Get all API keys for the organization associated with the API key (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - OrganizationApiKeysResponse - - 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.organizations.get_organization_api_keys() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/organizations/apiKeys", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - OrganizationApiKeysResponse, _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/organizations/types/__init__.py b/langfuse/api/resources/organizations/types/__init__.py deleted file mode 100644 index b3ea09797..000000000 --- a/langfuse/api/resources/organizations/types/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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 -from .memberships_response import MembershipsResponse -from .organization_api_key import OrganizationApiKey -from .organization_api_keys_response import OrganizationApiKeysResponse -from .organization_project import OrganizationProject -from .organization_projects_response import OrganizationProjectsResponse - -__all__ = [ - "DeleteMembershipRequest", - "MembershipDeletionResponse", - "MembershipRequest", - "MembershipResponse", - "MembershipRole", - "MembershipsResponse", - "OrganizationApiKey", - "OrganizationApiKeysResponse", - "OrganizationProject", - "OrganizationProjectsResponse", -] diff --git a/langfuse/api/resources/organizations/types/delete_membership_request.py b/langfuse/api/resources/organizations/types/delete_membership_request.py deleted file mode 100644 index 6752b0aae..000000000 --- a/langfuse/api/resources/organizations/types/delete_membership_request.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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 deleted file mode 100644 index f9c1915b7..000000000 --- a/langfuse/api/resources/organizations/types/membership_deletion_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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/organizations/types/membership_request.py b/langfuse/api/resources/organizations/types/membership_request.py deleted file mode 100644 index a7f046f51..000000000 --- a/langfuse/api/resources/organizations/types/membership_request.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .membership_role import MembershipRole - - -class MembershipRequest(pydantic_v1.BaseModel): - user_id: str = pydantic_v1.Field(alias="userId") - role: MembershipRole - - 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_response.py b/langfuse/api/resources/organizations/types/membership_response.py deleted file mode 100644 index e9d82f3c7..000000000 --- a/langfuse/api/resources/organizations/types/membership_response.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 .membership_role import MembershipRole - - -class MembershipResponse(pydantic_v1.BaseModel): - user_id: str = pydantic_v1.Field(alias="userId") - role: MembershipRole - email: str - name: 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 - 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/memberships_response.py b/langfuse/api/resources/organizations/types/memberships_response.py deleted file mode 100644 index 0a8091449..000000000 --- a/langfuse/api/resources/organizations/types/memberships_response.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 .membership_response import MembershipResponse - - -class MembershipsResponse(pydantic_v1.BaseModel): - memberships: typing.List[MembershipResponse] - - 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/organizations/types/organization_api_key.py b/langfuse/api/resources/organizations/types/organization_api_key.py deleted file mode 100644 index ad54bb182..000000000 --- a/langfuse/api/resources/organizations/types/organization_api_key.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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 OrganizationApiKey(pydantic_v1.BaseModel): - id: str - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - expires_at: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="expiresAt", default=None - ) - last_used_at: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="lastUsedAt", default=None - ) - note: typing.Optional[str] = None - public_key: str = pydantic_v1.Field(alias="publicKey") - display_secret_key: str = pydantic_v1.Field(alias="displaySecretKey") - - 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/organization_api_keys_response.py b/langfuse/api/resources/organizations/types/organization_api_keys_response.py deleted file mode 100644 index e19ce6373..000000000 --- a/langfuse/api/resources/organizations/types/organization_api_keys_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 .organization_api_key import OrganizationApiKey - - -class OrganizationApiKeysResponse(pydantic_v1.BaseModel): - api_keys: typing.List[OrganizationApiKey] = pydantic_v1.Field(alias="apiKeys") - - 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/organization_project.py b/langfuse/api/resources/organizations/types/organization_project.py deleted file mode 100644 index 87f245b9a..000000000 --- a/langfuse/api/resources/organizations/types/organization_project.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 OrganizationProject(pydantic_v1.BaseModel): - id: str - name: str - metadata: typing.Optional[typing.Dict[str, typing.Any]] = 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/organizations/types/organization_projects_response.py b/langfuse/api/resources/organizations/types/organization_projects_response.py deleted file mode 100644 index 1c939a3e0..000000000 --- a/langfuse/api/resources/organizations/types/organization_projects_response.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 .organization_project import OrganizationProject - - -class OrganizationProjectsResponse(pydantic_v1.BaseModel): - projects: typing.List[OrganizationProject] - - 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/projects/__init__.py b/langfuse/api/resources/projects/__init__.py deleted file mode 100644 index 26c74c1c7..000000000 --- a/langfuse/api/resources/projects/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - ApiKeyDeletionResponse, - ApiKeyList, - ApiKeyResponse, - ApiKeySummary, - Project, - ProjectDeletionResponse, - Projects, -) - -__all__ = [ - "ApiKeyDeletionResponse", - "ApiKeyList", - "ApiKeyResponse", - "ApiKeySummary", - "Project", - "ProjectDeletionResponse", - "Projects", -] diff --git a/langfuse/api/resources/projects/client.py b/langfuse/api/resources/projects/client.py deleted file mode 100644 index 5af232dfb..000000000 --- a/langfuse/api/resources/projects/client.py +++ /dev/null @@ -1,1106 +0,0 @@ -# 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.api_key_deletion_response import ApiKeyDeletionResponse -from .types.api_key_list import ApiKeyList -from .types.api_key_response import ApiKeyResponse -from .types.project import Project -from .types.project_deletion_response import ProjectDeletionResponse -from .types.projects import Projects - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class ProjectsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> Projects: - """ - Get Project associated with API key (requires project-scoped API key). You can use GET /api/public/organizations/projects to get all projects with an organization-scoped key. - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Projects - - 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.projects.get() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/projects", method="GET", request_options=request_options - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Projects, _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 create( - self, - *, - name: str, - retention: int, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> Project: - """ - Create a new project (requires organization-scoped API key) - - Parameters - ---------- - name : str - - retention : int - Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. - - metadata : typing.Optional[typing.Dict[str, typing.Any]] - Optional metadata for the project - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Project - - 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.projects.create( - name="name", - retention=1, - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/projects", - method="POST", - json={"name": name, "metadata": metadata, "retention": retention}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Project, _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 update( - self, - project_id: str, - *, - name: str, - retention: int, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> Project: - """ - Update a project by ID (requires organization-scoped API key). - - Parameters - ---------- - project_id : str - - name : str - - retention : int - Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. - - metadata : typing.Optional[typing.Dict[str, typing.Any]] - Optional metadata for the project - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Project - - 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.projects.update( - project_id="projectId", - name="name", - retention=1, - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}", - method="PUT", - json={"name": name, "metadata": metadata, "retention": retention}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Project, _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( - self, - project_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ProjectDeletionResponse: - """ - Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously. - - Parameters - ---------- - project_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ProjectDeletionResponse - - 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.projects.delete( - project_id="projectId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - ProjectDeletionResponse, _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_api_keys( - self, - project_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ApiKeyList: - """ - Get all API keys for a project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ApiKeyList - - 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.projects.get_api_keys( - project_id="projectId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ApiKeyList, _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 create_api_key( - self, - project_id: str, - *, - note: typing.Optional[str] = OMIT, - public_key: typing.Optional[str] = OMIT, - secret_key: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> ApiKeyResponse: - """ - Create a new API key for a project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - note : typing.Optional[str] - Optional note for the API key - - public_key : typing.Optional[str] - Optional predefined public key. Must start with 'pk-lf-'. If provided, secretKey must also be provided. - - secret_key : typing.Optional[str] - Optional predefined secret key. Must start with 'sk-lf-'. If provided, publicKey must also be provided. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ApiKeyResponse - - 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.projects.create_api_key( - project_id="projectId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", - method="POST", - json={"note": note, "publicKey": public_key, "secretKey": secret_key}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ApiKeyResponse, _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_api_key( - self, - project_id: str, - api_key_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ApiKeyDeletionResponse: - """ - Delete an API key for a project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - api_key_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ApiKeyDeletionResponse - - 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.projects.delete_api_key( - project_id="projectId", - api_key_id="apiKeyId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys/{jsonable_encoder(api_key_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - ApiKeyDeletionResponse, _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 AsyncProjectsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> Projects: - """ - Get Project associated with API key (requires project-scoped API key). You can use GET /api/public/organizations/projects to get all projects with an organization-scoped key. - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Projects - - 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.projects.get() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/projects", method="GET", request_options=request_options - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Projects, _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 create( - self, - *, - name: str, - retention: int, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> Project: - """ - Create a new project (requires organization-scoped API key) - - Parameters - ---------- - name : str - - retention : int - Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. - - metadata : typing.Optional[typing.Dict[str, typing.Any]] - Optional metadata for the project - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Project - - 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.projects.create( - name="name", - retention=1, - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/projects", - method="POST", - json={"name": name, "metadata": metadata, "retention": retention}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Project, _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 update( - self, - project_id: str, - *, - name: str, - retention: int, - metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> Project: - """ - Update a project by ID (requires organization-scoped API key). - - Parameters - ---------- - project_id : str - - name : str - - retention : int - Number of days to retain data. Must be 0 or at least 3 days. Requires data-retention entitlement for non-zero values. Optional. - - metadata : typing.Optional[typing.Dict[str, typing.Any]] - Optional metadata for the project - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Project - - 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.projects.update( - project_id="projectId", - name="name", - retention=1, - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}", - method="PUT", - json={"name": name, "metadata": metadata, "retention": retention}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Project, _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( - self, - project_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ProjectDeletionResponse: - """ - Delete a project by ID (requires organization-scoped API key). Project deletion is processed asynchronously. - - Parameters - ---------- - project_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ProjectDeletionResponse - - 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.projects.delete( - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - ProjectDeletionResponse, _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_api_keys( - self, - project_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ApiKeyList: - """ - Get all API keys for a project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ApiKeyList - - 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.projects.get_api_keys( - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ApiKeyList, _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 create_api_key( - self, - project_id: str, - *, - note: typing.Optional[str] = OMIT, - public_key: typing.Optional[str] = OMIT, - secret_key: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> ApiKeyResponse: - """ - Create a new API key for a project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - note : typing.Optional[str] - Optional note for the API key - - public_key : typing.Optional[str] - Optional predefined public key. Must start with 'pk-lf-'. If provided, secretKey must also be provided. - - secret_key : typing.Optional[str] - Optional predefined secret key. Must start with 'sk-lf-'. If provided, publicKey must also be provided. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ApiKeyResponse - - 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.projects.create_api_key( - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys", - method="POST", - json={"note": note, "publicKey": public_key, "secretKey": secret_key}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ApiKeyResponse, _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_api_key( - self, - project_id: str, - api_key_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> ApiKeyDeletionResponse: - """ - Delete an API key for a project (requires organization-scoped API key) - - Parameters - ---------- - project_id : str - - api_key_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ApiKeyDeletionResponse - - 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.projects.delete_api_key( - project_id="projectId", - api_key_id="apiKeyId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/projects/{jsonable_encoder(project_id)}/apiKeys/{jsonable_encoder(api_key_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - ApiKeyDeletionResponse, _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/projects/types/__init__.py b/langfuse/api/resources/projects/types/__init__.py deleted file mode 100644 index c59b62a62..000000000 --- a/langfuse/api/resources/projects/types/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .api_key_deletion_response import ApiKeyDeletionResponse -from .api_key_list import ApiKeyList -from .api_key_response import ApiKeyResponse -from .api_key_summary import ApiKeySummary -from .project import Project -from .project_deletion_response import ProjectDeletionResponse -from .projects import Projects - -__all__ = [ - "ApiKeyDeletionResponse", - "ApiKeyList", - "ApiKeyResponse", - "ApiKeySummary", - "Project", - "ProjectDeletionResponse", - "Projects", -] diff --git a/langfuse/api/resources/projects/types/api_key_deletion_response.py b/langfuse/api/resources/projects/types/api_key_deletion_response.py deleted file mode 100644 index 6084400de..000000000 --- a/langfuse/api/resources/projects/types/api_key_deletion_response.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 ApiKeyDeletionResponse(pydantic_v1.BaseModel): - """ - Response for API key deletion - """ - - success: bool - - 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/projects/types/api_key_list.py b/langfuse/api/resources/projects/types/api_key_list.py deleted file mode 100644 index 0a798ddbf..000000000 --- a/langfuse/api/resources/projects/types/api_key_list.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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 .api_key_summary import ApiKeySummary - - -class ApiKeyList(pydantic_v1.BaseModel): - """ - List of API keys for a project - """ - - api_keys: typing.List[ApiKeySummary] = pydantic_v1.Field(alias="apiKeys") - - 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/projects/types/api_key_response.py b/langfuse/api/resources/projects/types/api_key_response.py deleted file mode 100644 index fc9364faf..000000000 --- a/langfuse/api/resources/projects/types/api_key_response.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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 ApiKeyResponse(pydantic_v1.BaseModel): - """ - Response for API key creation - """ - - id: str - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - public_key: str = pydantic_v1.Field(alias="publicKey") - secret_key: str = pydantic_v1.Field(alias="secretKey") - display_secret_key: str = pydantic_v1.Field(alias="displaySecretKey") - note: typing.Optional[str] = None - - 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/projects/types/api_key_summary.py b/langfuse/api/resources/projects/types/api_key_summary.py deleted file mode 100644 index b95633731..000000000 --- a/langfuse/api/resources/projects/types/api_key_summary.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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 ApiKeySummary(pydantic_v1.BaseModel): - """ - Summary of an API key - """ - - id: str - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - expires_at: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="expiresAt", default=None - ) - last_used_at: typing.Optional[dt.datetime] = pydantic_v1.Field( - alias="lastUsedAt", default=None - ) - note: typing.Optional[str] = None - public_key: str = pydantic_v1.Field(alias="publicKey") - display_secret_key: str = pydantic_v1.Field(alias="displaySecretKey") - - 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/projects/types/project.py b/langfuse/api/resources/projects/types/project.py deleted file mode 100644 index cf257d406..000000000 --- a/langfuse/api/resources/projects/types/project.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 Project(pydantic_v1.BaseModel): - id: str - name: str - metadata: typing.Dict[str, typing.Any] = pydantic_v1.Field() - """ - Metadata for the project - """ - - retention_days: typing.Optional[int] = pydantic_v1.Field( - alias="retentionDays", default=None - ) - """ - Number of days to retain data. Null or 0 means no retention. Omitted if no retention is configured. - """ - - 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/projects/types/project_deletion_response.py b/langfuse/api/resources/projects/types/project_deletion_response.py deleted file mode 100644 index 62c05d3d8..000000000 --- a/langfuse/api/resources/projects/types/project_deletion_response.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 ProjectDeletionResponse(pydantic_v1.BaseModel): - success: bool - 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/projects/types/projects.py b/langfuse/api/resources/projects/types/projects.py deleted file mode 100644 index c5eaabfbd..000000000 --- a/langfuse/api/resources/projects/types/projects.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 .project import Project - - -class Projects(pydantic_v1.BaseModel): - data: typing.List[Project] - - 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/prompt_version/client.py b/langfuse/api/resources/prompt_version/client.py deleted file mode 100644 index 5387c6e25..000000000 --- a/langfuse/api/resources/prompt_version/client.py +++ /dev/null @@ -1,199 +0,0 @@ -# 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 ..prompts.types.prompt import Prompt - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class PromptVersionClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def update( - self, - name: str, - version: int, - *, - new_labels: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> Prompt: - """ - Update labels for a specific prompt version - - Parameters - ---------- - name : str - The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), - the folder path must be URL encoded. - - version : int - Version of the prompt to update - - new_labels : typing.Sequence[str] - New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Prompt - - 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.prompt_version.update( - name="name", - version=1, - new_labels=["newLabels", "newLabels"], - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/v2/prompts/{jsonable_encoder(name)}/versions/{jsonable_encoder(version)}", - method="PATCH", - json={"newLabels": new_labels}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Prompt, _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 AsyncPromptVersionClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def update( - self, - name: str, - version: int, - *, - new_labels: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> Prompt: - """ - Update labels for a specific prompt version - - Parameters - ---------- - name : str - The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), - the folder path must be URL encoded. - - version : int - Version of the prompt to update - - new_labels : typing.Sequence[str] - New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Prompt - - 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.prompt_version.update( - name="name", - version=1, - new_labels=["newLabels", "newLabels"], - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/v2/prompts/{jsonable_encoder(name)}/versions/{jsonable_encoder(version)}", - method="PATCH", - json={"newLabels": new_labels}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Prompt, _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/prompts/__init__.py b/langfuse/api/resources/prompts/__init__.py deleted file mode 100644 index ea2f2f56a..000000000 --- a/langfuse/api/resources/prompts/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - BasePrompt, - ChatMessage, - ChatMessageWithPlaceholders, - ChatMessageWithPlaceholders_Chatmessage, - ChatMessageWithPlaceholders_Placeholder, - ChatPrompt, - CreateChatPromptRequest, - CreatePromptRequest, - CreatePromptRequest_Chat, - CreatePromptRequest_Text, - CreateTextPromptRequest, - PlaceholderMessage, - Prompt, - PromptMeta, - PromptMetaListResponse, - PromptType, - Prompt_Chat, - Prompt_Text, - TextPrompt, -) - -__all__ = [ - "BasePrompt", - "ChatMessage", - "ChatMessageWithPlaceholders", - "ChatMessageWithPlaceholders_Chatmessage", - "ChatMessageWithPlaceholders_Placeholder", - "ChatPrompt", - "CreateChatPromptRequest", - "CreatePromptRequest", - "CreatePromptRequest_Chat", - "CreatePromptRequest_Text", - "CreateTextPromptRequest", - "PlaceholderMessage", - "Prompt", - "PromptMeta", - "PromptMetaListResponse", - "PromptType", - "Prompt_Chat", - "Prompt_Text", - "TextPrompt", -] diff --git a/langfuse/api/resources/prompts/client.py b/langfuse/api/resources/prompts/client.py deleted file mode 100644 index b8d6f31d4..000000000 --- a/langfuse/api/resources/prompts/client.py +++ /dev/null @@ -1,749 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.datetime_utils import serialize_datetime -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.create_prompt_request import CreatePromptRequest -from .types.prompt import Prompt -from .types.prompt_meta_list_response import PromptMetaListResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class PromptsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get( - self, - prompt_name: str, - *, - version: typing.Optional[int] = None, - label: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> Prompt: - """ - Get a prompt - - Parameters - ---------- - prompt_name : str - The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), - the folder path must be URL encoded. - - version : typing.Optional[int] - Version of the prompt to be retrieved. - - label : typing.Optional[str] - Label of the prompt to be retrieved. Defaults to "production" if no label or version is set. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Prompt - - 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.prompts.get( - prompt_name="promptName", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", - method="GET", - params={"version": version, "label": label}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Prompt, _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 list( - self, - *, - name: typing.Optional[str] = None, - label: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - from_updated_at: typing.Optional[dt.datetime] = None, - to_updated_at: typing.Optional[dt.datetime] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PromptMetaListResponse: - """ - Get a list of prompt names with versions and labels - - Parameters - ---------- - name : typing.Optional[str] - - label : typing.Optional[str] - - tag : typing.Optional[str] - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - from_updated_at : typing.Optional[dt.datetime] - Optional filter to only include prompt versions created/updated on or after a certain datetime (ISO 8601) - - to_updated_at : typing.Optional[dt.datetime] - Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PromptMetaListResponse - - 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.prompts.list() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/v2/prompts", - method="GET", - params={ - "name": name, - "label": label, - "tag": tag, - "page": page, - "limit": limit, - "fromUpdatedAt": serialize_datetime(from_updated_at) - if from_updated_at is not None - else None, - "toUpdatedAt": serialize_datetime(to_updated_at) - if to_updated_at is not None - else None, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PromptMetaListResponse, _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 create( - self, - *, - request: CreatePromptRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> Prompt: - """ - Create a new version for the prompt with the given `name` - - Parameters - ---------- - request : CreatePromptRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Prompt - - Examples - -------- - from langfuse import ( - ChatMessageWithPlaceholders_Chatmessage, - CreatePromptRequest_Chat, - ) - 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.prompts.create( - request=CreatePromptRequest_Chat( - name="name", - prompt=[ - ChatMessageWithPlaceholders_Chatmessage( - role="role", - content="content", - ), - ChatMessageWithPlaceholders_Chatmessage( - role="role", - content="content", - ), - ], - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/v2/prompts", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Prompt, _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( - self, - prompt_name: str, - *, - label: typing.Optional[str] = None, - version: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> None: - """ - Delete prompt versions. If neither version nor label is specified, all versions of the prompt are deleted. - - Parameters - ---------- - prompt_name : str - The name of the prompt - - label : typing.Optional[str] - Optional label to filter deletion. If specified, deletes all prompt versions that have this label. - - version : typing.Optional[int] - Optional version to filter deletion. If specified, deletes only this specific version of the prompt. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - 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.prompts.delete( - prompt_name="promptName", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", - method="DELETE", - params={"label": label, "version": version}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return - 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 AsyncPromptsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( - self, - prompt_name: str, - *, - version: typing.Optional[int] = None, - label: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> Prompt: - """ - Get a prompt - - Parameters - ---------- - prompt_name : str - The name of the prompt. If the prompt is in a folder (e.g., "folder/subfolder/prompt-name"), - the folder path must be URL encoded. - - version : typing.Optional[int] - Version of the prompt to be retrieved. - - label : typing.Optional[str] - Label of the prompt to be retrieved. Defaults to "production" if no label or version is set. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Prompt - - 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.prompts.get( - prompt_name="promptName", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", - method="GET", - params={"version": version, "label": label}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Prompt, _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 list( - self, - *, - name: typing.Optional[str] = None, - label: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - from_updated_at: typing.Optional[dt.datetime] = None, - to_updated_at: typing.Optional[dt.datetime] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> PromptMetaListResponse: - """ - Get a list of prompt names with versions and labels - - Parameters - ---------- - name : typing.Optional[str] - - label : typing.Optional[str] - - tag : typing.Optional[str] - - page : typing.Optional[int] - page number, starts at 1 - - limit : typing.Optional[int] - limit of items per page - - from_updated_at : typing.Optional[dt.datetime] - Optional filter to only include prompt versions created/updated on or after a certain datetime (ISO 8601) - - to_updated_at : typing.Optional[dt.datetime] - Optional filter to only include prompt versions created/updated before a certain datetime (ISO 8601) - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PromptMetaListResponse - - 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.prompts.list() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/v2/prompts", - method="GET", - params={ - "name": name, - "label": label, - "tag": tag, - "page": page, - "limit": limit, - "fromUpdatedAt": serialize_datetime(from_updated_at) - if from_updated_at is not None - else None, - "toUpdatedAt": serialize_datetime(to_updated_at) - if to_updated_at is not None - else None, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as( - PromptMetaListResponse, _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 create( - self, - *, - request: CreatePromptRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> Prompt: - """ - Create a new version for the prompt with the given `name` - - Parameters - ---------- - request : CreatePromptRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - Prompt - - Examples - -------- - import asyncio - - from langfuse import ( - ChatMessageWithPlaceholders_Chatmessage, - CreatePromptRequest_Chat, - ) - 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.prompts.create( - request=CreatePromptRequest_Chat( - name="name", - prompt=[ - ChatMessageWithPlaceholders_Chatmessage( - role="role", - content="content", - ), - ChatMessageWithPlaceholders_Chatmessage( - role="role", - content="content", - ), - ], - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/v2/prompts", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Prompt, _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( - self, - prompt_name: str, - *, - label: typing.Optional[str] = None, - version: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> None: - """ - Delete prompt versions. If neither version nor label is specified, all versions of the prompt are deleted. - - Parameters - ---------- - prompt_name : str - The name of the prompt - - label : typing.Optional[str] - Optional label to filter deletion. If specified, deletes all prompt versions that have this label. - - version : typing.Optional[int] - Optional version to filter deletion. If specified, deletes only this specific version of the prompt. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - 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.prompts.delete( - prompt_name="promptName", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}", - method="DELETE", - params={"label": label, "version": version}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return - 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/prompts/types/__init__.py b/langfuse/api/resources/prompts/types/__init__.py deleted file mode 100644 index 6678ec262..000000000 --- a/langfuse/api/resources/prompts/types/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .base_prompt import BasePrompt -from .chat_message import ChatMessage -from .chat_message_with_placeholders import ( - ChatMessageWithPlaceholders, - ChatMessageWithPlaceholders_Chatmessage, - ChatMessageWithPlaceholders_Placeholder, -) -from .chat_prompt import ChatPrompt -from .create_chat_prompt_request import CreateChatPromptRequest -from .create_prompt_request import ( - CreatePromptRequest, - CreatePromptRequest_Chat, - CreatePromptRequest_Text, -) -from .create_text_prompt_request import CreateTextPromptRequest -from .placeholder_message import PlaceholderMessage -from .prompt import Prompt, Prompt_Chat, Prompt_Text -from .prompt_meta import PromptMeta -from .prompt_meta_list_response import PromptMetaListResponse -from .prompt_type import PromptType -from .text_prompt import TextPrompt - -__all__ = [ - "BasePrompt", - "ChatMessage", - "ChatMessageWithPlaceholders", - "ChatMessageWithPlaceholders_Chatmessage", - "ChatMessageWithPlaceholders_Placeholder", - "ChatPrompt", - "CreateChatPromptRequest", - "CreatePromptRequest", - "CreatePromptRequest_Chat", - "CreatePromptRequest_Text", - "CreateTextPromptRequest", - "PlaceholderMessage", - "Prompt", - "PromptMeta", - "PromptMetaListResponse", - "PromptType", - "Prompt_Chat", - "Prompt_Text", - "TextPrompt", -] diff --git a/langfuse/api/resources/prompts/types/base_prompt.py b/langfuse/api/resources/prompts/types/base_prompt.py deleted file mode 100644 index eff295cc5..000000000 --- a/langfuse/api/resources/prompts/types/base_prompt.py +++ /dev/null @@ -1,69 +0,0 @@ -# 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 BasePrompt(pydantic_v1.BaseModel): - name: str - version: int - config: typing.Any - labels: typing.List[str] = pydantic_v1.Field() - """ - List of deployment labels of this prompt version. - """ - - tags: typing.List[str] = pydantic_v1.Field() - """ - List of tags. Used to filter via UI and API. The same across versions of a prompt. - """ - - commit_message: typing.Optional[str] = pydantic_v1.Field( - alias="commitMessage", default=None - ) - """ - Commit message for this prompt version. - """ - - resolution_graph: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( - alias="resolutionGraph", default=None - ) - """ - The dependency resolution graph for the current prompt. Null if prompt has no dependencies. - """ - - 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/prompts/types/chat_message.py b/langfuse/api/resources/prompts/types/chat_message.py deleted file mode 100644 index d009bc8cf..000000000 --- a/langfuse/api/resources/prompts/types/chat_message.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 ChatMessage(pydantic_v1.BaseModel): - role: str - content: 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/prompts/types/chat_message_with_placeholders.py b/langfuse/api/resources/prompts/types/chat_message_with_placeholders.py deleted file mode 100644 index dc12d5073..000000000 --- a/langfuse/api/resources/prompts/types/chat_message_with_placeholders.py +++ /dev/null @@ -1,87 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -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 ChatMessageWithPlaceholders_Chatmessage(pydantic_v1.BaseModel): - role: str - content: str - type: typing.Literal["chatmessage"] = "chatmessage" - - 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} - - -class ChatMessageWithPlaceholders_Placeholder(pydantic_v1.BaseModel): - name: str - type: typing.Literal["placeholder"] = "placeholder" - - 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} - - -ChatMessageWithPlaceholders = typing.Union[ - ChatMessageWithPlaceholders_Chatmessage, ChatMessageWithPlaceholders_Placeholder -] diff --git a/langfuse/api/resources/prompts/types/chat_prompt.py b/langfuse/api/resources/prompts/types/chat_prompt.py deleted file mode 100644 index 494449ea2..000000000 --- a/langfuse/api/resources/prompts/types/chat_prompt.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 .base_prompt import BasePrompt -from .chat_message_with_placeholders import ChatMessageWithPlaceholders - - -class ChatPrompt(BasePrompt): - prompt: typing.List[ChatMessageWithPlaceholders] - - 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/prompts/types/create_chat_prompt_request.py b/langfuse/api/resources/prompts/types/create_chat_prompt_request.py deleted file mode 100644 index 1442164a6..000000000 --- a/langfuse/api/resources/prompts/types/create_chat_prompt_request.py +++ /dev/null @@ -1,63 +0,0 @@ -# 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 .chat_message_with_placeholders import ChatMessageWithPlaceholders - - -class CreateChatPromptRequest(pydantic_v1.BaseModel): - name: str - prompt: typing.List[ChatMessageWithPlaceholders] - config: typing.Optional[typing.Any] = None - labels: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - List of deployment labels of this prompt version. - """ - - tags: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - List of tags to apply to all versions of this prompt. - """ - - commit_message: typing.Optional[str] = pydantic_v1.Field( - alias="commitMessage", default=None - ) - """ - Commit message for this prompt version. - """ - - 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/prompts/types/create_prompt_request.py b/langfuse/api/resources/prompts/types/create_prompt_request.py deleted file mode 100644 index b9518a7c4..000000000 --- a/langfuse/api/resources/prompts/types/create_prompt_request.py +++ /dev/null @@ -1,103 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -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 .chat_message_with_placeholders import ChatMessageWithPlaceholders - - -class CreatePromptRequest_Chat(pydantic_v1.BaseModel): - name: str - prompt: typing.List[ChatMessageWithPlaceholders] - config: typing.Optional[typing.Any] = None - labels: typing.Optional[typing.List[str]] = None - tags: typing.Optional[typing.List[str]] = None - commit_message: typing.Optional[str] = pydantic_v1.Field( - alias="commitMessage", default=None - ) - type: typing.Literal["chat"] = "chat" - - 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} - - -class CreatePromptRequest_Text(pydantic_v1.BaseModel): - name: str - prompt: str - config: typing.Optional[typing.Any] = None - labels: typing.Optional[typing.List[str]] = None - tags: typing.Optional[typing.List[str]] = None - commit_message: typing.Optional[str] = pydantic_v1.Field( - alias="commitMessage", default=None - ) - type: typing.Literal["text"] = "text" - - 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} - - -CreatePromptRequest = typing.Union[CreatePromptRequest_Chat, CreatePromptRequest_Text] diff --git a/langfuse/api/resources/prompts/types/create_text_prompt_request.py b/langfuse/api/resources/prompts/types/create_text_prompt_request.py deleted file mode 100644 index d35fbb24d..000000000 --- a/langfuse/api/resources/prompts/types/create_text_prompt_request.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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 CreateTextPromptRequest(pydantic_v1.BaseModel): - name: str - prompt: str - config: typing.Optional[typing.Any] = None - labels: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - List of deployment labels of this prompt version. - """ - - tags: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - List of tags to apply to all versions of this prompt. - """ - - commit_message: typing.Optional[str] = pydantic_v1.Field( - alias="commitMessage", default=None - ) - """ - Commit message for this prompt version. - """ - - 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/prompts/types/placeholder_message.py b/langfuse/api/resources/prompts/types/placeholder_message.py deleted file mode 100644 index a3352b391..000000000 --- a/langfuse/api/resources/prompts/types/placeholder_message.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 PlaceholderMessage(pydantic_v1.BaseModel): - name: 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/prompts/types/prompt.py b/langfuse/api/resources/prompts/types/prompt.py deleted file mode 100644 index 1ad894879..000000000 --- a/langfuse/api/resources/prompts/types/prompt.py +++ /dev/null @@ -1,111 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -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 .chat_message_with_placeholders import ChatMessageWithPlaceholders - - -class Prompt_Chat(pydantic_v1.BaseModel): - prompt: typing.List[ChatMessageWithPlaceholders] - name: str - version: int - config: typing.Any - labels: typing.List[str] - tags: typing.List[str] - commit_message: typing.Optional[str] = pydantic_v1.Field( - alias="commitMessage", default=None - ) - resolution_graph: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( - alias="resolutionGraph", default=None - ) - type: typing.Literal["chat"] = "chat" - - 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} - - -class Prompt_Text(pydantic_v1.BaseModel): - prompt: str - name: str - version: int - config: typing.Any - labels: typing.List[str] - tags: typing.List[str] - commit_message: typing.Optional[str] = pydantic_v1.Field( - alias="commitMessage", default=None - ) - resolution_graph: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( - alias="resolutionGraph", default=None - ) - type: typing.Literal["text"] = "text" - - 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} - - -Prompt = typing.Union[Prompt_Chat, Prompt_Text] diff --git a/langfuse/api/resources/prompts/types/prompt_meta.py b/langfuse/api/resources/prompts/types/prompt_meta.py deleted file mode 100644 index 35f8a06cf..000000000 --- a/langfuse/api/resources/prompts/types/prompt_meta.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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 .prompt_type import PromptType - - -class PromptMeta(pydantic_v1.BaseModel): - name: str - type: PromptType = pydantic_v1.Field() - """ - Indicates whether the prompt is a text or chat prompt. - """ - - versions: typing.List[int] - labels: typing.List[str] - tags: typing.List[str] - last_updated_at: dt.datetime = pydantic_v1.Field(alias="lastUpdatedAt") - last_config: typing.Any = pydantic_v1.Field(alias="lastConfig") - """ - Config object of the most recent prompt version that matches the filters (if any are provided) - """ - - 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/prompts/types/prompt_meta_list_response.py b/langfuse/api/resources/prompts/types/prompt_meta_list_response.py deleted file mode 100644 index d3dccf650..000000000 --- a/langfuse/api/resources/prompts/types/prompt_meta_list_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...utils.resources.pagination.types.meta_response import MetaResponse -from .prompt_meta import PromptMeta - - -class PromptMetaListResponse(pydantic_v1.BaseModel): - data: typing.List[PromptMeta] - meta: MetaResponse - - 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/prompts/types/text_prompt.py b/langfuse/api/resources/prompts/types/text_prompt.py deleted file mode 100644 index e149ea322..000000000 --- a/langfuse/api/resources/prompts/types/text_prompt.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 .base_prompt import BasePrompt - - -class TextPrompt(BasePrompt): - prompt: 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 - 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/scim/__init__.py b/langfuse/api/resources/scim/__init__.py deleted file mode 100644 index 29655a8da..000000000 --- a/langfuse/api/resources/scim/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - AuthenticationScheme, - BulkConfig, - EmptyResponse, - FilterConfig, - ResourceMeta, - ResourceType, - ResourceTypesResponse, - SchemaExtension, - SchemaResource, - SchemasResponse, - ScimEmail, - ScimFeatureSupport, - ScimName, - ScimUser, - ScimUsersListResponse, - ServiceProviderConfig, - UserMeta, -) - -__all__ = [ - "AuthenticationScheme", - "BulkConfig", - "EmptyResponse", - "FilterConfig", - "ResourceMeta", - "ResourceType", - "ResourceTypesResponse", - "SchemaExtension", - "SchemaResource", - "SchemasResponse", - "ScimEmail", - "ScimFeatureSupport", - "ScimName", - "ScimUser", - "ScimUsersListResponse", - "ServiceProviderConfig", - "UserMeta", -] diff --git a/langfuse/api/resources/scim/client.py b/langfuse/api/resources/scim/client.py deleted file mode 100644 index 38523a4f9..000000000 --- a/langfuse/api/resources/scim/client.py +++ /dev/null @@ -1,1042 +0,0 @@ -# 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.empty_response import EmptyResponse -from .types.resource_types_response import ResourceTypesResponse -from .types.schemas_response import SchemasResponse -from .types.scim_email import ScimEmail -from .types.scim_name import ScimName -from .types.scim_user import ScimUser -from .types.scim_users_list_response import ScimUsersListResponse -from .types.service_provider_config import ServiceProviderConfig - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class ScimClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get_service_provider_config( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> ServiceProviderConfig: - """ - Get SCIM Service Provider Configuration (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ServiceProviderConfig - - 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.scim.get_service_provider_config() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/scim/ServiceProviderConfig", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ServiceProviderConfig, _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_resource_types( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> ResourceTypesResponse: - """ - Get SCIM Resource Types (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ResourceTypesResponse - - 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.scim.get_resource_types() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/scim/ResourceTypes", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ResourceTypesResponse, _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_schemas( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> SchemasResponse: - """ - Get SCIM Schemas (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SchemasResponse - - 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.scim.get_schemas() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/scim/Schemas", method="GET", request_options=request_options - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(SchemasResponse, _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 list_users( - self, - *, - filter: typing.Optional[str] = None, - start_index: typing.Optional[int] = None, - count: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScimUsersListResponse: - """ - List users in the organization (requires organization-scoped API key) - - Parameters - ---------- - filter : typing.Optional[str] - Filter expression (e.g. userName eq "value") - - start_index : typing.Optional[int] - 1-based index of the first result to return (default 1) - - count : typing.Optional[int] - Maximum number of results to return (default 100) - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScimUsersListResponse - - 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.scim.list_users() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/scim/Users", - method="GET", - params={"filter": filter, "startIndex": start_index, "count": count}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScimUsersListResponse, _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 create_user( - self, - *, - user_name: str, - name: ScimName, - emails: typing.Optional[typing.Sequence[ScimEmail]] = OMIT, - active: typing.Optional[bool] = OMIT, - password: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScimUser: - """ - Create a new user in the organization (requires organization-scoped API key) - - Parameters - ---------- - user_name : str - User's email address (required) - - name : ScimName - User's name information - - emails : typing.Optional[typing.Sequence[ScimEmail]] - User's email addresses - - active : typing.Optional[bool] - Whether the user is active - - password : typing.Optional[str] - Initial password for the user - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScimUser - - Examples - -------- - from langfuse import ScimName - 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.scim.create_user( - user_name="userName", - name=ScimName(), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/scim/Users", - method="POST", - json={ - "userName": user_name, - "name": name, - "emails": emails, - "active": active, - "password": password, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScimUser, _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_user( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> ScimUser: - """ - Get a specific user by ID (requires organization-scoped API key) - - Parameters - ---------- - user_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScimUser - - 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.scim.get_user( - user_id="userId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/scim/Users/{jsonable_encoder(user_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScimUser, _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_user( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> EmptyResponse: - """ - Remove a user from the organization (requires organization-scoped API key). Note that this only removes the user from the organization but does not delete the user entity itself. - - Parameters - ---------- - user_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EmptyResponse - - 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.scim.delete_user( - user_id="userId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/scim/Users/{jsonable_encoder(user_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(EmptyResponse, _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 AsyncScimClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get_service_provider_config( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> ServiceProviderConfig: - """ - Get SCIM Service Provider Configuration (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ServiceProviderConfig - - 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.scim.get_service_provider_config() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/scim/ServiceProviderConfig", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ServiceProviderConfig, _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_resource_types( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> ResourceTypesResponse: - """ - Get SCIM Resource Types (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ResourceTypesResponse - - 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.scim.get_resource_types() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/scim/ResourceTypes", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ResourceTypesResponse, _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_schemas( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> SchemasResponse: - """ - Get SCIM Schemas (requires organization-scoped API key) - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SchemasResponse - - 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.scim.get_schemas() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/scim/Schemas", method="GET", request_options=request_options - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(SchemasResponse, _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 list_users( - self, - *, - filter: typing.Optional[str] = None, - start_index: typing.Optional[int] = None, - count: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScimUsersListResponse: - """ - List users in the organization (requires organization-scoped API key) - - Parameters - ---------- - filter : typing.Optional[str] - Filter expression (e.g. userName eq "value") - - start_index : typing.Optional[int] - 1-based index of the first result to return (default 1) - - count : typing.Optional[int] - Maximum number of results to return (default 100) - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScimUsersListResponse - - 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.scim.list_users() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/scim/Users", - method="GET", - params={"filter": filter, "startIndex": start_index, "count": count}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScimUsersListResponse, _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 create_user( - self, - *, - user_name: str, - name: ScimName, - emails: typing.Optional[typing.Sequence[ScimEmail]] = OMIT, - active: typing.Optional[bool] = OMIT, - password: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScimUser: - """ - Create a new user in the organization (requires organization-scoped API key) - - Parameters - ---------- - user_name : str - User's email address (required) - - name : ScimName - User's name information - - emails : typing.Optional[typing.Sequence[ScimEmail]] - User's email addresses - - active : typing.Optional[bool] - Whether the user is active - - password : typing.Optional[str] - Initial password for the user - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScimUser - - Examples - -------- - import asyncio - - from langfuse import ScimName - 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.scim.create_user( - user_name="userName", - name=ScimName(), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/scim/Users", - method="POST", - json={ - "userName": user_name, - "name": name, - "emails": emails, - "active": active, - "password": password, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScimUser, _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_user( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> ScimUser: - """ - Get a specific user by ID (requires organization-scoped API key) - - Parameters - ---------- - user_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScimUser - - 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.scim.get_user( - user_id="userId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/scim/Users/{jsonable_encoder(user_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScimUser, _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_user( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> EmptyResponse: - """ - Remove a user from the organization (requires organization-scoped API key). Note that this only removes the user from the organization but does not delete the user entity itself. - - Parameters - ---------- - user_id : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EmptyResponse - - 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.scim.delete_user( - user_id="userId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/scim/Users/{jsonable_encoder(user_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(EmptyResponse, _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/scim/types/__init__.py b/langfuse/api/resources/scim/types/__init__.py deleted file mode 100644 index c0b60e8c2..000000000 --- a/langfuse/api/resources/scim/types/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .authentication_scheme import AuthenticationScheme -from .bulk_config import BulkConfig -from .empty_response import EmptyResponse -from .filter_config import FilterConfig -from .resource_meta import ResourceMeta -from .resource_type import ResourceType -from .resource_types_response import ResourceTypesResponse -from .schema_extension import SchemaExtension -from .schema_resource import SchemaResource -from .schemas_response import SchemasResponse -from .scim_email import ScimEmail -from .scim_feature_support import ScimFeatureSupport -from .scim_name import ScimName -from .scim_user import ScimUser -from .scim_users_list_response import ScimUsersListResponse -from .service_provider_config import ServiceProviderConfig -from .user_meta import UserMeta - -__all__ = [ - "AuthenticationScheme", - "BulkConfig", - "EmptyResponse", - "FilterConfig", - "ResourceMeta", - "ResourceType", - "ResourceTypesResponse", - "SchemaExtension", - "SchemaResource", - "SchemasResponse", - "ScimEmail", - "ScimFeatureSupport", - "ScimName", - "ScimUser", - "ScimUsersListResponse", - "ServiceProviderConfig", - "UserMeta", -] diff --git a/langfuse/api/resources/scim/types/authentication_scheme.py b/langfuse/api/resources/scim/types/authentication_scheme.py deleted file mode 100644 index 6d6526901..000000000 --- a/langfuse/api/resources/scim/types/authentication_scheme.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 AuthenticationScheme(pydantic_v1.BaseModel): - name: str - description: str - spec_uri: str = pydantic_v1.Field(alias="specUri") - type: str - primary: bool - - 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/scim/types/bulk_config.py b/langfuse/api/resources/scim/types/bulk_config.py deleted file mode 100644 index 0b41af5cf..000000000 --- a/langfuse/api/resources/scim/types/bulk_config.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 BulkConfig(pydantic_v1.BaseModel): - supported: bool - max_operations: int = pydantic_v1.Field(alias="maxOperations") - max_payload_size: int = pydantic_v1.Field(alias="maxPayloadSize") - - 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/scim/types/empty_response.py b/langfuse/api/resources/scim/types/empty_response.py deleted file mode 100644 index 82105e8a3..000000000 --- a/langfuse/api/resources/scim/types/empty_response.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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 EmptyResponse(pydantic_v1.BaseModel): - """ - Empty response for 204 No Content responses - """ - - 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/scim/types/filter_config.py b/langfuse/api/resources/scim/types/filter_config.py deleted file mode 100644 index 2bd035867..000000000 --- a/langfuse/api/resources/scim/types/filter_config.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 FilterConfig(pydantic_v1.BaseModel): - supported: bool - max_results: int = pydantic_v1.Field(alias="maxResults") - - 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/scim/types/resource_meta.py b/langfuse/api/resources/scim/types/resource_meta.py deleted file mode 100644 index a61d14442..000000000 --- a/langfuse/api/resources/scim/types/resource_meta.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ResourceMeta(pydantic_v1.BaseModel): - resource_type: str = pydantic_v1.Field(alias="resourceType") - location: 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 - 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/scim/types/resource_type.py b/langfuse/api/resources/scim/types/resource_type.py deleted file mode 100644 index 264dc87cf..000000000 --- a/langfuse/api/resources/scim/types/resource_type.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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 .resource_meta import ResourceMeta -from .schema_extension import SchemaExtension - - -class ResourceType(pydantic_v1.BaseModel): - schemas: typing.Optional[typing.List[str]] = None - id: str - name: str - endpoint: str - description: str - schema_: str = pydantic_v1.Field(alias="schema") - schema_extensions: typing.List[SchemaExtension] = pydantic_v1.Field( - alias="schemaExtensions" - ) - meta: ResourceMeta - - 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/scim/types/resource_types_response.py b/langfuse/api/resources/scim/types/resource_types_response.py deleted file mode 100644 index cce65b8d1..000000000 --- a/langfuse/api/resources/scim/types/resource_types_response.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 .resource_type import ResourceType - - -class ResourceTypesResponse(pydantic_v1.BaseModel): - schemas: typing.List[str] - total_results: int = pydantic_v1.Field(alias="totalResults") - resources: typing.List[ResourceType] = pydantic_v1.Field(alias="Resources") - - 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/scim/types/schema_extension.py b/langfuse/api/resources/scim/types/schema_extension.py deleted file mode 100644 index c5ede44b9..000000000 --- a/langfuse/api/resources/scim/types/schema_extension.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 SchemaExtension(pydantic_v1.BaseModel): - schema_: str = pydantic_v1.Field(alias="schema") - required: bool - - 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/scim/types/schema_resource.py b/langfuse/api/resources/scim/types/schema_resource.py deleted file mode 100644 index e85cda9a0..000000000 --- a/langfuse/api/resources/scim/types/schema_resource.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 .resource_meta import ResourceMeta - - -class SchemaResource(pydantic_v1.BaseModel): - id: str - name: str - description: str - attributes: typing.List[typing.Any] - meta: ResourceMeta - - 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/scim/types/schemas_response.py b/langfuse/api/resources/scim/types/schemas_response.py deleted file mode 100644 index 4c7b8199a..000000000 --- a/langfuse/api/resources/scim/types/schemas_response.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 .schema_resource import SchemaResource - - -class SchemasResponse(pydantic_v1.BaseModel): - schemas: typing.List[str] - total_results: int = pydantic_v1.Field(alias="totalResults") - resources: typing.List[SchemaResource] = pydantic_v1.Field(alias="Resources") - - 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/scim/types/scim_email.py b/langfuse/api/resources/scim/types/scim_email.py deleted file mode 100644 index 71b817809..000000000 --- a/langfuse/api/resources/scim/types/scim_email.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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 ScimEmail(pydantic_v1.BaseModel): - primary: bool - value: str - type: 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/scim/types/scim_feature_support.py b/langfuse/api/resources/scim/types/scim_feature_support.py deleted file mode 100644 index 2aedc07b5..000000000 --- a/langfuse/api/resources/scim/types/scim_feature_support.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 ScimFeatureSupport(pydantic_v1.BaseModel): - supported: bool - - 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/scim/types/scim_name.py b/langfuse/api/resources/scim/types/scim_name.py deleted file mode 100644 index c2812a25a..000000000 --- a/langfuse/api/resources/scim/types/scim_name.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 ScimName(pydantic_v1.BaseModel): - formatted: typing.Optional[str] = None - - 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/scim/types/scim_user.py b/langfuse/api/resources/scim/types/scim_user.py deleted file mode 100644 index 581bab8c1..000000000 --- a/langfuse/api/resources/scim/types/scim_user.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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 .scim_email import ScimEmail -from .scim_name import ScimName -from .user_meta import UserMeta - - -class ScimUser(pydantic_v1.BaseModel): - schemas: typing.List[str] - id: str - user_name: str = pydantic_v1.Field(alias="userName") - name: ScimName - emails: typing.List[ScimEmail] - meta: UserMeta - - 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/scim/types/scim_users_list_response.py b/langfuse/api/resources/scim/types/scim_users_list_response.py deleted file mode 100644 index 3c41a4d16..000000000 --- a/langfuse/api/resources/scim/types/scim_users_list_response.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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 .scim_user import ScimUser - - -class ScimUsersListResponse(pydantic_v1.BaseModel): - schemas: typing.List[str] - total_results: int = pydantic_v1.Field(alias="totalResults") - start_index: int = pydantic_v1.Field(alias="startIndex") - items_per_page: int = pydantic_v1.Field(alias="itemsPerPage") - resources: typing.List[ScimUser] = pydantic_v1.Field(alias="Resources") - - 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/scim/types/service_provider_config.py b/langfuse/api/resources/scim/types/service_provider_config.py deleted file mode 100644 index 9bf611ae6..000000000 --- a/langfuse/api/resources/scim/types/service_provider_config.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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 .authentication_scheme import AuthenticationScheme -from .bulk_config import BulkConfig -from .filter_config import FilterConfig -from .resource_meta import ResourceMeta -from .scim_feature_support import ScimFeatureSupport - - -class ServiceProviderConfig(pydantic_v1.BaseModel): - schemas: typing.List[str] - documentation_uri: str = pydantic_v1.Field(alias="documentationUri") - patch: ScimFeatureSupport - bulk: BulkConfig - filter: FilterConfig - change_password: ScimFeatureSupport = pydantic_v1.Field(alias="changePassword") - sort: ScimFeatureSupport - etag: ScimFeatureSupport - authentication_schemes: typing.List[AuthenticationScheme] = pydantic_v1.Field( - alias="authenticationSchemes" - ) - meta: ResourceMeta - - 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/scim/types/user_meta.py b/langfuse/api/resources/scim/types/user_meta.py deleted file mode 100644 index 09cb7e6a0..000000000 --- a/langfuse/api/resources/scim/types/user_meta.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 UserMeta(pydantic_v1.BaseModel): - resource_type: str = pydantic_v1.Field(alias="resourceType") - created: typing.Optional[str] = None - last_modified: typing.Optional[str] = pydantic_v1.Field( - alias="lastModified", default=None - ) - - 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/__init__.py b/langfuse/api/resources/score/__init__.py deleted file mode 100644 index 566310af3..000000000 --- a/langfuse/api/resources/score/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import CreateScoreRequest, CreateScoreResponse - -__all__ = ["CreateScoreRequest", "CreateScoreResponse"] diff --git a/langfuse/api/resources/score/client.py b/langfuse/api/resources/score/client.py deleted file mode 100644 index 0c259929f..000000000 --- a/langfuse/api/resources/score/client.py +++ /dev/null @@ -1,322 +0,0 @@ -# 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.create_score_request import CreateScoreRequest -from .types.create_score_response import CreateScoreResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class ScoreClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def create( - self, - *, - request: CreateScoreRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CreateScoreResponse: - """ - Create a score (supports both trace and session scores) - - Parameters - ---------- - request : CreateScoreRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CreateScoreResponse - - Examples - -------- - from langfuse import CreateScoreRequest - 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.score.create( - request=CreateScoreRequest( - name="name", - value=1.1, - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/scores", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CreateScoreResponse, _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( - self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> None: - """ - Delete 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 - ------- - None - - 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.score.delete( - score_id="scoreId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/scores/{jsonable_encoder(score_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return - 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 AsyncScoreClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def create( - self, - *, - request: CreateScoreRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CreateScoreResponse: - """ - Create a score (supports both trace and session scores) - - Parameters - ---------- - request : CreateScoreRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CreateScoreResponse - - Examples - -------- - import asyncio - - from langfuse import CreateScoreRequest - 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.score.create( - request=CreateScoreRequest( - name="name", - value=1.1, - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/scores", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CreateScoreResponse, _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( - self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> None: - """ - Delete 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 - ------- - None - - 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.score.delete( - score_id="scoreId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/scores/{jsonable_encoder(score_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return - 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/score/types/__init__.py b/langfuse/api/resources/score/types/__init__.py deleted file mode 100644 index 72d61f6f3..000000000 --- a/langfuse/api/resources/score/types/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .create_score_request import CreateScoreRequest -from .create_score_response import CreateScoreResponse - -__all__ = ["CreateScoreRequest", "CreateScoreResponse"] diff --git a/langfuse/api/resources/score/types/create_score_request.py b/langfuse/api/resources/score/types/create_score_request.py deleted file mode 100644 index 1f79f4a64..000000000 --- a/langfuse/api/resources/score/types/create_score_request.py +++ /dev/null @@ -1,97 +0,0 @@ -# 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 ...commons.types.create_score_value import CreateScoreValue -from ...commons.types.score_data_type import ScoreDataType - - -class CreateScoreRequest(pydantic_v1.BaseModel): - """ - Examples - -------- - from langfuse import CreateScoreRequest - - CreateScoreRequest( - name="novelty", - value=0.9, - trace_id="cdef-1234-5678-90ab", - ) - """ - - id: typing.Optional[str] = None - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - value: CreateScoreValue = pydantic_v1.Field() - """ - 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) - """ - - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Dict[str, typing.Any]] = None - environment: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - 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] = pydantic_v1.Field(alias="queueId", default=None) - """ - The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. - """ - - data_type: typing.Optional[ScoreDataType] = pydantic_v1.Field( - alias="dataType", default=None - ) - """ - 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] = pydantic_v1.Field(alias="configId", default=None) - """ - 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. - """ - - 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/types/create_score_response.py b/langfuse/api/resources/score/types/create_score_response.py deleted file mode 100644 index a8c90fce2..000000000 --- a/langfuse/api/resources/score/types/create_score_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 CreateScoreResponse(pydantic_v1.BaseModel): - id: str = pydantic_v1.Field() - """ - The id of the created object in Langfuse - """ - - 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/score_configs/__init__.py b/langfuse/api/resources/score_configs/__init__.py deleted file mode 100644 index da401d35d..000000000 --- a/langfuse/api/resources/score_configs/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import CreateScoreConfigRequest, ScoreConfigs, UpdateScoreConfigRequest - -__all__ = ["CreateScoreConfigRequest", "ScoreConfigs", "UpdateScoreConfigRequest"] diff --git a/langfuse/api/resources/score_configs/client.py b/langfuse/api/resources/score_configs/client.py deleted file mode 100644 index 7faea8312..000000000 --- a/langfuse/api/resources/score_configs/client.py +++ /dev/null @@ -1,632 +0,0 @@ -# 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 ..commons.types.score_config import ScoreConfig -from .types.create_score_config_request import CreateScoreConfigRequest -from .types.score_configs import ScoreConfigs -from .types.update_score_config_request import UpdateScoreConfigRequest - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class ScoreConfigsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def create( - self, - *, - request: CreateScoreConfigRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScoreConfig: - """ - Create a score configuration (config). Score configs are used to define the structure of scores - - Parameters - ---------- - request : CreateScoreConfigRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfig - - Examples - -------- - from langfuse import CreateScoreConfigRequest, ScoreDataType - 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.score_configs.create( - request=CreateScoreConfigRequest( - name="name", - data_type=ScoreDataType.NUMERIC, - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/score-configs", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfig, _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( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScoreConfigs: - """ - Get all score configs - - 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 - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfigs - - 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.score_configs.get() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/score-configs", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfigs, _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_by_id( - self, config_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> ScoreConfig: - """ - Get a score config - - Parameters - ---------- - config_id : str - The unique langfuse identifier of a score config - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfig - - 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.score_configs.get_by_id( - config_id="configId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/score-configs/{jsonable_encoder(config_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfig, _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 update( - self, - config_id: str, - *, - request: UpdateScoreConfigRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScoreConfig: - """ - Update a score config - - Parameters - ---------- - config_id : str - The unique langfuse identifier of a score config - - request : UpdateScoreConfigRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfig - - Examples - -------- - from langfuse import UpdateScoreConfigRequest - 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.score_configs.update( - config_id="configId", - request=UpdateScoreConfigRequest(), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/score-configs/{jsonable_encoder(config_id)}", - method="PATCH", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfig, _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 AsyncScoreConfigsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def create( - self, - *, - request: CreateScoreConfigRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScoreConfig: - """ - Create a score configuration (config). Score configs are used to define the structure of scores - - Parameters - ---------- - request : CreateScoreConfigRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfig - - Examples - -------- - import asyncio - - from langfuse import CreateScoreConfigRequest, ScoreDataType - 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.score_configs.create( - request=CreateScoreConfigRequest( - name="name", - data_type=ScoreDataType.NUMERIC, - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/score-configs", - method="POST", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfig, _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( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScoreConfigs: - """ - Get all score configs - - 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 - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfigs - - 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.score_configs.get() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/score-configs", - method="GET", - params={"page": page, "limit": limit}, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfigs, _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_by_id( - self, config_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> ScoreConfig: - """ - Get a score config - - Parameters - ---------- - config_id : str - The unique langfuse identifier of a score config - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfig - - 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.score_configs.get_by_id( - config_id="configId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/score-configs/{jsonable_encoder(config_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfig, _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 update( - self, - config_id: str, - *, - request: UpdateScoreConfigRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> ScoreConfig: - """ - Update a score config - - Parameters - ---------- - config_id : str - The unique langfuse identifier of a score config - - request : UpdateScoreConfigRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ScoreConfig - - Examples - -------- - import asyncio - - from langfuse import UpdateScoreConfigRequest - 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.score_configs.update( - config_id="configId", - request=UpdateScoreConfigRequest(), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/score-configs/{jsonable_encoder(config_id)}", - method="PATCH", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(ScoreConfig, _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/score_configs/types/__init__.py b/langfuse/api/resources/score_configs/types/__init__.py deleted file mode 100644 index 1c328b614..000000000 --- a/langfuse/api/resources/score_configs/types/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .create_score_config_request import CreateScoreConfigRequest -from .score_configs import ScoreConfigs -from .update_score_config_request import UpdateScoreConfigRequest - -__all__ = ["CreateScoreConfigRequest", "ScoreConfigs", "UpdateScoreConfigRequest"] diff --git a/langfuse/api/resources/score_configs/types/create_score_config_request.py b/langfuse/api/resources/score_configs/types/create_score_config_request.py deleted file mode 100644 index e136af157..000000000 --- a/langfuse/api/resources/score_configs/types/create_score_config_request.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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 ...commons.types.config_category import ConfigCategory -from ...commons.types.score_data_type import ScoreDataType - - -class CreateScoreConfigRequest(pydantic_v1.BaseModel): - name: str - data_type: ScoreDataType = pydantic_v1.Field(alias="dataType") - categories: typing.Optional[typing.List[ConfigCategory]] = pydantic_v1.Field( - default=None - ) - """ - Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed - """ - - min_value: typing.Optional[float] = pydantic_v1.Field( - alias="minValue", default=None - ) - """ - Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ - """ - - max_value: typing.Optional[float] = pydantic_v1.Field( - alias="maxValue", default=None - ) - """ - Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ - """ - - description: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage - """ - - 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_configs/types/score_configs.py b/langfuse/api/resources/score_configs/types/score_configs.py deleted file mode 100644 index fc84e28a3..000000000 --- a/langfuse/api/resources/score_configs/types/score_configs.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.score_config import ScoreConfig -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class ScoreConfigs(pydantic_v1.BaseModel): - data: typing.List[ScoreConfig] - meta: MetaResponse - - 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/score_configs/types/update_score_config_request.py b/langfuse/api/resources/score_configs/types/update_score_config_request.py deleted file mode 100644 index ce5f980b8..000000000 --- a/langfuse/api/resources/score_configs/types/update_score_config_request.py +++ /dev/null @@ -1,81 +0,0 @@ -# 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 ...commons.types.config_category import ConfigCategory - - -class UpdateScoreConfigRequest(pydantic_v1.BaseModel): - is_archived: typing.Optional[bool] = pydantic_v1.Field( - alias="isArchived", default=None - ) - """ - The status of the score config showing if it is archived or not - """ - - name: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The name of the score config - """ - - categories: typing.Optional[typing.List[ConfigCategory]] = pydantic_v1.Field( - default=None - ) - """ - Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed - """ - - min_value: typing.Optional[float] = pydantic_v1.Field( - alias="minValue", default=None - ) - """ - Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ - """ - - max_value: typing.Optional[float] = pydantic_v1.Field( - alias="maxValue", default=None - ) - """ - Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ - """ - - description: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage - """ - - 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/__init__.py b/langfuse/api/resources/score_v_2/__init__.py deleted file mode 100644 index 40599eec1..000000000 --- a/langfuse/api/resources/score_v_2/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import ( - GetScoresResponse, - GetScoresResponseData, - GetScoresResponseDataBoolean, - GetScoresResponseDataCategorical, - GetScoresResponseDataNumeric, - GetScoresResponseData_Boolean, - GetScoresResponseData_Categorical, - GetScoresResponseData_Numeric, - GetScoresResponseTraceData, -) - -__all__ = [ - "GetScoresResponse", - "GetScoresResponseData", - "GetScoresResponseDataBoolean", - "GetScoresResponseDataCategorical", - "GetScoresResponseDataNumeric", - "GetScoresResponseData_Boolean", - "GetScoresResponseData_Categorical", - "GetScoresResponseData_Numeric", - "GetScoresResponseTraceData", -] diff --git a/langfuse/api/resources/score_v_2/types/__init__.py b/langfuse/api/resources/score_v_2/types/__init__.py deleted file mode 100644 index 480ed3406..000000000 --- a/langfuse/api/resources/score_v_2/types/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .get_scores_response import GetScoresResponse -from .get_scores_response_data import ( - GetScoresResponseData, - GetScoresResponseData_Boolean, - GetScoresResponseData_Categorical, - GetScoresResponseData_Numeric, -) -from .get_scores_response_data_boolean import GetScoresResponseDataBoolean -from .get_scores_response_data_categorical import GetScoresResponseDataCategorical -from .get_scores_response_data_numeric import GetScoresResponseDataNumeric -from .get_scores_response_trace_data import GetScoresResponseTraceData - -__all__ = [ - "GetScoresResponse", - "GetScoresResponseData", - "GetScoresResponseDataBoolean", - "GetScoresResponseDataCategorical", - "GetScoresResponseDataNumeric", - "GetScoresResponseData_Boolean", - "GetScoresResponseData_Categorical", - "GetScoresResponseData_Numeric", - "GetScoresResponseTraceData", -] diff --git a/langfuse/api/resources/score_v_2/types/get_scores_response.py b/langfuse/api/resources/score_v_2/types/get_scores_response.py deleted file mode 100644 index 777bb799b..000000000 --- a/langfuse/api/resources/score_v_2/types/get_scores_response.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...utils.resources.pagination.types.meta_response import MetaResponse -from .get_scores_response_data import GetScoresResponseData - - -class GetScoresResponse(pydantic_v1.BaseModel): - data: typing.List[GetScoresResponseData] - meta: MetaResponse - - 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/score_v_2/types/get_scores_response_data.py b/langfuse/api/resources/score_v_2/types/get_scores_response_data.py deleted file mode 100644 index e09f31cb9..000000000 --- a/langfuse/api/resources/score_v_2/types/get_scores_response_data.py +++ /dev/null @@ -1,215 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -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 ...commons.types.score_source import ScoreSource -from .get_scores_response_trace_data import GetScoresResponseTraceData - - -class GetScoresResponseData_Numeric(pydantic_v1.BaseModel): - trace: typing.Optional[GetScoresResponseTraceData] = None - value: float - id: str - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - source: ScoreSource - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["NUMERIC"] = pydantic_v1.Field( - alias="dataType", default="NUMERIC" - ) - - 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} - - -class GetScoresResponseData_Categorical(pydantic_v1.BaseModel): - trace: typing.Optional[GetScoresResponseTraceData] = None - value: float - string_value: str = pydantic_v1.Field(alias="stringValue") - id: str - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - source: ScoreSource - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["CATEGORICAL"] = pydantic_v1.Field( - alias="dataType", default="CATEGORICAL" - ) - - 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} - - -class GetScoresResponseData_Boolean(pydantic_v1.BaseModel): - trace: typing.Optional[GetScoresResponseTraceData] = None - value: float - string_value: str = pydantic_v1.Field(alias="stringValue") - id: str - trace_id: typing.Optional[str] = pydantic_v1.Field(alias="traceId", default=None) - session_id: typing.Optional[str] = pydantic_v1.Field( - alias="sessionId", default=None - ) - observation_id: typing.Optional[str] = pydantic_v1.Field( - alias="observationId", default=None - ) - dataset_run_id: typing.Optional[str] = pydantic_v1.Field( - alias="datasetRunId", default=None - ) - name: str - source: ScoreSource - timestamp: dt.datetime - created_at: dt.datetime = pydantic_v1.Field(alias="createdAt") - updated_at: dt.datetime = pydantic_v1.Field(alias="updatedAt") - author_user_id: typing.Optional[str] = pydantic_v1.Field( - alias="authorUserId", default=None - ) - comment: typing.Optional[str] = None - metadata: typing.Optional[typing.Any] = None - config_id: typing.Optional[str] = pydantic_v1.Field(alias="configId", default=None) - queue_id: typing.Optional[str] = pydantic_v1.Field(alias="queueId", default=None) - environment: typing.Optional[str] = None - data_type: typing.Literal["BOOLEAN"] = pydantic_v1.Field( - alias="dataType", default="BOOLEAN" - ) - - 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} - - -GetScoresResponseData = typing.Union[ - GetScoresResponseData_Numeric, - GetScoresResponseData_Categorical, - GetScoresResponseData_Boolean, -] diff --git a/langfuse/api/resources/score_v_2/types/get_scores_response_data_boolean.py b/langfuse/api/resources/score_v_2/types/get_scores_response_data_boolean.py deleted file mode 100644 index 48012990c..000000000 --- a/langfuse/api/resources/score_v_2/types/get_scores_response_data_boolean.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 ...commons.types.boolean_score import BooleanScore -from .get_scores_response_trace_data import GetScoresResponseTraceData - - -class GetScoresResponseDataBoolean(BooleanScore): - trace: typing.Optional[GetScoresResponseTraceData] = None - - 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/types/get_scores_response_data_categorical.py b/langfuse/api/resources/score_v_2/types/get_scores_response_data_categorical.py deleted file mode 100644 index 6e27f6d64..000000000 --- a/langfuse/api/resources/score_v_2/types/get_scores_response_data_categorical.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 ...commons.types.categorical_score import CategoricalScore -from .get_scores_response_trace_data import GetScoresResponseTraceData - - -class GetScoresResponseDataCategorical(CategoricalScore): - trace: typing.Optional[GetScoresResponseTraceData] = None - - 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/types/get_scores_response_data_numeric.py b/langfuse/api/resources/score_v_2/types/get_scores_response_data_numeric.py deleted file mode 100644 index f7342833f..000000000 --- a/langfuse/api/resources/score_v_2/types/get_scores_response_data_numeric.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 ...commons.types.numeric_score import NumericScore -from .get_scores_response_trace_data import GetScoresResponseTraceData - - -class GetScoresResponseDataNumeric(NumericScore): - trace: typing.Optional[GetScoresResponseTraceData] = None - - 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/types/get_scores_response_trace_data.py b/langfuse/api/resources/score_v_2/types/get_scores_response_trace_data.py deleted file mode 100644 index 6e5539e35..000000000 --- a/langfuse/api/resources/score_v_2/types/get_scores_response_trace_data.py +++ /dev/null @@ -1,57 +0,0 @@ -# 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 GetScoresResponseTraceData(pydantic_v1.BaseModel): - user_id: typing.Optional[str] = pydantic_v1.Field(alias="userId", default=None) - """ - The user ID associated with the trace referenced by score - """ - - tags: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - A list of tags associated with the trace referenced by score - """ - - environment: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - The environment of the trace referenced by score - """ - - 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/sessions/__init__.py b/langfuse/api/resources/sessions/__init__.py deleted file mode 100644 index 048704297..000000000 --- a/langfuse/api/resources/sessions/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import PaginatedSessions - -__all__ = ["PaginatedSessions"] diff --git a/langfuse/api/resources/sessions/client.py b/langfuse/api/resources/sessions/client.py deleted file mode 100644 index d5ae779c3..000000000 --- a/langfuse/api/resources/sessions/client.py +++ /dev/null @@ -1,367 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.datetime_utils import serialize_datetime -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 ..commons.types.session_with_traces import SessionWithTraces -from .types.paginated_sessions import PaginatedSessions - - -class SessionsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = 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, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedSessions: - """ - Get sessions - - 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. - - from_timestamp : typing.Optional[dt.datetime] - Optional filter to only include sessions created on or after a certain datetime (ISO 8601) - - to_timestamp : typing.Optional[dt.datetime] - Optional filter to only include sessions created before a certain datetime (ISO 8601) - - environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Optional filter for sessions where the environment is one of the provided values. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedSessions - - 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.sessions.list() - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/sessions", - method="GET", - params={ - "page": page, - "limit": limit, - "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, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedSessions, _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( - self, - session_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> SessionWithTraces: - """ - Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=` - - Parameters - ---------- - session_id : str - The unique id of a session - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SessionWithTraces - - 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.sessions.get( - session_id="sessionId", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/sessions/{jsonable_encoder(session_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(SessionWithTraces, _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 AsyncSessionsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - *, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = 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, - request_options: typing.Optional[RequestOptions] = None, - ) -> PaginatedSessions: - """ - Get sessions - - 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. - - from_timestamp : typing.Optional[dt.datetime] - Optional filter to only include sessions created on or after a certain datetime (ISO 8601) - - to_timestamp : typing.Optional[dt.datetime] - Optional filter to only include sessions created before a certain datetime (ISO 8601) - - environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] - Optional filter for sessions where the environment is one of the provided values. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PaginatedSessions - - 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.sessions.list() - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/sessions", - method="GET", - params={ - "page": page, - "limit": limit, - "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, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(PaginatedSessions, _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( - self, - session_id: str, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> SessionWithTraces: - """ - Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=` - - Parameters - ---------- - session_id : str - The unique id of a session - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SessionWithTraces - - 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.sessions.get( - session_id="sessionId", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/sessions/{jsonable_encoder(session_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(SessionWithTraces, _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/sessions/types/__init__.py b/langfuse/api/resources/sessions/types/__init__.py deleted file mode 100644 index 42d63b428..000000000 --- a/langfuse/api/resources/sessions/types/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .paginated_sessions import PaginatedSessions - -__all__ = ["PaginatedSessions"] diff --git a/langfuse/api/resources/sessions/types/paginated_sessions.py b/langfuse/api/resources/sessions/types/paginated_sessions.py deleted file mode 100644 index 5dd9fb497..000000000 --- a/langfuse/api/resources/sessions/types/paginated_sessions.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.session import Session -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class PaginatedSessions(pydantic_v1.BaseModel): - data: typing.List[Session] - meta: MetaResponse - - 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/trace/__init__.py b/langfuse/api/resources/trace/__init__.py deleted file mode 100644 index 17855e971..000000000 --- a/langfuse/api/resources/trace/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .types import DeleteTraceResponse, Sort, Traces - -__all__ = ["DeleteTraceResponse", "Sort", "Traces"] diff --git a/langfuse/api/resources/trace/types/__init__.py b/langfuse/api/resources/trace/types/__init__.py deleted file mode 100644 index 929a1e047..000000000 --- a/langfuse/api/resources/trace/types/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .delete_trace_response import DeleteTraceResponse -from .sort import Sort -from .traces import Traces - -__all__ = ["DeleteTraceResponse", "Sort", "Traces"] diff --git a/langfuse/api/resources/trace/types/delete_trace_response.py b/langfuse/api/resources/trace/types/delete_trace_response.py deleted file mode 100644 index 450c894e2..000000000 --- a/langfuse/api/resources/trace/types/delete_trace_response.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 DeleteTraceResponse(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/trace/types/sort.py b/langfuse/api/resources/trace/types/sort.py deleted file mode 100644 index 76a5045b6..000000000 --- a/langfuse/api/resources/trace/types/sort.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 Sort(pydantic_v1.BaseModel): - id: 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/trace/types/traces.py b/langfuse/api/resources/trace/types/traces.py deleted file mode 100644 index 09f58978f..000000000 --- a/langfuse/api/resources/trace/types/traces.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 ...commons.types.trace_with_details import TraceWithDetails -from ...utils.resources.pagination.types.meta_response import MetaResponse - - -class Traces(pydantic_v1.BaseModel): - data: typing.List[TraceWithDetails] - meta: MetaResponse - - 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/utils/__init__.py b/langfuse/api/resources/utils/__init__.py deleted file mode 100644 index b4ac87b8a..000000000 --- a/langfuse/api/resources/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .resources import MetaResponse, pagination - -__all__ = ["MetaResponse", "pagination"] diff --git a/langfuse/api/resources/utils/resources/__init__.py b/langfuse/api/resources/utils/resources/__init__.py deleted file mode 100644 index 7e65ff270..000000000 --- a/langfuse/api/resources/utils/resources/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from . import pagination -from .pagination import MetaResponse - -__all__ = ["MetaResponse", "pagination"] diff --git a/langfuse/api/resources/utils/resources/pagination/types/__init__.py b/langfuse/api/resources/utils/resources/pagination/types/__init__.py deleted file mode 100644 index 79bb6018e..000000000 --- a/langfuse/api/resources/utils/resources/pagination/types/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from .meta_response import MetaResponse - -__all__ = ["MetaResponse"] diff --git a/langfuse/api/resources/utils/resources/pagination/types/meta_response.py b/langfuse/api/resources/utils/resources/pagination/types/meta_response.py deleted file mode 100644 index 2d082c68f..000000000 --- a/langfuse/api/resources/utils/resources/pagination/types/meta_response.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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 MetaResponse(pydantic_v1.BaseModel): - page: int = pydantic_v1.Field() - """ - current page number - """ - - limit: int = pydantic_v1.Field() - """ - number of items per page - """ - - total_items: int = pydantic_v1.Field(alias="totalItems") - """ - number of total items given the current filters/selection (if any) - """ - - total_pages: int = pydantic_v1.Field(alias="totalPages") - """ - number of total pages given the current limit - """ - - 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/scim/__init__.py b/langfuse/api/scim/__init__.py new file mode 100644 index 000000000..6c4126f3c --- /dev/null +++ b/langfuse/api/scim/__init__.py @@ -0,0 +1,94 @@ +# 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 ( + AuthenticationScheme, + BulkConfig, + EmptyResponse, + FilterConfig, + ResourceMeta, + ResourceType, + ResourceTypesResponse, + SchemaExtension, + SchemaResource, + SchemasResponse, + ScimEmail, + ScimFeatureSupport, + ScimName, + ScimUser, + ScimUsersListResponse, + ServiceProviderConfig, + UserMeta, + ) +_dynamic_imports: typing.Dict[str, str] = { + "AuthenticationScheme": ".types", + "BulkConfig": ".types", + "EmptyResponse": ".types", + "FilterConfig": ".types", + "ResourceMeta": ".types", + "ResourceType": ".types", + "ResourceTypesResponse": ".types", + "SchemaExtension": ".types", + "SchemaResource": ".types", + "SchemasResponse": ".types", + "ScimEmail": ".types", + "ScimFeatureSupport": ".types", + "ScimName": ".types", + "ScimUser": ".types", + "ScimUsersListResponse": ".types", + "ServiceProviderConfig": ".types", + "UserMeta": ".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__ = [ + "AuthenticationScheme", + "BulkConfig", + "EmptyResponse", + "FilterConfig", + "ResourceMeta", + "ResourceType", + "ResourceTypesResponse", + "SchemaExtension", + "SchemaResource", + "SchemasResponse", + "ScimEmail", + "ScimFeatureSupport", + "ScimName", + "ScimUser", + "ScimUsersListResponse", + "ServiceProviderConfig", + "UserMeta", +] diff --git a/langfuse/api/scim/client.py b/langfuse/api/scim/client.py new file mode 100644 index 000000000..30d0815bf --- /dev/null +++ b/langfuse/api/scim/client.py @@ -0,0 +1,686 @@ +# 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 AsyncRawScimClient, RawScimClient +from .types.empty_response import EmptyResponse +from .types.resource_types_response import ResourceTypesResponse +from .types.schemas_response import SchemasResponse +from .types.scim_email import ScimEmail +from .types.scim_name import ScimName +from .types.scim_user import ScimUser +from .types.scim_users_list_response import ScimUsersListResponse +from .types.service_provider_config import ServiceProviderConfig + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ScimClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawScimClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawScimClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawScimClient + """ + return self._raw_client + + def get_service_provider_config( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ServiceProviderConfig: + """ + Get SCIM Service Provider Configuration (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ServiceProviderConfig + + 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.scim.get_service_provider_config() + """ + _response = self._raw_client.get_service_provider_config( + request_options=request_options + ) + return _response.data + + def get_resource_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ResourceTypesResponse: + """ + Get SCIM Resource Types (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ResourceTypesResponse + + 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.scim.get_resource_types() + """ + _response = self._raw_client.get_resource_types(request_options=request_options) + return _response.data + + def get_schemas( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> SchemasResponse: + """ + Get SCIM Schemas (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SchemasResponse + + 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.scim.get_schemas() + """ + _response = self._raw_client.get_schemas(request_options=request_options) + return _response.data + + def list_users( + self, + *, + filter: typing.Optional[str] = None, + start_index: typing.Optional[int] = None, + count: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScimUsersListResponse: + """ + List users in the organization (requires organization-scoped API key) + + Parameters + ---------- + filter : typing.Optional[str] + Filter expression (e.g. userName eq "value") + + start_index : typing.Optional[int] + 1-based index of the first result to return (default 1) + + count : typing.Optional[int] + Maximum number of results to return (default 100) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScimUsersListResponse + + 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.scim.list_users() + """ + _response = self._raw_client.list_users( + filter=filter, + start_index=start_index, + count=count, + request_options=request_options, + ) + return _response.data + + def create_user( + self, + *, + user_name: str, + name: ScimName, + emails: typing.Optional[typing.Sequence[ScimEmail]] = OMIT, + active: typing.Optional[bool] = OMIT, + password: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScimUser: + """ + Create a new user in the organization (requires organization-scoped API key) + + Parameters + ---------- + user_name : str + User's email address (required) + + name : ScimName + User's name information + + emails : typing.Optional[typing.Sequence[ScimEmail]] + User's email addresses + + active : typing.Optional[bool] + Whether the user is active + + password : typing.Optional[str] + Initial password for the user + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScimUser + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.scim import ScimName + + 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.scim.create_user( + user_name="userName", + name=ScimName(), + ) + """ + _response = self._raw_client.create_user( + user_name=user_name, + name=name, + emails=emails, + active=active, + password=password, + request_options=request_options, + ) + return _response.data + + def get_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ScimUser: + """ + Get a specific user by ID (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScimUser + + 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.scim.get_user( + user_id="userId", + ) + """ + _response = self._raw_client.get_user(user_id, request_options=request_options) + return _response.data + + def delete_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> EmptyResponse: + """ + Remove a user from the organization (requires organization-scoped API key). Note that this only removes the user from the organization but does not delete the user entity itself. + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EmptyResponse + + 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.scim.delete_user( + user_id="userId", + ) + """ + _response = self._raw_client.delete_user( + user_id, request_options=request_options + ) + return _response.data + + +class AsyncScimClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawScimClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawScimClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawScimClient + """ + return self._raw_client + + async def get_service_provider_config( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ServiceProviderConfig: + """ + Get SCIM Service Provider Configuration (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ServiceProviderConfig + + 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.scim.get_service_provider_config() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_service_provider_config( + request_options=request_options + ) + return _response.data + + async def get_resource_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ResourceTypesResponse: + """ + Get SCIM Resource Types (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ResourceTypesResponse + + 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.scim.get_resource_types() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_resource_types( + request_options=request_options + ) + return _response.data + + async def get_schemas( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> SchemasResponse: + """ + Get SCIM Schemas (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SchemasResponse + + 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.scim.get_schemas() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_schemas(request_options=request_options) + return _response.data + + async def list_users( + self, + *, + filter: typing.Optional[str] = None, + start_index: typing.Optional[int] = None, + count: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScimUsersListResponse: + """ + List users in the organization (requires organization-scoped API key) + + Parameters + ---------- + filter : typing.Optional[str] + Filter expression (e.g. userName eq "value") + + start_index : typing.Optional[int] + 1-based index of the first result to return (default 1) + + count : typing.Optional[int] + Maximum number of results to return (default 100) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScimUsersListResponse + + 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.scim.list_users() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_users( + filter=filter, + start_index=start_index, + count=count, + request_options=request_options, + ) + return _response.data + + async def create_user( + self, + *, + user_name: str, + name: ScimName, + emails: typing.Optional[typing.Sequence[ScimEmail]] = OMIT, + active: typing.Optional[bool] = OMIT, + password: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScimUser: + """ + Create a new user in the organization (requires organization-scoped API key) + + Parameters + ---------- + user_name : str + User's email address (required) + + name : ScimName + User's name information + + emails : typing.Optional[typing.Sequence[ScimEmail]] + User's email addresses + + active : typing.Optional[bool] + Whether the user is active + + password : typing.Optional[str] + Initial password for the user + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScimUser + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.scim import ScimName + + 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.scim.create_user( + user_name="userName", + name=ScimName(), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_user( + user_name=user_name, + name=name, + emails=emails, + active=active, + password=password, + request_options=request_options, + ) + return _response.data + + async def get_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ScimUser: + """ + Get a specific user by ID (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScimUser + + 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.scim.get_user( + user_id="userId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_user( + user_id, request_options=request_options + ) + return _response.data + + async def delete_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> EmptyResponse: + """ + Remove a user from the organization (requires organization-scoped API key). Note that this only removes the user from the organization but does not delete the user entity itself. + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EmptyResponse + + 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.scim.delete_user( + user_id="userId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_user( + user_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/scim/raw_client.py b/langfuse/api/scim/raw_client.py new file mode 100644 index 000000000..e65f46592 --- /dev/null +++ b/langfuse/api/scim/raw_client.py @@ -0,0 +1,1528 @@ +# 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.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.empty_response import EmptyResponse +from .types.resource_types_response import ResourceTypesResponse +from .types.schemas_response import SchemasResponse +from .types.scim_email import ScimEmail +from .types.scim_name import ScimName +from .types.scim_user import ScimUser +from .types.scim_users_list_response import ScimUsersListResponse +from .types.service_provider_config import ServiceProviderConfig + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawScimClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_service_provider_config( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ServiceProviderConfig]: + """ + Get SCIM Service Provider Configuration (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ServiceProviderConfig] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/scim/ServiceProviderConfig", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ServiceProviderConfig, + parse_obj_as( + type_=ServiceProviderConfig, # 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_resource_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ResourceTypesResponse]: + """ + Get SCIM Resource Types (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ResourceTypesResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/scim/ResourceTypes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ResourceTypesResponse, + parse_obj_as( + type_=ResourceTypesResponse, # 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_schemas( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[SchemasResponse]: + """ + Get SCIM Schemas (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SchemasResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/scim/Schemas", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SchemasResponse, + parse_obj_as( + type_=SchemasResponse, # 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 list_users( + self, + *, + filter: typing.Optional[str] = None, + start_index: typing.Optional[int] = None, + count: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ScimUsersListResponse]: + """ + List users in the organization (requires organization-scoped API key) + + Parameters + ---------- + filter : typing.Optional[str] + Filter expression (e.g. userName eq "value") + + start_index : typing.Optional[int] + 1-based index of the first result to return (default 1) + + count : typing.Optional[int] + Maximum number of results to return (default 100) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ScimUsersListResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/scim/Users", + method="GET", + params={ + "filter": filter, + "startIndex": start_index, + "count": count, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScimUsersListResponse, + parse_obj_as( + type_=ScimUsersListResponse, # 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 create_user( + self, + *, + user_name: str, + name: ScimName, + emails: typing.Optional[typing.Sequence[ScimEmail]] = OMIT, + active: typing.Optional[bool] = OMIT, + password: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ScimUser]: + """ + Create a new user in the organization (requires organization-scoped API key) + + Parameters + ---------- + user_name : str + User's email address (required) + + name : ScimName + User's name information + + emails : typing.Optional[typing.Sequence[ScimEmail]] + User's email addresses + + active : typing.Optional[bool] + Whether the user is active + + password : typing.Optional[str] + Initial password for the user + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ScimUser] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/scim/Users", + method="POST", + json={ + "userName": user_name, + "name": convert_and_respect_annotation_metadata( + object_=name, annotation=ScimName, direction="write" + ), + "emails": convert_and_respect_annotation_metadata( + object_=emails, + annotation=typing.Sequence[ScimEmail], + direction="write", + ), + "active": active, + "password": password, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScimUser, + parse_obj_as( + type_=ScimUser, # 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_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ScimUser]: + """ + Get a specific user by ID (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ScimUser] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/scim/Users/{jsonable_encoder(user_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScimUser, + parse_obj_as( + type_=ScimUser, # 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 delete_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[EmptyResponse]: + """ + Remove a user from the organization (requires organization-scoped API key). Note that this only removes the user from the organization but does not delete the user entity itself. + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[EmptyResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/scim/Users/{jsonable_encoder(user_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EmptyResponse, + parse_obj_as( + type_=EmptyResponse, # 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 AsyncRawScimClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_service_provider_config( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ServiceProviderConfig]: + """ + Get SCIM Service Provider Configuration (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ServiceProviderConfig] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/scim/ServiceProviderConfig", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ServiceProviderConfig, + parse_obj_as( + type_=ServiceProviderConfig, # 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_resource_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ResourceTypesResponse]: + """ + Get SCIM Resource Types (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ResourceTypesResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/scim/ResourceTypes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ResourceTypesResponse, + parse_obj_as( + type_=ResourceTypesResponse, # 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_schemas( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[SchemasResponse]: + """ + Get SCIM Schemas (requires organization-scoped API key) + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SchemasResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/scim/Schemas", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SchemasResponse, + parse_obj_as( + type_=SchemasResponse, # 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 list_users( + self, + *, + filter: typing.Optional[str] = None, + start_index: typing.Optional[int] = None, + count: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ScimUsersListResponse]: + """ + List users in the organization (requires organization-scoped API key) + + Parameters + ---------- + filter : typing.Optional[str] + Filter expression (e.g. userName eq "value") + + start_index : typing.Optional[int] + 1-based index of the first result to return (default 1) + + count : typing.Optional[int] + Maximum number of results to return (default 100) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ScimUsersListResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/scim/Users", + method="GET", + params={ + "filter": filter, + "startIndex": start_index, + "count": count, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScimUsersListResponse, + parse_obj_as( + type_=ScimUsersListResponse, # 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 create_user( + self, + *, + user_name: str, + name: ScimName, + emails: typing.Optional[typing.Sequence[ScimEmail]] = OMIT, + active: typing.Optional[bool] = OMIT, + password: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ScimUser]: + """ + Create a new user in the organization (requires organization-scoped API key) + + Parameters + ---------- + user_name : str + User's email address (required) + + name : ScimName + User's name information + + emails : typing.Optional[typing.Sequence[ScimEmail]] + User's email addresses + + active : typing.Optional[bool] + Whether the user is active + + password : typing.Optional[str] + Initial password for the user + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ScimUser] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/scim/Users", + method="POST", + json={ + "userName": user_name, + "name": convert_and_respect_annotation_metadata( + object_=name, annotation=ScimName, direction="write" + ), + "emails": convert_and_respect_annotation_metadata( + object_=emails, + annotation=typing.Sequence[ScimEmail], + direction="write", + ), + "active": active, + "password": password, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScimUser, + parse_obj_as( + type_=ScimUser, # 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_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ScimUser]: + """ + Get a specific user by ID (requires organization-scoped API key) + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ScimUser] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/scim/Users/{jsonable_encoder(user_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScimUser, + parse_obj_as( + type_=ScimUser, # 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 delete_user( + self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[EmptyResponse]: + """ + Remove a user from the organization (requires organization-scoped API key). Note that this only removes the user from the organization but does not delete the user entity itself. + + Parameters + ---------- + user_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[EmptyResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/scim/Users/{jsonable_encoder(user_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EmptyResponse, + parse_obj_as( + type_=EmptyResponse, # 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/scim/types/__init__.py b/langfuse/api/scim/types/__init__.py new file mode 100644 index 000000000..9d6483e3d --- /dev/null +++ b/langfuse/api/scim/types/__init__.py @@ -0,0 +1,92 @@ +# 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 .authentication_scheme import AuthenticationScheme + from .bulk_config import BulkConfig + from .empty_response import EmptyResponse + from .filter_config import FilterConfig + from .resource_meta import ResourceMeta + from .resource_type import ResourceType + from .resource_types_response import ResourceTypesResponse + from .schema_extension import SchemaExtension + from .schema_resource import SchemaResource + from .schemas_response import SchemasResponse + from .scim_email import ScimEmail + from .scim_feature_support import ScimFeatureSupport + from .scim_name import ScimName + from .scim_user import ScimUser + from .scim_users_list_response import ScimUsersListResponse + from .service_provider_config import ServiceProviderConfig + from .user_meta import UserMeta +_dynamic_imports: typing.Dict[str, str] = { + "AuthenticationScheme": ".authentication_scheme", + "BulkConfig": ".bulk_config", + "EmptyResponse": ".empty_response", + "FilterConfig": ".filter_config", + "ResourceMeta": ".resource_meta", + "ResourceType": ".resource_type", + "ResourceTypesResponse": ".resource_types_response", + "SchemaExtension": ".schema_extension", + "SchemaResource": ".schema_resource", + "SchemasResponse": ".schemas_response", + "ScimEmail": ".scim_email", + "ScimFeatureSupport": ".scim_feature_support", + "ScimName": ".scim_name", + "ScimUser": ".scim_user", + "ScimUsersListResponse": ".scim_users_list_response", + "ServiceProviderConfig": ".service_provider_config", + "UserMeta": ".user_meta", +} + + +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__ = [ + "AuthenticationScheme", + "BulkConfig", + "EmptyResponse", + "FilterConfig", + "ResourceMeta", + "ResourceType", + "ResourceTypesResponse", + "SchemaExtension", + "SchemaResource", + "SchemasResponse", + "ScimEmail", + "ScimFeatureSupport", + "ScimName", + "ScimUser", + "ScimUsersListResponse", + "ServiceProviderConfig", + "UserMeta", +] diff --git a/langfuse/api/scim/types/authentication_scheme.py b/langfuse/api/scim/types/authentication_scheme.py new file mode 100644 index 000000000..fc1fceb14 --- /dev/null +++ b/langfuse/api/scim/types/authentication_scheme.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class AuthenticationScheme(UniversalBaseModel): + name: str + description: str + spec_uri: typing_extensions.Annotated[str, FieldMetadata(alias="specUri")] + type: str + primary: bool + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/bulk_config.py b/langfuse/api/scim/types/bulk_config.py new file mode 100644 index 000000000..4a3ae719f --- /dev/null +++ b/langfuse/api/scim/types/bulk_config.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class BulkConfig(UniversalBaseModel): + supported: bool + max_operations: typing_extensions.Annotated[ + int, FieldMetadata(alias="maxOperations") + ] + max_payload_size: typing_extensions.Annotated[ + int, FieldMetadata(alias="maxPayloadSize") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/empty_response.py b/langfuse/api/scim/types/empty_response.py new file mode 100644 index 000000000..1371104f8 --- /dev/null +++ b/langfuse/api/scim/types/empty_response.py @@ -0,0 +1,16 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class EmptyResponse(UniversalBaseModel): + """ + Empty response for 204 No Content responses + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/filter_config.py b/langfuse/api/scim/types/filter_config.py new file mode 100644 index 000000000..ba9986e56 --- /dev/null +++ b/langfuse/api/scim/types/filter_config.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class FilterConfig(UniversalBaseModel): + supported: bool + max_results: typing_extensions.Annotated[int, FieldMetadata(alias="maxResults")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/resource_meta.py b/langfuse/api/scim/types/resource_meta.py new file mode 100644 index 000000000..99be2a96e --- /dev/null +++ b/langfuse/api/scim/types/resource_meta.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class ResourceMeta(UniversalBaseModel): + resource_type: typing_extensions.Annotated[str, FieldMetadata(alias="resourceType")] + location: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/resource_type.py b/langfuse/api/scim/types/resource_type.py new file mode 100644 index 000000000..9913c465d --- /dev/null +++ b/langfuse/api/scim/types/resource_type.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .resource_meta import ResourceMeta +from .schema_extension import SchemaExtension + + +class ResourceType(UniversalBaseModel): + schemas: typing.Optional[typing.List[str]] = None + id: str + name: str + endpoint: str + description: str + schema_: typing_extensions.Annotated[str, FieldMetadata(alias="schema")] + schema_extensions: typing_extensions.Annotated[ + typing.List[SchemaExtension], FieldMetadata(alias="schemaExtensions") + ] + meta: ResourceMeta + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/resource_types_response.py b/langfuse/api/scim/types/resource_types_response.py new file mode 100644 index 000000000..8ff1c47d9 --- /dev/null +++ b/langfuse/api/scim/types/resource_types_response.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .resource_type import ResourceType + + +class ResourceTypesResponse(UniversalBaseModel): + schemas: typing.List[str] + total_results: typing_extensions.Annotated[int, FieldMetadata(alias="totalResults")] + resources: typing_extensions.Annotated[ + typing.List[ResourceType], FieldMetadata(alias="Resources") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/schema_extension.py b/langfuse/api/scim/types/schema_extension.py new file mode 100644 index 000000000..4a09d6192 --- /dev/null +++ b/langfuse/api/scim/types/schema_extension.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class SchemaExtension(UniversalBaseModel): + schema_: typing_extensions.Annotated[str, FieldMetadata(alias="schema")] + required: bool + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/schema_resource.py b/langfuse/api/scim/types/schema_resource.py new file mode 100644 index 000000000..cafc07dcb --- /dev/null +++ b/langfuse/api/scim/types/schema_resource.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .resource_meta import ResourceMeta + + +class SchemaResource(UniversalBaseModel): + id: str + name: str + description: str + attributes: typing.List[typing.Any] + meta: ResourceMeta + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/schemas_response.py b/langfuse/api/scim/types/schemas_response.py new file mode 100644 index 000000000..3162f9431 --- /dev/null +++ b/langfuse/api/scim/types/schemas_response.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .schema_resource import SchemaResource + + +class SchemasResponse(UniversalBaseModel): + schemas: typing.List[str] + total_results: typing_extensions.Annotated[int, FieldMetadata(alias="totalResults")] + resources: typing_extensions.Annotated[ + typing.List[SchemaResource], FieldMetadata(alias="Resources") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/scim_email.py b/langfuse/api/scim/types/scim_email.py new file mode 100644 index 000000000..7d589f0cf --- /dev/null +++ b/langfuse/api/scim/types/scim_email.py @@ -0,0 +1,16 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ScimEmail(UniversalBaseModel): + primary: bool + value: str + type: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/scim_feature_support.py b/langfuse/api/scim/types/scim_feature_support.py new file mode 100644 index 000000000..4c24a694d --- /dev/null +++ b/langfuse/api/scim/types/scim_feature_support.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ScimFeatureSupport(UniversalBaseModel): + supported: bool + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/scim_name.py b/langfuse/api/scim/types/scim_name.py new file mode 100644 index 000000000..53d2d79e3 --- /dev/null +++ b/langfuse/api/scim/types/scim_name.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class ScimName(UniversalBaseModel): + formatted: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/scim_user.py b/langfuse/api/scim/types/scim_user.py new file mode 100644 index 000000000..22d6d50fe --- /dev/null +++ b/langfuse/api/scim/types/scim_user.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .scim_email import ScimEmail +from .scim_name import ScimName +from .user_meta import UserMeta + + +class ScimUser(UniversalBaseModel): + schemas: typing.List[str] + id: str + user_name: typing_extensions.Annotated[str, FieldMetadata(alias="userName")] + name: ScimName + emails: typing.List[ScimEmail] + meta: UserMeta + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/scim_users_list_response.py b/langfuse/api/scim/types/scim_users_list_response.py new file mode 100644 index 000000000..bcfba30bb --- /dev/null +++ b/langfuse/api/scim/types/scim_users_list_response.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .scim_user import ScimUser + + +class ScimUsersListResponse(UniversalBaseModel): + schemas: typing.List[str] + total_results: typing_extensions.Annotated[int, FieldMetadata(alias="totalResults")] + start_index: typing_extensions.Annotated[int, FieldMetadata(alias="startIndex")] + items_per_page: typing_extensions.Annotated[ + int, FieldMetadata(alias="itemsPerPage") + ] + resources: typing_extensions.Annotated[ + typing.List[ScimUser], FieldMetadata(alias="Resources") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/service_provider_config.py b/langfuse/api/scim/types/service_provider_config.py new file mode 100644 index 000000000..48add080e --- /dev/null +++ b/langfuse/api/scim/types/service_provider_config.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .authentication_scheme import AuthenticationScheme +from .bulk_config import BulkConfig +from .filter_config import FilterConfig +from .resource_meta import ResourceMeta +from .scim_feature_support import ScimFeatureSupport + + +class ServiceProviderConfig(UniversalBaseModel): + schemas: typing.List[str] + documentation_uri: typing_extensions.Annotated[ + str, FieldMetadata(alias="documentationUri") + ] + patch: ScimFeatureSupport + bulk: BulkConfig + filter: FilterConfig + change_password: typing_extensions.Annotated[ + ScimFeatureSupport, FieldMetadata(alias="changePassword") + ] + sort: ScimFeatureSupport + etag: ScimFeatureSupport + authentication_schemes: typing_extensions.Annotated[ + typing.List[AuthenticationScheme], FieldMetadata(alias="authenticationSchemes") + ] + meta: ResourceMeta + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/scim/types/user_meta.py b/langfuse/api/scim/types/user_meta.py new file mode 100644 index 000000000..033ed4fa1 --- /dev/null +++ b/langfuse/api/scim/types/user_meta.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class UserMeta(UniversalBaseModel): + resource_type: typing_extensions.Annotated[str, FieldMetadata(alias="resourceType")] + created: typing.Optional[str] = None + last_modified: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="lastModified") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score/__init__.py b/langfuse/api/score/__init__.py new file mode 100644 index 000000000..3d0c7422a --- /dev/null +++ b/langfuse/api/score/__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 CreateScoreRequest, CreateScoreResponse +_dynamic_imports: typing.Dict[str, str] = { + "CreateScoreRequest": ".types", + "CreateScoreResponse": ".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__ = ["CreateScoreRequest", "CreateScoreResponse"] diff --git a/langfuse/api/score/client.py b/langfuse/api/score/client.py new file mode 100644 index 000000000..7a2ba1b83 --- /dev/null +++ b/langfuse/api/score/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 AsyncRawScoreClient, RawScoreClient +from .types.create_score_response import CreateScoreResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ScoreClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawScoreClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawScoreClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawScoreClient + """ + 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, + ) -> CreateScoreResponse: + """ + Create a score (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 + ------- + CreateScoreResponse + + 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.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 (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.score.delete( + score_id="scoreId", + ) + """ + _response = self._raw_client.delete(score_id, request_options=request_options) + return _response.data + + +class AsyncScoreClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawScoreClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawScoreClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawScoreClient + """ + 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, + ) -> CreateScoreResponse: + """ + Create a score (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 + ------- + CreateScoreResponse + + 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.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 (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.score.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/raw_client.py b/langfuse/api/score/raw_client.py new file mode 100644 index 000000000..7c20eee08 --- /dev/null +++ b/langfuse/api/score/raw_client.py @@ -0,0 +1,545 @@ +# 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 ..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_response import CreateScoreResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawScoreClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + 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, + ) -> HttpResponse[CreateScoreResponse]: + """ + Create a score (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 + ------- + HttpResponse[CreateScoreResponse] + """ + _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, + "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, + "configId": config_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateScoreResponse, + parse_obj_as( + type_=CreateScoreResponse, # 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 delete( + self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + Delete 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 + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/scores/{jsonable_encoder(score_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 AsyncRawScoreClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + 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, + ) -> AsyncHttpResponse[CreateScoreResponse]: + """ + Create a score (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 + ------- + AsyncHttpResponse[CreateScoreResponse] + """ + _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, + "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, + "configId": config_id, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateScoreResponse, + parse_obj_as( + type_=CreateScoreResponse, # 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 delete( + self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Delete 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 + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/scores/{jsonable_encoder(score_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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/score/types/__init__.py b/langfuse/api/score/types/__init__.py new file mode 100644 index 000000000..4a759a978 --- /dev/null +++ b/langfuse/api/score/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_request import CreateScoreRequest + from .create_score_response import CreateScoreResponse +_dynamic_imports: typing.Dict[str, str] = { + "CreateScoreRequest": ".create_score_request", + "CreateScoreResponse": ".create_score_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateScoreRequest", "CreateScoreResponse"] diff --git a/langfuse/api/score/types/create_score_request.py b/langfuse/api/score/types/create_score_request.py new file mode 100644 index 000000000..5491031ca --- /dev/null +++ b/langfuse/api/score/types/create_score_request.py @@ -0,0 +1,75 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +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 + + +class CreateScoreRequest(UniversalBaseModel): + """ + Examples + -------- + from langfuse.score import CreateScoreRequest + + CreateScoreRequest( + name="novelty", + value=0.9, + trace_id="cdef-1234-5678-90ab", + ) + """ + + id: typing.Optional[str] = None + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + value: CreateScoreValue = pydantic.Field() + """ + 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) + """ + + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. + """ + + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = pydantic.Field(default=None) + """ + The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue. + """ + + data_type: typing_extensions.Annotated[ + typing.Optional[ScoreDataType], FieldMetadata(alias="dataType") + ] = pydantic.Field(default=None) + """ + 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_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = pydantic.Field(default=None) + """ + 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. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score/types/create_score_response.py b/langfuse/api/score/types/create_score_response.py new file mode 100644 index 000000000..1c20c0f3a --- /dev/null +++ b/langfuse/api/score/types/create_score_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class CreateScoreResponse(UniversalBaseModel): + id: str = pydantic.Field() + """ + The id of the created object in Langfuse + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_configs/__init__.py b/langfuse/api/score_configs/__init__.py new file mode 100644 index 000000000..16f409522 --- /dev/null +++ b/langfuse/api/score_configs/__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 .types import CreateScoreConfigRequest, ScoreConfigs, UpdateScoreConfigRequest +_dynamic_imports: typing.Dict[str, str] = { + "CreateScoreConfigRequest": ".types", + "ScoreConfigs": ".types", + "UpdateScoreConfigRequest": ".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__ = ["CreateScoreConfigRequest", "ScoreConfigs", "UpdateScoreConfigRequest"] diff --git a/langfuse/api/score_configs/client.py b/langfuse/api/score_configs/client.py new file mode 100644 index 000000000..97df2b05a --- /dev/null +++ b/langfuse/api/score_configs/client.py @@ -0,0 +1,526 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..commons.types.config_category import ConfigCategory +from ..commons.types.score_config import ScoreConfig +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 AsyncRawScoreConfigsClient, RawScoreConfigsClient +from .types.score_configs import ScoreConfigs + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ScoreConfigsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawScoreConfigsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawScoreConfigsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawScoreConfigsClient + """ + return self._raw_client + + def create( + self, + *, + name: str, + data_type: ScoreDataType, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScoreConfig: + """ + Create a score configuration (config). Score configs are used to define the structure of scores + + Parameters + ---------- + name : str + + data_type : ScoreDataType + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfig + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.commons import ScoreDataType + + 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_configs.create( + name="name", + data_type=ScoreDataType.NUMERIC, + ) + """ + _response = self._raw_client.create( + name=name, + data_type=data_type, + categories=categories, + min_value=min_value, + max_value=max_value, + description=description, + request_options=request_options, + ) + return _response.data + + def get( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScoreConfigs: + """ + Get all score configs + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfigs + + 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_configs.get() + """ + _response = self._raw_client.get( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + def get_by_id( + self, config_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ScoreConfig: + """ + Get a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfig + + 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_configs.get_by_id( + config_id="configId", + ) + """ + _response = self._raw_client.get_by_id( + config_id, request_options=request_options + ) + return _response.data + + def update( + self, + config_id: str, + *, + is_archived: typing.Optional[bool] = OMIT, + name: typing.Optional[str] = OMIT, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScoreConfig: + """ + Update a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + is_archived : typing.Optional[bool] + The status of the score config showing if it is archived or not + + name : typing.Optional[str] + The name of the score config + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfig + + 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_configs.update( + config_id="configId", + ) + """ + _response = self._raw_client.update( + config_id, + is_archived=is_archived, + name=name, + categories=categories, + min_value=min_value, + max_value=max_value, + description=description, + request_options=request_options, + ) + return _response.data + + +class AsyncScoreConfigsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawScoreConfigsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawScoreConfigsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawScoreConfigsClient + """ + return self._raw_client + + async def create( + self, + *, + name: str, + data_type: ScoreDataType, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScoreConfig: + """ + Create a score configuration (config). Score configs are used to define the structure of scores + + Parameters + ---------- + name : str + + data_type : ScoreDataType + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfig + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.commons import ScoreDataType + + 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_configs.create( + name="name", + data_type=ScoreDataType.NUMERIC, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + name=name, + data_type=data_type, + categories=categories, + min_value=min_value, + max_value=max_value, + description=description, + request_options=request_options, + ) + return _response.data + + async def get( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScoreConfigs: + """ + Get all score configs + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfigs + + 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_configs.get() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def get_by_id( + self, config_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ScoreConfig: + """ + Get a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfig + + 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_configs.get_by_id( + config_id="configId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_by_id( + config_id, request_options=request_options + ) + return _response.data + + async def update( + self, + config_id: str, + *, + is_archived: typing.Optional[bool] = OMIT, + name: typing.Optional[str] = OMIT, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ScoreConfig: + """ + Update a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + is_archived : typing.Optional[bool] + The status of the score config showing if it is archived or not + + name : typing.Optional[str] + The name of the score config + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ScoreConfig + + 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_configs.update( + config_id="configId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + config_id, + is_archived=is_archived, + name=name, + categories=categories, + min_value=min_value, + max_value=max_value, + description=description, + request_options=request_options, + ) + return _response.data diff --git a/langfuse/api/score_configs/raw_client.py b/langfuse/api/score_configs/raw_client.py new file mode 100644 index 000000000..733f79214 --- /dev/null +++ b/langfuse/api/score_configs/raw_client.py @@ -0,0 +1,1012 @@ +# 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 ..commons.types.config_category import ConfigCategory +from ..commons.types.score_config import ScoreConfig +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.score_configs import ScoreConfigs + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawScoreConfigsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + *, + name: str, + data_type: ScoreDataType, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ScoreConfig]: + """ + Create a score configuration (config). Score configs are used to define the structure of scores + + Parameters + ---------- + name : str + + data_type : ScoreDataType + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ScoreConfig] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/score-configs", + method="POST", + json={ + "name": name, + "dataType": data_type, + "categories": convert_and_respect_annotation_metadata( + object_=categories, + annotation=typing.Sequence[ConfigCategory], + direction="write", + ), + "minValue": min_value, + "maxValue": max_value, + "description": description, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfig, + parse_obj_as( + type_=ScoreConfig, # 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( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ScoreConfigs]: + """ + Get all score configs + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ScoreConfigs] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/score-configs", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfigs, + parse_obj_as( + type_=ScoreConfigs, # 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_by_id( + self, config_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ScoreConfig]: + """ + Get a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ScoreConfig] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/score-configs/{jsonable_encoder(config_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfig, + parse_obj_as( + type_=ScoreConfig, # 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 update( + self, + config_id: str, + *, + is_archived: typing.Optional[bool] = OMIT, + name: typing.Optional[str] = OMIT, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ScoreConfig]: + """ + Update a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + is_archived : typing.Optional[bool] + The status of the score config showing if it is archived or not + + name : typing.Optional[str] + The name of the score config + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ScoreConfig] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/score-configs/{jsonable_encoder(config_id)}", + method="PATCH", + json={ + "isArchived": is_archived, + "name": name, + "categories": convert_and_respect_annotation_metadata( + object_=categories, + annotation=typing.Sequence[ConfigCategory], + direction="write", + ), + "minValue": min_value, + "maxValue": max_value, + "description": description, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfig, + parse_obj_as( + type_=ScoreConfig, # 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 AsyncRawScoreConfigsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + *, + name: str, + data_type: ScoreDataType, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ScoreConfig]: + """ + Create a score configuration (config). Score configs are used to define the structure of scores + + Parameters + ---------- + name : str + + data_type : ScoreDataType + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ScoreConfig] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/score-configs", + method="POST", + json={ + "name": name, + "dataType": data_type, + "categories": convert_and_respect_annotation_metadata( + object_=categories, + annotation=typing.Sequence[ConfigCategory], + direction="write", + ), + "minValue": min_value, + "maxValue": max_value, + "description": description, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfig, + parse_obj_as( + type_=ScoreConfig, # 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( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ScoreConfigs]: + """ + Get all score configs + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ScoreConfigs] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/score-configs", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfigs, + parse_obj_as( + type_=ScoreConfigs, # 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_by_id( + self, config_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ScoreConfig]: + """ + Get a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ScoreConfig] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/score-configs/{jsonable_encoder(config_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfig, + parse_obj_as( + type_=ScoreConfig, # 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 update( + self, + config_id: str, + *, + is_archived: typing.Optional[bool] = OMIT, + name: typing.Optional[str] = OMIT, + categories: typing.Optional[typing.Sequence[ConfigCategory]] = OMIT, + min_value: typing.Optional[float] = OMIT, + max_value: typing.Optional[float] = OMIT, + description: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ScoreConfig]: + """ + Update a score config + + Parameters + ---------- + config_id : str + The unique langfuse identifier of a score config + + is_archived : typing.Optional[bool] + The status of the score config showing if it is archived or not + + name : typing.Optional[str] + The name of the score config + + categories : typing.Optional[typing.Sequence[ConfigCategory]] + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + + min_value : typing.Optional[float] + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + + max_value : typing.Optional[float] + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + + description : typing.Optional[str] + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ScoreConfig] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/score-configs/{jsonable_encoder(config_id)}", + method="PATCH", + json={ + "isArchived": is_archived, + "name": name, + "categories": convert_and_respect_annotation_metadata( + object_=categories, + annotation=typing.Sequence[ConfigCategory], + direction="write", + ), + "minValue": min_value, + "maxValue": max_value, + "description": description, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ScoreConfig, + parse_obj_as( + type_=ScoreConfig, # 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/score_configs/types/__init__.py b/langfuse/api/score_configs/types/__init__.py new file mode 100644 index 000000000..10ef4f679 --- /dev/null +++ b/langfuse/api/score_configs/types/__init__.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_score_config_request import CreateScoreConfigRequest + from .score_configs import ScoreConfigs + from .update_score_config_request import UpdateScoreConfigRequest +_dynamic_imports: typing.Dict[str, str] = { + "CreateScoreConfigRequest": ".create_score_config_request", + "ScoreConfigs": ".score_configs", + "UpdateScoreConfigRequest": ".update_score_config_request", +} + + +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__ = ["CreateScoreConfigRequest", "ScoreConfigs", "UpdateScoreConfigRequest"] diff --git a/langfuse/api/score_configs/types/create_score_config_request.py b/langfuse/api/score_configs/types/create_score_config_request.py new file mode 100644 index 000000000..acd2d1dc3 --- /dev/null +++ b/langfuse/api/score_configs/types/create_score_config_request.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...commons.types.config_category import ConfigCategory +from ...commons.types.score_data_type import ScoreDataType +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class CreateScoreConfigRequest(UniversalBaseModel): + name: str + data_type: typing_extensions.Annotated[ + ScoreDataType, FieldMetadata(alias="dataType") + ] + categories: typing.Optional[typing.List[ConfigCategory]] = pydantic.Field( + default=None + ) + """ + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + """ + + min_value: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="minValue") + ] = pydantic.Field(default=None) + """ + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + """ + + max_value: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="maxValue") + ] = pydantic.Field(default=None) + """ + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_configs/types/score_configs.py b/langfuse/api/score_configs/types/score_configs.py new file mode 100644 index 000000000..d19763e9f --- /dev/null +++ b/langfuse/api/score_configs/types/score_configs.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.score_config import ScoreConfig +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class ScoreConfigs(UniversalBaseModel): + data: typing.List[ScoreConfig] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_configs/types/update_score_config_request.py b/langfuse/api/score_configs/types/update_score_config_request.py new file mode 100644 index 000000000..5237c544f --- /dev/null +++ b/langfuse/api/score_configs/types/update_score_config_request.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...commons.types.config_category import ConfigCategory +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class UpdateScoreConfigRequest(UniversalBaseModel): + is_archived: typing_extensions.Annotated[ + typing.Optional[bool], FieldMetadata(alias="isArchived") + ] = pydantic.Field(default=None) + """ + The status of the score config showing if it is archived or not + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the score config + """ + + categories: typing.Optional[typing.List[ConfigCategory]] = pydantic.Field( + default=None + ) + """ + Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed + """ + + min_value: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="minValue") + ] = pydantic.Field(default=None) + """ + Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞ + """ + + max_value: typing_extensions.Annotated[ + typing.Optional[float], FieldMetadata(alias="maxValue") + ] = pydantic.Field(default=None) + """ + Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞ + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_v2/__init__.py b/langfuse/api/score_v2/__init__.py new file mode 100644 index 000000000..2d0fcec36 --- /dev/null +++ b/langfuse/api/score_v2/__init__.py @@ -0,0 +1,70 @@ +# 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, + GetScoresResponseDataNumeric, + GetScoresResponseData_Boolean, + GetScoresResponseData_Categorical, + GetScoresResponseData_Numeric, + GetScoresResponseTraceData, + ) +_dynamic_imports: typing.Dict[str, str] = { + "GetScoresResponse": ".types", + "GetScoresResponseData": ".types", + "GetScoresResponseDataBoolean": ".types", + "GetScoresResponseDataCategorical": ".types", + "GetScoresResponseDataNumeric": ".types", + "GetScoresResponseData_Boolean": ".types", + "GetScoresResponseData_Categorical": ".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", + "GetScoresResponseDataNumeric", + "GetScoresResponseData_Boolean", + "GetScoresResponseData_Categorical", + "GetScoresResponseData_Numeric", + "GetScoresResponseTraceData", +] diff --git a/langfuse/api/score_v2/client.py b/langfuse/api/score_v2/client.py new file mode 100644 index 000000000..a4eefb828 --- /dev/null +++ b/langfuse/api/score_v2/client.py @@ -0,0 +1,390 @@ +# 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, + queue_id: typing.Optional[str] = None, + data_type: typing.Optional[ScoreDataType] = None, + trace_tags: typing.Optional[typing.Union[str, typing.Sequence[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. + + 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. + + 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, + queue_id=queue_id, + data_type=data_type, + trace_tags=trace_tags, + 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, + queue_id: typing.Optional[str] = None, + data_type: typing.Optional[ScoreDataType] = None, + trace_tags: typing.Optional[typing.Union[str, typing.Sequence[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. + + 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. + + 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, + queue_id=queue_id, + data_type=data_type, + trace_tags=trace_tags, + 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/resources/score_v_2/client.py b/langfuse/api/score_v2/raw_client.py similarity index 56% rename from langfuse/api/resources/score_v_2/client.py rename to langfuse/api/score_v2/raw_client.py index 04569dc3a..0fb0b1836 100644 --- a/langfuse/api/resources/score_v_2/client.py +++ b/langfuse/api/score_v2/raw_client.py @@ -4,12 +4,6 @@ import typing from json.decoder import JSONDecodeError -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.datetime_utils import serialize_datetime -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 @@ -18,10 +12,17 @@ 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 ScoreV2Client: +class RawScoreV2Client: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper @@ -47,7 +48,7 @@ def get( data_type: typing.Optional[ScoreDataType] = None, trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> GetScoresResponse: + ) -> HttpResponse[GetScoresResponse]: """ Get a list of scores (supports both trace and session scores) @@ -112,21 +113,7 @@ def get( Returns ------- - GetScoresResponse - - 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.score_v_2.get() + HttpResponse[GetScoresResponse] """ _response = self._client_wrapper.httpx_client.request( "api/public/v2/scores", @@ -159,33 +146,85 @@ def get( ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(GetScoresResponse, _response.json()) # type: ignore + _data = typing.cast( + GetScoresResponse, + parse_obj_as( + type_=GetScoresResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: - raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) + 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_by_id( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> Score: + ) -> HttpResponse[Score]: """ Get a score (supports both trace and session scores) @@ -199,23 +238,7 @@ def get_by_id( Returns ------- - Score - - 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.score_v_2.get_by_id( - score_id="scoreId", - ) + HttpResponse[Score] """ _response = self._client_wrapper.httpx_client.request( f"api/public/v2/scores/{jsonable_encoder(score_id)}", @@ -224,32 +247,84 @@ def get_by_id( ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Score, _response.json()) # type: ignore + _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(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) + 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 AsyncScoreV2Client: +class AsyncRawScoreV2Client: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper @@ -275,7 +350,7 @@ async def get( data_type: typing.Optional[ScoreDataType] = None, trace_tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> GetScoresResponse: + ) -> AsyncHttpResponse[GetScoresResponse]: """ Get a list of scores (supports both trace and session scores) @@ -340,29 +415,7 @@ async def get( Returns ------- - GetScoresResponse - - 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.score_v_2.get() - - - asyncio.run(main()) + AsyncHttpResponse[GetScoresResponse] """ _response = await self._client_wrapper.httpx_client.request( "api/public/v2/scores", @@ -395,33 +448,85 @@ async def main() -> None: ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(GetScoresResponse, _response.json()) # type: ignore + _data = typing.cast( + GetScoresResponse, + parse_obj_as( + type_=GetScoresResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: - raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) + 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_by_id( self, score_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> Score: + ) -> AsyncHttpResponse[Score]: """ Get a score (supports both trace and session scores) @@ -435,31 +540,7 @@ async def get_by_id( Returns ------- - Score - - 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.score_v_2.get_by_id( - score_id="scoreId", - ) - - - asyncio.run(main()) + AsyncHttpResponse[Score] """ _response = await self._client_wrapper.httpx_client.request( f"api/public/v2/scores/{jsonable_encoder(score_id)}", @@ -468,26 +549,78 @@ async def main() -> None: ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Score, _response.json()) # type: ignore + _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(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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( - pydantic_v1.parse_obj_as(typing.Any, _response.json()) - ) # type: ignore + 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, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) + 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/score_v2/types/__init__.py b/langfuse/api/score_v2/types/__init__.py new file mode 100644 index 000000000..d54db47b8 --- /dev/null +++ b/langfuse/api/score_v2/types/__init__.py @@ -0,0 +1,70 @@ +# 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_Numeric, + ) + from .get_scores_response_data_boolean import GetScoresResponseDataBoolean + from .get_scores_response_data_categorical import GetScoresResponseDataCategorical + 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", + "GetScoresResponseDataNumeric": ".get_scores_response_data_numeric", + "GetScoresResponseData_Boolean": ".get_scores_response_data", + "GetScoresResponseData_Categorical": ".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", + "GetScoresResponseDataNumeric", + "GetScoresResponseData_Boolean", + "GetScoresResponseData_Categorical", + "GetScoresResponseData_Numeric", + "GetScoresResponseTraceData", +] diff --git a/langfuse/api/score_v2/types/get_scores_response.py b/langfuse/api/score_v2/types/get_scores_response.py new file mode 100644 index 000000000..0ca4d0e40 --- /dev/null +++ b/langfuse/api/score_v2/types/get_scores_response.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse +from .get_scores_response_data import GetScoresResponseData + + +class GetScoresResponse(UniversalBaseModel): + data: typing.List[GetScoresResponseData] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_v2/types/get_scores_response_data.py b/langfuse/api/score_v2/types/get_scores_response_data.py new file mode 100644 index 000000000..ffa3bcb9f --- /dev/null +++ b/langfuse/api/score_v2/types/get_scores_response_data.py @@ -0,0 +1,163 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...commons.types.score_source import ScoreSource +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .get_scores_response_trace_data import GetScoresResponseTraceData + + +class GetScoresResponseData_Numeric(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["NUMERIC"], FieldMetadata(alias="dataType") + ] = "NUMERIC" + trace: typing.Optional[GetScoresResponseTraceData] = None + value: float + id: str + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + source: ScoreSource + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class GetScoresResponseData_Categorical(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["CATEGORICAL"], FieldMetadata(alias="dataType") + ] = "CATEGORICAL" + trace: typing.Optional[GetScoresResponseTraceData] = None + value: float + string_value: typing_extensions.Annotated[str, FieldMetadata(alias="stringValue")] + id: str + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + source: ScoreSource + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class GetScoresResponseData_Boolean(UniversalBaseModel): + data_type: typing_extensions.Annotated[ + typing.Literal["BOOLEAN"], FieldMetadata(alias="dataType") + ] = "BOOLEAN" + trace: typing.Optional[GetScoresResponseTraceData] = None + value: float + string_value: typing_extensions.Annotated[str, FieldMetadata(alias="stringValue")] + id: str + trace_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="traceId") + ] = None + session_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="sessionId") + ] = None + observation_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="observationId") + ] = None + dataset_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="datasetRunId") + ] = None + name: str + source: ScoreSource + timestamp: dt.datetime + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + author_user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="authorUserId") + ] = None + comment: typing.Optional[str] = None + metadata: typing.Optional[typing.Any] = None + config_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="configId") + ] = None + queue_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="queueId") + ] = None + environment: typing.Optional[str] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +GetScoresResponseData = typing_extensions.Annotated[ + typing.Union[ + GetScoresResponseData_Numeric, + GetScoresResponseData_Categorical, + GetScoresResponseData_Boolean, + ], + pydantic.Field(discriminator="data_type"), +] diff --git a/langfuse/api/score_v2/types/get_scores_response_data_boolean.py b/langfuse/api/score_v2/types/get_scores_response_data_boolean.py new file mode 100644 index 000000000..eee645cd3 --- /dev/null +++ b/langfuse/api/score_v2/types/get_scores_response_data_boolean.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.boolean_score import BooleanScore +from .get_scores_response_trace_data import GetScoresResponseTraceData + + +class GetScoresResponseDataBoolean(BooleanScore): + trace: typing.Optional[GetScoresResponseTraceData] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_v2/types/get_scores_response_data_categorical.py b/langfuse/api/score_v2/types/get_scores_response_data_categorical.py new file mode 100644 index 000000000..edd4e46fa --- /dev/null +++ b/langfuse/api/score_v2/types/get_scores_response_data_categorical.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.categorical_score import CategoricalScore +from .get_scores_response_trace_data import GetScoresResponseTraceData + + +class GetScoresResponseDataCategorical(CategoricalScore): + trace: typing.Optional[GetScoresResponseTraceData] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_v2/types/get_scores_response_data_numeric.py b/langfuse/api/score_v2/types/get_scores_response_data_numeric.py new file mode 100644 index 000000000..350be0e0c --- /dev/null +++ b/langfuse/api/score_v2/types/get_scores_response_data_numeric.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.numeric_score import NumericScore +from .get_scores_response_trace_data import GetScoresResponseTraceData + + +class GetScoresResponseDataNumeric(NumericScore): + trace: typing.Optional[GetScoresResponseTraceData] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/score_v2/types/get_scores_response_trace_data.py b/langfuse/api/score_v2/types/get_scores_response_trace_data.py new file mode 100644 index 000000000..674057172 --- /dev/null +++ b/langfuse/api/score_v2/types/get_scores_response_trace_data.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class GetScoresResponseTraceData(UniversalBaseModel): + user_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="userId") + ] = pydantic.Field(default=None) + """ + The user ID associated with the trace referenced by score + """ + + tags: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of tags associated with the trace referenced by score + """ + + environment: typing.Optional[str] = pydantic.Field(default=None) + """ + The environment of the trace referenced by score + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/sessions/__init__.py b/langfuse/api/sessions/__init__.py new file mode 100644 index 000000000..89b7e63bc --- /dev/null +++ b/langfuse/api/sessions/__init__.py @@ -0,0 +1,40 @@ +# 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 PaginatedSessions +_dynamic_imports: typing.Dict[str, str] = {"PaginatedSessions": ".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__ = ["PaginatedSessions"] diff --git a/langfuse/api/sessions/client.py b/langfuse/api/sessions/client.py new file mode 100644 index 000000000..b99829feb --- /dev/null +++ b/langfuse/api/sessions/client.py @@ -0,0 +1,262 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..commons.types.session_with_traces import SessionWithTraces +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawSessionsClient, RawSessionsClient +from .types.paginated_sessions import PaginatedSessions + + +class SessionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSessionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSessionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSessionsClient + """ + return self._raw_client + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = 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, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedSessions: + """ + Get sessions + + 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. + + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created on or after a certain datetime (ISO 8601) + + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created before a certain datetime (ISO 8601) + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for sessions where the environment is one of the provided values. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedSessions + + 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.sessions.list() + """ + _response = self._raw_client.list( + page=page, + limit=limit, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + environment=environment, + request_options=request_options, + ) + return _response.data + + def get( + self, + session_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> SessionWithTraces: + """ + Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=` + + Parameters + ---------- + session_id : str + The unique id of a session + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SessionWithTraces + + 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.sessions.get( + session_id="sessionId", + ) + """ + _response = self._raw_client.get(session_id, request_options=request_options) + return _response.data + + +class AsyncSessionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSessionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSessionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSessionsClient + """ + return self._raw_client + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = 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, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedSessions: + """ + Get sessions + + 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. + + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created on or after a certain datetime (ISO 8601) + + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created before a certain datetime (ISO 8601) + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for sessions where the environment is one of the provided values. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedSessions + + 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.sessions.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + page=page, + limit=limit, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + environment=environment, + request_options=request_options, + ) + return _response.data + + async def get( + self, + session_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> SessionWithTraces: + """ + Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=` + + Parameters + ---------- + session_id : str + The unique id of a session + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SessionWithTraces + + 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.sessions.get( + session_id="sessionId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + session_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/sessions/raw_client.py b/langfuse/api/sessions/raw_client.py new file mode 100644 index 000000000..9c39a9a5a --- /dev/null +++ b/langfuse/api/sessions/raw_client.py @@ -0,0 +1,500 @@ +# 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.session_with_traces import SessionWithTraces +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.paginated_sessions import PaginatedSessions + + +class RawSessionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = 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, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedSessions]: + """ + Get sessions + + 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. + + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created on or after a certain datetime (ISO 8601) + + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created before a certain datetime (ISO 8601) + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for sessions where the environment is one of the provided values. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedSessions] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/sessions", + method="GET", + params={ + "page": page, + "limit": limit, + "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, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedSessions, + parse_obj_as( + type_=PaginatedSessions, # 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( + self, + session_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SessionWithTraces]: + """ + Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=` + + Parameters + ---------- + session_id : str + The unique id of a session + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SessionWithTraces] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/sessions/{jsonable_encoder(session_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SessionWithTraces, + parse_obj_as( + type_=SessionWithTraces, # 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 AsyncRawSessionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = 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, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PaginatedSessions]: + """ + Get sessions + + 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. + + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created on or after a certain datetime (ISO 8601) + + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include sessions created before a certain datetime (ISO 8601) + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Optional filter for sessions where the environment is one of the provided values. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedSessions] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/sessions", + method="GET", + params={ + "page": page, + "limit": limit, + "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, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedSessions, + parse_obj_as( + type_=PaginatedSessions, # 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( + self, + session_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SessionWithTraces]: + """ + Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=` + + Parameters + ---------- + session_id : str + The unique id of a session + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SessionWithTraces] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/sessions/{jsonable_encoder(session_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SessionWithTraces, + parse_obj_as( + type_=SessionWithTraces, # 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/sessions/types/__init__.py b/langfuse/api/sessions/types/__init__.py new file mode 100644 index 000000000..45f21e139 --- /dev/null +++ b/langfuse/api/sessions/types/__init__.py @@ -0,0 +1,40 @@ +# 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 .paginated_sessions import PaginatedSessions +_dynamic_imports: typing.Dict[str, str] = {"PaginatedSessions": ".paginated_sessions"} + + +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__ = ["PaginatedSessions"] diff --git a/langfuse/api/sessions/types/paginated_sessions.py b/langfuse/api/sessions/types/paginated_sessions.py new file mode 100644 index 000000000..5d7bd3886 --- /dev/null +++ b/langfuse/api/sessions/types/paginated_sessions.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.session import Session +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class PaginatedSessions(UniversalBaseModel): + data: typing.List[Session] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/trace/__init__.py b/langfuse/api/trace/__init__.py new file mode 100644 index 000000000..fc9889dfa --- /dev/null +++ b/langfuse/api/trace/__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 .types import DeleteTraceResponse, Sort, Traces +_dynamic_imports: typing.Dict[str, str] = { + "DeleteTraceResponse": ".types", + "Sort": ".types", + "Traces": ".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__ = ["DeleteTraceResponse", "Sort", "Traces"] diff --git a/langfuse/api/resources/trace/client.py b/langfuse/api/trace/client.py similarity index 61% rename from langfuse/api/resources/trace/client.py rename to langfuse/api/trace/client.py index e1f837f50..e5dcbb33c 100644 --- a/langfuse/api/resources/trace/client.py +++ b/langfuse/api/trace/client.py @@ -2,20 +2,11 @@ import datetime as dt import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.datetime_utils import serialize_datetime -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 ..commons.types.trace_with_full_details import TraceWithFullDetails +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawTraceClient, RawTraceClient from .types.delete_trace_response import DeleteTraceResponse from .types.traces import Traces @@ -25,7 +16,18 @@ class TraceClient: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = RawTraceClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTraceClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTraceClient + """ + return self._raw_client def get( self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None @@ -47,9 +49,9 @@ def get( Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -61,36 +63,8 @@ def get( trace_id="traceId", ) """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/traces/{jsonable_encoder(trace_id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(TraceWithFullDetails, _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) + _response = self._raw_client.get(trace_id, request_options=request_options) + return _response.data def delete( self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None @@ -112,9 +86,9 @@ def delete( Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -126,36 +100,8 @@ def delete( trace_id="traceId", ) """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/traces/{jsonable_encoder(trace_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DeleteTraceResponse, _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) + _response = self._raw_client.delete(trace_id, request_options=request_options) + return _response.data def list( self, @@ -333,9 +279,9 @@ def list( Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -345,56 +291,24 @@ def list( ) client.trace.list() """ - _response = self._client_wrapper.httpx_client.request( - "api/public/traces", - method="GET", - params={ - "page": page, - "limit": limit, - "userId": user_id, - "name": name, - "sessionId": session_id, - "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, - "orderBy": order_by, - "tags": tags, - "version": version, - "release": release, - "environment": environment, - "fields": fields, - "filter": filter, - }, + _response = self._raw_client.list( + page=page, + limit=limit, + user_id=user_id, + name=name, + session_id=session_id, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + order_by=order_by, + tags=tags, + version=version, + release=release, + environment=environment, + fields=fields, + filter=filter, request_options=request_options, ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Traces, _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) + return _response.data def delete_multiple( self, @@ -419,9 +333,9 @@ def delete_multiple( Examples -------- - from langfuse.client import FernLangfuse + from langfuse import LangfuseAPI - client = FernLangfuse( + 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", @@ -433,43 +347,26 @@ def delete_multiple( trace_ids=["traceIds", "traceIds"], ) """ - _response = self._client_wrapper.httpx_client.request( - "api/public/traces", - method="DELETE", - json={"traceIds": trace_ids}, - request_options=request_options, - omit=OMIT, + _response = self._raw_client.delete_multiple( + trace_ids=trace_ids, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DeleteTraceResponse, _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) + return _response.data class AsyncTraceClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper + self._raw_client = AsyncRawTraceClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTraceClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTraceClient + """ + return self._raw_client async def get( self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None @@ -493,9 +390,9 @@ async def get( -------- import asyncio - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -513,36 +410,10 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/traces/{jsonable_encoder(trace_id)}", - method="GET", - request_options=request_options, + _response = await self._raw_client.get( + trace_id, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(TraceWithFullDetails, _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) + return _response.data async def delete( self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None @@ -566,9 +437,9 @@ async def delete( -------- import asyncio - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -586,36 +457,10 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/traces/{jsonable_encoder(trace_id)}", - method="DELETE", - request_options=request_options, + _response = await self._raw_client.delete( + trace_id, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DeleteTraceResponse, _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) + return _response.data async def list( self, @@ -795,9 +640,9 @@ async def list( -------- import asyncio - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -813,56 +658,24 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/traces", - method="GET", - params={ - "page": page, - "limit": limit, - "userId": user_id, - "name": name, - "sessionId": session_id, - "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, - "orderBy": order_by, - "tags": tags, - "version": version, - "release": release, - "environment": environment, - "fields": fields, - "filter": filter, - }, + _response = await self._raw_client.list( + page=page, + limit=limit, + user_id=user_id, + name=name, + session_id=session_id, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + order_by=order_by, + tags=tags, + version=version, + release=release, + environment=environment, + fields=fields, + filter=filter, request_options=request_options, ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(Traces, _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) + return _response.data async def delete_multiple( self, @@ -889,9 +702,9 @@ async def delete_multiple( -------- import asyncio - from langfuse.client import AsyncFernLangfuse + from langfuse import AsyncLangfuseAPI - client = AsyncFernLangfuse( + 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", @@ -909,35 +722,7 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/traces", - method="DELETE", - json={"traceIds": trace_ids}, - request_options=request_options, - omit=OMIT, + _response = await self._raw_client.delete_multiple( + trace_ids=trace_ids, request_options=request_options ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(DeleteTraceResponse, _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) + return _response.data diff --git a/langfuse/api/trace/raw_client.py b/langfuse/api/trace/raw_client.py new file mode 100644 index 000000000..10f3d15ec --- /dev/null +++ b/langfuse/api/trace/raw_client.py @@ -0,0 +1,1208 @@ +# 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.trace_with_full_details import TraceWithFullDetails +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.delete_trace_response import DeleteTraceResponse +from .types.traces import Traces + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTraceClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get( + self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[TraceWithFullDetails]: + """ + Get a specific trace + + Parameters + ---------- + trace_id : str + The unique langfuse identifier of a trace + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TraceWithFullDetails] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/traces/{jsonable_encoder(trace_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TraceWithFullDetails, + parse_obj_as( + type_=TraceWithFullDetails, # 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 delete( + self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeleteTraceResponse]: + """ + Delete a specific trace + + Parameters + ---------- + trace_id : str + The unique langfuse identifier of the trace to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteTraceResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/traces/{jsonable_encoder(trace_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTraceResponse, + parse_obj_as( + type_=DeleteTraceResponse, # 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 list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + user_id: typing.Optional[str] = None, + name: typing.Optional[str] = None, + session_id: typing.Optional[str] = None, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + order_by: typing.Optional[str] = None, + tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + version: typing.Optional[str] = None, + release: typing.Optional[str] = None, + environment: 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[Traces]: + """ + Get list of traces + + 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] + + name : typing.Optional[str] + + session_id : typing.Optional[str] + + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include traces with a trace.timestamp on or after a certain datetime (ISO 8601) + + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include traces with a trace.timestamp before a certain datetime (ISO 8601) + + order_by : typing.Optional[str] + Format of the string [field].[asc/desc]. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: timestamp.asc + + tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Only traces that include all of these tags will be returned. + + version : typing.Optional[str] + Optional filter to only include traces with a certain version. + + release : typing.Optional[str] + Optional filter to only include traces with a certain release. + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + 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: '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'. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, sessionId, tags, version, release, environment, fromTimestamp, toTimestamp). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Trace Fields + - `id` (string) - Trace ID + - `name` (string) - Trace name + - `timestamp` (datetime) - Trace timestamp + - `userId` (string) - User ID + - `sessionId` (string) - Session ID + - `environment` (string) - Environment tag + - `version` (string) - Version tag + - `release` (string) - Release tag + - `tags` (arrayOptions) - Array of tags + - `bookmarked` (boolean) - Bookmark status + + ### Structured Data + - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. + + ### Aggregated Metrics (from observations) + These metrics are aggregated from all observations within the trace: + - `latency` (number) - Latency in seconds (time from first observation start to last observation end) + - `inputTokens` (number) - Total input tokens across all observations + - `outputTokens` (number) - Total output tokens across all observations + - `totalTokens` (number) - Total tokens (alias: `tokens`) + - `inputCost` (number) - Total input cost in USD + - `outputCost` (number) - Total output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Observation Level Aggregations + These fields aggregate observation levels within the trace: + - `level` (string) - Highest severity level (ERROR > WARNING > DEFAULT > DEBUG) + - `warningCount` (number) - Count of WARNING level observations + - `errorCount` (number) - Count of ERROR level observations + - `defaultCount` (number) - Count of DEFAULT level observations + - `debugCount` (number) - Count of DEBUG level observations + + ### Scores (requires join with scores table) + - `scores_avg` (number) - Average of numeric scores (alias: `scores`) + - `score_categories` (categoryOptions) - Categorical score values + + ## Filter Examples + ```json + [ + { + "type": "datetime", + "column": "timestamp", + "operator": ">=", + "value": "2024-01-01T00:00:00Z" + }, + { + "type": "string", + "column": "userId", + "operator": "=", + "value": "user-123" + }, + { + "type": "number", + "column": "totalCost", + "operator": ">=", + "value": 0.01 + }, + { + "type": "arrayOptions", + "column": "tags", + "operator": "all of", + "value": ["production", "critical"] + }, + { + "type": "stringObject", + "column": "metadata", + "key": "customer_tier", + "operator": "=", + "value": "enterprise" + } + ] + ``` + + ## Performance Notes + - Filtering on `userId`, `sessionId`, or `metadata` may enable skip indexes for better query performance + - Score filters require a join with the scores table and may impact query performance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Traces] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/traces", + method="GET", + params={ + "page": page, + "limit": limit, + "userId": user_id, + "name": name, + "sessionId": session_id, + "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, + "orderBy": order_by, + "tags": tags, + "version": version, + "release": release, + "environment": environment, + "fields": fields, + "filter": filter, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Traces, + parse_obj_as( + type_=Traces, # 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 delete_multiple( + self, + *, + trace_ids: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteTraceResponse]: + """ + Delete multiple traces + + Parameters + ---------- + trace_ids : typing.Sequence[str] + List of trace IDs to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteTraceResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/traces", + method="DELETE", + json={ + "traceIds": trace_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTraceResponse, + parse_obj_as( + type_=DeleteTraceResponse, # 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 AsyncRawTraceClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get( + self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TraceWithFullDetails]: + """ + Get a specific trace + + Parameters + ---------- + trace_id : str + The unique langfuse identifier of a trace + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TraceWithFullDetails] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/traces/{jsonable_encoder(trace_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TraceWithFullDetails, + parse_obj_as( + type_=TraceWithFullDetails, # 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 delete( + self, trace_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeleteTraceResponse]: + """ + Delete a specific trace + + Parameters + ---------- + trace_id : str + The unique langfuse identifier of the trace to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteTraceResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/traces/{jsonable_encoder(trace_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTraceResponse, + parse_obj_as( + type_=DeleteTraceResponse, # 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 list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + user_id: typing.Optional[str] = None, + name: typing.Optional[str] = None, + session_id: typing.Optional[str] = None, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + order_by: typing.Optional[str] = None, + tags: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, + version: typing.Optional[str] = None, + release: typing.Optional[str] = None, + environment: 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[Traces]: + """ + Get list of traces + + 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] + + name : typing.Optional[str] + + session_id : typing.Optional[str] + + from_timestamp : typing.Optional[dt.datetime] + Optional filter to only include traces with a trace.timestamp on or after a certain datetime (ISO 8601) + + to_timestamp : typing.Optional[dt.datetime] + Optional filter to only include traces with a trace.timestamp before a certain datetime (ISO 8601) + + order_by : typing.Optional[str] + Format of the string [field].[asc/desc]. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: timestamp.asc + + tags : typing.Optional[typing.Union[str, typing.Sequence[str]]] + Only traces that include all of these tags will be returned. + + version : typing.Optional[str] + Optional filter to only include traces with a certain version. + + release : typing.Optional[str] + Optional filter to only include traces with a certain release. + + environment : typing.Optional[typing.Union[str, typing.Sequence[str]]] + 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: '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'. + + filter : typing.Optional[str] + JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, sessionId, tags, version, release, environment, fromTimestamp, toTimestamp). + + ## Filter Structure + Each filter condition has the following structure: + ```json + [ + { + "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null" + "column": string, // Required. Column to filter on (see available columns below) + "operator": string, // Required. Operator based on type: + // - datetime: ">", "<", ">=", "<=" + // - string: "=", "contains", "does not contain", "starts with", "ends with" + // - stringOptions: "any of", "none of" + // - categoryOptions: "any of", "none of" + // - arrayOptions: "any of", "none of", "all of" + // - number: "=", ">", "<", ">=", "<=" + // - stringObject: "=", "contains", "does not contain", "starts with", "ends with" + // - numberObject: "=", ">", "<", ">=", "<=" + // - boolean: "=", "<>" + // - null: "is null", "is not null" + "value": any, // Required (except for null type). Value to compare against. Type depends on filter type + "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata + } + ] + ``` + + ## Available Columns + + ### Core Trace Fields + - `id` (string) - Trace ID + - `name` (string) - Trace name + - `timestamp` (datetime) - Trace timestamp + - `userId` (string) - User ID + - `sessionId` (string) - Session ID + - `environment` (string) - Environment tag + - `version` (string) - Version tag + - `release` (string) - Release tag + - `tags` (arrayOptions) - Array of tags + - `bookmarked` (boolean) - Bookmark status + + ### Structured Data + - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys. + + ### Aggregated Metrics (from observations) + These metrics are aggregated from all observations within the trace: + - `latency` (number) - Latency in seconds (time from first observation start to last observation end) + - `inputTokens` (number) - Total input tokens across all observations + - `outputTokens` (number) - Total output tokens across all observations + - `totalTokens` (number) - Total tokens (alias: `tokens`) + - `inputCost` (number) - Total input cost in USD + - `outputCost` (number) - Total output cost in USD + - `totalCost` (number) - Total cost in USD + + ### Observation Level Aggregations + These fields aggregate observation levels within the trace: + - `level` (string) - Highest severity level (ERROR > WARNING > DEFAULT > DEBUG) + - `warningCount` (number) - Count of WARNING level observations + - `errorCount` (number) - Count of ERROR level observations + - `defaultCount` (number) - Count of DEFAULT level observations + - `debugCount` (number) - Count of DEBUG level observations + + ### Scores (requires join with scores table) + - `scores_avg` (number) - Average of numeric scores (alias: `scores`) + - `score_categories` (categoryOptions) - Categorical score values + + ## Filter Examples + ```json + [ + { + "type": "datetime", + "column": "timestamp", + "operator": ">=", + "value": "2024-01-01T00:00:00Z" + }, + { + "type": "string", + "column": "userId", + "operator": "=", + "value": "user-123" + }, + { + "type": "number", + "column": "totalCost", + "operator": ">=", + "value": 0.01 + }, + { + "type": "arrayOptions", + "column": "tags", + "operator": "all of", + "value": ["production", "critical"] + }, + { + "type": "stringObject", + "column": "metadata", + "key": "customer_tier", + "operator": "=", + "value": "enterprise" + } + ] + ``` + + ## Performance Notes + - Filtering on `userId`, `sessionId`, or `metadata` may enable skip indexes for better query performance + - Score filters require a join with the scores table and may impact query performance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Traces] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/traces", + method="GET", + params={ + "page": page, + "limit": limit, + "userId": user_id, + "name": name, + "sessionId": session_id, + "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, + "orderBy": order_by, + "tags": tags, + "version": version, + "release": release, + "environment": environment, + "fields": fields, + "filter": filter, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Traces, + parse_obj_as( + type_=Traces, # 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 delete_multiple( + self, + *, + trace_ids: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteTraceResponse]: + """ + Delete multiple traces + + Parameters + ---------- + trace_ids : typing.Sequence[str] + List of trace IDs to delete + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteTraceResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/traces", + method="DELETE", + json={ + "traceIds": trace_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTraceResponse, + parse_obj_as( + type_=DeleteTraceResponse, # 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/trace/types/__init__.py b/langfuse/api/trace/types/__init__.py new file mode 100644 index 000000000..3eab30d21 --- /dev/null +++ b/langfuse/api/trace/types/__init__.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .delete_trace_response import DeleteTraceResponse + from .sort import Sort + from .traces import Traces +_dynamic_imports: typing.Dict[str, str] = { + "DeleteTraceResponse": ".delete_trace_response", + "Sort": ".sort", + "Traces": ".traces", +} + + +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__ = ["DeleteTraceResponse", "Sort", "Traces"] diff --git a/langfuse/api/trace/types/delete_trace_response.py b/langfuse/api/trace/types/delete_trace_response.py new file mode 100644 index 000000000..173075a83 --- /dev/null +++ b/langfuse/api/trace/types/delete_trace_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class DeleteTraceResponse(UniversalBaseModel): + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/trace/types/sort.py b/langfuse/api/trace/types/sort.py new file mode 100644 index 000000000..b6b992870 --- /dev/null +++ b/langfuse/api/trace/types/sort.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class Sort(UniversalBaseModel): + id: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/trace/types/traces.py b/langfuse/api/trace/types/traces.py new file mode 100644 index 000000000..b559118e1 --- /dev/null +++ b/langfuse/api/trace/types/traces.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...commons.types.trace_with_details import TraceWithDetails +from ...core.pydantic_utilities import UniversalBaseModel +from ...utils.pagination.types.meta_response import MetaResponse + + +class Traces(UniversalBaseModel): + data: typing.List[TraceWithDetails] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/utils/__init__.py b/langfuse/api/utils/__init__.py new file mode 100644 index 000000000..b272f64b5 --- /dev/null +++ b/langfuse/api/utils/__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 . import pagination + from .pagination import MetaResponse +_dynamic_imports: typing.Dict[str, str] = { + "MetaResponse": ".pagination", + "pagination": ".pagination", +} + + +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__ = ["MetaResponse", "pagination"] diff --git a/langfuse/api/utils/pagination/__init__.py b/langfuse/api/utils/pagination/__init__.py new file mode 100644 index 000000000..50821832d --- /dev/null +++ b/langfuse/api/utils/pagination/__init__.py @@ -0,0 +1,40 @@ +# 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 MetaResponse +_dynamic_imports: typing.Dict[str, str] = {"MetaResponse": ".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__ = ["MetaResponse"] diff --git a/langfuse/api/utils/pagination/types/__init__.py b/langfuse/api/utils/pagination/types/__init__.py new file mode 100644 index 000000000..5c0d83028 --- /dev/null +++ b/langfuse/api/utils/pagination/types/__init__.py @@ -0,0 +1,40 @@ +# 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 .meta_response import MetaResponse +_dynamic_imports: typing.Dict[str, str] = {"MetaResponse": ".meta_response"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["MetaResponse"] diff --git a/langfuse/api/utils/pagination/types/meta_response.py b/langfuse/api/utils/pagination/types/meta_response.py new file mode 100644 index 000000000..54d3847be --- /dev/null +++ b/langfuse/api/utils/pagination/types/meta_response.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata + + +class MetaResponse(UniversalBaseModel): + page: int = pydantic.Field() + """ + current page number + """ + + limit: int = pydantic.Field() + """ + number of items per page + """ + + total_items: typing_extensions.Annotated[int, FieldMetadata(alias="totalItems")] = ( + pydantic.Field() + ) + """ + number of total items given the current filters/selection (if any) + """ + + total_pages: typing_extensions.Annotated[int, FieldMetadata(alias="totalPages")] = ( + pydantic.Field() + ) + """ + number of total pages given the current limit + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/batch_evaluation.py b/langfuse/batch_evaluation.py index 35e1ea938..cb074af42 100644 --- a/langfuse/batch_evaluation.py +++ b/langfuse/batch_evaluation.py @@ -22,7 +22,7 @@ Union, ) -from langfuse.api.resources.commons.types import ( +from langfuse.api import ( ObservationsView, TraceWithFullDetails, ) diff --git a/langfuse/experiment.py b/langfuse/experiment.py index 44c0bb859..4d585ecf4 100644 --- a/langfuse/experiment.py +++ b/langfuse/experiment.py @@ -8,7 +8,6 @@ import asyncio import logging from typing import ( - TYPE_CHECKING, Any, Awaitable, Dict, @@ -19,10 +18,7 @@ Union, ) -from langfuse.api import ScoreDataType - -if TYPE_CHECKING: - from langfuse._client.datasets import DatasetItemClient +from langfuse.api import DatasetItem, ScoreDataType class LocalExperimentItem(TypedDict, total=False): @@ -76,20 +72,20 @@ class LocalExperimentItem(TypedDict, total=False): metadata: Optional[Dict[str, Any]] -ExperimentItem = Union[LocalExperimentItem, "DatasetItemClient"] +ExperimentItem = Union[LocalExperimentItem, DatasetItem] """Type alias for items that can be processed in experiments. Can be either: - LocalExperimentItem: Dict-like items with 'input', 'expected_output', 'metadata' keys -- DatasetItemClient: Items from Langfuse datasets with .input, .expected_output, .metadata attributes +- DatasetItem: Items from Langfuse datasets with .input, .expected_output, .metadata attributes """ -ExperimentData = Union[List[LocalExperimentItem], List["DatasetItemClient"]] +ExperimentData = Union[List[LocalExperimentItem], List[DatasetItem]] """Type alias for experiment datasets. Represents the collection of items to process in an experiment. Can be either: - List[LocalExperimentItem]: Local data items as dictionaries -- List[DatasetItemClient]: Items from a Langfuse dataset (typically from dataset.items) +- List[DatasetItem]: Items from a Langfuse dataset (typically from dataset.items) """ @@ -222,7 +218,7 @@ class ExperimentItemResult: Attributes: item: The original experiment item that was processed. Can be either a dictionary with 'input', 'expected_output', and 'metadata' keys, - or a DatasetItemClient from Langfuse datasets. + or a DatasetItem from Langfuse datasets. output: The actual output produced by the task function for this item. Can be any type depending on what your task function returns. evaluations: List of evaluation results for this item. Each evaluation diff --git a/langfuse/langchain/CallbackHandler.py b/langfuse/langchain/CallbackHandler.py index 7f6479867..4485853ad 100644 --- a/langfuse/langchain/CallbackHandler.py +++ b/langfuse/langchain/CallbackHandler.py @@ -16,7 +16,9 @@ import pydantic from opentelemetry import context, trace from opentelemetry.context import _RUNTIME_CONTEXT +from opentelemetry.util._decorator import _AgnosticContextManager +from langfuse import propagate_attributes from langfuse._client.attributes import LangfuseOtelSpanAttributes from langfuse._client.client import Langfuse from langfuse._client.get_client import get_client @@ -28,10 +30,10 @@ LangfuseSpan, LangfuseTool, ) -from langfuse.types import TraceContext from langfuse._utils import _get_timestamp from langfuse.langchain.utils import _extract_model_name from langfuse.logger import langfuse_logger +from langfuse.types import TraceContext try: import langchain @@ -96,14 +98,12 @@ def __init__( self, *, public_key: Optional[str] = None, - update_trace: bool = False, trace_context: Optional[TraceContext] = None, ) -> None: """Initialize the LangchainCallbackHandler. Args: public_key: Optional Langfuse public key. If not provided, will use the default client configuration. - update_trace: Whether to update the Langfuse trace with the chains input / output / metadata / name. Defaults to False. trace_context: Optional context for connecting to an existing trace (distributed tracing) or setting a custom trace id for the root LangChain run. Pass a `TraceContext` dict, e.g. `{"trace_id": ""}` (and optionally `{"parent_span_id": ""}`) to link @@ -118,10 +118,8 @@ def __init__( handler = CallbackHandler(trace_context={"trace_id": "my-trace-id"}) ``` """ - self.client = get_client(public_key=public_key) - self.run_inline = True - - self.runs: Dict[ + self._langfuse_client = get_client(public_key=public_key) + self._runs: Dict[ UUID, Union[ LangfuseSpan, @@ -132,13 +130,13 @@ def __init__( LangfuseRetriever, ], ] = {} - self.context_tokens: Dict[UUID, Token] = {} - self.prompt_to_parent_run_map: Dict[UUID, Any] = {} - self.updated_completion_start_time_memo: Set[UUID] = set() + self._context_tokens: Dict[UUID, Token] = {} + self._prompt_to_parent_run_map: Dict[UUID, Any] = {} + self._updated_completion_start_time_memo: Set[UUID] = set() + self._propagation_context_manager: Optional[_AgnosticContextManager] = None + self._trace_context = trace_context self.last_trace_id: Optional[str] = None - self.update_trace = update_trace - self.trace_context = trace_context def on_llm_new_token( self, @@ -153,14 +151,14 @@ def on_llm_new_token( f"on llm new token: run_id: {run_id} parent_run_id: {parent_run_id}" ) if ( - run_id in self.runs - and isinstance(self.runs[run_id], LangfuseGeneration) - and run_id not in self.updated_completion_start_time_memo + run_id in self._runs + and isinstance(self._runs[run_id], LangfuseGeneration) + and run_id not in self._updated_completion_start_time_memo ): - current_generation = cast(LangfuseGeneration, self.runs[run_id]) + current_generation = cast(LangfuseGeneration, self._runs[run_id]) current_generation.update(completion_start_time=_get_timestamp()) - self.updated_completion_start_time_memo.add(run_id) + self._updated_completion_start_time_memo.add(run_id) def _get_observation_type_from_serialized( self, serialized: Optional[Dict[str, Any]], callback_type: str, **kwargs: Any @@ -267,12 +265,14 @@ def on_retriever_error( except Exception as e: langfuse_logger.exception(e) - def _parse_langfuse_trace_attributes_from_metadata( - self, - metadata: Optional[Dict[str, Any]], + def _parse_langfuse_trace_attributes( + self, *, metadata: Optional[Dict[str, Any]], tags: Optional[List[str]] ) -> Dict[str, Any]: attributes: Dict[str, Any] = {} + if metadata is None and tags is not None: + return {"tags": tags} + if metadata is None: return attributes @@ -286,8 +286,13 @@ def _parse_langfuse_trace_attributes_from_metadata( ): attributes["user_id"] = metadata["langfuse_user_id"] - if "langfuse_tags" in metadata and isinstance(metadata["langfuse_tags"], list): - attributes["tags"] = [str(tag) for tag in metadata["langfuse_tags"]] + if tags is not None or ( + "langfuse_tags" in metadata and isinstance(metadata["langfuse_tags"], list) + ): + merged_tags = list(set(metadata["langfuse_tags"]) | set(tags or [])) + attributes["tags"] = [str(tag) for tag in set(merged_tags)] + + attributes["metadata"] = _strip_langfuse_keys_from_dict(metadata, False) return attributes @@ -318,10 +323,26 @@ def on_chain_start( serialized, "chain", **kwargs ) + # Handle trace attribute propagation at the root of the chain + if parent_run_id is None: + parsed_trace_attributes = self._parse_langfuse_trace_attributes( + metadata=metadata, tags=tags + ) + + self._propagation_context_manager = propagate_attributes( + trace_name=span_name, + user_id=parsed_trace_attributes.get("user_id", None), + session_id=parsed_trace_attributes.get("session_id", None), + tags=parsed_trace_attributes.get("tags", None), + metadata=parsed_trace_attributes.get("metadata", None), + ) + + self._propagation_context_manager.__enter__() + obs = self._get_parent_observation(parent_run_id) if isinstance(obs, Langfuse): span = obs.start_observation( - trace_context=self.trace_context, + trace_context=self._trace_context, name=span_name, as_type=observation_type, metadata=span_metadata, @@ -345,24 +366,7 @@ def on_chain_start( self._attach_observation(run_id, span) - if parent_run_id is None: - span.update_trace( - **( - cast( - Any, - { - "input": inputs, - "name": span_name, - "metadata": span_metadata, - }, - ) - if self.update_trace - else {} - ), - **self._parse_langfuse_trace_attributes_from_metadata(metadata), - ) - - self.last_trace_id = self.runs[run_id].trace_id + self.last_trace_id = self._runs[run_id].trace_id except Exception as e: langfuse_logger.exception(e) @@ -385,17 +389,17 @@ def _register_langfuse_prompt( langfuse_prompt = metadata and metadata.get("langfuse_prompt", None) if langfuse_prompt: - self.prompt_to_parent_run_map[parent_run_id] = langfuse_prompt + self._prompt_to_parent_run_map[parent_run_id] = langfuse_prompt # If we have a registered prompt that has not been linked to a generation yet, we need to allow _children_ of that chain to link to it. # Otherwise, we only allow generations on the same level of the prompt rendering to be linked, not if they are nested. - elif parent_run_id in self.prompt_to_parent_run_map: - registered_prompt = self.prompt_to_parent_run_map[parent_run_id] - self.prompt_to_parent_run_map[run_id] = registered_prompt + elif parent_run_id in self._prompt_to_parent_run_map: + registered_prompt = self._prompt_to_parent_run_map[parent_run_id] + self._prompt_to_parent_run_map[run_id] = registered_prompt def _deregister_langfuse_prompt(self, run_id: Optional[UUID]) -> None: - if run_id is not None and run_id in self.prompt_to_parent_run_map: - del self.prompt_to_parent_run_map[run_id] + if run_id is not None and run_id in self._prompt_to_parent_run_map: + del self._prompt_to_parent_run_map[run_id] def _get_parent_observation( self, parent_run_id: Optional[UUID] @@ -408,10 +412,10 @@ def _get_parent_observation( LangfuseSpan, LangfuseTool, ]: - if parent_run_id and parent_run_id in self.runs: - return self.runs[parent_run_id] + if parent_run_id and parent_run_id in self._runs: + return self._runs[parent_run_id] - return self.client + return self._langfuse_client def _attach_observation( self, @@ -428,8 +432,8 @@ def _attach_observation( ctx = trace.set_span_in_context(observation._otel_span) token = context.attach(ctx) - self.runs[run_id] = observation - self.context_tokens[run_id] = token + self._runs[run_id] = observation + self._context_tokens[run_id] = token def _detach_observation( self, run_id: UUID @@ -443,7 +447,7 @@ def _detach_observation( LangfuseTool, ] ]: - token = self.context_tokens.pop(run_id, None) + token = self._context_tokens.pop(run_id, None) if token: try: @@ -468,7 +472,7 @@ def _detach_observation( LangfuseSpan, LangfuseTool, ], - self.runs.pop(run_id, None), + self._runs.pop(run_id, None), ) def on_agent_action( @@ -485,7 +489,7 @@ def on_agent_action( "on_agent_action", run_id, parent_run_id, action=action ) - agent_run = self.runs.get(run_id, None) + agent_run = self._runs.get(run_id, None) if agent_run is not None: agent_run._otel_span.set_attribute( @@ -514,7 +518,7 @@ def on_agent_finish( ) # Langchain is sending same run ID for both agent finish and chain end # handle cleanup of observation in the chain end callback - agent_run = self.runs.get(run_id, None) + agent_run = self._runs.get(run_id, None) if agent_run is not None: agent_run._otel_span.set_attribute( @@ -550,8 +554,11 @@ def on_chain_end( input=kwargs.get("inputs"), ) - if parent_run_id is None and self.update_trace: - span.update_trace(output=outputs, input=kwargs.get("inputs")) + if ( + parent_run_id is None + and self._propagation_context_manager is not None + ): + self._propagation_context_manager.__exit__(None, None, None) span.end() @@ -818,7 +825,7 @@ def __on_llm_action( serialized=serialized, metadata=metadata, kwargs=kwargs ) registered_prompt = ( - self.prompt_to_parent_run_map.get(parent_run_id) + self._prompt_to_parent_run_map.get(parent_run_id) if parent_run_id is not None else None ) @@ -847,7 +854,7 @@ def __on_llm_action( ) # type: ignore self._attach_observation(run_id, generation) - self.last_trace_id = self.runs[run_id].trace_id + self.last_trace_id = self._runs[run_id].trace_id except Exception as e: langfuse_logger.exception(e) @@ -954,7 +961,7 @@ def on_llm_end( langfuse_logger.exception(e) finally: - self.updated_completion_start_time_memo.discard(run_id) + self._updated_completion_start_time_memo.discard(run_id) def on_llm_error( self, @@ -1210,7 +1217,9 @@ def _parse_usage_model(usage: Union[pydantic.BaseModel, dict]) -> Any: usage_model["input"] = max(0, usage_model["input"] - value) if f"input_modality_{item['modality']}" in usage_model: - usage_model[f"input_modality_{item['modality']}"] = max(0, usage_model[f"input_modality_{item['modality']}"] - value) + usage_model[f"input_modality_{item['modality']}"] = max( + 0, usage_model[f"input_modality_{item['modality']}"] - value + ) usage_model = {k: v for k, v in usage_model.items() if isinstance(v, int)} diff --git a/langfuse/model.py b/langfuse/model.py index 75803d215..ab5591603 100644 --- a/langfuse/model.py +++ b/langfuse/model.py @@ -4,52 +4,10 @@ from abc import ABC, abstractmethod from typing import Any, Dict, List, Literal, Optional, Sequence, Tuple, TypedDict, Union -from langfuse.api.resources.commons.types.dataset import ( - Dataset, # noqa: F401 -) - -# these imports need to stay here, otherwise imports from our clients wont work -from langfuse.api.resources.commons.types.dataset_item import DatasetItem # noqa: F401 - -# noqa: F401 -from langfuse.api.resources.commons.types.dataset_run import DatasetRun # noqa: F401 - -# noqa: F401 -from langfuse.api.resources.commons.types.dataset_status import ( # noqa: F401 - DatasetStatus, -) -from langfuse.api.resources.commons.types.map_value import MapValue # noqa: F401 -from langfuse.api.resources.commons.types.observation import Observation # noqa: F401 -from langfuse.api.resources.commons.types.trace_with_full_details import ( # noqa: F401 - TraceWithFullDetails, -) - -# noqa: F401 -from langfuse.api.resources.dataset_items.types.create_dataset_item_request import ( # noqa: F401 - CreateDatasetItemRequest, -) -from langfuse.api.resources.dataset_run_items.types.create_dataset_run_item_request import ( # noqa: F401 - CreateDatasetRunItemRequest, -) - -# noqa: F401 -from langfuse.api.resources.datasets.types.create_dataset_request import ( # noqa: F401 - CreateDatasetRequest, -) -from langfuse.api.resources.prompts import Prompt, Prompt_Chat, Prompt_Text +from langfuse.api import Prompt, Prompt_Chat, Prompt_Text from langfuse.logger import langfuse_logger -class ModelUsage(TypedDict): - unit: Optional[str] - input: Optional[int] - output: Optional[int] - total: Optional[int] - input_cost: Optional[float] - output_cost: Optional[float] - total_cost: Optional[float] - - class ChatMessageDict(TypedDict): role: str content: str diff --git a/langfuse/types.py b/langfuse/types.py index 32ebb32d4..067088e40 100644 --- a/langfuse/types.py +++ b/langfuse/types.py @@ -17,16 +17,12 @@ def my_evaluator(*, output: str, **kwargs) -> Evaluation: ``` """ -from datetime import datetime from typing import ( Any, Dict, - List, Literal, - Optional, Protocol, TypedDict, - Union, ) try: @@ -34,43 +30,14 @@ def my_evaluator(*, output: str, **kwargs) -> Evaluation: except ImportError: from typing_extensions import NotRequired -from pydantic import BaseModel -from langfuse.api import MediaContentType, UsageDetails -from langfuse.model import MapValue, ModelUsage, PromptClient +from langfuse.api import MediaContentType SpanLevel = Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"] ScoreDataType = Literal["NUMERIC", "CATEGORICAL", "BOOLEAN"] -class TraceMetadata(TypedDict): - name: Optional[str] - user_id: Optional[str] - session_id: Optional[str] - version: Optional[str] - release: Optional[str] - metadata: Optional[Any] - tags: Optional[List[str]] - public: Optional[bool] - - -class ObservationParams(TraceMetadata, TypedDict): - input: Optional[Any] - output: Optional[Any] - level: Optional[SpanLevel] - status_message: Optional[str] - start_time: Optional[datetime] - end_time: Optional[datetime] - completion_start_time: Optional[datetime] - model: Optional[str] - model_parameters: Optional[Dict[str, MapValue]] - usage: Optional[Union[BaseModel, ModelUsage]] - usage_details: Optional[UsageDetails] - cost_details: Optional[Dict[str, float]] - prompt: Optional[PromptClient] - - class MaskFunction(Protocol): """A function that masks data. @@ -106,8 +73,6 @@ class TraceContext(TypedDict): __all__ = [ "SpanLevel", "ScoreDataType", - "TraceMetadata", - "ObservationParams", "MaskFunction", "ParsedMediaReference", "TraceContext", diff --git a/poetry.lock b/poetry.lock index 1ff0fca7f..775820966 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,7 +6,6 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -14,24 +13,22 @@ files = [ [[package]] name = "anyio" -version = "4.11.0" +version = "4.12.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ - {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, - {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, + {file = "anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb"}, + {file = "anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" -sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -trio = ["trio (>=0.31.0)"] +trio = ["trio (>=0.31.0)", "trio (>=0.32.0)"] [[package]] name = "attrs" @@ -39,7 +36,6 @@ version = "25.4.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, @@ -51,7 +47,6 @@ version = "0.0.130" description = "Universal library for evaluating AI models" optional = false python-versions = ">=3.8.0" -groups = ["dev"] files = [ {file = "autoevals-0.0.130-py3-none-any.whl", hash = "sha256:ffb7b3a21070d2a4e593bb118180c04e43531e608bffd854624377bd857ceec0"}, {file = "autoevals-0.0.130.tar.gz", hash = "sha256:92f87ab95a575b56d9d7377e6f1399932d09180d2f3a8266b4f693f46f49b86d"}, @@ -75,7 +70,6 @@ version = "2.2.1" description = "Function decoration for backoff and retry" optional = false python-versions = ">=3.7,<4.0" -groups = ["main"] files = [ {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, @@ -87,8 +81,6 @@ version = "1.2.0" description = "Backport of asyncio.Runner, a context manager that controls event loop life cycle." optional = false python-versions = "<3.11,>=3.8" -groups = ["dev"] -markers = "python_version == \"3.10\"" files = [ {file = "backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5"}, {file = "backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162"}, @@ -96,26 +88,24 @@ files = [ [[package]] name = "certifi" -version = "2025.10.5" +version = "2025.11.12" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] files = [ - {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, - {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, ] [[package]] name = "cfgv" -version = "3.4.0" +version = "3.5.0" description = "Validate configuration and produce human readable error messages." optional = false -python-versions = ">=3.8" -groups = ["dev"] +python-versions = ">=3.10" files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, + {file = "cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0"}, + {file = "cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132"}, ] [[package]] @@ -124,7 +114,6 @@ version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, @@ -247,7 +236,6 @@ version = "0.14.0" description = "Mustache templating language renderer" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443"}, {file = "chevron-0.14.0.tar.gz", hash = "sha256:87613aafdf6d77b6a90ff073165a61ae5086e21ad49057aa0e53681601800ebf"}, @@ -259,12 +247,10 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "distlib" @@ -272,7 +258,6 @@ version = "0.4.0" description = "Distribution utilities" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, @@ -284,7 +269,6 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -292,15 +276,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.3.0" +version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] -markers = "python_version == \"3.10\"" files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] [package.dependencies] @@ -311,14 +293,13 @@ test = ["pytest (>=6)"] [[package]] name = "execnet" -version = "2.1.1" +version = "2.1.2" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, ] [package.extras] @@ -330,7 +311,6 @@ version = "3.20.1" description = "A platform independent file lock." optional = false python-versions = ">=3.10" -groups = ["dev"] files = [ {file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"}, {file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"}, @@ -338,14 +318,13 @@ files = [ [[package]] name = "googleapis-common-protos" -version = "1.70.0" +version = "1.72.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ - {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, - {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, + {file = "googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038"}, + {file = "googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5"}, ] [package.dependencies] @@ -360,7 +339,6 @@ version = "0.16.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, @@ -372,7 +350,6 @@ version = "1.0.9" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, @@ -394,7 +371,6 @@ version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -407,7 +383,7 @@ httpcore = "==1.*" idna = "*" [package.extras] -brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -419,7 +395,6 @@ version = "2.6.15" description = "File identification library for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757"}, {file = "identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf"}, @@ -434,7 +409,6 @@ version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, @@ -449,7 +423,6 @@ version = "8.7.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -459,12 +432,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -473,7 +446,6 @@ version = "2.3.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.10" -groups = ["dev"] files = [ {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, @@ -485,7 +457,6 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -499,114 +470,113 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jiter" -version = "0.11.1" +version = "0.12.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "jiter-0.11.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ed58841a491bbbf3f7c55a6b68fff568439ab73b2cce27ace0e169057b5851df"}, - {file = "jiter-0.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:499beb9b2d7e51d61095a8de39ebcab1d1778f2a74085f8305a969f6cee9f3e4"}, - {file = "jiter-0.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b87b2821795e28cc990939b68ce7a038edea680a24910bd68a79d54ff3f03c02"}, - {file = "jiter-0.11.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83f6fa494d8bba14ab100417c80e70d32d737e805cb85be2052d771c76fcd1f8"}, - {file = "jiter-0.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fbc6aea1daa2ec6f5ed465f0c5e7b0607175062ceebbea5ca70dd5ddab58083"}, - {file = "jiter-0.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:302288e2edc43174bb2db838e94688d724f9aad26c5fb9a74f7a5fb427452a6a"}, - {file = "jiter-0.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85db563fe3b367bb568af5d29dea4d4066d923b8e01f3417d25ebecd958de815"}, - {file = "jiter-0.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f1c1ba2b6b22f775444ef53bc2d5778396d3520abc7b2e1da8eb0c27cb3ffb10"}, - {file = "jiter-0.11.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:523be464b14f8fd0cc78da6964b87b5515a056427a2579f9085ce30197a1b54a"}, - {file = "jiter-0.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25b99b3f04cd2a38fefb22e822e35eb203a2cd37d680dbbc0c0ba966918af336"}, - {file = "jiter-0.11.1-cp310-cp310-win32.whl", hash = "sha256:47a79e90545a596bb9104109777894033347b11180d4751a216afef14072dbe7"}, - {file = "jiter-0.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:cace75621ae9bd66878bf69fbd4dfc1a28ef8661e0c2d0eb72d3d6f1268eddf5"}, - {file = "jiter-0.11.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b0088ff3c374ce8ce0168523ec8e97122ebb788f950cf7bb8e39c7dc6a876a2"}, - {file = "jiter-0.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74433962dd3c3090655e02e461267095d6c84f0741c7827de11022ef8d7ff661"}, - {file = "jiter-0.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d98030e345e6546df2cc2c08309c502466c66c4747b043f1a0d415fada862b8"}, - {file = "jiter-0.11.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d6db0b2e788db46bec2cf729a88b6dd36959af2abd9fa2312dfba5acdd96dcb"}, - {file = "jiter-0.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55678fbbda261eafe7289165dd2ddd0e922df5f9a1ae46d7c79a5a15242bd7d1"}, - {file = "jiter-0.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a6b74fae8e40497653b52ce6ca0f1b13457af769af6fb9c1113efc8b5b4d9be"}, - {file = "jiter-0.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a55a453f8b035eb4f7852a79a065d616b7971a17f5e37a9296b4b38d3b619e4"}, - {file = "jiter-0.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2638148099022e6bdb3f42904289cd2e403609356fb06eb36ddec2d50958bc29"}, - {file = "jiter-0.11.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:252490567a5d990986f83b95a5f1ca1bf205ebd27b3e9e93bb7c2592380e29b9"}, - {file = "jiter-0.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d431d52b0ca2436eea6195f0f48528202100c7deda354cb7aac0a302167594d5"}, - {file = "jiter-0.11.1-cp311-cp311-win32.whl", hash = "sha256:db6f41e40f8bae20c86cb574b48c4fd9f28ee1c71cb044e9ec12e78ab757ba3a"}, - {file = "jiter-0.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0cc407b8e6cdff01b06bb80f61225c8b090c3df108ebade5e0c3c10993735b19"}, - {file = "jiter-0.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:fe04ea475392a91896d1936367854d346724a1045a247e5d1c196410473b8869"}, - {file = "jiter-0.11.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c92148eec91052538ce6823dfca9525f5cfc8b622d7f07e9891a280f61b8c96c"}, - {file = "jiter-0.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ecd4da91b5415f183a6be8f7158d127bdd9e6a3174138293c0d48d6ea2f2009d"}, - {file = "jiter-0.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e3ac25c00b9275684d47aa42febaa90a9958e19fd1726c4ecf755fbe5e553b"}, - {file = "jiter-0.11.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d7305c0a841858f866cd459cd9303f73883fb5e097257f3d4a3920722c69d4"}, - {file = "jiter-0.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e86fa10e117dce22c547f31dd6d2a9a222707d54853d8de4e9a2279d2c97f239"}, - {file = "jiter-0.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae5ef1d48aec7e01ee8420155d901bb1d192998fa811a65ebb82c043ee186711"}, - {file = "jiter-0.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb68e7bf65c990531ad8715e57d50195daf7c8e6f1509e617b4e692af1108939"}, - {file = "jiter-0.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43b30c8154ded5845fa454ef954ee67bfccce629b2dea7d01f795b42bc2bda54"}, - {file = "jiter-0.11.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:586cafbd9dd1f3ce6a22b4a085eaa6be578e47ba9b18e198d4333e598a91db2d"}, - {file = "jiter-0.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:677cc2517d437a83bb30019fd4cf7cad74b465914c56ecac3440d597ac135250"}, - {file = "jiter-0.11.1-cp312-cp312-win32.whl", hash = "sha256:fa992af648fcee2b850a3286a35f62bbbaeddbb6dbda19a00d8fbc846a947b6e"}, - {file = "jiter-0.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:88b5cae9fa51efeb3d4bd4e52bfd4c85ccc9cac44282e2a9640893a042ba4d87"}, - {file = "jiter-0.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:9a6cae1ab335551917f882f2c3c1efe7617b71b4c02381e4382a8fc80a02588c"}, - {file = "jiter-0.11.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:71b6a920a5550f057d49d0e8bcc60945a8da998019e83f01adf110e226267663"}, - {file = "jiter-0.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b3de72e925388453a5171be83379549300db01284f04d2a6f244d1d8de36f94"}, - {file = "jiter-0.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc19dd65a2bd3d9c044c5b4ebf657ca1e6003a97c0fc10f555aa4f7fb9821c00"}, - {file = "jiter-0.11.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d58faaa936743cd1464540562f60b7ce4fd927e695e8bc31b3da5b914baa9abd"}, - {file = "jiter-0.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:902640c3103625317291cb73773413b4d71847cdf9383ba65528745ff89f1d14"}, - {file = "jiter-0.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30405f726e4c2ed487b176c09f8b877a957f535d60c1bf194abb8dadedb5836f"}, - {file = "jiter-0.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3217f61728b0baadd2551844870f65219ac4a1285d5e1a4abddff3d51fdabe96"}, - {file = "jiter-0.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1364cc90c03a8196f35f396f84029f12abe925415049204446db86598c8b72c"}, - {file = "jiter-0.11.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:53a54bf8e873820ab186b2dca9f6c3303f00d65ae5e7b7d6bda1b95aa472d646"}, - {file = "jiter-0.11.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7e29aca023627b0e0c2392d4248f6414d566ff3974fa08ff2ac8dbb96dfee92a"}, - {file = "jiter-0.11.1-cp313-cp313-win32.whl", hash = "sha256:f153e31d8bca11363751e875c0a70b3d25160ecbaee7b51e457f14498fb39d8b"}, - {file = "jiter-0.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:f773f84080b667c69c4ea0403fc67bb08b07e2b7ce1ef335dea5868451e60fed"}, - {file = "jiter-0.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:635ecd45c04e4c340d2187bcb1cea204c7cc9d32c1364d251564bf42e0e39c2d"}, - {file = "jiter-0.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d892b184da4d94d94ddb4031296931c74ec8b325513a541ebfd6dfb9ae89904b"}, - {file = "jiter-0.11.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa22c223a3041dacb2fcd37c70dfd648b44662b4a48e242592f95bda5ab09d58"}, - {file = "jiter-0.11.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:330e8e6a11ad4980cd66a0f4a3e0e2e0f646c911ce047014f984841924729789"}, - {file = "jiter-0.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:09e2e386ebf298547ca3a3704b729471f7ec666c2906c5c26c1a915ea24741ec"}, - {file = "jiter-0.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:fe4a431c291157e11cee7c34627990ea75e8d153894365a3bc84b7a959d23ca8"}, - {file = "jiter-0.11.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:0fa1f70da7a8a9713ff8e5f75ec3f90c0c870be6d526aa95e7c906f6a1c8c676"}, - {file = "jiter-0.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:569ee559e5046a42feb6828c55307cf20fe43308e3ae0d8e9e4f8d8634d99944"}, - {file = "jiter-0.11.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f69955fa1d92e81987f092b233f0be49d4c937da107b7f7dcf56306f1d3fcce9"}, - {file = "jiter-0.11.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:090f4c9d4a825e0fcbd0a2647c9a88a0f366b75654d982d95a9590745ff0c48d"}, - {file = "jiter-0.11.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf3d8cedf9e9d825233e0dcac28ff15c47b7c5512fdfe2e25fd5bbb6e6b0cee"}, - {file = "jiter-0.11.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aa9b1958f9c30d3d1a558b75f0626733c60eb9b7774a86b34d88060be1e67fe"}, - {file = "jiter-0.11.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42d1ca16590b768c5e7d723055acd2633908baacb3628dd430842e2e035aa90"}, - {file = "jiter-0.11.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5db4c2486a023820b701a17aec9c5a6173c5ba4393f26662f032f2de9c848b0f"}, - {file = "jiter-0.11.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4573b78777ccfac954859a6eff45cbd9d281d80c8af049d0f1a3d9fc323d5c3a"}, - {file = "jiter-0.11.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:7593ac6f40831d7961cb67633c39b9fef6689a211d7919e958f45710504f52d3"}, - {file = "jiter-0.11.1-cp314-cp314-win32.whl", hash = "sha256:87202ec6ff9626ff5f9351507def98fcf0df60e9a146308e8ab221432228f4ea"}, - {file = "jiter-0.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:a5dd268f6531a182c89d0dd9a3f8848e86e92dfff4201b77a18e6b98aa59798c"}, - {file = "jiter-0.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:5d761f863f912a44748a21b5c4979c04252588ded8d1d2760976d2e42cd8d991"}, - {file = "jiter-0.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2cc5a3965285ddc33e0cab933e96b640bc9ba5940cea27ebbbf6695e72d6511c"}, - {file = "jiter-0.11.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b572b3636a784c2768b2342f36a23078c8d3aa6d8a30745398b1bab58a6f1a8"}, - {file = "jiter-0.11.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad93e3d67a981f96596d65d2298fe8d1aa649deb5374a2fb6a434410ee11915e"}, - {file = "jiter-0.11.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83097ce379e202dcc3fe3fc71a16d523d1ee9192c8e4e854158f96b3efe3f2f"}, - {file = "jiter-0.11.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7042c51e7fbeca65631eb0c332f90c0c082eab04334e7ccc28a8588e8e2804d9"}, - {file = "jiter-0.11.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a68d679c0e47649a61df591660507608adc2652442de7ec8276538ac46abe08"}, - {file = "jiter-0.11.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1b0da75dbf4b6ec0b3c9e604d1ee8beaf15bc046fff7180f7d89e3cdbd3bb51"}, - {file = "jiter-0.11.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:69dd514bf0fa31c62147d6002e5ca2b3e7ef5894f5ac6f0a19752385f4e89437"}, - {file = "jiter-0.11.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:bb31ac0b339efa24c0ca606febd8b77ef11c58d09af1b5f2be4c99e907b11111"}, - {file = "jiter-0.11.1-cp314-cp314t-win32.whl", hash = "sha256:b2ce0d6156a1d3ad41da3eec63b17e03e296b78b0e0da660876fccfada86d2f7"}, - {file = "jiter-0.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f4db07d127b54c4a2d43b4cf05ff0193e4f73e0dd90c74037e16df0b29f666e1"}, - {file = "jiter-0.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:28e4fdf2d7ebfc935523e50d1efa3970043cfaa161674fe66f9642409d001dfe"}, - {file = "jiter-0.11.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:baa99c8db49467527658bb479857344daf0a14dff909b7f6714579ac439d1253"}, - {file = "jiter-0.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:860fe55fa3b01ad0edf2adde1098247ff5c303d0121f9ce028c03d4f88c69502"}, - {file = "jiter-0.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:173dd349d99b6feaf5a25a6fbcaf3489a6f947708d808240587a23df711c67db"}, - {file = "jiter-0.11.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:14ac1dca837514cc946a6ac2c4995d9695303ecc754af70a3163d057d1a444ab"}, - {file = "jiter-0.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69af47de5f93a231d5b85f7372d3284a5be8edb4cc758f006ec5a1406965ac5e"}, - {file = "jiter-0.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:685f8b3abd3bbd3e06e4dfe2429ff87fd5d7a782701151af99b1fcbd80e31b2b"}, - {file = "jiter-0.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d04afa2d4e5526e54ae8a58feea953b1844bf6e3526bc589f9de68e86d0ea01"}, - {file = "jiter-0.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e92b927259035b50d8e11a8fdfe0ebd014d883e4552d37881643fa289a4bcf1"}, - {file = "jiter-0.11.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e7bd8be4fad8d4c5558b7801770cd2da6c072919c6f247cc5336edb143f25304"}, - {file = "jiter-0.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:121381a77a3c85987f3eba0d30ceaca9116f7463bedeec2fa79b2e7286b89b60"}, - {file = "jiter-0.11.1-cp39-cp39-win32.whl", hash = "sha256:160225407f6dfabdf9be1b44e22f06bc293a78a28ffa4347054698bd712dad06"}, - {file = "jiter-0.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:028e0d59bcdfa1079f8df886cdaefc6f515c27a5288dec956999260c7e4a7cfd"}, - {file = "jiter-0.11.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:e642b5270e61dd02265866398707f90e365b5db2eb65a4f30c789d826682e1f6"}, - {file = "jiter-0.11.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:464ba6d000585e4e2fd1e891f31f1231f497273414f5019e27c00a4b8f7a24ad"}, - {file = "jiter-0.11.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:055568693ab35e0bf3a171b03bb40b2dcb10352359e0ab9b5ed0da2bf1eb6f6f"}, - {file = "jiter-0.11.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c69ea798d08a915ba4478113efa9e694971e410056392f4526d796f136d3fa"}, - {file = "jiter-0.11.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:0d4d6993edc83cf75e8c6828a8d6ce40a09ee87e38c7bfba6924f39e1337e21d"}, - {file = "jiter-0.11.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f78d151c83a87a6cf5461d5ee55bc730dd9ae227377ac6f115b922989b95f838"}, - {file = "jiter-0.11.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9022974781155cd5521d5cb10997a03ee5e31e8454c9d999dcdccd253f2353f"}, - {file = "jiter-0.11.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18c77aaa9117510d5bdc6a946baf21b1f0cfa58ef04d31c8d016f206f2118960"}, - {file = "jiter-0.11.1.tar.gz", hash = "sha256:849dcfc76481c0ea0099391235b7ca97d7279e0fa4c86005457ac7c88e8b76dc"}, +files = [ + {file = "jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65"}, + {file = "jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e"}, + {file = "jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62"}, + {file = "jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8"}, + {file = "jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb"}, + {file = "jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc"}, + {file = "jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74"}, + {file = "jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2"}, + {file = "jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025"}, + {file = "jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca"}, + {file = "jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4"}, + {file = "jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11"}, + {file = "jiter-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8f8a7e317190b2c2d60eb2e8aa835270b008139562d70fe732e1c0020ec53c9"}, + {file = "jiter-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2218228a077e784c6c8f1a8e5d6b8cb1dea62ce25811c356364848554b2056cd"}, + {file = "jiter-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9354ccaa2982bf2188fd5f57f79f800ef622ec67beb8329903abf6b10da7d423"}, + {file = "jiter-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2607185ea89b4af9a604d4c7ec40e45d3ad03ee66998b031134bc510232bb7"}, + {file = "jiter-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a585a5e42d25f2e71db5f10b171f5e5ea641d3aa44f7df745aa965606111cc2"}, + {file = "jiter-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd9e21d34edff5a663c631f850edcb786719c960ce887a5661e9c828a53a95d9"}, + {file = "jiter-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a612534770470686cd5431478dc5a1b660eceb410abade6b1b74e320ca98de6"}, + {file = "jiter-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3985aea37d40a908f887b34d05111e0aae822943796ebf8338877fee2ab67725"}, + {file = "jiter-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b1207af186495f48f72529f8d86671903c8c10127cac6381b11dddc4aaa52df6"}, + {file = "jiter-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef2fb241de583934c9915a33120ecc06d94aa3381a134570f59eed784e87001e"}, + {file = "jiter-0.12.0-cp311-cp311-win32.whl", hash = "sha256:453b6035672fecce8007465896a25b28a6b59cfe8fbc974b2563a92f5a92a67c"}, + {file = "jiter-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:ca264b9603973c2ad9435c71a8ec8b49f8f715ab5ba421c85a51cde9887e421f"}, + {file = "jiter-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:cb00ef392e7d684f2754598c02c409f376ddcef857aae796d559e6cacc2d78a5"}, + {file = "jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37"}, + {file = "jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274"}, + {file = "jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3"}, + {file = "jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf"}, + {file = "jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1"}, + {file = "jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df"}, + {file = "jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403"}, + {file = "jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126"}, + {file = "jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9"}, + {file = "jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86"}, + {file = "jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44"}, + {file = "jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb"}, + {file = "jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789"}, + {file = "jiter-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6cc49d5130a14b732e0612bc76ae8db3b49898732223ef8b7599aa8d9810683e"}, + {file = "jiter-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37f27a32ce36364d2fa4f7fdc507279db604d27d239ea2e044c8f148410defe1"}, + {file = "jiter-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc0944aa3d4b4773e348cda635252824a78f4ba44328e042ef1ff3f6080d1cf"}, + {file = "jiter-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da25c62d4ee1ffbacb97fac6dfe4dcd6759ebdc9015991e92a6eae5816287f44"}, + {file = "jiter-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:048485c654b838140b007390b8182ba9774621103bd4d77c9c3f6f117474ba45"}, + {file = "jiter-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:635e737fbb7315bef0037c19b88b799143d2d7d3507e61a76751025226b3ac87"}, + {file = "jiter-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e017c417b1ebda911bd13b1e40612704b1f5420e30695112efdbed8a4b389ed"}, + {file = "jiter-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89b0bfb8b2bf2351fba36bb211ef8bfceba73ef58e7f0c68fb67b5a2795ca2f9"}, + {file = "jiter-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f5aa5427a629a824a543672778c9ce0c5e556550d1569bb6ea28a85015287626"}, + {file = "jiter-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed53b3d6acbcb0fd0b90f20c7cb3b24c357fe82a3518934d4edfa8c6898e498c"}, + {file = "jiter-0.12.0-cp313-cp313-win32.whl", hash = "sha256:4747de73d6b8c78f2e253a2787930f4fffc68da7fa319739f57437f95963c4de"}, + {file = "jiter-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e25012eb0c456fcc13354255d0338cd5397cce26c77b2832b3c4e2e255ea5d9a"}, + {file = "jiter-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:c97b92c54fe6110138c872add030a1f99aea2401ddcdaa21edf74705a646dd60"}, + {file = "jiter-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53839b35a38f56b8be26a7851a48b89bc47e5d88e900929df10ed93b95fea3d6"}, + {file = "jiter-0.12.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f669548e55c91ab47fef8bddd9c954dab1938644e715ea49d7e117015110a4"}, + {file = "jiter-0.12.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:351d54f2b09a41600ffea43d081522d792e81dcfb915f6d2d242744c1cc48beb"}, + {file = "jiter-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2a5e90604620f94bf62264e7c2c038704d38217b7465b863896c6d7c902b06c7"}, + {file = "jiter-0.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:88ef757017e78d2860f96250f9393b7b577b06a956ad102c29c8237554380db3"}, + {file = "jiter-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c46d927acd09c67a9fb1416df45c5a04c27e83aae969267e98fba35b74e99525"}, + {file = "jiter-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:774ff60b27a84a85b27b88cd5583899c59940bcc126caca97eb2a9df6aa00c49"}, + {file = "jiter-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5433fab222fb072237df3f637d01b81f040a07dcac1cb4a5c75c7aa9ed0bef1"}, + {file = "jiter-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8c593c6e71c07866ec6bfb790e202a833eeec885022296aff6b9e0b92d6a70e"}, + {file = "jiter-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90d32894d4c6877a87ae00c6b915b609406819dce8bc0d4e962e4de2784e567e"}, + {file = "jiter-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:798e46eed9eb10c3adbbacbd3bdb5ecd4cf7064e453d00dbef08802dae6937ff"}, + {file = "jiter-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f1368f0a6719ea80013a4eb90ba72e75d7ea67cfc7846db2ca504f3df0169a"}, + {file = "jiter-0.12.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65f04a9d0b4406f7e51279710b27484af411896246200e461d80d3ba0caa901a"}, + {file = "jiter-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:fd990541982a24281d12b67a335e44f117e4c6cbad3c3b75c7dea68bf4ce3a67"}, + {file = "jiter-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b111b0e9152fa7df870ecaebb0bd30240d9f7fff1f2003bcb4ed0f519941820b"}, + {file = "jiter-0.12.0-cp314-cp314-win32.whl", hash = "sha256:a78befb9cc0a45b5a5a0d537b06f8544c2ebb60d19d02c41ff15da28a9e22d42"}, + {file = "jiter-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1fe01c082f6aafbe5c8faf0ff074f38dfb911d53f07ec333ca03f8f6226debf"}, + {file = "jiter-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:d72f3b5a432a4c546ea4bedc84cce0c3404874f1d1676260b9c7f048a9855451"}, + {file = "jiter-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e6ded41aeba3603f9728ed2b6196e4df875348ab97b28fc8afff115ed42ba7a7"}, + {file = "jiter-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a947920902420a6ada6ad51892082521978e9dd44a802663b001436e4b771684"}, + {file = "jiter-0.12.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:add5e227e0554d3a52cf390a7635edaffdf4f8fce4fdbcef3cc2055bb396a30c"}, + {file = "jiter-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9b1cda8fcb736250d7e8711d4580ebf004a46771432be0ae4796944b5dfa5d"}, + {file = "jiter-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeb12a2223fe0135c7ff1356a143d57f95bbf1f4a66584f1fc74df21d86b993"}, + {file = "jiter-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c596cc0f4cb574877550ce4ecd51f8037469146addd676d7c1a30ebe6391923f"}, + {file = "jiter-0.12.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ab4c823b216a4aeab3fdbf579c5843165756bd9ad87cc6b1c65919c4715f783"}, + {file = "jiter-0.12.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e427eee51149edf962203ff8db75a7514ab89be5cb623fb9cea1f20b54f1107b"}, + {file = "jiter-0.12.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:edb868841f84c111255ba5e80339d386d937ec1fdce419518ce1bd9370fac5b6"}, + {file = "jiter-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8bbcfe2791dfdb7c5e48baf646d37a6a3dcb5a97a032017741dea9f817dca183"}, + {file = "jiter-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2fa940963bf02e1d8226027ef461e36af472dea85d36054ff835aeed944dd873"}, + {file = "jiter-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:506c9708dd29b27288f9f8f1140c3cb0e3d8ddb045956d7757b1fa0e0f39a473"}, + {file = "jiter-0.12.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c9d28b218d5f9e5f69a0787a196322a5056540cb378cac8ff542b4fa7219966c"}, + {file = "jiter-0.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d0ee12028daf8cfcf880dd492349a122a64f42c059b6c62a2b0c96a83a8da820"}, + {file = "jiter-0.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b135ebe757a82d67ed2821526e72d0acf87dd61f6013e20d3c45b8048af927b"}, + {file = "jiter-0.12.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15d7fafb81af8a9e3039fc305529a61cd933eecee33b4251878a1c89859552a3"}, + {file = "jiter-0.12.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92d1f41211d8a8fe412faad962d424d334764c01dac6691c44691c2e4d3eedaf"}, + {file = "jiter-0.12.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a64a48d7c917b8f32f25c176df8749ecf08cec17c466114727efe7441e17f6d"}, + {file = "jiter-0.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122046f3b3710b85de99d9aa2f3f0492a8233a2f54a64902b096efc27ea747b5"}, + {file = "jiter-0.12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:27ec39225e03c32c6b863ba879deb427882f243ae46f0d82d68b695fa5b48b40"}, + {file = "jiter-0.12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26b9e155ddc132225a39b1995b3b9f0fe0f79a6d5cbbeacf103271e7d309b404"}, + {file = "jiter-0.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ab05b7c58e29bb9e60b70c2e0094c98df79a1e42e397b9bb6eaa989b7a66dd0"}, + {file = "jiter-0.12.0-cp39-cp39-win32.whl", hash = "sha256:59f9f9df87ed499136db1c2b6c9efb902f964bed42a582ab7af413b6a293e7b0"}, + {file = "jiter-0.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:d3719596a1ebe7a48a498e8d5d0c4bf7553321d4c3eee1d620628d51351a3928"}, + {file = "jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8"}, + {file = "jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3"}, + {file = "jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e"}, + {file = "jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d"}, + {file = "jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb"}, + {file = "jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b"}, + {file = "jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f"}, + {file = "jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c"}, + {file = "jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b"}, ] [[package]] @@ -615,7 +585,6 @@ version = "1.33" description = "Apply JSON-Patches (RFC 6902)" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" -groups = ["dev"] files = [ {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, @@ -630,7 +599,6 @@ version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, @@ -642,7 +610,6 @@ version = "4.25.1" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"}, {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"}, @@ -664,7 +631,6 @@ version = "2025.9.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, @@ -675,24 +641,24 @@ referencing = ">=0.31.0" [[package]] name = "langchain" -version = "1.0.1" +version = "1.2.0" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0.0,>=3.10.0" -groups = ["dev"] files = [ - {file = "langchain-1.0.1-py3-none-any.whl", hash = "sha256:48fd616413fee4843f12cad49e8b74ad6fc159640142ca885d03bd925cd24503"}, - {file = "langchain-1.0.1.tar.gz", hash = "sha256:8bf60e096ca9c06626d9161a46651405ef1d12b5863f7679283666f83f760dc5"}, + {file = "langchain-1.2.0-py3-none-any.whl", hash = "sha256:82f0d17aa4fbb11560b30e1e7d4aeb75e3ad71ce09b85c90ab208b181a24ffac"}, + {file = "langchain-1.2.0.tar.gz", hash = "sha256:a087d1e2b2969819e29a91a6d5f98302aafe31bd49ba377ecee3bf5a5dcfe14a"}, ] [package.dependencies] -langchain-core = ">=1.0.0,<2.0.0" -langgraph = ">=1.0.0,<1.1.0" +langchain-core = ">=1.2.1,<2.0.0" +langgraph = ">=1.0.2,<1.1.0" pydantic = ">=2.7.4,<3.0.0" [package.extras] anthropic = ["langchain-anthropic"] aws = ["langchain-aws"] +azure-ai = ["langchain-azure-ai"] community = ["langchain-community"] deepseek = ["langchain-deepseek"] fireworks = ["langchain-fireworks"] @@ -709,14 +675,13 @@ xai = ["langchain-xai"] [[package]] name = "langchain-core" -version = "1.0.7" +version = "1.2.2" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0.0,>=3.10.0" -groups = ["dev"] files = [ - {file = "langchain_core-1.0.7-py3-none-any.whl", hash = "sha256:76af258b0e95a7915b8e301706a45ded50c75b80ff35329394d4df964416e32a"}, - {file = "langchain_core-1.0.7.tar.gz", hash = "sha256:6c64399cb0f163a7e45a764cce75d80fd08b82f4e0274ca892cfbcaa2f29200b"}, + {file = "langchain_core-1.2.2-py3-none-any.whl", hash = "sha256:3a83dc14217de5cba11b1a0bd43c48702401bbd18dc25cac2ffab5ac83a61cd0"}, + {file = "langchain_core-1.2.2.tar.gz", hash = "sha256:3f9c28ec6d0fe47636d28b19799794458d55da81f37309832b2b9d11c93c5e95"}, ] [package.dependencies] @@ -727,6 +692,7 @@ pydantic = ">=2.7.4,<3.0.0" pyyaml = ">=5.3.0,<7.0.0" tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0" typing-extensions = ">=4.7.0,<5.0.0" +uuid-utils = ">=0.12.0,<1.0" [[package]] name = "langchain-openai" @@ -734,7 +700,6 @@ version = "0.3.34" description = "An integration package connecting OpenAI and LangChain" optional = false python-versions = "<4.0.0,>=3.9.0" -groups = ["dev"] files = [ {file = "langchain_openai-0.3.34-py3-none-any.whl", hash = "sha256:08d61d68a6d869c70d542171e149b9065668dedfc4fafcd4de8aeb5b933030a9"}, {file = "langchain_openai-0.3.34.tar.gz", hash = "sha256:57916d462be5b8fd19e5cb2f00d4e5cf0465266a292d583de2fc693a55ba190e"}, @@ -747,50 +712,47 @@ tiktoken = ">=0.7.0,<1.0.0" [[package]] name = "langgraph" -version = "1.0.2" +version = "1.0.5" description = "Building stateful, multi-actor applications with LLMs" optional = false python-versions = ">=3.10" -groups = ["dev"] files = [ - {file = "langgraph-1.0.2-py3-none-any.whl", hash = "sha256:b3d56b8c01de857b5fb1da107e8eab6e30512a377685eeedb4f76456724c9729"}, - {file = "langgraph-1.0.2.tar.gz", hash = "sha256:dae1af08d6025cb1fcaed68f502c01af7d634d9044787c853a46c791cfc52f67"}, + {file = "langgraph-1.0.5-py3-none-any.whl", hash = "sha256:b4cfd173dca3c389735b47228ad8b295e6f7b3df779aba3a1e0c23871f81281e"}, + {file = "langgraph-1.0.5.tar.gz", hash = "sha256:7f6ae59622386b60fe9fa0ad4c53f42016b668455ed604329e7dc7904adbf3f8"}, ] [package.dependencies] langchain-core = ">=0.1" langgraph-checkpoint = ">=2.1.0,<4.0.0" langgraph-prebuilt = ">=1.0.2,<1.1.0" -langgraph-sdk = ">=0.2.2,<0.3.0" +langgraph-sdk = ">=0.3.0,<0.4.0" pydantic = ">=2.7.4" xxhash = ">=3.5.0" [[package]] name = "langgraph-checkpoint" -version = "3.0.0" +version = "3.0.1" description = "Library with base interfaces for LangGraph checkpoint savers." optional = false python-versions = ">=3.10" -groups = ["dev"] files = [ - {file = "langgraph_checkpoint-3.0.0-py3-none-any.whl", hash = "sha256:560beb83e629784ab689212a3d60834fb3196b4bbe1d6ac18e5cad5d85d46010"}, - {file = "langgraph_checkpoint-3.0.0.tar.gz", hash = "sha256:f738695ad938878d8f4775d907d9629e9fcd345b1950196effb08f088c52369e"}, + {file = "langgraph_checkpoint-3.0.1-py3-none-any.whl", hash = "sha256:9b04a8d0edc0474ce4eaf30c5d731cee38f11ddff50a6177eead95b5c4e4220b"}, + {file = "langgraph_checkpoint-3.0.1.tar.gz", hash = "sha256:59222f875f85186a22c494aedc65c4e985a3df27e696e5016ba0b98a5ed2cee0"}, ] [package.dependencies] langchain-core = ">=0.2.38" -ormsgpack = ">=1.10.0" +ormsgpack = ">=1.12.0" [[package]] name = "langgraph-prebuilt" -version = "1.0.2" +version = "1.0.5" description = "Library with high-level APIs for creating and executing LangGraph agents and tools." optional = false python-versions = ">=3.10" -groups = ["dev"] files = [ - {file = "langgraph_prebuilt-1.0.2-py3-none-any.whl", hash = "sha256:d9499f7c449fb637ee7b87e3f6a3b74095f4202053c74d33894bd839ea4c57c7"}, - {file = "langgraph_prebuilt-1.0.2.tar.gz", hash = "sha256:9896dbabf04f086eb59df4294f54ab5bdb21cd78e27e0a10e695dffd1cc6097d"}, + {file = "langgraph_prebuilt-1.0.5-py3-none-any.whl", hash = "sha256:22369563e1848862ace53fbc11b027c28dd04a9ac39314633bb95f2a7e258496"}, + {file = "langgraph_prebuilt-1.0.5.tar.gz", hash = "sha256:85802675ad778cc7240fd02d47db1e0b59c0c86d8369447d77ce47623845db2d"}, ] [package.dependencies] @@ -799,14 +761,13 @@ langgraph-checkpoint = ">=2.1.0,<4.0.0" [[package]] name = "langgraph-sdk" -version = "0.2.9" +version = "0.3.0" description = "SDK for interacting with LangGraph API" optional = false -python-versions = ">=3.9" -groups = ["dev"] +python-versions = ">=3.10" files = [ - {file = "langgraph_sdk-0.2.9-py3-none-any.whl", hash = "sha256:fbf302edadbf0fb343596f91c597794e936ef68eebc0d3e1d358b6f9f72a1429"}, - {file = "langgraph_sdk-0.2.9.tar.gz", hash = "sha256:b3bd04c6be4fa382996cd2be8fbc1e7cc94857d2bc6b6f4599a7f2a245975303"}, + {file = "langgraph_sdk-0.3.0-py3-none-any.whl", hash = "sha256:c1ade483fba17ae354ee920e4779042b18d5aba875f2a858ba569f62f628f26f"}, + {file = "langgraph_sdk-0.3.0.tar.gz", hash = "sha256:4145bc3c34feae227ae918341f66d3ba7d1499722c1ef4a8aae5ea828897d1d4"}, ] [package.dependencies] @@ -815,14 +776,13 @@ orjson = ">=3.10.1" [[package]] name = "langsmith" -version = "0.4.37" -description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." +version = "0.5.0" +description = "Client library to connect to the LangSmith Observability and Evaluation Platform." optional = false -python-versions = ">=3.9" -groups = ["dev"] +python-versions = ">=3.10" files = [ - {file = "langsmith-0.4.37-py3-none-any.whl", hash = "sha256:e34a94ce7277646299e4703a0f6e2d2c43647a28e8b800bb7ef82fd87a0ec766"}, - {file = "langsmith-0.4.37.tar.gz", hash = "sha256:d9a0eb6dd93f89843ac982c9f92be93cf2bcabbe19957f362c547766c7366c71"}, + {file = "langsmith-0.5.0-py3-none-any.whl", hash = "sha256:a83750cb3dccb33148d4ffe005e3e03080fad13e01671efbb74c9a68813bfef8"}, + {file = "langsmith-0.5.0.tar.gz", hash = "sha256:5cadf1ddd30e838cf61679f4a776aaef638d4b02ffbceba9f73283caebd39e1b"}, ] [package.dependencies] @@ -832,23 +792,108 @@ packaging = ">=23.2" pydantic = ">=1,<3" requests = ">=2.0.0" requests-toolbelt = ">=1.0.0" +uuid-utils = ">=0.12.0,<1.0" zstandard = ">=0.23.0" [package.extras] -claude-agent-sdk = ["claude-agent-sdk (>=0.1.0) ; python_version >= \"3.10\""] +claude-agent-sdk = ["claude-agent-sdk (>=0.1.0)"] langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2)"] openai-agents = ["openai-agents (>=0.0.3)"] otel = ["opentelemetry-api (>=1.30.0)", "opentelemetry-exporter-otlp-proto-http (>=1.30.0)", "opentelemetry-sdk (>=1.30.0)"] pytest = ["pytest (>=7.0.0)", "rich (>=13.9.4)", "vcrpy (>=7.0.0)"] vcr = ["vcrpy (>=7.0.0)"] +[[package]] +name = "librt" +version = "0.7.4" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +files = [ + {file = "librt-0.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dc300cb5a5a01947b1ee8099233156fdccd5001739e5f596ecfbc0dab07b5a3b"}, + {file = "librt-0.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee8d3323d921e0f6919918a97f9b5445a7dfe647270b2629ec1008aa676c0bc0"}, + {file = "librt-0.7.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:95cb80854a355b284c55f79674f6187cc9574df4dc362524e0cce98c89ee8331"}, + {file = "librt-0.7.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca1caedf8331d8ad6027f93b52d68ed8f8009f5c420c246a46fe9d3be06be0f"}, + {file = "librt-0.7.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a6f1236151e6fe1da289351b5b5bce49651c91554ecc7b70a947bced6fe212"}, + {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7766b57aeebaf3f1dac14fdd4a75c9a61f2ed56d8ebeefe4189db1cb9d2a3783"}, + {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1c4c89fb01157dd0a3bfe9e75cd6253b0a1678922befcd664eca0772a4c6c979"}, + {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7fa8beef580091c02b4fd26542de046b2abfe0aaefa02e8bcf68acb7618f2b3"}, + {file = "librt-0.7.4-cp310-cp310-win32.whl", hash = "sha256:543c42fa242faae0466fe72d297976f3c710a357a219b1efde3a0539a68a6997"}, + {file = "librt-0.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:25cc40d8eb63f0a7ea4c8f49f524989b9df901969cb860a2bc0e4bad4b8cb8a8"}, + {file = "librt-0.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3485b9bb7dfa66167d5500ffdafdc35415b45f0da06c75eb7df131f3357b174a"}, + {file = "librt-0.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:188b4b1a770f7f95ea035d5bbb9d7367248fc9d12321deef78a269ebf46a5729"}, + {file = "librt-0.7.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1b668b1c840183e4e38ed5a99f62fac44c3a3eef16870f7f17cfdfb8b47550ed"}, + {file = "librt-0.7.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e8f864b521f6cfedb314d171630f827efee08f5c3462bcbc2244ab8e1768cd6"}, + {file = "librt-0.7.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df7c9def4fc619a9c2ab402d73a0c5b53899abe090e0100323b13ccb5a3dd82"}, + {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f79bc3595b6ed159a1bf0cdc70ed6ebec393a874565cab7088a219cca14da727"}, + {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77772a4b8b5f77d47d883846928c36d730b6e612a6388c74cba33ad9eb149c11"}, + {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:064a286e6ab0b4c900e228ab4fa9cb3811b4b83d3e0cc5cd816b2d0f548cb61c"}, + {file = "librt-0.7.4-cp311-cp311-win32.whl", hash = "sha256:42da201c47c77b6cc91fc17e0e2b330154428d35d6024f3278aa2683e7e2daf2"}, + {file = "librt-0.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:d31acb5886c16ae1711741f22504195af46edec8315fe69b77e477682a87a83e"}, + {file = "librt-0.7.4-cp311-cp311-win_arm64.whl", hash = "sha256:114722f35093da080a333b3834fff04ef43147577ed99dd4db574b03a5f7d170"}, + {file = "librt-0.7.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dd3b5c37e0fb6666c27cf4e2c88ae43da904f2155c4cfc1e5a2fdce3b9fcf92"}, + {file = "librt-0.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c5de1928c486201b23ed0cc4ac92e6e07be5cd7f3abc57c88a9cf4f0f32108"}, + {file = "librt-0.7.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078ae52ffb3f036396cc4aed558e5b61faedd504a3c1f62b8ae34bf95ae39d94"}, + {file = "librt-0.7.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce58420e25097b2fc201aef9b9f6d65df1eb8438e51154e1a7feb8847e4a55ab"}, + {file = "librt-0.7.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b719c8730c02a606dc0e8413287e8e94ac2d32a51153b300baf1f62347858fba"}, + {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3749ef74c170809e6dee68addec9d2458700a8de703de081c888e92a8b015cf9"}, + {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b35c63f557653c05b5b1b6559a074dbabe0afee28ee2a05b6c9ba21ad0d16a74"}, + {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1ef704e01cb6ad39ad7af668d51677557ca7e5d377663286f0ee1b6b27c28e5f"}, + {file = "librt-0.7.4-cp312-cp312-win32.whl", hash = "sha256:c66c2b245926ec15188aead25d395091cb5c9df008d3b3207268cd65557d6286"}, + {file = "librt-0.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:71a56f4671f7ff723451f26a6131754d7c1809e04e22ebfbac1db8c9e6767a20"}, + {file = "librt-0.7.4-cp312-cp312-win_arm64.whl", hash = "sha256:419eea245e7ec0fe664eb7e85e7ff97dcdb2513ca4f6b45a8ec4a3346904f95a"}, + {file = "librt-0.7.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d44a1b1ba44cbd2fc3cb77992bef6d6fdb1028849824e1dd5e4d746e1f7f7f0b"}, + {file = "librt-0.7.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c9cab4b3de1f55e6c30a84c8cee20e4d3b2476f4d547256694a1b0163da4fe32"}, + {file = "librt-0.7.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2857c875f1edd1feef3c371fbf830a61b632fb4d1e57160bb1e6a3206e6abe67"}, + {file = "librt-0.7.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b370a77be0a16e1ad0270822c12c21462dc40496e891d3b0caf1617c8cc57e20"}, + {file = "librt-0.7.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d05acd46b9a52087bfc50c59dfdf96a2c480a601e8898a44821c7fd676598f74"}, + {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:70969229cb23d9c1a80e14225838d56e464dc71fa34c8342c954fc50e7516dee"}, + {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4450c354b89dbb266730893862dbff06006c9ed5b06b6016d529b2bf644fc681"}, + {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:adefe0d48ad35b90b6f361f6ff5a1bd95af80c17d18619c093c60a20e7a5b60c"}, + {file = "librt-0.7.4-cp313-cp313-win32.whl", hash = "sha256:21ea710e96c1e050635700695095962a22ea420d4b3755a25e4909f2172b4ff2"}, + {file = "librt-0.7.4-cp313-cp313-win_amd64.whl", hash = "sha256:772e18696cf5a64afee908662fbcb1f907460ddc851336ee3a848ef7684c8e1e"}, + {file = "librt-0.7.4-cp313-cp313-win_arm64.whl", hash = "sha256:52e34c6af84e12921748c8354aa6acf1912ca98ba60cdaa6920e34793f1a0788"}, + {file = "librt-0.7.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4f1ee004942eaaed6e06c087d93ebc1c67e9a293e5f6b9b5da558df6bf23dc5d"}, + {file = "librt-0.7.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d854c6dc0f689bad7ed452d2a3ecff58029d80612d336a45b62c35e917f42d23"}, + {file = "librt-0.7.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4f7339d9e445280f23d63dea842c0c77379c4a47471c538fc8feedab9d8d063"}, + {file = "librt-0.7.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39003fc73f925e684f8521b2dbf34f61a5deb8a20a15dcf53e0d823190ce8848"}, + {file = "librt-0.7.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb15ee29d95875ad697d449fe6071b67f730f15a6961913a2b0205015ca0843"}, + {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02a69369862099e37d00765583052a99d6a68af7e19b887e1b78fee0146b755a"}, + {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ec72342cc4d62f38b25a94e28b9efefce41839aecdecf5e9627473ed04b7be16"}, + {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:776dbb9bfa0fc5ce64234b446995d8d9f04badf64f544ca036bd6cff6f0732ce"}, + {file = "librt-0.7.4-cp314-cp314-win32.whl", hash = "sha256:0f8cac84196d0ffcadf8469d9ded4d4e3a8b1c666095c2a291e22bf58e1e8a9f"}, + {file = "librt-0.7.4-cp314-cp314-win_amd64.whl", hash = "sha256:037f5cb6fe5abe23f1dc058054d50e9699fcc90d0677eee4e4f74a8677636a1a"}, + {file = "librt-0.7.4-cp314-cp314-win_arm64.whl", hash = "sha256:a5deebb53d7a4d7e2e758a96befcd8edaaca0633ae71857995a0f16033289e44"}, + {file = "librt-0.7.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b4c25312c7f4e6ab35ab16211bdf819e6e4eddcba3b2ea632fb51c9a2a97e105"}, + {file = "librt-0.7.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:618b7459bb392bdf373f2327e477597fff8f9e6a1878fffc1b711c013d1b0da4"}, + {file = "librt-0.7.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1437c3f72a30c7047f16fd3e972ea58b90172c3c6ca309645c1c68984f05526a"}, + {file = "librt-0.7.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c96cb76f055b33308f6858b9b594618f1b46e147a4d03a4d7f0c449e304b9b95"}, + {file = "librt-0.7.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28f990e6821204f516d09dc39966ef8b84556ffd648d5926c9a3f681e8de8906"}, + {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc4aebecc79781a1b77d7d4e7d9fe080385a439e198d993b557b60f9117addaf"}, + {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:022cc673e69283a42621dd453e2407cf1647e77f8bd857d7ad7499901e62376f"}, + {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2b3ca211ae8ea540569e9c513da052699b7b06928dcda61247cb4f318122bdb5"}, + {file = "librt-0.7.4-cp314-cp314t-win32.whl", hash = "sha256:8a461f6456981d8c8e971ff5a55f2e34f4e60871e665d2f5fde23ee74dea4eeb"}, + {file = "librt-0.7.4-cp314-cp314t-win_amd64.whl", hash = "sha256:721a7b125a817d60bf4924e1eec2a7867bfcf64cfc333045de1df7a0629e4481"}, + {file = "librt-0.7.4-cp314-cp314t-win_arm64.whl", hash = "sha256:76b2ba71265c0102d11458879b4d53ccd0b32b0164d14deb8d2b598a018e502f"}, + {file = "librt-0.7.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6fc4aa67fedd827a601f97f0e61cc72711d0a9165f2c518e9a7c38fc1568b9ad"}, + {file = "librt-0.7.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e710c983d29d9cc4da29113b323647db286eaf384746344f4a233708cca1a82c"}, + {file = "librt-0.7.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:43a2515a33f2bc17b15f7fb49ff6426e49cb1d5b2539bc7f8126b9c5c7f37164"}, + {file = "librt-0.7.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fd766bb9ace3498f6b93d32f30c0e7c8ce6b727fecbc84d28160e217bb66254"}, + {file = "librt-0.7.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce1b44091355b68cffd16e2abac07c1cafa953fa935852d3a4dd8975044ca3bf"}, + {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5a72b905420c4bb2c10c87b5c09fe6faf4a76d64730e3802feef255e43dfbf5a"}, + {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07c4d7c9305e75a0edd3427b79c7bd1d019cd7eddaa7c89dbb10e0c7946bffbb"}, + {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2e734c2c54423c6dcc77f58a8585ba83b9f72e422f9edf09cab1096d4a4bdc82"}, + {file = "librt-0.7.4-cp39-cp39-win32.whl", hash = "sha256:a34ae11315d4e26326aaf04e21ccd8d9b7de983635fba38d73e203a9c8e3fe3d"}, + {file = "librt-0.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:7e4b5ffa1614ad4f32237d739699be444be28de95071bfa4e66a8da9fa777798"}, + {file = "librt-0.7.4.tar.gz", hash = "sha256:3871af56c59864d5fd21d1ac001eb2fb3b140d52ba0454720f2e4a19812404ba"}, +] + [[package]] name = "markupsafe" version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["dev", "docs"] files = [ {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, @@ -943,53 +988,53 @@ files = [ [[package]] name = "mypy" -version = "1.18.2" +version = "1.19.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, - {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, - {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, - {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, - {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, - {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, - {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, - {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, - {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, - {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, - {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, - {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, - {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, - {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, - {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, - {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, +files = [ + {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, + {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, + {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, + {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, + {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, + {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, + {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, + {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, + {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, + {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, + {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, + {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, + {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, + {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, + {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, + {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, ] [package.dependencies] +librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} mypy_extensions = ">=1.0.0" pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -1008,7 +1053,6 @@ version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, @@ -1020,7 +1064,6 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -1028,14 +1071,13 @@ files = [ [[package]] name = "openai" -version = "2.5.0" +version = "2.13.0" description = "The official Python library for the openai API" optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] +python-versions = ">=3.9" files = [ - {file = "openai-2.5.0-py3-none-any.whl", hash = "sha256:21380e5f52a71666dbadbf322dd518bdf2b9d11ed0bb3f96bea17310302d6280"}, - {file = "openai-2.5.0.tar.gz", hash = "sha256:f8fa7611f96886a0f31ac6b97e58bc0ada494b255ee2cfd51c8eb502cfcb4814"}, + {file = "openai-2.13.0-py3-none-any.whl", hash = "sha256:746521065fed68df2f9c2d85613bb50844343ea81f60009b60e6a600c9352c79"}, + {file = "openai-2.13.0.tar.gz", hash = "sha256:9ff633b07a19469ec476b1e2b5b26c5ef700886524a7a72f65e6f0b5203142d5"}, ] [package.dependencies] @@ -1060,7 +1102,6 @@ version = "1.38.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "opentelemetry_api-1.38.0-py3-none-any.whl", hash = "sha256:2891b0197f47124454ab9f0cf58f3be33faca394457ac3e09daba13ff50aa582"}, {file = "opentelemetry_api-1.38.0.tar.gz", hash = "sha256:f4c193b5e8acb0912b06ac5b16321908dd0843d75049c091487322284a3eea12"}, @@ -1076,7 +1117,6 @@ version = "1.38.0" description = "OpenTelemetry Protobuf encoding" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_common-1.38.0-py3-none-any.whl", hash = "sha256:03cb76ab213300fe4f4c62b7d8f17d97fcfd21b89f0b5ce38ea156327ddda74a"}, {file = "opentelemetry_exporter_otlp_proto_common-1.38.0.tar.gz", hash = "sha256:e333278afab4695aa8114eeb7bf4e44e65c6607d54968271a249c180b2cb605c"}, @@ -1091,7 +1131,6 @@ version = "1.38.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_http-1.38.0-py3-none-any.whl", hash = "sha256:84b937305edfc563f08ec69b9cb2298be8188371217e867c1854d77198d0825b"}, {file = "opentelemetry_exporter_otlp_proto_http-1.38.0.tar.gz", hash = "sha256:f16bd44baf15cbe07633c5112ffc68229d0edbeac7b37610be0b2def4e21e90b"}, @@ -1112,7 +1151,6 @@ version = "0.59b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "opentelemetry_instrumentation-0.59b0-py3-none-any.whl", hash = "sha256:44082cc8fe56b0186e87ee8f7c17c327c4c2ce93bdbe86496e600985d74368ee"}, {file = "opentelemetry_instrumentation-0.59b0.tar.gz", hash = "sha256:6010f0faaacdaf7c4dff8aac84e226d23437b331dcda7e70367f6d73a7db1adc"}, @@ -1130,7 +1168,6 @@ version = "0.59b0" description = "Thread context propagation support for OpenTelemetry" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "opentelemetry_instrumentation_threading-0.59b0-py3-none-any.whl", hash = "sha256:76da2fc01fe1dccebff6581080cff9e42ac7b27cc61eb563f3c4435c727e8eca"}, {file = "opentelemetry_instrumentation_threading-0.59b0.tar.gz", hash = "sha256:ce5658730b697dcbc0e0d6d13643a69fd8aeb1b32fa8db3bade8ce114c7975f3"}, @@ -1147,7 +1184,6 @@ version = "1.38.0" description = "OpenTelemetry Python Proto" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "opentelemetry_proto-1.38.0-py3-none-any.whl", hash = "sha256:b6ebe54d3217c42e45462e2a1ae28c3e2bf2ec5a5645236a490f55f45f1a0a18"}, {file = "opentelemetry_proto-1.38.0.tar.gz", hash = "sha256:88b161e89d9d372ce723da289b7da74c3a8354a8e5359992be813942969ed468"}, @@ -1162,7 +1198,6 @@ version = "1.38.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "opentelemetry_sdk-1.38.0-py3-none-any.whl", hash = "sha256:1c66af6564ecc1553d72d811a01df063ff097cdc82ce188da9951f93b8d10f6b"}, {file = "opentelemetry_sdk-1.38.0.tar.gz", hash = "sha256:93df5d4d871ed09cb4272305be4d996236eedb232253e3ab864c8620f051cebe"}, @@ -1179,7 +1214,6 @@ version = "0.59b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "opentelemetry_semantic_conventions-0.59b0-py3-none-any.whl", hash = "sha256:35d3b8833ef97d614136e253c1da9342b4c3c083bbaf29ce31d572a1c3825eed"}, {file = "opentelemetry_semantic_conventions-0.59b0.tar.gz", hash = "sha256:7a6db3f30d70202d5bf9fa4b69bc866ca6a30437287de6c510fb594878aed6b0"}, @@ -1191,161 +1225,155 @@ typing-extensions = ">=4.5.0" [[package]] name = "orjson" -version = "3.11.3" +version = "3.11.5" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "orjson-3.11.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:29cb1f1b008d936803e2da3d7cba726fc47232c45df531b29edf0b232dd737e7"}, - {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97dceed87ed9139884a55db8722428e27bd8452817fbf1869c58b49fecab1120"}, - {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58533f9e8266cb0ac298e259ed7b4d42ed3fa0b78ce76860626164de49e0d467"}, - {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c212cfdd90512fe722fa9bd620de4d46cda691415be86b2e02243242ae81873"}, - {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff835b5d3e67d9207343effb03760c00335f8b5285bfceefd4dc967b0e48f6a"}, - {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5aa4682912a450c2db89cbd92d356fef47e115dffba07992555542f344d301b"}, - {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d18dd34ea2e860553a579df02041845dee0af8985dff7f8661306f95504ddf"}, - {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8b11701bc43be92ea42bd454910437b355dfb63696c06fe953ffb40b5f763b4"}, - {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90368277087d4af32d38bd55f9da2ff466d25325bf6167c8f382d8ee40cb2bbc"}, - {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd7ff459fb393358d3a155d25b275c60b07a2c83dcd7ea962b1923f5a1134569"}, - {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8d902867b699bcd09c176a280b1acdab57f924489033e53d0afe79817da37e6"}, - {file = "orjson-3.11.3-cp310-cp310-win32.whl", hash = "sha256:bb93562146120bb51e6b154962d3dadc678ed0fce96513fa6bc06599bb6f6edc"}, - {file = "orjson-3.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:976c6f1975032cc327161c65d4194c549f2589d88b105a5e3499429a54479770"}, - {file = "orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f"}, - {file = "orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91"}, - {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904"}, - {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6"}, - {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d"}, - {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038"}, - {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb"}, - {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2"}, - {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55"}, - {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1"}, - {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824"}, - {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f"}, - {file = "orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204"}, - {file = "orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b"}, - {file = "orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e"}, - {file = "orjson-3.11.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8c752089db84333e36d754c4baf19c0e1437012242048439c7e80eb0e6426e3b"}, - {file = "orjson-3.11.3-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9b8761b6cf04a856eb544acdd82fc594b978f12ac3602d6374a7edb9d86fd2c2"}, - {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b13974dc8ac6ba22feaa867fc19135a3e01a134b4f7c9c28162fed4d615008a"}, - {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f83abab5bacb76d9c821fd5c07728ff224ed0e52d7a71b7b3de822f3df04e15c"}, - {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6fbaf48a744b94091a56c62897b27c31ee2da93d826aa5b207131a1e13d4064"}, - {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc779b4f4bba2847d0d2940081a7b6f7b5877e05408ffbb74fa1faf4a136c424"}, - {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd4b909ce4c50faa2192da6bb684d9848d4510b736b0611b6ab4020ea6fd2d23"}, - {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524b765ad888dc5518bbce12c77c2e83dee1ed6b0992c1790cc5fb49bb4b6667"}, - {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84fd82870b97ae3cdcea9d8746e592b6d40e1e4d4527835fc520c588d2ded04f"}, - {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbecb9709111be913ae6879b07bafd4b0785b44c1eb5cac8ac76da048b3885a1"}, - {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dba358d55aee552bd868de348f4736ca5a4086d9a62e2bfbbeeb5629fe8b0cc"}, - {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eabcf2e84f1d7105f84580e03012270c7e97ecb1fb1618bda395061b2a84a049"}, - {file = "orjson-3.11.3-cp312-cp312-win32.whl", hash = "sha256:3782d2c60b8116772aea8d9b7905221437fdf53e7277282e8d8b07c220f96cca"}, - {file = "orjson-3.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:79b44319268af2eaa3e315b92298de9a0067ade6e6003ddaef72f8e0bedb94f1"}, - {file = "orjson-3.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:0e92a4e83341ef79d835ca21b8bd13e27c859e4e9e4d7b63defc6e58462a3710"}, - {file = "orjson-3.11.3-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:af40c6612fd2a4b00de648aa26d18186cd1322330bd3a3cc52f87c699e995810"}, - {file = "orjson-3.11.3-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:9f1587f26c235894c09e8b5b7636a38091a9e6e7fe4531937534749c04face43"}, - {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61dcdad16da5bb486d7227a37a2e789c429397793a6955227cedbd7252eb5a27"}, - {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c6d71478e2cbea0a709e8a06365fa63da81da6498a53e4c4f065881d21ae8f"}, - {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94112e0098470b665cb0ed06efb187154b63649403b8d5e9aedeb482b4548c"}, - {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b756575aaa2a855a75192f356bbda11a89169830e1439cfb1a3e1a6dde7be"}, - {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9416cc19a349c167ef76135b2fe40d03cea93680428efee8771f3e9fb66079d"}, - {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b822caf5b9752bc6f246eb08124c3d12bf2175b66ab74bac2ef3bbf9221ce1b2"}, - {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:414f71e3bdd5573893bf5ecdf35c32b213ed20aa15536fe2f588f946c318824f"}, - {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:828e3149ad8815dc14468f36ab2a4b819237c155ee1370341b91ea4c8672d2ee"}, - {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac9e05f25627ffc714c21f8dfe3a579445a5c392a9c8ae7ba1d0e9fb5333f56e"}, - {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e44fbe4000bd321d9f3b648ae46e0196d21577cf66ae684a96ff90b1f7c93633"}, - {file = "orjson-3.11.3-cp313-cp313-win32.whl", hash = "sha256:2039b7847ba3eec1f5886e75e6763a16e18c68a63efc4b029ddf994821e2e66b"}, - {file = "orjson-3.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:29be5ac4164aa8bdcba5fa0700a3c9c316b411d8ed9d39ef8a882541bd452fae"}, - {file = "orjson-3.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:18bd1435cb1f2857ceb59cfb7de6f92593ef7b831ccd1b9bfb28ca530e539dce"}, - {file = "orjson-3.11.3-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cf4b81227ec86935568c7edd78352a92e97af8da7bd70bdfdaa0d2e0011a1ab4"}, - {file = "orjson-3.11.3-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:bc8bc85b81b6ac9fc4dae393a8c159b817f4c2c9dee5d12b773bddb3b95fc07e"}, - {file = "orjson-3.11.3-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:88dcfc514cfd1b0de038443c7b3e6a9797ffb1b3674ef1fd14f701a13397f82d"}, - {file = "orjson-3.11.3-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d61cd543d69715d5fc0a690c7c6f8dcc307bc23abef9738957981885f5f38229"}, - {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2b7b153ed90ababadbef5c3eb39549f9476890d339cf47af563aea7e07db2451"}, - {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7909ae2460f5f494fecbcd10613beafe40381fd0316e35d6acb5f3a05bfda167"}, - {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2030c01cbf77bc67bee7eef1e7e31ecf28649353987775e3583062c752da0077"}, - {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a0169ebd1cbd94b26c7a7ad282cf5c2744fce054133f959e02eb5265deae1872"}, - {file = "orjson-3.11.3-cp314-cp314-win32.whl", hash = "sha256:0c6d7328c200c349e3a4c6d8c83e0a5ad029bdc2d417f234152bf34842d0fc8d"}, - {file = "orjson-3.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:317bbe2c069bbc757b1a2e4105b64aacd3bc78279b66a6b9e51e846e4809f804"}, - {file = "orjson-3.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:e8f6a7a27d7b7bec81bd5924163e9af03d49bbb63013f107b48eb5d16db711bc"}, - {file = "orjson-3.11.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:56afaf1e9b02302ba636151cfc49929c1bb66b98794291afd0e5f20fecaf757c"}, - {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913f629adef31d2d350d41c051ce7e33cf0fd06a5d1cb28d49b1899b23b903aa"}, - {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0a23b41f8f98b4e61150a03f83e4f0d566880fe53519d445a962929a4d21045"}, - {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d721fee37380a44f9d9ce6c701b3960239f4fb3d5ceea7f31cbd43882edaa2f"}, - {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73b92a5b69f31b1a58c0c7e31080aeaec49c6e01b9522e71ff38d08f15aa56de"}, - {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2489b241c19582b3f1430cc5d732caefc1aaf378d97e7fb95b9e56bed11725f"}, - {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5189a5dab8b0312eadaf9d58d3049b6a52c454256493a557405e77a3d67ab7f"}, - {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9d8787bdfbb65a85ea76d0e96a3b1bed7bf0fbcb16d40408dc1172ad784a49d2"}, - {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:8e531abd745f51f8035e207e75e049553a86823d189a51809c078412cefb399a"}, - {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8ab962931015f170b97a3dd7bd933399c1bae8ed8ad0fb2a7151a5654b6941c7"}, - {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:124d5ba71fee9c9902c4a7baa9425e663f7f0aecf73d31d54fe3dd357d62c1a7"}, - {file = "orjson-3.11.3-cp39-cp39-win32.whl", hash = "sha256:22724d80ee5a815a44fc76274bb7ba2e7464f5564aacb6ecddaa9970a83e3225"}, - {file = "orjson-3.11.3-cp39-cp39-win_amd64.whl", hash = "sha256:215c595c792a87d4407cb72dd5e0f6ee8e694ceeb7f9102b533c5a9bf2a916bb"}, - {file = "orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a"}, +files = [ + {file = "orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401"}, + {file = "orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8"}, + {file = "orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167"}, + {file = "orjson-3.11.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9c8494625ad60a923af6b2b0bd74107146efe9b55099e20d7740d995f338fcd8"}, + {file = "orjson-3.11.5-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:7bb2ce0b82bc9fd1168a513ddae7a857994b780b2945a8c51db4ab1c4b751ebc"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67394d3becd50b954c4ecd24ac90b5051ee7c903d167459f93e77fc6f5b4c968"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:298d2451f375e5f17b897794bcc3e7b821c0f32b4788b9bcae47ada24d7f3cf7"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa5e4244063db8e1d87e0f54c3f7522f14b2dc937e65d5241ef0076a096409fd"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1db2088b490761976c1b2e956d5d4e6409f3732e9d79cfa69f876c5248d1baf9"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2ed66358f32c24e10ceea518e16eb3549e34f33a9d51f99ce23b0251776a1ef"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2021afda46c1ed64d74b555065dbd4c2558d510d8cec5ea6a53001b3e5e82a9"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b42ffbed9128e547a1647a3e50bc88ab28ae9daa61713962e0d3dd35e820c125"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8d5f16195bb671a5dd3d1dbea758918bada8f6cc27de72bd64adfbd748770814"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c0e5d9f7a0227df2927d343a6e3859bebf9208b427c79bd31949abcc2fa32fa5"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23d04c4543e78f724c4dfe656b3791b5f98e4c9253e13b2636f1af5d90e4a880"}, + {file = "orjson-3.11.5-cp311-cp311-win32.whl", hash = "sha256:c404603df4865f8e0afe981aa3c4b62b406e6d06049564d58934860b62b7f91d"}, + {file = "orjson-3.11.5-cp311-cp311-win_amd64.whl", hash = "sha256:9645ef655735a74da4990c24ffbd6894828fbfa117bc97c1edd98c282ecb52e1"}, + {file = "orjson-3.11.5-cp311-cp311-win_arm64.whl", hash = "sha256:1cbf2735722623fcdee8e712cbaaab9e372bbcb0c7924ad711b261c2eccf4a5c"}, + {file = "orjson-3.11.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:334e5b4bff9ad101237c2d799d9fd45737752929753bf4faf4b207335a416b7d"}, + {file = "orjson-3.11.5-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:ff770589960a86eae279f5d8aa536196ebda8273a2a07db2a54e82b93bc86626"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed24250e55efbcb0b35bed7caaec8cedf858ab2f9f2201f17b8938c618c8ca6f"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a66d7769e98a08a12a139049aac2f0ca3adae989817f8c43337455fbc7669b85"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86cfc555bfd5794d24c6a1903e558b50644e5e68e6471d66502ce5cb5fdef3f9"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a230065027bc2a025e944f9d4714976a81e7ecfa940923283bca7bbc1f10f626"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b29d36b60e606df01959c4b982729c8845c69d1963f88686608be9ced96dbfaa"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74099c6b230d4261fdc3169d50efc09abf38ace1a42ea2f9994b1d79153d477"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e697d06ad57dd0c7a737771d470eedc18e68dfdefcdd3b7de7f33dfda5b6212e"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e08ca8a6c851e95aaecc32bc44a5aa75d0ad26af8cdac7c77e4ed93acf3d5b69"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e8b5f96c05fce7d0218df3fdfeb962d6b8cfff7e3e20264306b46dd8b217c0f3"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ddbfdb5099b3e6ba6d6ea818f61997bb66de14b411357d24c4612cf1ebad08ca"}, + {file = "orjson-3.11.5-cp312-cp312-win32.whl", hash = "sha256:9172578c4eb09dbfcf1657d43198de59b6cef4054de385365060ed50c458ac98"}, + {file = "orjson-3.11.5-cp312-cp312-win_amd64.whl", hash = "sha256:2b91126e7b470ff2e75746f6f6ee32b9ab67b7a93c8ba1d15d3a0caaf16ec875"}, + {file = "orjson-3.11.5-cp312-cp312-win_arm64.whl", hash = "sha256:acbc5fac7e06777555b0722b8ad5f574739e99ffe99467ed63da98f97f9ca0fe"}, + {file = "orjson-3.11.5-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3b01799262081a4c47c035dd77c1301d40f568f77cc7ec1bb7db5d63b0a01629"}, + {file = "orjson-3.11.5-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:61de247948108484779f57a9f406e4c84d636fa5a59e411e6352484985e8a7c3"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:894aea2e63d4f24a7f04a1908307c738d0dce992e9249e744b8f4e8dd9197f39"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ddc21521598dbe369d83d4d40338e23d4101dad21dae0e79fa20465dbace019f"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cce16ae2f5fb2c53c3eafdd1706cb7b6530a67cc1c17abe8ec747f5cd7c0c51"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e46c762d9f0e1cfb4ccc8515de7f349abbc95b59cb5a2bd68df5973fdef913f8"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7345c759276b798ccd6d77a87136029e71e66a8bbf2d2755cbdde1d82e78706"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75bc2e59e6a2ac1dd28901d07115abdebc4563b5b07dd612bf64260a201b1c7f"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:54aae9b654554c3b4edd61896b978568c6daa16af96fa4681c9b5babd469f863"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4bdd8d164a871c4ec773f9de0f6fe8769c2d6727879c37a9666ba4183b7f8228"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a261fef929bcf98a60713bf5e95ad067cea16ae345d9a35034e73c3990e927d2"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c028a394c766693c5c9909dec76b24f37e6a1b91999e8d0c0d5feecbe93c3e05"}, + {file = "orjson-3.11.5-cp313-cp313-win32.whl", hash = "sha256:2cc79aaad1dfabe1bd2d50ee09814a1253164b3da4c00a78c458d82d04b3bdef"}, + {file = "orjson-3.11.5-cp313-cp313-win_amd64.whl", hash = "sha256:ff7877d376add4e16b274e35a3f58b7f37b362abf4aa31863dadacdd20e3a583"}, + {file = "orjson-3.11.5-cp313-cp313-win_arm64.whl", hash = "sha256:59ac72ea775c88b163ba8d21b0177628bd015c5dd060647bbab6e22da3aad287"}, + {file = "orjson-3.11.5-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e446a8ea0a4c366ceafc7d97067bfd55292969143b57e3c846d87fc701e797a0"}, + {file = "orjson-3.11.5-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:53deb5addae9c22bbe3739298f5f2196afa881ea75944e7720681c7080909a81"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cd00d49d6063d2b8791da5d4f9d20539c5951f965e45ccf4e96d33505ce68f"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fd15f9fc8c203aeceff4fda211157fad114dde66e92e24097b3647a08f4ee9e"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9df95000fbe6777bf9820ae82ab7578e8662051bb5f83d71a28992f539d2cda7"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a8d676748fca47ade5bc3da7430ed7767afe51b2f8100e3cd65e151c0eaceb"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa0f513be38b40234c77975e68805506cad5d57b3dfd8fe3baa7f4f4051e15b4"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1863e75b92891f553b7922ce4ee10ed06db061e104f2b7815de80cdcb135ad"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4be86b58e9ea262617b8ca6251a2f0d63cc132a6da4b5fcc8e0a4128782c829"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b923c1c13fa02084eb38c9c065afd860a5cff58026813319a06949c3af5732ac"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1b6bd351202b2cd987f35a13b5e16471cf4d952b42a73c391cc537974c43ef6d"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb150d529637d541e6af06bbe3d02f5498d628b7f98267ff87647584293ab439"}, + {file = "orjson-3.11.5-cp314-cp314-win32.whl", hash = "sha256:9cc1e55c884921434a84a0c3dd2699eb9f92e7b441d7f53f3941079ec6ce7499"}, + {file = "orjson-3.11.5-cp314-cp314-win_amd64.whl", hash = "sha256:a4f3cb2d874e03bc7767c8f88adaa1a9a05cecea3712649c3b58589ec7317310"}, + {file = "orjson-3.11.5-cp314-cp314-win_arm64.whl", hash = "sha256:38b22f476c351f9a1c43e5b07d8b5a02eb24a6ab8e75f700f7d479d4568346a5"}, + {file = "orjson-3.11.5-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1b280e2d2d284a6713b0cfec7b08918ebe57df23e3f76b27586197afca3cb1e9"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d8a112b274fae8c5f0f01954cb0480137072c271f3f4958127b010dfefaec"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0a2ae6f09ac7bd47d2d5a5305c1d9ed08ac057cda55bb0a49fa506f0d2da00"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0d87bd1896faac0d10b4f849016db81a63e4ec5df38757ffae84d45ab38aa71"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:801a821e8e6099b8c459ac7540b3c32dba6013437c57fdcaec205b169754f38c"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a0f6ac618c98c74b7fbc8c0172ba86f9e01dbf9f62aa0b1776c2231a7bffe5"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea7339bdd22e6f1060c55ac31b6a755d86a5b2ad3657f2669ec243f8e3b2bdb"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4dad582bc93cef8f26513e12771e76385a7e6187fd713157e971c784112aad56"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:0522003e9f7fba91982e83a97fec0708f5a714c96c4209db7104e6b9d132f111"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7403851e430a478440ecc1258bcbacbfbd8175f9ac1e39031a7121dd0de05ff8"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5f691263425d3177977c8d1dd896cde7b98d93cbf390b2544a090675e83a6a0a"}, + {file = "orjson-3.11.5-cp39-cp39-win32.whl", hash = "sha256:61026196a1c4b968e1b1e540563e277843082e9e97d78afa03eb89315af531f1"}, + {file = "orjson-3.11.5-cp39-cp39-win_amd64.whl", hash = "sha256:09b94b947ac08586af635ef922d69dc9bc63321527a3a04647f4986a73f4bd30"}, + {file = "orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5"}, ] [[package]] name = "ormsgpack" -version = "1.11.0" -description = "" +version = "1.12.1" +description = "Fast, correct Python msgpack library supporting dataclasses, datetimes, and numpy" optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "ormsgpack-1.11.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:03d4e658dd6e1882a552ce1d13cc7b49157414e7d56a4091fbe7823225b08cba"}, - {file = "ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bb67eb913c2b703f0ed39607fc56e50724dd41f92ce080a586b4d6149eb3fe4"}, - {file = "ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e54175b92411f73a238e5653a998627f6660de3def37d9dd7213e0fd264ca56"}, - {file = "ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca2b197f4556e1823d1319869d4c5dc278be335286d2308b0ed88b59a5afcc25"}, - {file = "ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc62388262f58c792fe1e450e1d9dbcc174ed2fb0b43db1675dd7c5ff2319d6a"}, - {file = "ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c48bc10af74adfbc9113f3fb160dc07c61ad9239ef264c17e449eba3de343dc2"}, - {file = "ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a608d3a1d4fa4acdc5082168a54513cff91f47764cef435e81a483452f5f7647"}, - {file = "ormsgpack-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:97217b4f7f599ba45916b9c4c4b1d5656e8e2a4d91e2e191d72a7569d3c30923"}, - {file = "ormsgpack-1.11.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c7be823f47d8e36648d4bc90634b93f02b7d7cc7480081195f34767e86f181fb"}, - {file = "ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68accf15d1b013812755c0eb7a30e1fc2f81eb603a1a143bf0cda1b301cfa797"}, - {file = "ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:805d06fb277d9a4e503c0c707545b49cde66cbb2f84e5cf7c58d81dfc20d8658"}, - {file = "ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1e57cdf003e77acc43643bda151dc01f97147a64b11cdee1380bb9698a7601c"}, - {file = "ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37fc05bdaabd994097c62e2f3e08f66b03f856a640ede6dc5ea340bd15b77f4d"}, - {file = "ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a6e9db6c73eb46b2e4d97bdffd1368a66f54e6806b563a997b19c004ef165e1d"}, - {file = "ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e9c44eae5ac0196ffc8b5ed497c75511056508f2303fa4d36b208eb820cf209e"}, - {file = "ormsgpack-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:11d0dfaf40ae7c6de4f7dbd1e4892e2e6a55d911ab1774357c481158d17371e4"}, - {file = "ormsgpack-1.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:0c63a3f7199a3099c90398a1bdf0cb577b06651a442dc5efe67f2882665e5b02"}, - {file = "ormsgpack-1.11.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3434d0c8d67de27d9010222de07fb6810fb9af3bb7372354ffa19257ac0eb83b"}, - {file = "ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2da5bd097e8dbfa4eb0d4ccfe79acd6f538dee4493579e2debfe4fc8f4ca89b"}, - {file = "ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fdbaa0a5a8606a486960b60c24f2d5235d30ac7a8b98eeaea9854bffef14dc3d"}, - {file = "ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3682f24f800c1837017ee90ce321086b2cbaef88db7d4cdbbda1582aa6508159"}, - {file = "ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fcca21202bb05ccbf3e0e92f560ee59b9331182e4c09c965a28155efbb134993"}, - {file = "ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c30e5c4655ba46152d722ec7468e8302195e6db362ec1ae2c206bc64f6030e43"}, - {file = "ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7138a341f9e2c08c59368f03d3be25e8b87b3baaf10d30fb1f6f6b52f3d47944"}, - {file = "ormsgpack-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d4bd8589b78a11026d47f4edf13c1ceab9088bb12451f34396afe6497db28a27"}, - {file = "ormsgpack-1.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:e5e746a1223e70f111d4001dab9585ac8639eee8979ca0c8db37f646bf2961da"}, - {file = "ormsgpack-1.11.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e7b36ab7b45cb95217ae1f05f1318b14a3e5ef73cb00804c0f06233f81a14e8"}, - {file = "ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43402d67e03a9a35cc147c8c03f0c377cad016624479e1ee5b879b8425551484"}, - {file = "ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:64fd992f932764d6306b70ddc755c1bc3405c4c6a69f77a36acf7af1c8f5ada4"}, - {file = "ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0362fb7fe4a29c046c8ea799303079a09372653a1ce5a5a588f3bbb8088368d0"}, - {file = "ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:de2f7a65a9d178ed57be49eba3d0fc9b833c32beaa19dbd4ba56014d3c20b152"}, - {file = "ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f38cfae95461466055af966fc922d06db4e1654966385cda2828653096db34da"}, - {file = "ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c88396189d238f183cea7831b07a305ab5c90d6d29b53288ae11200bd956357b"}, - {file = "ormsgpack-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:5403d1a945dd7c81044cebeca3f00a28a0f4248b33242a5d2d82111628043725"}, - {file = "ormsgpack-1.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c57357b8d43b49722b876edf317bdad9e6d52071b523fdd7394c30cd1c67d5a0"}, - {file = "ormsgpack-1.11.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d390907d90fd0c908211592c485054d7a80990697ef4dff4e436ac18e1aab98a"}, - {file = "ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6153c2e92e789509098e04c9aa116b16673bd88ec78fbe0031deeb34ab642d10"}, - {file = "ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2b2c2a065a94d742212b2018e1fecd8f8d72f3c50b53a97d1f407418093446d"}, - {file = "ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:110e65b5340f3d7ef8b0009deae3c6b169437e6b43ad5a57fd1748085d29d2ac"}, - {file = "ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c27e186fca96ab34662723e65b420919910acbbc50fc8e1a44e08f26268cb0e0"}, - {file = "ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d56b1f877c13d499052d37a3db2378a97d5e1588d264f5040b3412aee23d742c"}, - {file = "ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c88e28cd567c0a3269f624b4ade28142d5e502c8e826115093c572007af5be0a"}, - {file = "ormsgpack-1.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:8811160573dc0a65f62f7e0792c4ca6b7108dfa50771edb93f9b84e2d45a08ae"}, - {file = "ormsgpack-1.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:23e30a8d3c17484cf74e75e6134322255bd08bc2b5b295cc9c442f4bae5f3c2d"}, - {file = "ormsgpack-1.11.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2905816502adfaf8386a01dd85f936cd378d243f4f5ee2ff46f67f6298dc90d5"}, - {file = "ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c04402fb9a0a9b9f18fbafd6d5f8398ee99b3ec619fb63952d3a954bc9d47daa"}, - {file = "ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a025ec07ac52056ecfd9e57b5cbc6fff163f62cb9805012b56cda599157f8ef2"}, - {file = "ormsgpack-1.11.0-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:354c6a5039faf63b63d8f42ec7915583a4a56e10b319284370a5a89c4382d985"}, - {file = "ormsgpack-1.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7058c85cc13dd329bc7b528e38626c6babcd0066d6e9163330a1509fe0aa4707"}, - {file = "ormsgpack-1.11.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e15b634be324fb18dab7aa82ab929a0d57d42c12650ae3dedd07d8d31b17733"}, - {file = "ormsgpack-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6329e6eae9dfe600962739a6e060ea82885ec58b8338875c5ac35080da970f94"}, - {file = "ormsgpack-1.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b27546c28f92b9eb757620f7f1ed89fb7b07be3b9f4ba1b7de75761ec1c4bcc8"}, - {file = "ormsgpack-1.11.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:26a17919d9144b4ac7112dbbadef07927abbe436be2cf99a703a19afe7dd5c8b"}, - {file = "ormsgpack-1.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5352868ee4cdc00656bf216b56bc654f72ac3008eb36e12561f6337bb7104b45"}, - {file = "ormsgpack-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:2ffe36f1f441a40949e8587f5aa3d3fc9f100576925aab667117403eab494338"}, - {file = "ormsgpack-1.11.0.tar.gz", hash = "sha256:7c9988e78fedba3292541eb3bb274fa63044ef4da2ddb47259ea70c05dee4206"}, +python-versions = ">=3.10" +files = [ + {file = "ormsgpack-1.12.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:62e3614cab63fa5aa42f5f0ca3cd12899f0bfc5eb8a5a0ebab09d571c89d427d"}, + {file = "ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86d9fbf85c05c69c33c229d2eba7c8c3500a56596cd8348131c918acd040d6af"}, + {file = "ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8d246e66f09d8e0f96e770829149ee83206e90ed12f5987998bb7be84aec99fe"}, + {file = "ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfc2c830a1ed2d00de713d08c9e62efa699e8fd29beafa626aaebe466f583ebb"}, + {file = "ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc892757d8f9eea5208268a527cf93c98409802f6a9f7c8d71a7b8f9ba5cb944"}, + {file = "ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0de1dbcf11ea739ac4a882b43d5c2055e6d99ce64e8d6502e25d6d881700c017"}, + {file = "ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d5065dfb9ec4db93241c60847624d9aeef4ccb449c26a018c216b55c69be83c0"}, + {file = "ormsgpack-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d17103c4726181d7000c61b751c881f1b6f401d146df12da028fc730227df19"}, + {file = "ormsgpack-1.12.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4038f59ae0e19dac5e5d9aae4ec17ff84a79e046342ee73ccdecf3547ecf0d34"}, + {file = "ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16c63b0c5a3eec467e4bb33a14dabba076b7d934dff62898297b5c0b5f7c3cb3"}, + {file = "ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74fd6a8e037eb310dda865298e8d122540af00fe5658ec18b97a1d34f4012e4d"}, + {file = "ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ad60308e233dd824a1859eabb5fe092e123e885eafa4ad5789322329c80fb5"}, + {file = "ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:35127464c941c1219acbe1a220e48d55e7933373d12257202f4042f7044b4c90"}, + {file = "ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c48d1c50794692d1e6e3f8c3bb65f5c3acfaae9347e506484a65d60b3d91fb50"}, + {file = "ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b512b2ad6feaaefdc26e05431ed2843e42483041e354e167c53401afaa83d919"}, + {file = "ormsgpack-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:93f30db95e101a9616323bfc50807ad00e7f6197cea2216d2d24af42afc77d88"}, + {file = "ormsgpack-1.12.1-cp311-cp311-win_arm64.whl", hash = "sha256:d75b5fa14f6abffce2c392ee03b4731199d8a964c81ee8645c4c79af0e80fd50"}, + {file = "ormsgpack-1.12.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4d7fb0e1b6fbc701d75269f7405a4f79230a6ce0063fb1092e4f6577e312f86d"}, + {file = "ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43a9353e2db5b024c91a47d864ef15eaa62d81824cfc7740fed4cef7db738694"}, + {file = "ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc8fe866b7706fc25af0adf1f600bc06ece5b15ca44e34641327198b821e5c3c"}, + {file = "ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:813755b5f598a78242042e05dfd1ada4e769e94b98c9ab82554550f97ff4d641"}, + {file = "ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8eea2a13536fae45d78f93f2cc846c9765c7160c85f19cfefecc20873c137cdd"}, + {file = "ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7a02ebda1a863cbc604740e76faca8eee1add322db2dcbe6cf32669fffdff65c"}, + {file = "ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c0bd63897c439931cdf29348e5e6e8c330d529830e848d10767615c0f3d1b82"}, + {file = "ormsgpack-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:362f2e812f8d7035dc25a009171e09d7cc97cb30d3c9e75a16aeae00ca3c1dcf"}, + {file = "ormsgpack-1.12.1-cp312-cp312-win_arm64.whl", hash = "sha256:6190281e381db2ed0045052208f47a995ccf61eed48f1215ae3cce3fbccd59c5"}, + {file = "ormsgpack-1.12.1-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9663d6b3ecc917c063d61a99169ce196a80f3852e541ae404206836749459279"}, + {file = "ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32e85cfbaf01a94a92520e7fe7851cfcfe21a5698299c28ab86194895f9b9233"}, + {file = "ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dabfd2c24b59c7c69870a5ecee480dfae914a42a0c2e7c9d971cf531e2ba471a"}, + {file = "ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bbf2b64afeded34ccd8e25402e4bca038757913931fa0d693078d75563f6f9"}, + {file = "ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9959a71dde1bd0ced84af17facc06a8afada495a34e9cb1bad8e9b20d4c59cef"}, + {file = "ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:e9be0e3b62d758f21f5b20e0e06b3a240ec546c4a327bf771f5825462aa74714"}, + {file = "ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a29d49ab7fdd77ea787818e60cb4ef491708105b9c4c9b0f919201625eb036b5"}, + {file = "ormsgpack-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:c418390b47a1d367e803f6c187f77e4d67c7ae07ba962e3a4a019001f4b0291a"}, + {file = "ormsgpack-1.12.1-cp313-cp313-win_arm64.whl", hash = "sha256:cfa22c91cffc10a7fbd43729baff2de7d9c28cef2509085a704168ae31f02568"}, + {file = "ormsgpack-1.12.1-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b93c91efb1a70751a1902a5b43b27bd8fd38e0ca0365cf2cde2716423c15c3a6"}, + {file = "ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf0ea0389167b5fa8d2933dd3f33e887ec4ba68f89c25214d7eec4afd746d22"}, + {file = "ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4c29af837f35af3375070689e781161e7cf019eb2f7cd641734ae45cd001c0d"}, + {file = "ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336fc65aa0fe65896a3dabaae31e332a0a98b4a00ad7b0afde21a7505fd23ff3"}, + {file = "ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:940f60aabfefe71dd6b82cb33f4ff10b2e7f5fcfa5f103cdb0a23b6aae4c713c"}, + {file = "ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:596ad9e1b6d4c95595c54aaf49b1392609ca68f562ce06f4f74a5bc4053bcda4"}, + {file = "ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:575210e8fcbc7b0375026ba040a5eef223e9f66a4453d9623fc23282ae09c3c8"}, + {file = "ormsgpack-1.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:647daa3718572280893456be44c60aea6690b7f2edc54c55648ee66e8f06550f"}, + {file = "ormsgpack-1.12.1-cp314-cp314-win_arm64.whl", hash = "sha256:a8b3ab762a6deaf1b6490ab46dda0c51528cf8037e0246c40875c6fe9e37b699"}, + {file = "ormsgpack-1.12.1-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:12087214e436c1f6c28491949571abea759a63111908c4f7266586d78144d7a8"}, + {file = "ormsgpack-1.12.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6d54c14cf86ef13f10ccade94d1e7de146aa9b17d371e18b16e95f329393b7"}, + {file = "ormsgpack-1.12.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f3584d07882b7ea2a1a589f795a3af97fe4c2932b739408e6d1d9d286cad862"}, + {file = "ormsgpack-1.12.1.tar.gz", hash = "sha256:a3877fde1e4f27a39f92681a0aab6385af3a41d0c25375d33590ae20410ea2ac"}, ] [[package]] @@ -1354,7 +1382,6 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -1366,7 +1393,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1378,7 +1404,6 @@ version = "15.0.4" description = "API Documentation for Python Projects" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "pdoc-15.0.4-py3-none-any.whl", hash = "sha256:f9028e85e7bb8475b054e69bde1f6d26fc4693d25d9fa1b1ce9009bec7f7a5c4"}, {file = "pdoc-15.0.4.tar.gz", hash = "sha256:cf9680f10f5b4863381f44ef084b1903f8f356acb0d4cc6b64576ba9fb712c82"}, @@ -1391,14 +1416,13 @@ pygments = ">=2.12.0" [[package]] name = "platformdirs" -version = "4.5.0" +version = "4.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.10" -groups = ["dev"] files = [ - {file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"}, - {file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"}, + {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, + {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, ] [package.extras] @@ -1412,7 +1436,6 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -1428,7 +1451,6 @@ version = "0.9.0" description = "A fast C-implemented library for Levenshtein distance" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "polyleven-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6e00207fbe0fcdde206b9b277cf14bb9db8801f8d303204b1572870797399974"}, {file = "polyleven-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d400f255af038f77b37d5010532e0e82d07160457c8282e5b40632987ab815be"}, @@ -1493,7 +1515,6 @@ version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, @@ -1508,171 +1529,172 @@ virtualenv = ">=20.10.0" [[package]] name = "protobuf" -version = "6.33.0" +version = "6.33.2" description = "" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ - {file = "protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035"}, - {file = "protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee"}, - {file = "protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455"}, - {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90"}, - {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298"}, - {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef"}, - {file = "protobuf-6.33.0-cp39-cp39-win32.whl", hash = "sha256:cd33a8e38ea3e39df66e1bbc462b076d6e5ba3a4ebbde58219d777223a7873d3"}, - {file = "protobuf-6.33.0-cp39-cp39-win_amd64.whl", hash = "sha256:c963e86c3655af3a917962c9619e1a6b9670540351d7af9439d06064e3317cc9"}, - {file = "protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995"}, - {file = "protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954"}, + {file = "protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d"}, + {file = "protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4"}, + {file = "protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43"}, + {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e"}, + {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872"}, + {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f"}, + {file = "protobuf-6.33.2-cp39-cp39-win32.whl", hash = "sha256:7109dcc38a680d033ffb8bf896727423528db9163be1b6a02d6a49606dcadbfe"}, + {file = "protobuf-6.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:2981c58f582f44b6b13173e12bb8656711189c2a70250845f264b877f00b1913"}, + {file = "protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c"}, + {file = "protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4"}, ] [[package]] name = "pydantic" -version = "2.12.3" +version = "2.12.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ - {file = "pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf"}, - {file = "pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74"}, + {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, + {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.41.4" +pydantic-core = "2.41.5" typing-extensions = ">=4.14.1" typing-inspection = ">=0.4.2" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] +timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.41.4" +version = "2.41.5" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"}, - {file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"}, - {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"}, - {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"}, - {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"}, - {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"}, - {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"}, - {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"}, - {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"}, - {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"}, - {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"}, - {file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"}, - {file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"}, - {file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"}, - {file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"}, - {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"}, - {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"}, - {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"}, - {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"}, - {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"}, - {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"}, - {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"}, - {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"}, - {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"}, - {file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"}, - {file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"}, - {file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"}, - {file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"}, - {file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"}, - {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"}, - {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"}, - {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"}, - {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"}, - {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"}, - {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"}, - {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"}, - {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"}, - {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"}, - {file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"}, - {file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"}, - {file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"}, - {file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"}, - {file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"}, - {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"}, - {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"}, - {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"}, - {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"}, - {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"}, - {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"}, - {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"}, - {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"}, - {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"}, - {file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"}, - {file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"}, - {file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"}, - {file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"}, - {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"}, - {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"}, - {file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"}, - {file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"}, - {file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"}, - {file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"}, - {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"}, - {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"}, - {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"}, - {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"}, - {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"}, - {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"}, - {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"}, - {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"}, - {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"}, - {file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"}, - {file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"}, - {file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"}, - {file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"}, - {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"}, - {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"}, - {file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"}, - {file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"}, - {file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"}, - {file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"}, - {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"}, - {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"}, - {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"}, - {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"}, - {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"}, - {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"}, - {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"}, - {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"}, - {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"}, - {file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"}, - {file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"}, - {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"}, - {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"}, - {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"}, - {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"}, - {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"}, - {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"}, - {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"}, - {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"}, - {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"}, - {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"}, - {file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"}, +files = [ + {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, + {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"}, + {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"}, + {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"}, + {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"}, + {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"}, + {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"}, + {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"}, + {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"}, + {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"}, + {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"}, + {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"}, + {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"}, + {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"}, + {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"}, + {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"}, + {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"}, ] [package.dependencies] @@ -1684,7 +1706,6 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev", "docs"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -1699,7 +1720,6 @@ version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, @@ -1723,7 +1743,6 @@ version = "1.1.1" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pytest_asyncio-1.1.1-py3-none-any.whl", hash = "sha256:726339d30fcfde24691f589445b9b67d058b311ac632b1d704e97f20f1d878da"}, {file = "pytest_asyncio-1.1.1.tar.gz", hash = "sha256:b72d215c38e2c91dbb32f275e0b5be69602d7869910e109360e375129960a649"}, @@ -1743,7 +1762,6 @@ version = "1.1.3" description = "pytest-httpserver is a httpserver for pytest" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pytest_httpserver-1.1.3-py3-none-any.whl", hash = "sha256:5f84757810233e19e2bb5287f3826a71c97a3740abe3a363af9155c0f82fdbb9"}, {file = "pytest_httpserver-1.1.3.tar.gz", hash = "sha256:af819d6b533f84b4680b9416a5b3f67f1df3701f1da54924afd4d6e4ba5917ec"}, @@ -1758,7 +1776,6 @@ version = "2.4.0" description = "pytest plugin to abort hanging tests" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2"}, {file = "pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a"}, @@ -1773,7 +1790,6 @@ version = "3.8.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, @@ -1794,7 +1810,6 @@ version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, @@ -1877,7 +1892,6 @@ version = "0.37.0" description = "JSON Referencing + Python" optional = false python-versions = ">=3.10" -groups = ["dev"] files = [ {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, @@ -1890,127 +1904,126 @@ typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} [[package]] name = "regex" -version = "2025.9.18" +version = "2025.11.3" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "regex-2025.9.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:12296202480c201c98a84aecc4d210592b2f55e200a1d193235c4db92b9f6788"}, - {file = "regex-2025.9.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:220381f1464a581f2ea988f2220cf2a67927adcef107d47d6897ba5a2f6d51a4"}, - {file = "regex-2025.9.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87f681bfca84ebd265278b5daa1dcb57f4db315da3b5d044add7c30c10442e61"}, - {file = "regex-2025.9.18-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34d674cbba70c9398074c8a1fcc1a79739d65d1105de2a3c695e2b05ea728251"}, - {file = "regex-2025.9.18-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:385c9b769655cb65ea40b6eea6ff763cbb6d69b3ffef0b0db8208e1833d4e746"}, - {file = "regex-2025.9.18-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8900b3208e022570ae34328712bef6696de0804c122933414014bae791437ab2"}, - {file = "regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c204e93bf32cd7a77151d44b05eb36f469d0898e3fba141c026a26b79d9914a0"}, - {file = "regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3acc471d1dd7e5ff82e6cacb3b286750decd949ecd4ae258696d04f019817ef8"}, - {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6479d5555122433728760e5f29edb4c2b79655a8deb681a141beb5c8a025baea"}, - {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:431bd2a8726b000eb6f12429c9b438a24062a535d06783a93d2bcbad3698f8a8"}, - {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0cc3521060162d02bd36927e20690129200e5ac9d2c6d32b70368870b122db25"}, - {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a021217b01be2d51632ce056d7a837d3fa37c543ede36e39d14063176a26ae29"}, - {file = "regex-2025.9.18-cp310-cp310-win32.whl", hash = "sha256:4a12a06c268a629cb67cc1d009b7bb0be43e289d00d5111f86a2efd3b1949444"}, - {file = "regex-2025.9.18-cp310-cp310-win_amd64.whl", hash = "sha256:47acd811589301298c49db2c56bde4f9308d6396da92daf99cba781fa74aa450"}, - {file = "regex-2025.9.18-cp310-cp310-win_arm64.whl", hash = "sha256:16bd2944e77522275e5ee36f867e19995bcaa533dcb516753a26726ac7285442"}, - {file = "regex-2025.9.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:51076980cd08cd13c88eb7365427ae27f0d94e7cebe9ceb2bb9ffdae8fc4d82a"}, - {file = "regex-2025.9.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:828446870bd7dee4e0cbeed767f07961aa07f0ea3129f38b3ccecebc9742e0b8"}, - {file = "regex-2025.9.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c28821d5637866479ec4cc23b8c990f5bc6dd24e5e4384ba4a11d38a526e1414"}, - {file = "regex-2025.9.18-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:726177ade8e481db669e76bf99de0b278783be8acd11cef71165327abd1f170a"}, - {file = "regex-2025.9.18-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5cca697da89b9f8ea44115ce3130f6c54c22f541943ac8e9900461edc2b8bd4"}, - {file = "regex-2025.9.18-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dfbde38f38004703c35666a1e1c088b778e35d55348da2b7b278914491698d6a"}, - {file = "regex-2025.9.18-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2f422214a03fab16bfa495cfec72bee4aaa5731843b771860a471282f1bf74f"}, - {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a295916890f4df0902e4286bc7223ee7f9e925daa6dcdec4192364255b70561a"}, - {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5db95ff632dbabc8c38c4e82bf545ab78d902e81160e6e455598014f0abe66b9"}, - {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb967eb441b0f15ae610b7069bdb760b929f267efbf522e814bbbfffdf125ce2"}, - {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f04d2f20da4053d96c08f7fde6e1419b7ec9dbcee89c96e3d731fca77f411b95"}, - {file = "regex-2025.9.18-cp311-cp311-win32.whl", hash = "sha256:895197241fccf18c0cea7550c80e75f185b8bd55b6924fcae269a1a92c614a07"}, - {file = "regex-2025.9.18-cp311-cp311-win_amd64.whl", hash = "sha256:7e2b414deae99166e22c005e154a5513ac31493db178d8aec92b3269c9cce8c9"}, - {file = "regex-2025.9.18-cp311-cp311-win_arm64.whl", hash = "sha256:fb137ec7c5c54f34a25ff9b31f6b7b0c2757be80176435bf367111e3f71d72df"}, - {file = "regex-2025.9.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:436e1b31d7efd4dcd52091d076482031c611dde58bf9c46ca6d0a26e33053a7e"}, - {file = "regex-2025.9.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c190af81e5576b9c5fdc708f781a52ff20f8b96386c6e2e0557a78402b029f4a"}, - {file = "regex-2025.9.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e4121f1ce2b2b5eec4b397cc1b277686e577e658d8f5870b7eb2d726bd2300ab"}, - {file = "regex-2025.9.18-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:300e25dbbf8299d87205e821a201057f2ef9aa3deb29caa01cd2cac669e508d5"}, - {file = "regex-2025.9.18-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b47fcf9f5316c0bdaf449e879407e1b9937a23c3b369135ca94ebc8d74b1742"}, - {file = "regex-2025.9.18-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:57a161bd3acaa4b513220b49949b07e252165e6b6dc910ee7617a37ff4f5b425"}, - {file = "regex-2025.9.18-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f130c3a7845ba42de42f380fff3c8aebe89a810747d91bcf56d40a069f15352"}, - {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f96fa342b6f54dcba928dd452e8d8cb9f0d63e711d1721cd765bb9f73bb048d"}, - {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f0d676522d68c207828dcd01fb6f214f63f238c283d9f01d85fc664c7c85b56"}, - {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40532bff8a1a0621e7903ae57fce88feb2e8a9a9116d341701302c9302aef06e"}, - {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:039f11b618ce8d71a1c364fdee37da1012f5a3e79b1b2819a9f389cd82fd6282"}, - {file = "regex-2025.9.18-cp312-cp312-win32.whl", hash = "sha256:e1dd06f981eb226edf87c55d523131ade7285137fbde837c34dc9d1bf309f459"}, - {file = "regex-2025.9.18-cp312-cp312-win_amd64.whl", hash = "sha256:3d86b5247bf25fa3715e385aa9ff272c307e0636ce0c9595f64568b41f0a9c77"}, - {file = "regex-2025.9.18-cp312-cp312-win_arm64.whl", hash = "sha256:032720248cbeeae6444c269b78cb15664458b7bb9ed02401d3da59fe4d68c3a5"}, - {file = "regex-2025.9.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a40f929cd907c7e8ac7566ac76225a77701a6221bca937bdb70d56cb61f57b2"}, - {file = "regex-2025.9.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c90471671c2cdf914e58b6af62420ea9ecd06d1554d7474d50133ff26ae88feb"}, - {file = "regex-2025.9.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a351aff9e07a2dabb5022ead6380cff17a4f10e4feb15f9100ee56c4d6d06af"}, - {file = "regex-2025.9.18-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc4b8e9d16e20ddfe16430c23468a8707ccad3365b06d4536142e71823f3ca29"}, - {file = "regex-2025.9.18-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b8cdbddf2db1c5e80338ba2daa3cfa3dec73a46fff2a7dda087c8efbf12d62f"}, - {file = "regex-2025.9.18-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a276937d9d75085b2c91fb48244349c6954f05ee97bba0963ce24a9d915b8b68"}, - {file = "regex-2025.9.18-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92a8e375ccdc1256401c90e9dc02b8642894443d549ff5e25e36d7cf8a80c783"}, - {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0dc6893b1f502d73037cf807a321cdc9be29ef3d6219f7970f842475873712ac"}, - {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a61e85bfc63d232ac14b015af1261f826260c8deb19401c0597dbb87a864361e"}, - {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ef86a9ebc53f379d921fb9a7e42b92059ad3ee800fcd9e0fe6181090e9f6c23"}, - {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d3bc882119764ba3a119fbf2bd4f1b47bc56c1da5d42df4ed54ae1e8e66fdf8f"}, - {file = "regex-2025.9.18-cp313-cp313-win32.whl", hash = "sha256:3810a65675845c3bdfa58c3c7d88624356dd6ee2fc186628295e0969005f928d"}, - {file = "regex-2025.9.18-cp313-cp313-win_amd64.whl", hash = "sha256:16eaf74b3c4180ede88f620f299e474913ab6924d5c4b89b3833bc2345d83b3d"}, - {file = "regex-2025.9.18-cp313-cp313-win_arm64.whl", hash = "sha256:4dc98ba7dd66bd1261927a9f49bd5ee2bcb3660f7962f1ec02617280fc00f5eb"}, - {file = "regex-2025.9.18-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:fe5d50572bc885a0a799410a717c42b1a6b50e2f45872e2b40f4f288f9bce8a2"}, - {file = "regex-2025.9.18-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b9d9a2d6cda6621551ca8cf7a06f103adf72831153f3c0d982386110870c4d3"}, - {file = "regex-2025.9.18-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:13202e4c4ac0ef9a317fff817674b293c8f7e8c68d3190377d8d8b749f566e12"}, - {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:874ff523b0fecffb090f80ae53dc93538f8db954c8bb5505f05b7787ab3402a0"}, - {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d13ab0490128f2bb45d596f754148cd750411afc97e813e4b3a61cf278a23bb6"}, - {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:05440bc172bc4b4b37fb9667e796597419404dbba62e171e1f826d7d2a9ebcef"}, - {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5514b8e4031fdfaa3d27e92c75719cbe7f379e28cacd939807289bce76d0e35a"}, - {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:65d3c38c39efce73e0d9dc019697b39903ba25b1ad45ebbd730d2cf32741f40d"}, - {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ae77e447ebc144d5a26d50055c6ddba1d6ad4a865a560ec7200b8b06bc529368"}, - {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e3ef8cf53dc8df49d7e28a356cf824e3623764e9833348b655cfed4524ab8a90"}, - {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9feb29817df349c976da9a0debf775c5c33fc1c8ad7b9f025825da99374770b7"}, - {file = "regex-2025.9.18-cp313-cp313t-win32.whl", hash = "sha256:168be0d2f9b9d13076940b1ed774f98595b4e3c7fc54584bba81b3cc4181742e"}, - {file = "regex-2025.9.18-cp313-cp313t-win_amd64.whl", hash = "sha256:d59ecf3bb549e491c8104fea7313f3563c7b048e01287db0a90485734a70a730"}, - {file = "regex-2025.9.18-cp313-cp313t-win_arm64.whl", hash = "sha256:dbef80defe9fb21310948a2595420b36c6d641d9bea4c991175829b2cc4bc06a"}, - {file = "regex-2025.9.18-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c6db75b51acf277997f3adcd0ad89045d856190d13359f15ab5dda21581d9129"}, - {file = "regex-2025.9.18-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8f9698b6f6895d6db810e0bda5364f9ceb9e5b11328700a90cae573574f61eea"}, - {file = "regex-2025.9.18-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29cd86aa7cb13a37d0f0d7c21d8d949fe402ffa0ea697e635afedd97ab4b69f1"}, - {file = "regex-2025.9.18-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c9f285a071ee55cd9583ba24dde006e53e17780bb309baa8e4289cd472bcc47"}, - {file = "regex-2025.9.18-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5adf266f730431e3be9021d3e5b8d5ee65e563fec2883ea8093944d21863b379"}, - {file = "regex-2025.9.18-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1137cabc0f38807de79e28d3f6e3e3f2cc8cfb26bead754d02e6d1de5f679203"}, - {file = "regex-2025.9.18-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cc9e5525cada99699ca9223cce2d52e88c52a3d2a0e842bd53de5497c604164"}, - {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bbb9246568f72dce29bcd433517c2be22c7791784b223a810225af3b50d1aafb"}, - {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6a52219a93dd3d92c675383efff6ae18c982e2d7651c792b1e6d121055808743"}, - {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:ae9b3840c5bd456780e3ddf2f737ab55a79b790f6409182012718a35c6d43282"}, - {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d488c236ac497c46a5ac2005a952c1a0e22a07be9f10c3e735bc7d1209a34773"}, - {file = "regex-2025.9.18-cp314-cp314-win32.whl", hash = "sha256:0c3506682ea19beefe627a38872d8da65cc01ffa25ed3f2e422dffa1474f0788"}, - {file = "regex-2025.9.18-cp314-cp314-win_amd64.whl", hash = "sha256:57929d0f92bebb2d1a83af372cd0ffba2263f13f376e19b1e4fa32aec4efddc3"}, - {file = "regex-2025.9.18-cp314-cp314-win_arm64.whl", hash = "sha256:6a4b44df31d34fa51aa5c995d3aa3c999cec4d69b9bd414a8be51984d859f06d"}, - {file = "regex-2025.9.18-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b176326bcd544b5e9b17d6943f807697c0cb7351f6cfb45bf5637c95ff7e6306"}, - {file = "regex-2025.9.18-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0ffd9e230b826b15b369391bec167baed57c7ce39efc35835448618860995946"}, - {file = "regex-2025.9.18-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec46332c41add73f2b57e2f5b642f991f6b15e50e9f86285e08ffe3a512ac39f"}, - {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b80fa342ed1ea095168a3f116637bd1030d39c9ff38dc04e54ef7c521e01fc95"}, - {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4d97071c0ba40f0cf2a93ed76e660654c399a0a04ab7d85472239460f3da84b"}, - {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0ac936537ad87cef9e0e66c5144484206c1354224ee811ab1519a32373e411f3"}, - {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dec57f96d4def58c422d212d414efe28218d58537b5445cf0c33afb1b4768571"}, - {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48317233294648bf7cd068857f248e3a57222259a5304d32c7552e2284a1b2ad"}, - {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:274687e62ea3cf54846a9b25fc48a04459de50af30a7bd0b61a9e38015983494"}, - {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a78722c86a3e7e6aadf9579e3b0ad78d955f2d1f1a8ca4f67d7ca258e8719d4b"}, - {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:06104cd203cdef3ade989a1c45b6215bf42f8b9dd705ecc220c173233f7cba41"}, - {file = "regex-2025.9.18-cp314-cp314t-win32.whl", hash = "sha256:2e1eddc06eeaffd249c0adb6fafc19e2118e6308c60df9db27919e96b5656096"}, - {file = "regex-2025.9.18-cp314-cp314t-win_amd64.whl", hash = "sha256:8620d247fb8c0683ade51217b459cb4a1081c0405a3072235ba43a40d355c09a"}, - {file = "regex-2025.9.18-cp314-cp314t-win_arm64.whl", hash = "sha256:b7531a8ef61de2c647cdf68b3229b071e46ec326b3138b2180acb4275f470b01"}, - {file = "regex-2025.9.18-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3dbcfcaa18e9480669030d07371713c10b4f1a41f791ffa5cb1a99f24e777f40"}, - {file = "regex-2025.9.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1e85f73ef7095f0380208269055ae20524bfde3f27c5384126ddccf20382a638"}, - {file = "regex-2025.9.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9098e29b3ea4ffffeade423f6779665e2a4f8db64e699c0ed737ef0db6ba7b12"}, - {file = "regex-2025.9.18-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90b6b7a2d0f45b7ecaaee1aec6b362184d6596ba2092dd583ffba1b78dd0231c"}, - {file = "regex-2025.9.18-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c81b892af4a38286101502eae7aec69f7cd749a893d9987a92776954f3943408"}, - {file = "regex-2025.9.18-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3b524d010973f2e1929aeb635418d468d869a5f77b52084d9f74c272189c251d"}, - {file = "regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b498437c026a3d5d0be0020023ff76d70ae4d77118e92f6f26c9d0423452446"}, - {file = "regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0716e4d6e58853d83f6563f3cf25c281ff46cf7107e5f11879e32cb0b59797d9"}, - {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:065b6956749379d41db2625f880b637d4acc14c0a4de0d25d609a62850e96d36"}, - {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d4a691494439287c08ddb9b5793da605ee80299dd31e95fa3f323fac3c33d9d4"}, - {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ef8d10cc0989565bcbe45fb4439f044594d5c2b8919d3d229ea2c4238f1d55b0"}, - {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4baeb1b16735ac969a7eeecc216f1f8b7caf60431f38a2671ae601f716a32d25"}, - {file = "regex-2025.9.18-cp39-cp39-win32.whl", hash = "sha256:8e5f41ad24a1e0b5dfcf4c4e5d9f5bd54c895feb5708dd0c1d0d35693b24d478"}, - {file = "regex-2025.9.18-cp39-cp39-win_amd64.whl", hash = "sha256:50e8290707f2fb8e314ab3831e594da71e062f1d623b05266f8cfe4db4949afd"}, - {file = "regex-2025.9.18-cp39-cp39-win_arm64.whl", hash = "sha256:039a9d7195fd88c943d7c777d4941e8ef736731947becce773c31a1009cb3c35"}, - {file = "regex-2025.9.18.tar.gz", hash = "sha256:c5ba23274c61c6fef447ba6a39333297d0c247f53059dba0bca415cac511edc4"}, +files = [ + {file = "regex-2025.11.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2b441a4ae2c8049106e8b39973bfbddfb25a179dda2bdb99b0eeb60c40a6a3af"}, + {file = "regex-2025.11.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2fa2eed3f76677777345d2f81ee89f5de2f5745910e805f7af7386a920fa7313"}, + {file = "regex-2025.11.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8b4a27eebd684319bdf473d39f1d79eed36bf2cd34bd4465cdb4618d82b3d56"}, + {file = "regex-2025.11.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cf77eac15bd264986c4a2c63353212c095b40f3affb2bc6b4ef80c4776c1a28"}, + {file = "regex-2025.11.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b7f9ee819f94c6abfa56ec7b1dbab586f41ebbdc0a57e6524bd5e7f487a878c7"}, + {file = "regex-2025.11.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:838441333bc90b829406d4a03cb4b8bf7656231b84358628b0406d803931ef32"}, + {file = "regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe6d3f0c9e3b7e8c0c694b24d25e677776f5ca26dce46fd6b0489f9c8339391"}, + {file = "regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2ab815eb8a96379a27c3b6157fcb127c8f59c36f043c1678110cea492868f1d5"}, + {file = "regex-2025.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:728a9d2d173a65b62bdc380b7932dd8e74ed4295279a8fe1021204ce210803e7"}, + {file = "regex-2025.11.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:509dc827f89c15c66a0c216331260d777dd6c81e9a4e4f830e662b0bb296c313"}, + {file = "regex-2025.11.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:849202cd789e5f3cf5dcc7822c34b502181b4824a65ff20ce82da5524e45e8e9"}, + {file = "regex-2025.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b6f78f98741dcc89607c16b1e9426ee46ce4bf31ac5e6b0d40e81c89f3481ea5"}, + {file = "regex-2025.11.3-cp310-cp310-win32.whl", hash = "sha256:149eb0bba95231fb4f6d37c8f760ec9fa6fabf65bab555e128dde5f2475193ec"}, + {file = "regex-2025.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:ee3a83ce492074c35a74cc76cf8235d49e77b757193a5365ff86e3f2f93db9fd"}, + {file = "regex-2025.11.3-cp310-cp310-win_arm64.whl", hash = "sha256:38af559ad934a7b35147716655d4a2f79fcef2d695ddfe06a06ba40ae631fa7e"}, + {file = "regex-2025.11.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eadade04221641516fa25139273505a1c19f9bf97589a05bc4cfcd8b4a618031"}, + {file = "regex-2025.11.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feff9e54ec0dd3833d659257f5c3f5322a12eee58ffa360984b716f8b92983f4"}, + {file = "regex-2025.11.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3b30bc921d50365775c09a7ed446359e5c0179e9e2512beec4a60cbcef6ddd50"}, + {file = "regex-2025.11.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f99be08cfead2020c7ca6e396c13543baea32343b7a9a5780c462e323bd8872f"}, + {file = "regex-2025.11.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6dd329a1b61c0ee95ba95385fb0c07ea0d3fe1a21e1349fa2bec272636217118"}, + {file = "regex-2025.11.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c5238d32f3c5269d9e87be0cf096437b7622b6920f5eac4fd202468aaeb34d2"}, + {file = "regex-2025.11.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10483eefbfb0adb18ee9474498c9a32fcf4e594fbca0543bb94c48bac6183e2e"}, + {file = "regex-2025.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78c2d02bb6e1da0720eedc0bad578049cad3f71050ef8cd065ecc87691bed2b0"}, + {file = "regex-2025.11.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b49cd2aad93a1790ce9cffb18964f6d3a4b0b3dbdbd5de094b65296fce6e58"}, + {file = "regex-2025.11.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:885b26aa3ee56433b630502dc3d36ba78d186a00cc535d3806e6bfd9ed3c70ab"}, + {file = "regex-2025.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ddd76a9f58e6a00f8772e72cff8ebcff78e022be95edf018766707c730593e1e"}, + {file = "regex-2025.11.3-cp311-cp311-win32.whl", hash = "sha256:3e816cc9aac1cd3cc9a4ec4d860f06d40f994b5c7b4d03b93345f44e08cc68bf"}, + {file = "regex-2025.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:087511f5c8b7dfbe3a03f5d5ad0c2a33861b1fc387f21f6f60825a44865a385a"}, + {file = "regex-2025.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:1ff0d190c7f68ae7769cd0313fe45820ba07ffebfddfaa89cc1eb70827ba0ddc"}, + {file = "regex-2025.11.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bc8ab71e2e31b16e40868a40a69007bc305e1109bd4658eb6cad007e0bf67c41"}, + {file = "regex-2025.11.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:22b29dda7e1f7062a52359fca6e58e548e28c6686f205e780b02ad8ef710de36"}, + {file = "regex-2025.11.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a91e4a29938bc1a082cc28fdea44be420bf2bebe2665343029723892eb073e1"}, + {file = "regex-2025.11.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b884f4226602ad40c5d55f52bf91a9df30f513864e0054bad40c0e9cf1afb7"}, + {file = "regex-2025.11.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e0b11b2b2433d1c39c7c7a30e3f3d0aeeea44c2a8d0bae28f6b95f639927a69"}, + {file = "regex-2025.11.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87eb52a81ef58c7ba4d45c3ca74e12aa4b4e77816f72ca25258a85b3ea96cb48"}, + {file = "regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a12ab1f5c29b4e93db518f5e3872116b7e9b1646c9f9f426f777b50d44a09e8c"}, + {file = "regex-2025.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7521684c8c7c4f6e88e35ec89680ee1aa8358d3f09d27dfbdf62c446f5d4c695"}, + {file = "regex-2025.11.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7fe6e5440584e94cc4b3f5f4d98a25e29ca12dccf8873679a635638349831b98"}, + {file = "regex-2025.11.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8e026094aa12b43f4fd74576714e987803a315c76edb6b098b9809db5de58f74"}, + {file = "regex-2025.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:435bbad13e57eb5606a68443af62bed3556de2f46deb9f7d4237bc2f1c9fb3a0"}, + {file = "regex-2025.11.3-cp312-cp312-win32.whl", hash = "sha256:3839967cf4dc4b985e1570fd8d91078f0c519f30491c60f9ac42a8db039be204"}, + {file = "regex-2025.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:e721d1b46e25c481dc5ded6f4b3f66c897c58d2e8cfdf77bbced84339108b0b9"}, + {file = "regex-2025.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:64350685ff08b1d3a6fff33f45a9ca183dc1d58bbfe4981604e70ec9801bbc26"}, + {file = "regex-2025.11.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1e448051717a334891f2b9a620fe36776ebf3dd8ec46a0b877c8ae69575feb4"}, + {file = "regex-2025.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b5aca4d5dfd7fbfbfbdaf44850fcc7709a01146a797536a8f84952e940cca76"}, + {file = "regex-2025.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:04d2765516395cf7dda331a244a3282c0f5ae96075f728629287dfa6f76ba70a"}, + {file = "regex-2025.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d9903ca42bfeec4cebedba8022a7c97ad2aab22e09573ce9976ba01b65e4361"}, + {file = "regex-2025.11.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:639431bdc89d6429f6721625e8129413980ccd62e9d3f496be618a41d205f160"}, + {file = "regex-2025.11.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f117efad42068f9715677c8523ed2be1518116d1c49b1dd17987716695181efe"}, + {file = "regex-2025.11.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4aecb6f461316adf9f1f0f6a4a1a3d79e045f9b71ec76055a791affa3b285850"}, + {file = "regex-2025.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b3a5f320136873cc5561098dfab677eea139521cb9a9e8db98b7e64aef44cbc"}, + {file = "regex-2025.11.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:75fa6f0056e7efb1f42a1c34e58be24072cb9e61a601340cc1196ae92326a4f9"}, + {file = "regex-2025.11.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dbe6095001465294f13f1adcd3311e50dd84e5a71525f20a10bd16689c61ce0b"}, + {file = "regex-2025.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:454d9b4ae7881afbc25015b8627c16d88a597479b9dea82b8c6e7e2e07240dc7"}, + {file = "regex-2025.11.3-cp313-cp313-win32.whl", hash = "sha256:28ba4d69171fc6e9896337d4fc63a43660002b7da53fc15ac992abcf3410917c"}, + {file = "regex-2025.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:bac4200befe50c670c405dc33af26dad5a3b6b255dd6c000d92fe4629f9ed6a5"}, + {file = "regex-2025.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:2292cd5a90dab247f9abe892ac584cb24f0f54680c73fcb4a7493c66c2bf2467"}, + {file = "regex-2025.11.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1eb1ebf6822b756c723e09f5186473d93236c06c579d2cc0671a722d2ab14281"}, + {file = "regex-2025.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1e00ec2970aab10dc5db34af535f21fcf32b4a31d99e34963419636e2f85ae39"}, + {file = "regex-2025.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a4cb042b615245d5ff9b3794f56be4138b5adc35a4166014d31d1814744148c7"}, + {file = "regex-2025.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44f264d4bf02f3176467d90b294d59bf1db9fe53c141ff772f27a8b456b2a9ed"}, + {file = "regex-2025.11.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7be0277469bf3bd7a34a9c57c1b6a724532a0d235cd0dc4e7f4316f982c28b19"}, + {file = "regex-2025.11.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d31e08426ff4b5b650f68839f5af51a92a5b51abd8554a60c2fbc7c71f25d0b"}, + {file = "regex-2025.11.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e43586ce5bd28f9f285a6e729466841368c4a0353f6fd08d4ce4630843d3648a"}, + {file = "regex-2025.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f9397d561a4c16829d4e6ff75202c1c08b68a3bdbfe29dbfcdb31c9830907c6"}, + {file = "regex-2025.11.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:dd16e78eb18ffdb25ee33a0682d17912e8cc8a770e885aeee95020046128f1ce"}, + {file = "regex-2025.11.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:ffcca5b9efe948ba0661e9df0fa50d2bc4b097c70b9810212d6b62f05d83b2dd"}, + {file = "regex-2025.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c56b4d162ca2b43318ac671c65bd4d563e841a694ac70e1a976ac38fcf4ca1d2"}, + {file = "regex-2025.11.3-cp313-cp313t-win32.whl", hash = "sha256:9ddc42e68114e161e51e272f667d640f97e84a2b9ef14b7477c53aac20c2d59a"}, + {file = "regex-2025.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7a7c7fdf755032ffdd72c77e3d8096bdcb0eb92e89e17571a196f03d88b11b3c"}, + {file = "regex-2025.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:df9eb838c44f570283712e7cff14c16329a9f0fb19ca492d21d4b7528ee6821e"}, + {file = "regex-2025.11.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9697a52e57576c83139d7c6f213d64485d3df5bf84807c35fa409e6c970801c6"}, + {file = "regex-2025.11.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e18bc3f73bd41243c9b38a6d9f2366cd0e0137a9aebe2d8ff76c5b67d4c0a3f4"}, + {file = "regex-2025.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:61a08bcb0ec14ff4e0ed2044aad948d0659604f824cbd50b55e30b0ec6f09c73"}, + {file = "regex-2025.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9c30003b9347c24bcc210958c5d167b9e4f9be786cb380a7d32f14f9b84674f"}, + {file = "regex-2025.11.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e1e592789704459900728d88d41a46fe3969b82ab62945560a31732ffc19a6d"}, + {file = "regex-2025.11.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6538241f45eb5a25aa575dbba1069ad786f68a4f2773a29a2bd3dd1f9de787be"}, + {file = "regex-2025.11.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce22519c989bb72a7e6b36a199384c53db7722fe669ba891da75907fe3587db"}, + {file = "regex-2025.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:66d559b21d3640203ab9075797a55165d79017520685fb407b9234d72ab63c62"}, + {file = "regex-2025.11.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:669dcfb2e38f9e8c69507bace46f4889e3abbfd9b0c29719202883c0a603598f"}, + {file = "regex-2025.11.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:32f74f35ff0f25a5021373ac61442edcb150731fbaa28286bbc8bb1582c89d02"}, + {file = "regex-2025.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e6c7a21dffba883234baefe91bc3388e629779582038f75d2a5be918e250f0ed"}, + {file = "regex-2025.11.3-cp314-cp314-win32.whl", hash = "sha256:795ea137b1d809eb6836b43748b12634291c0ed55ad50a7d72d21edf1cd565c4"}, + {file = "regex-2025.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f95fbaa0ee1610ec0fc6b26668e9917a582ba80c52cc6d9ada15e30aa9ab9ad"}, + {file = "regex-2025.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:dfec44d532be4c07088c3de2876130ff0fbeeacaa89a137decbbb5f665855a0f"}, + {file = "regex-2025.11.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ba0d8a5d7f04f73ee7d01d974d47c5834f8a1b0224390e4fe7c12a3a92a78ecc"}, + {file = "regex-2025.11.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:442d86cf1cfe4faabf97db7d901ef58347efd004934da045c745e7b5bd57ac49"}, + {file = "regex-2025.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fd0a5e563c756de210bb964789b5abe4f114dacae9104a47e1a649b910361536"}, + {file = "regex-2025.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf3490bcbb985a1ae97b2ce9ad1c0f06a852d5b19dde9b07bdf25bf224248c95"}, + {file = "regex-2025.11.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3809988f0a8b8c9dcc0f92478d6501fac7200b9ec56aecf0ec21f4a2ec4b6009"}, + {file = "regex-2025.11.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f4ff94e58e84aedb9c9fce66d4ef9f27a190285b451420f297c9a09f2b9abee9"}, + {file = "regex-2025.11.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eb542fd347ce61e1321b0a6b945d5701528dca0cd9759c2e3bb8bd57e47964d"}, + {file = "regex-2025.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2d5919075a1f2e413c00b056ea0c2f065b3f5fe83c3d07d325ab92dce51d6"}, + {file = "regex-2025.11.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3f8bf11a4827cc7ce5a53d4ef6cddd5ad25595d3c1435ef08f76825851343154"}, + {file = "regex-2025.11.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:22c12d837298651e5550ac1d964e4ff57c3f56965fc1812c90c9fb2028eaf267"}, + {file = "regex-2025.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ba394a3dda9ad41c7c780f60f6e4a70988741415ae96f6d1bf6c239cf01379"}, + {file = "regex-2025.11.3-cp314-cp314t-win32.whl", hash = "sha256:4bf146dca15cdd53224a1bf46d628bd7590e4a07fbb69e720d561aea43a32b38"}, + {file = "regex-2025.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:adad1a1bcf1c9e76346e091d22d23ac54ef28e1365117d99521631078dfec9de"}, + {file = "regex-2025.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c54f768482cef41e219720013cd05933b6f971d9562544d691c68699bf2b6801"}, + {file = "regex-2025.11.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:81519e25707fc076978c6143b81ea3dc853f176895af05bf7ec51effe818aeec"}, + {file = "regex-2025.11.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3bf28b1873a8af8bbb58c26cc56ea6e534d80053b41fb511a35795b6de507e6a"}, + {file = "regex-2025.11.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:856a25c73b697f2ce2a24e7968285579e62577a048526161a2c0f53090bea9f9"}, + {file = "regex-2025.11.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a3d571bd95fade53c86c0517f859477ff3a93c3fde10c9e669086f038e0f207"}, + {file = "regex-2025.11.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:732aea6de26051af97b94bc98ed86448821f839d058e5d259c72bf6d73ad0fc0"}, + {file = "regex-2025.11.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:51c1c1847128238f54930edb8805b660305dca164645a9fd29243f5610beea34"}, + {file = "regex-2025.11.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22dd622a402aad4558277305350699b2be14bc59f64d64ae1d928ce7d072dced"}, + {file = "regex-2025.11.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f3b5a391c7597ffa96b41bd5cbd2ed0305f515fcbb367dfa72735679d5502364"}, + {file = "regex-2025.11.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:cc4076a5b4f36d849fd709284b4a3b112326652f3b0466f04002a6c15a0c96c1"}, + {file = "regex-2025.11.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a295ca2bba5c1c885826ce3125fa0b9f702a1be547d821c01d65f199e10c01e2"}, + {file = "regex-2025.11.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b4774ff32f18e0504bfc4e59a3e71e18d83bc1e171a3c8ed75013958a03b2f14"}, + {file = "regex-2025.11.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e7d1cdfa88ef33a2ae6aa0d707f9255eb286ffbd90045f1088246833223aee"}, + {file = "regex-2025.11.3-cp39-cp39-win32.whl", hash = "sha256:74d04244852ff73b32eeede4f76f51c5bcf44bc3c207bc3e6cf1c5c45b890708"}, + {file = "regex-2025.11.3-cp39-cp39-win_amd64.whl", hash = "sha256:7a50cd39f73faa34ec18d6720ee25ef10c4c1839514186fcda658a06c06057a2"}, + {file = "regex-2025.11.3-cp39-cp39-win_arm64.whl", hash = "sha256:43b4fb020e779ca81c1b5255015fe2b82816c76ec982354534ad9ec09ad7c9e3"}, + {file = "regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01"}, ] [[package]] @@ -2019,7 +2032,6 @@ version = "2.32.5" description = "Python HTTP for Humans." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, @@ -2041,7 +2053,6 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["dev"] files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -2052,167 +2063,126 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "rpds-py" -version = "0.27.1" +version = "0.30.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef"}, - {file = "rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10"}, - {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808"}, - {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8"}, - {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9"}, - {file = "rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4"}, - {file = "rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1"}, - {file = "rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881"}, - {file = "rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde"}, - {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21"}, - {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9"}, - {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948"}, - {file = "rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39"}, - {file = "rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15"}, - {file = "rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746"}, - {file = "rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90"}, - {file = "rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444"}, - {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a"}, - {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1"}, - {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998"}, - {file = "rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39"}, - {file = "rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594"}, - {file = "rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502"}, - {file = "rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b"}, - {file = "rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274"}, - {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd"}, - {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2"}, - {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002"}, - {file = "rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3"}, - {file = "rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83"}, - {file = "rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d"}, - {file = "rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228"}, - {file = "rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef"}, - {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081"}, - {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd"}, - {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7"}, - {file = "rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688"}, - {file = "rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797"}, - {file = "rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334"}, - {file = "rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60"}, - {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e"}, - {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212"}, - {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675"}, - {file = "rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3"}, - {file = "rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456"}, - {file = "rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3"}, - {file = "rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2"}, - {file = "rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb"}, - {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734"}, - {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb"}, - {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0"}, - {file = "rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a"}, - {file = "rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772"}, - {file = "rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527"}, - {file = "rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e"}, - {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786"}, - {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec"}, - {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b"}, - {file = "rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52"}, - {file = "rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859"}, - {file = "rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8"}, +python-versions = ">=3.10" +files = [ + {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"}, + {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"}, + {file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"}, + {file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"}, + {file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"}, + {file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"}, + {file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"}, + {file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"}, + {file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"}, ] [[package]] @@ -2221,7 +2191,6 @@ version = "0.12.12" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc"}, {file = "ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727"}, @@ -2250,7 +2219,6 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -2262,7 +2230,6 @@ version = "9.1.2" description = "Retry code until it succeeds" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"}, {file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"}, @@ -2278,7 +2245,6 @@ version = "0.12.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970"}, {file = "tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16"}, @@ -2352,8 +2318,6 @@ version = "2.3.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, @@ -2405,7 +2369,6 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -2427,7 +2390,6 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, @@ -2439,7 +2401,6 @@ version = "0.4.2" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, @@ -2450,32 +2411,61 @@ typing-extensions = ">=4.12.0" [[package]] name = "urllib3" -version = "2.6.0" +version = "2.6.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ - {file = "urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f"}, - {file = "urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1"}, + {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"}, + {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"}, ] [package.extras] -brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.2.0)", "brotlicffi (>=1.2.0.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] +zstd = ["backports-zstd (>=1.0.0)"] + +[[package]] +name = "uuid-utils" +version = "0.12.0" +description = "Drop-in replacement for Python UUID with bindings in Rust" +optional = false +python-versions = ">=3.9" +files = [ + {file = "uuid_utils-0.12.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3b9b30707659292f207b98f294b0e081f6d77e1fbc760ba5b41331a39045f514"}, + {file = "uuid_utils-0.12.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:add3d820c7ec14ed37317375bea30249699c5d08ff4ae4dbee9fc9bce3bfbf65"}, + {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8fce83ecb3b16af29c7809669056c4b6e7cc912cab8c6d07361645de12dd79"}, + {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec921769afcb905035d785582b0791d02304a7850fbd6ce924c1a8976380dfc6"}, + {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f3b060330f5899a92d5c723547dc6a95adef42433e9748f14c66859a7396664"}, + {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:908dfef7f0bfcf98d406e5dc570c25d2f2473e49b376de41792b6e96c1d5d291"}, + {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c6a24148926bd0ca63e8a2dabf4cc9dc329a62325b3ad6578ecd60fbf926506"}, + {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:64a91e632669f059ef605f1771d28490b1d310c26198e46f754e8846dddf12f4"}, + {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:93c082212470bb4603ca3975916c205a9d7ef1443c0acde8fbd1e0f5b36673c7"}, + {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:431b1fb7283ba974811b22abd365f2726f8f821ab33f0f715be389640e18d039"}, + {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd7838c40149100299fa37cbd8bab5ee382372e8e65a148002a37d380df7c8"}, + {file = "uuid_utils-0.12.0-cp39-abi3-win32.whl", hash = "sha256:487f17c0fee6cbc1d8b90fe811874174a9b1b5683bf2251549e302906a50fed3"}, + {file = "uuid_utils-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:9598e7c9da40357ae8fffc5d6938b1a7017f09a1acbcc95e14af8c65d48c655a"}, + {file = "uuid_utils-0.12.0-cp39-abi3-win_arm64.whl", hash = "sha256:c9bea7c5b2aa6f57937ebebeee4d4ef2baad10f86f1b97b58a3f6f34c14b4e84"}, + {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e2209d361f2996966ab7114f49919eb6aaeabc6041672abbbbf4fdbb8ec1acc0"}, + {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d9636bcdbd6cfcad2b549c352b669412d0d1eb09be72044a2f13e498974863cd"}, + {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cd8543a3419251fb78e703ce3b15fdfafe1b7c542cf40caf0775e01db7e7674"}, + {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e98db2d8977c052cb307ae1cb5cc37a21715e8d415dbc65863b039397495a013"}, + {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8f2bdf5e4ffeb259ef6d15edae92aed60a1d6f07cbfab465d836f6b12b48da8"}, + {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c3ec53c0cb15e1835870c139317cc5ec06e35aa22843e3ed7d9c74f23f23898"}, + {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84e5c0eba209356f7f389946a3a47b2cc2effd711b3fc7c7f155ad9f7d45e8a3"}, + {file = "uuid_utils-0.12.0.tar.gz", hash = "sha256:252bd3d311b5d6b7f5dfce7a5857e27bb4458f222586bb439463231e5a9cbd64"}, +] [[package]] name = "virtualenv" -version = "20.35.3" +version = "20.35.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ - {file = "virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a"}, - {file = "virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44"}, + {file = "virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b"}, + {file = "virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c"}, ] [package.dependencies] @@ -2486,7 +2476,7 @@ typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\"" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "werkzeug" @@ -2494,7 +2484,6 @@ version = "3.1.4" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905"}, {file = "werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e"}, @@ -2512,7 +2501,6 @@ version = "1.17.3" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, @@ -2603,7 +2591,6 @@ version = "3.6.0" description = "Python binding for xxHash" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71"}, {file = "xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d"}, @@ -2753,14 +2740,13 @@ version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -2773,7 +2759,6 @@ version = "0.25.0" description = "Zstandard bindings for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd"}, {file = "zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7"}, @@ -2877,9 +2862,9 @@ files = [ ] [package.extras] -cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] +cffi = ["cffi (>=1.17,<2.0)", "cffi (>=2.0.0b)"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "bb4ec20d58e29f5d71599357de616571eeae016818d5c246774f0c5bc01d3d0e" +content-hash = "5d2a27e5da32203bea9c89d43e7d1536be7da32fa2b504cf6de1a0749a4ac867" diff --git a/pyproject.toml b/pyproject.toml index 97866a2bc..6ae7c8aec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" [tool.poetry.dependencies] python = ">=3.10,<4.0" httpx = ">=0.15.4,<1.0" -pydantic = ">=1.10.7, <3.0" +pydantic = "^2" backoff = ">=1.10.0" openai = { version = ">=0.27.8", optional = true } wrapt = "^1.14" @@ -94,10 +94,5 @@ module = [ ignore_missing_imports = true [[tool.mypy.overrides]] -module = [ - "langfuse.api.resources.*", - "langfuse.api.core.*", - "langfuse.api.client" -] -ignore_errors = true - +module = "langfuse.api.*" +disable_error_code = ["redundant-cast", "no-untyped-def"] diff --git a/tests/test_datasets.py b/tests/test_datasets.py index c1b81868d..af19fd470 100644 --- a/tests/test_datasets.py +++ b/tests/test_datasets.py @@ -7,8 +7,7 @@ from langchain_openai import OpenAI from langfuse import Langfuse, observe -from langfuse.api.resources.commons.types.dataset_status import DatasetStatus -from langfuse.api.resources.commons.types.observation import Observation +from langfuse.api import DatasetStatus, Observation from langfuse.langchain import CallbackHandler from tests.utils import create_uuid, get_api @@ -220,7 +219,7 @@ def test_get_dataset_runs(): langfuse.flush() time.sleep(1) # Give API time to process - runs = langfuse.api.datasets.get_runs(dataset_name) + runs = langfuse.api.datasets.get_runs(dataset_name=dataset_name) assert len(runs.data) == 2 assert runs.data[0].name == run_name_2 diff --git a/tests/test_error_parsing.py b/tests/test_error_parsing.py index db53f3d4d..36a7decbf 100644 --- a/tests/test_error_parsing.py +++ b/tests/test_error_parsing.py @@ -5,14 +5,14 @@ generate_error_message_fern, ) from langfuse._utils.request import APIError, APIErrors -from langfuse.api.core import ApiError -from langfuse.api.resources.commons.errors import ( +from langfuse.api import ( AccessDeniedError, MethodNotAllowedError, NotFoundError, + ServiceUnavailableError, UnauthorizedError, ) -from langfuse.api.resources.health.errors import ServiceUnavailableError +from langfuse.api.core import ApiError def test_generate_error_message_api_error(): diff --git a/tests/test_json.py b/tests/test_json.py index 26b6919fa..1f7ef6ece 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -12,7 +12,7 @@ import langfuse from langfuse._utils.serializer import EventSerializer -from langfuse.api.resources.commons.types.observation_level import ObservationLevel +from langfuse.api import ObservationLevel class TestModel(BaseModel): diff --git a/tests/test_langchain.py b/tests/test_langchain.py index 14c25446f..d83a57ff2 100644 --- a/tests/test_langchain.py +++ b/tests/test_langchain.py @@ -14,7 +14,7 @@ from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import END, START, MessagesState, StateGraph from langgraph.prebuilt import ToolNode -from pydantic.v1 import BaseModel, Field +from pydantic import BaseModel, Field from langfuse._client.client import Langfuse from langfuse.langchain import CallbackHandler diff --git a/tests/test_prompt.py b/tests/test_prompt.py index bc3a5b7eb..5c263d462 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -10,8 +10,7 @@ PromptCache, PromptCacheItem, ) -from langfuse.api.resources.commons.errors.not_found_error import NotFoundError -from langfuse.api.resources.prompts import Prompt_Chat, Prompt_Text +from langfuse.api import NotFoundError, Prompt_Chat, Prompt_Text from langfuse.model import ChatPromptClient, TextPromptClient from tests.utils import create_uuid, get_api @@ -312,7 +311,7 @@ def test_compile_with_placeholders( variables, placeholders, expected_len, expected_contents ) -> None: """Test compile_with_placeholders with different variable/placeholder combinations.""" - from langfuse.api.resources.prompts import Prompt_Chat + from langfuse.api import Prompt_Chat from langfuse.model import ChatPromptClient mock_prompt = Prompt_Chat( @@ -682,7 +681,7 @@ def test_prompt_end_to_end(): @pytest.fixture def langfuse(): from langfuse._client.resource_manager import LangfuseResourceManager - + langfuse_instance = Langfuse() langfuse_instance.api = Mock() diff --git a/tests/test_prompt_compilation.py b/tests/test_prompt_compilation.py index 039556c5d..1b96a14dd 100644 --- a/tests/test_prompt_compilation.py +++ b/tests/test_prompt_compilation.py @@ -1,7 +1,7 @@ import pytest from langchain_core.prompts import ChatPromptTemplate, PromptTemplate -from langfuse.api.resources.prompts import ChatMessage, Prompt_Chat +from langfuse.api import ChatMessage, Prompt_Chat from langfuse.model import ( ChatPromptClient, Prompt_Text, @@ -735,7 +735,7 @@ def test_chat_prompt_with_json_variables(self): def test_chat_prompt_with_placeholders_langchain(self): """Test that chat prompts with placeholders work correctly with Langchain.""" - from langfuse.api.resources.prompts import Prompt_Chat + from langfuse.api import Prompt_Chat chat_messages = [ ChatMessage( @@ -804,7 +804,7 @@ def test_chat_prompt_with_placeholders_langchain(self): def test_get_langchain_prompt_with_unresolved_placeholders(self): """Test that unresolved placeholders become MessagesPlaceholder objects.""" - from langfuse.api.resources.prompts import Prompt_Chat + from langfuse.api import Prompt_Chat from langfuse.model import ChatPromptClient chat_messages = [ @@ -854,7 +854,7 @@ def test_get_langchain_prompt_with_unresolved_placeholders(self): def test_tool_calls_preservation_in_message_placeholder(): """Test that tool calls are preserved when compiling message placeholders.""" - from langfuse.api.resources.prompts import Prompt_Chat + from langfuse.api import Prompt_Chat chat_messages = [ {"role": "system", "content": "You are a helpful assistant."}, diff --git a/tests/test_propagate_attributes.py b/tests/test_propagate_attributes.py index 83c88e48a..40661ae39 100644 --- a/tests/test_propagate_attributes.py +++ b/tests/test_propagate_attributes.py @@ -7,6 +7,7 @@ import concurrent.futures import time +from datetime import datetime import pytest from opentelemetry.instrumentation.threading import ThreadingInstrumentor @@ -14,6 +15,8 @@ from langfuse import propagate_attributes from langfuse._client.attributes import LangfuseOtelSpanAttributes, _serialize from langfuse._client.constants import LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT +from langfuse._client.datasets import DatasetClient +from langfuse.api import Dataset, DatasetItem, DatasetStatus from tests.test_otel import TestOTelBase @@ -2437,16 +2440,10 @@ def test_experiment_attributes_propagate_with_dataset( self, langfuse_client, memory_exporter, monkeypatch ): """Test experiment attribute propagation with Langfuse dataset.""" - import time - from datetime import datetime - - from langfuse._client.attributes import _serialize - from langfuse._client.datasets import DatasetClient, DatasetItemClient - from langfuse.model import Dataset, DatasetItem, DatasetStatus # Mock the async API to create dataset run items async def mock_create_dataset_run_item(*args, **kwargs): - from langfuse.api.resources.dataset_run_items.types import DatasetRunItem + from langfuse.api import DatasetRunItem request = kwargs.get("request") return DatasetRunItem( @@ -2491,8 +2488,11 @@ async def mock_create_dataset_run_item(*args, **kwargs): ) # Create dataset client with items - dataset_item_client = DatasetItemClient(mock_dataset_item, langfuse_client) - dataset = DatasetClient(mock_dataset, [dataset_item_client]) + dataset = DatasetClient( + dataset=mock_dataset, + items=[mock_dataset_item], + langfuse_client=langfuse_client, + ) # Task with child spans def task_with_children(*, item, **kwargs): diff --git a/tests/utils.py b/tests/utils.py index b6aeeb185..7d6530b45 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,15 +1,9 @@ import base64 import os -import typing from time import sleep from uuid import uuid4 -try: - import pydantic.v1 as pydantic # type: ignore -except ImportError: - import pydantic # type: ignore - -from langfuse.api.client import FernLangfuse +from langfuse.api import LangfuseAPI def create_uuid(): @@ -19,69 +13,13 @@ def create_uuid(): def get_api(): sleep(2) - return FernLangfuse( + return LangfuseAPI( username=os.environ.get("LANGFUSE_PUBLIC_KEY"), password=os.environ.get("LANGFUSE_SECRET_KEY"), base_url=os.environ.get("LANGFUSE_BASE_URL"), ) -class LlmUsageWithCost(pydantic.BaseModel): - prompt_tokens: typing.Optional[int] = pydantic.Field( - alias="promptTokens", default=None - ) - completion_tokens: typing.Optional[int] = pydantic.Field( - alias="completionTokens", default=None - ) - total_tokens: typing.Optional[int] = pydantic.Field( - alias="totalTokens", default=None - ) - input_cost: typing.Optional[float] = pydantic.Field(alias="inputCost", default=None) - output_cost: typing.Optional[float] = pydantic.Field( - alias="outputCost", default=None - ) - total_cost: typing.Optional[float] = pydantic.Field(alias="totalCost", default=None) - - -class CompletionUsage(pydantic.BaseModel): - completion_tokens: int - """Number of tokens in the generated completion.""" - - prompt_tokens: int - """Number of tokens in the prompt.""" - - total_tokens: int - """Total number of tokens used in the request (prompt + completion).""" - - -class LlmUsage(pydantic.BaseModel): - prompt_tokens: typing.Optional[int] = pydantic.Field( - alias="promptTokens", default=None - ) - completion_tokens: typing.Optional[int] = pydantic.Field( - alias="completionTokens", default=None - ) - total_tokens: typing.Optional[int] = pydantic.Field( - alias="totalTokens", default=None - ) - - 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: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().dict(**kwargs_with_defaults) - - def encode_file_to_base64(image_path) -> str: with open(image_path, "rb") as file: return base64.b64encode(file.read()).decode("utf-8")